key-cape/src/internal/server/oidc/optional_mfa_test.go
tegwick ac8ed65203
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 40s
Support opt-in MFA per browser client with authoritative enrollment checks
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a092fe-13b1-7f12-ac74-7d258af4d79c
2026-09-13 00:27:28 +02:00

50 lines
2 KiB
Go

package oidc_test
import (
"errors"
"keycape/internal/domain"
"keycape/internal/server/oidc"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
func TestOptionalMFAFollowsEnrollment(t *testing.T) {
for _, tc := range []struct {
name string
enrolled bool
lookupErr error
acr []string
want int
challenge bool
}{
{name: "unenrolled despite global requirement", want: http.StatusFound},
{name: "enrolled", enrolled: true, want: http.StatusOK, challenge: true},
{name: "lookup unavailable", lookupErr: errors.New("provider unavailable"), want: http.StatusInternalServerError},
{name: "explicit step up", acr: []string{"aal2"}, lookupErr: errors.New("must not query enrollment"), want: http.StatusOK, challenge: true},
} {
t.Run(tc.name, func(t *testing.T) {
sessions := oidc.NewSessionStore()
h := &oidc.AuthorizeHandler{
ClientConfig: map[string]*domain.Client{"test-client": {ClientID: "test-client", MFAOptional: true}},
Auth: &mockAuthProvider{callbackResult: &domain.AuthResult{Username: "alice"}},
MFA: &mockMFAProvider{required: true, requiredErr: errors.New("global policy must not be queried"), enrolled: tc.enrolled, enrolledErr: tc.lookupErr},
Sessions: sessions, Emitter: &captureEmitter{},
}
h.PendingStates().Store("optional", &oidc.PendingState{ClientID: "test-client", RedirectURI: "https://app.example/callback", State: "optional", ACRValues: tc.acr, ExpiresAt: time.Now().Add(time.Minute)})
rec := httptest.NewRecorder()
h.ServeHTTPCallback(rec, httptest.NewRequest(http.MethodGet, "/authorize/callback?code=x&state=optional", nil))
if rec.Code != tc.want {
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
}
if strings.Contains(rec.Body.String(), "KeyCape MFA") != tc.challenge {
t.Fatal("unexpected MFA challenge state")
}
if tc.want != http.StatusFound && strings.Contains(rec.Header().Get("Location"), "code=") {
t.Fatal("issued authorization code before MFA")
}
})
}
}