Skip to content

Commit 7a7d413

Browse files
committed
Fixed premature user creation
1 parent 1a3f138 commit 7a7d413

File tree

1 file changed

+18
-18
lines changed

1 file changed

+18
-18
lines changed

internal/auth/oidc.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -224,19 +224,24 @@ func (h *OIDCHandler) HandleCallback(w http.ResponseWriter, r *http.Request) {
224224
username = sub
225225
}
226226

227+
// Resolve roles before creating user to avoid orphaned records on rejection
228+
resolvedRoles := h.resolveClaimRoles(claims)
229+
if len(resolvedRoles) == 0 && h.config.RejectUnmapped {
230+
h.log.Warn("OIDC: login rejected — no mapped roles for user %s", username)
231+
http.Redirect(w, r, "/login?error=no_mapped_roles", http.StatusFound)
232+
return
233+
}
234+
227235
user, err := h.findOrCreateOIDCUser(ctx, sub, username, email)
228236
if err != nil {
229237
h.log.Error("OIDC: failed to find or create user (sub=%s, username=%s): %v", sub, username, err)
230238
http.Error(w, "Failed to authenticate user", http.StatusInternalServerError)
231239
return
232240
}
233241

234-
// Map OIDC claims to roles
235-
mapped := h.mapClaimsToRoles(ctx, user.ID, claims)
236-
if !mapped && h.config.RejectUnmapped {
237-
h.log.Warn("OIDC: login rejected — no mapped roles for user %s", username)
238-
http.Redirect(w, r, "/login?error=no_mapped_roles", http.StatusFound)
239-
return
242+
// Assign resolved roles to user
243+
for _, roleName := range resolvedRoles {
244+
_ = h.store.AssignRole(ctx, user.ID, roleName, "oidc")
240245
}
241246

242247
// Get user roles
@@ -321,18 +326,18 @@ func (h *OIDCHandler) findOrCreateOIDCUser(ctx context.Context, sub, username, e
321326
return user, nil
322327
}
323328

324-
// Maps OIDC claim values to local roles
325-
func (h *OIDCHandler) mapClaimsToRoles(ctx context.Context, userID string, claims map[string]any) bool {
329+
// Resolve OIDC claim values to local roles
330+
func (h *OIDCHandler) resolveClaimRoles(claims map[string]any) []string {
326331
if h.config.RoleClaim == "" {
327-
return true
332+
return nil
328333
}
329334

330335
// Extract groups/roles from claims
331336
var claimValues []string
332337
claimValue, ok := claims[h.config.RoleClaim]
333338
if !ok {
334339
h.log.Warn("OIDC: role claim %q not found in token claims", h.config.RoleClaim)
335-
return false
340+
return nil
336341
}
337342
switch v := claimValue.(type) {
338343
case []any:
@@ -342,7 +347,6 @@ func (h *OIDCHandler) mapClaimsToRoles(ctx context.Context, userID string, claim
342347
}
343348
}
344349
case string:
345-
// Try JSON array
346350
var arr []string
347351
if err := json.Unmarshal([]byte(v), &arr); err == nil {
348352
claimValues = arr
@@ -351,11 +355,11 @@ func (h *OIDCHandler) mapClaimsToRoles(ctx context.Context, userID string, claim
351355
}
352356
}
353357

354-
// Resolve claim values to local role names without writing anything yet
358+
// Resolve claim values to local role names
355359
var resolvedRoles []string
356360
if len(h.config.RoleMapping) > 0 {
357361
for _, claimVal := range claimValues {
358-
for mapKey, localRole := range h.config.RoleMapping { // yaml map keys apparently lower case themselves?
362+
for mapKey, localRole := range h.config.RoleMapping {
359363
if strings.EqualFold(claimVal, mapKey) {
360364
resolvedRoles = append(resolvedRoles, localRole)
361365
break
@@ -367,11 +371,7 @@ func (h *OIDCHandler) mapClaimsToRoles(ctx context.Context, userID string, claim
367371
resolvedRoles = claimValues
368372
}
369373

370-
// Assign only after we know what (if anything) resolved
371-
for _, roleName := range resolvedRoles {
372-
_ = h.store.AssignRole(ctx, userID, roleName, "oidc")
373-
}
374-
return len(resolvedRoles) > 0
374+
return resolvedRoles
375375
}
376376

377377
// Calls the configured extra claims URL with the access token.

0 commit comments

Comments
 (0)