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

@ -656,6 +656,51 @@ func TestAuthorizeCallback_MFANotRequired_SessionRecordsMFAVerifiedFalse(t *test
}
}
func TestAuthorizeCallback_ClientPolicyCanDisableEnrolledMFA(t *testing.T) {
disabled := false
mfa := &mockMFAProvider{required: true}
h := &oidc.AuthorizeHandler{
ClientConfig: map[string]*domain.Client{"test-client": {
ClientID: "test-client", DisplayName: "Test", MFARequired: &disabled,
}},
Auth: &mockAuthProvider{callbackResult: &domain.AuthResult{Username: "alice"}},
MFA: mfa, Sessions: oidc.NewSessionStore(), Emitter: &captureEmitter{},
}
h.PendingStates().Store("state-client-aal1", &oidc.PendingState{
ClientID: "test-client", RedirectURI: "https://app.example/callback",
State: "state-client-aal1", ExpiresAt: time.Now().Add(time.Minute),
})
req := httptest.NewRequest(http.MethodGet, "/authorize/callback?code=x&state=state-client-aal1", nil)
rec := httptest.NewRecorder()
h.ServeHTTPCallback(rec, req)
if rec.Code != http.StatusFound {
t.Fatalf("status = %d, body=%s", rec.Code, rec.Body.String())
}
}
func TestAuthorizeCallback_ACRStepUpOverridesClientAAL1(t *testing.T) {
disabled := false
mfa := &mockMFAProvider{required: false}
h := &oidc.AuthorizeHandler{
ClientConfig: map[string]*domain.Client{"test-client": {
ClientID: "test-client", DisplayName: "Test", MFARequired: &disabled,
}},
Auth: &mockAuthProvider{callbackResult: &domain.AuthResult{Username: "alice"}},
MFA: mfa, Sessions: oidc.NewSessionStore(), Emitter: &captureEmitter{},
}
h.PendingStates().Store("state-step-up", &oidc.PendingState{
ClientID: "test-client", RedirectURI: "https://app.example/callback",
State: "state-step-up", ACRValues: []string{"aal2"},
ExpiresAt: time.Now().Add(time.Minute),
})
req := httptest.NewRequest(http.MethodGet, "/authorize/callback?code=x&state=state-step-up", nil)
rec := httptest.NewRecorder()
h.ServeHTTPCallback(rec, req)
if rec.Code != http.StatusOK || !strings.Contains(rec.Body.String(), "KeyCape MFA") {
t.Fatalf("expected MFA challenge, status=%d body=%s", rec.Code, rec.Body.String())
}
}
func TestAuthorizeCallback_MFASubmission_InvalidToken_AuthFailure(t *testing.T) {
auth := &mockAuthProvider{}
mfa := &mockMFAProvider{