From 15935e9d5f461592b56f8558e618c0df75e460ea Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Sun, 22 Mar 2026 20:44:03 +0100 Subject: [PATCH] fix(auth): do not allow to register in invite mode (#9101) Signed-off-by: Ettore Di Giacinto --- core/http/auth/oauth.go | 16 ++++++++++++++++ core/http/react-ui/src/pages/Login.jsx | 25 ++++++++++++++++++------- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/core/http/auth/oauth.go b/core/http/auth/oauth.go index e4b37c4e9..adaca7790 100644 --- a/core/http/auth/oauth.go +++ b/core/http/auth/oauth.go @@ -213,6 +213,22 @@ func (m *OAuthManager) CallbackHandler(providerName string, db *gorm.DB, adminEm }) } + // In invite mode, block new OAuth users who don't have a valid invite code + // to prevent phantom pending records from accumulating in the DB. + // The first user (no users in DB yet) is always allowed through. + if registrationMode == "invite" && inviteCode == "" { + var existing User + if err := db.Where("provider = ? AND subject = ?", providerName, userInfo.Subject).First(&existing).Error; err != nil { + // Check if this would be the first user (always allowed) + var userCount int64 + db.Model(&User{}).Count(&userCount) + if userCount > 0 { + // New user without invite code — reject without creating a DB record + return c.Redirect(http.StatusTemporaryRedirect, "/login?error=invite_required") + } + } + } + // Upsert user (with invite code support) user, err := upsertOAuthUser(db, providerName, userInfo, adminEmail, registrationMode) if err != nil { diff --git a/core/http/react-ui/src/pages/Login.jsx b/core/http/react-ui/src/pages/Login.jsx index 56227c6fd..34d02c8f4 100644 --- a/core/http/react-ui/src/pages/Login.jsx +++ b/core/http/react-ui/src/pages/Login.jsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react' -import { useNavigate, useParams } from 'react-router-dom' +import { useNavigate, useParams, useSearchParams } from 'react-router-dom' import { useAuth } from '../context/AuthContext' import { apiUrl } from '../utils/basePath' import './auth.css' @@ -7,6 +7,7 @@ import './auth.css' export default function Login() { const navigate = useNavigate() const { code: urlInviteCode } = useParams() + const [searchParams] = useSearchParams() const { authEnabled, user, loading: authLoading, refresh } = useAuth() const [providers, setProviders] = useState([]) const [hasUsers, setHasUsers] = useState(true) @@ -42,6 +43,14 @@ export default function Login() { } }, [urlInviteCode]) + // Show error from OAuth redirect (e.g. invite_required) + useEffect(() => { + const errorParam = searchParams.get('error') + if (errorParam === 'invite_required') { + setError('A valid invite code is required to register') + } + }, [searchParams]) + useEffect(() => { fetch(apiUrl('/api/auth/status')) .then(r => r.json()) @@ -252,12 +261,14 @@ export default function Login() { -

- Don't have an account?{' '} - -

+ {!(registrationMode === 'invite' && hasUsers && !urlInviteCode) && ( +

+ Don't have an account?{' '} + +

+ )} )}