Skip to content

Commit 15935e9

Browse files
authored
fix(auth): do not allow to register in invite mode (#9101)
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
1 parent 5d410e5 commit 15935e9

2 files changed

Lines changed: 34 additions & 7 deletions

File tree

core/http/auth/oauth.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,22 @@ func (m *OAuthManager) CallbackHandler(providerName string, db *gorm.DB, adminEm
213213
})
214214
}
215215

216+
// In invite mode, block new OAuth users who don't have a valid invite code
217+
// to prevent phantom pending records from accumulating in the DB.
218+
// The first user (no users in DB yet) is always allowed through.
219+
if registrationMode == "invite" && inviteCode == "" {
220+
var existing User
221+
if err := db.Where("provider = ? AND subject = ?", providerName, userInfo.Subject).First(&existing).Error; err != nil {
222+
// Check if this would be the first user (always allowed)
223+
var userCount int64
224+
db.Model(&User{}).Count(&userCount)
225+
if userCount > 0 {
226+
// New user without invite code — reject without creating a DB record
227+
return c.Redirect(http.StatusTemporaryRedirect, "/login?error=invite_required")
228+
}
229+
}
230+
}
231+
216232
// Upsert user (with invite code support)
217233
user, err := upsertOAuthUser(db, providerName, userInfo, adminEmail, registrationMode)
218234
if err != nil {

core/http/react-ui/src/pages/Login.jsx

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import { useState, useEffect } from 'react'
2-
import { useNavigate, useParams } from 'react-router-dom'
2+
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
33
import { useAuth } from '../context/AuthContext'
44
import { apiUrl } from '../utils/basePath'
55
import './auth.css'
66

77
export default function Login() {
88
const navigate = useNavigate()
99
const { code: urlInviteCode } = useParams()
10+
const [searchParams] = useSearchParams()
1011
const { authEnabled, user, loading: authLoading, refresh } = useAuth()
1112
const [providers, setProviders] = useState([])
1213
const [hasUsers, setHasUsers] = useState(true)
@@ -42,6 +43,14 @@ export default function Login() {
4243
}
4344
}, [urlInviteCode])
4445

46+
// Show error from OAuth redirect (e.g. invite_required)
47+
useEffect(() => {
48+
const errorParam = searchParams.get('error')
49+
if (errorParam === 'invite_required') {
50+
setError('A valid invite code is required to register')
51+
}
52+
}, [searchParams])
53+
4554
useEffect(() => {
4655
fetch(apiUrl('/api/auth/status'))
4756
.then(r => r.json())
@@ -252,12 +261,14 @@ export default function Login() {
252261
<button type="submit" className="btn btn-primary login-btn-full" disabled={submitting}>
253262
{submitting ? 'Signing in...' : 'Sign In'}
254263
</button>
255-
<p className="login-footer">
256-
Don't have an account?{' '}
257-
<button type="button" className="login-link" onClick={() => { setMode('register'); setError(''); setMessage('') }}>
258-
Register
259-
</button>
260-
</p>
264+
{!(registrationMode === 'invite' && hasUsers && !urlInviteCode) && (
265+
<p className="login-footer">
266+
Don't have an account?{' '}
267+
<button type="button" className="login-link" onClick={() => { setMode('register'); setError(''); setMessage('') }}>
268+
Register
269+
</button>
270+
</p>
271+
)}
261272
</form>
262273
)}
263274

0 commit comments

Comments
 (0)