KEY-WP-0008: honor per-client mfaRequired and acr_values step-up
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 30s

Allow coulomb-social ordinary login at AAL1 via mfaRequired: false while
keeping provider requireForAll for clients without an override. Preserve
explicit acr_values=aal2 for step-up.
This commit is contained in:
tegwick 2026-08-09 22:42:51 +02:00
parent 8e976bc60f
commit 3bef507cb8
7 changed files with 168 additions and 1 deletions

View file

@ -27,6 +27,7 @@ type PendingState struct {
Scopes []string
ExpiresAt time.Time
AuthenticatedUser string
ACRValues []string
}
// pendingStateStore is a thread-safe map of state → PendingState.
@ -107,6 +108,7 @@ func (h *AuthorizeHandler) serveAuthorize(w http.ResponseWriter, r *http.Request
nonce := q.Get("nonce")
codeChallenge := q.Get("code_challenge")
codeChallengeMethod := q.Get("code_challenge_method")
acrValues := strings.Fields(q.Get("acr_values"))
// Emit auth_start telemetry immediately.
h.Emitter.Emit(ctx, telemetry.Event{
@ -195,6 +197,7 @@ func (h *AuthorizeHandler) serveAuthorize(w http.ResponseWriter, r *http.Request
State: state,
Nonce: nonce,
Scopes: strings.Fields(scope),
ACRValues: acrValues,
ExpiresAt: time.Now().Add(10 * time.Minute),
})
@ -280,7 +283,7 @@ func (h *AuthorizeHandler) ServeHTTPCallback(w http.ResponseWriter, r *http.Requ
}
// Check MFA requirement.
mfaRequired, err := h.MFA.CheckMFARequired(ctx, result.Username)
mfaRequired, _, err := h.mfaRequirement(ps, result.Username)
if err != nil {
h.Emitter.Emit(ctx, telemetry.Event{
Timestamp: time.Now(),
@ -312,6 +315,20 @@ func (h *AuthorizeHandler) ServeHTTPCallback(w http.ResponseWriter, r *http.Requ
h.completeAuthorization(w, r, ps, result.Username, mfaRequired)
}
func (h *AuthorizeHandler) mfaRequirement(ps *PendingState, username string) (bool, bool, error) {
for _, acr := range ps.ACRValues {
switch strings.ToLower(acr) {
case "aal2", "mfa", "urn:netkingdom:aal2":
return true, false, nil
}
}
if client, ok := h.ClientConfig[ps.ClientID]; ok && client.MFARequired != nil {
return *client.MFARequired, false, nil
}
required, err := h.MFA.CheckMFARequired(context.Background(), username)
return required, true, err
}
func (h *AuthorizeHandler) serveMFASubmission(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if err := r.ParseForm(); err != nil {