package oidc_test import ( "context" "encoding/json" "net/http" "net/http/httptest" "net/url" "strings" "testing" "time" "keycape/internal/domain" profileerrors "keycape/internal/errors" "keycape/internal/server/oidc" "keycape/internal/server/telemetry" ) // --------------------------------------------------------------------------- // Mock implementations // --------------------------------------------------------------------------- // mockAuthProvider implements domain.AuthProvider. type mockAuthProvider struct { authorizeURL string authorizeErr error callbackResult *domain.AuthResult callbackErr error } func (m *mockAuthProvider) AuthorizeURL(_ context.Context, _ domain.AuthRequest) (string, error) { if m.authorizeErr != nil { return "", m.authorizeErr } return m.authorizeURL, nil } func (m *mockAuthProvider) HandleCallback(_ context.Context, _ domain.CallbackParams) (*domain.AuthResult, error) { return m.callbackResult, m.callbackErr } // mockMFAProvider implements domain.MFAProvider. type mockMFAProvider struct { required bool requiredErr error validateErr error validateCalls int validatedUser string validatedToken string } func (m *mockMFAProvider) CheckMFARequired(_ context.Context, _ string) (bool, error) { return m.required, m.requiredErr } func (m *mockMFAProvider) ValidateMFAToken(_ context.Context, user, token string) error { m.validateCalls++ m.validatedUser = user m.validatedToken = token return m.validateErr } // captureEmitter captures the last emitted event. type captureEmitter struct { events []telemetry.Event } func (c *captureEmitter) Emit(_ context.Context, ev telemetry.Event) { c.events = append(c.events, ev) } func (c *captureEmitter) last() telemetry.Event { if len(c.events) == 0 { return telemetry.Event{} } return c.events[len(c.events)-1] } // --------------------------------------------------------------------------- // Test helpers // --------------------------------------------------------------------------- func testClient() map[string]*domain.Client { return map[string]*domain.Client{ "test-client": { ClientID: "test-client", DisplayName: "Test Client", RedirectURIs: []string{"https://app.example.com/callback"}, AllowedScopes: []string{"openid", "profile", "email"}, ClientType: "public", }, "netkingdom-bootstrap-console": { ClientID: "netkingdom-bootstrap-console", DisplayName: "NetKingdom Bootstrap Console", RedirectURIs: []string{ "http://127.0.0.1:8876/oidc/callback", "http://localhost:8876/oidc/callback", }, AllowedScopes: []string{"openid", "profile", "email", "groups"}, ClientType: "public", }, } } func newAuthorizeHandler(auth domain.AuthProvider, mfa domain.MFAProvider, emitter telemetry.Emitter) *oidc.AuthorizeHandler { return &oidc.AuthorizeHandler{ ClientConfig: testClient(), Auth: auth, MFA: mfa, Sessions: oidc.NewSessionStore(), Emitter: emitter, } } func validAuthorizeParams() url.Values { return url.Values{ "client_id": []string{"test-client"}, "redirect_uri": []string{"https://app.example.com/callback"}, "response_type": []string{"code"}, "scope": []string{"openid profile"}, "state": []string{"random-state"}, "code_challenge": []string{"E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"}, "code_challenge_method": []string{"S256"}, } } func authorizeRequest(params url.Values) *http.Request { return httptest.NewRequest(http.MethodGet, "/authorize?"+params.Encode(), nil) } func decodeProfileError(t *testing.T, body string) profileerrors.ErrorType { t.Helper() var pe profileerrors.ProfileError if err := json.Unmarshal([]byte(body), &pe); err != nil { t.Fatalf("could not decode ProfileError: %v (body: %q)", err, body) } return pe.Error } // --------------------------------------------------------------------------- // T06 Authorization Endpoint Tests // --------------------------------------------------------------------------- func TestAuthorizeHandler_ValidRequest_RedirectsToAuthelia(t *testing.T) { auth := &mockAuthProvider{authorizeURL: "https://authelia.example.com/auth?state=xyz"} mfa := &mockMFAProvider{} emitter := &captureEmitter{} h := newAuthorizeHandler(auth, mfa, emitter) req := authorizeRequest(validAuthorizeParams()) w := httptest.NewRecorder() h.ServeHTTP(w, req) if w.Code != http.StatusFound { t.Errorf("expected 302 redirect, got %d (body: %s)", w.Code, w.Body.String()) } loc := w.Header().Get("Location") if loc != "https://authelia.example.com/auth?state=xyz" { t.Errorf("expected redirect to Authelia, got %q", loc) } } func TestAuthorizeHandler_BootstrapConsoleRedirectURI_RedirectsToAuthelia(t *testing.T) { auth := &mockAuthProvider{authorizeURL: "https://authelia.example.com/auth?state=bootstrap"} mfa := &mockMFAProvider{} emitter := &captureEmitter{} h := newAuthorizeHandler(auth, mfa, emitter) params := validAuthorizeParams() params.Set("client_id", "netkingdom-bootstrap-console") params.Set("redirect_uri", "http://127.0.0.1:8876/oidc/callback") req := authorizeRequest(params) w := httptest.NewRecorder() h.ServeHTTP(w, req) if w.Code != http.StatusFound { t.Errorf("expected 302 redirect, got %d (body: %s)", w.Code, w.Body.String()) } if loc := w.Header().Get("Location"); loc != "https://authelia.example.com/auth?state=bootstrap" { t.Errorf("expected Authelia redirect, got %q", loc) } } func TestAuthorizeHandler_EmitsAuthStart(t *testing.T) { auth := &mockAuthProvider{authorizeURL: "https://authelia.example.com/auth"} mfa := &mockMFAProvider{} emitter := &captureEmitter{} h := newAuthorizeHandler(auth, mfa, emitter) req := authorizeRequest(validAuthorizeParams()) w := httptest.NewRecorder() h.ServeHTTP(w, req) found := false for _, ev := range emitter.events { if ev.EventType == telemetry.EventAuthStart { found = true break } } if !found { t.Error("expected auth_start telemetry event to be emitted") } } func TestAuthorizeHandler_MissingCodeChallenge_InvalidProfileUsage(t *testing.T) { auth := &mockAuthProvider{} mfa := &mockMFAProvider{} emitter := &captureEmitter{} h := newAuthorizeHandler(auth, mfa, emitter) params := validAuthorizeParams() params.Del("code_challenge") params.Del("code_challenge_method") req := authorizeRequest(params) w := httptest.NewRecorder() h.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400, got %d", w.Code) } errType := decodeProfileError(t, w.Body.String()) if errType != profileerrors.ErrInvalidProfileUsage { t.Errorf("expected invalid_profile_usage, got %q", errType) } } func TestAuthorizeHandler_WildcardRedirectURI_RejectedForSafety(t *testing.T) { auth := &mockAuthProvider{} mfa := &mockMFAProvider{} emitter := &captureEmitter{} clients := map[string]*domain.Client{ "wildcard-client": { ClientID: "wildcard-client", RedirectURIs: []string{"https://app.example.com/*"}, ClientType: "public", }, } h := &oidc.AuthorizeHandler{ ClientConfig: clients, Auth: auth, MFA: mfa, Sessions: oidc.NewSessionStore(), Emitter: emitter, } params := validAuthorizeParams() params.Set("client_id", "wildcard-client") params.Set("redirect_uri", "https://app.example.com/anything") req := authorizeRequest(params) w := httptest.NewRecorder() h.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400, got %d", w.Code) } errType := decodeProfileError(t, w.Body.String()) if errType != profileerrors.ErrRejectedForSafety { t.Errorf("expected rejected_for_profile_safety, got %q", errType) } } func TestAuthorizeHandler_UnknownClient_InvalidProfileUsage(t *testing.T) { auth := &mockAuthProvider{} mfa := &mockMFAProvider{} emitter := &captureEmitter{} h := newAuthorizeHandler(auth, mfa, emitter) params := validAuthorizeParams() params.Set("client_id", "no-such-client") req := authorizeRequest(params) w := httptest.NewRecorder() h.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400, got %d", w.Code) } errType := decodeProfileError(t, w.Body.String()) if errType != profileerrors.ErrInvalidProfileUsage { t.Errorf("expected invalid_profile_usage, got %q", errType) } } func TestAuthorizeHandler_WrongResponseType_FeatureNotSupported(t *testing.T) { auth := &mockAuthProvider{} mfa := &mockMFAProvider{} emitter := &captureEmitter{} h := newAuthorizeHandler(auth, mfa, emitter) params := validAuthorizeParams() params.Set("response_type", "token") req := authorizeRequest(params) w := httptest.NewRecorder() h.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400, got %d", w.Code) } errType := decodeProfileError(t, w.Body.String()) if errType != profileerrors.ErrFeatureNotSupported { t.Errorf("expected feature_not_supported_by_profile, got %q", errType) } } func TestAuthorizeHandler_MissingOpenIDScope_InvalidProfileUsage(t *testing.T) { auth := &mockAuthProvider{} mfa := &mockMFAProvider{} emitter := &captureEmitter{} h := newAuthorizeHandler(auth, mfa, emitter) params := validAuthorizeParams() params.Set("scope", "profile email") req := authorizeRequest(params) w := httptest.NewRecorder() h.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400, got %d", w.Code) } errType := decodeProfileError(t, w.Body.String()) if errType != profileerrors.ErrInvalidProfileUsage { t.Errorf("expected invalid_profile_usage, got %q", errType) } } func TestAuthorizeHandler_PlainCodeChallengeMethod_RejectedForSafety(t *testing.T) { auth := &mockAuthProvider{} mfa := &mockMFAProvider{} emitter := &captureEmitter{} h := newAuthorizeHandler(auth, mfa, emitter) params := validAuthorizeParams() params.Set("code_challenge_method", "plain") req := authorizeRequest(params) w := httptest.NewRecorder() h.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400, got %d", w.Code) } errType := decodeProfileError(t, w.Body.String()) if errType != profileerrors.ErrRejectedForSafety { t.Errorf("expected rejected_for_profile_safety, got %q", errType) } } func TestAuthorizeHandler_UnknownRedirectURI_InvalidProfileUsage(t *testing.T) { auth := &mockAuthProvider{} mfa := &mockMFAProvider{} emitter := &captureEmitter{} h := newAuthorizeHandler(auth, mfa, emitter) params := validAuthorizeParams() params.Set("redirect_uri", "https://evil.example.com/callback") req := authorizeRequest(params) w := httptest.NewRecorder() h.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400, got %d", w.Code) } errType := decodeProfileError(t, w.Body.String()) if errType != profileerrors.ErrInvalidProfileUsage { t.Errorf("expected invalid_profile_usage, got %q", errType) } } // --------------------------------------------------------------------------- // Callback tests // --------------------------------------------------------------------------- func TestAuthorizeCallback_Success_RedirectsWithCode(t *testing.T) { auth := &mockAuthProvider{ callbackResult: &domain.AuthResult{Username: "alice"}, } mfa := &mockMFAProvider{required: false} emitter := &captureEmitter{} sessions := oidc.NewSessionStore() h := &oidc.AuthorizeHandler{ ClientConfig: testClient(), Auth: auth, MFA: mfa, Sessions: sessions, Emitter: emitter, } req := httptest.NewRequest(http.MethodGet, "/authorize/callback?code=authelia-code&state=random-state", nil) // Simulate that there is an ongoing PKCE flow stored in query param forwarding // The callback needs the original client context. We store it via a pre-seeded // pending session keyed by state. // For the callback handler, we expect it to look up the pending state by the // "state" parameter that was originally embedded. We seed the pending state. h.PendingStates().Store("random-state", &oidc.PendingState{ ClientID: "test-client", RedirectURI: "https://app.example.com/callback", PKCEChallenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", PKCEChallengeMethod: "S256", State: "random-state", Scopes: []string{"openid", "profile"}, ExpiresAt: time.Now().Add(5 * time.Minute), }) w := httptest.NewRecorder() h.ServeHTTPCallback(w, req) if w.Code != http.StatusFound { t.Errorf("expected 302 redirect, got %d (body: %s)", w.Code, w.Body.String()) } loc := w.Header().Get("Location") parsed, err := url.Parse(loc) if err != nil { t.Fatalf("invalid Location header: %v", err) } if parsed.Query().Get("code") == "" { t.Error("expected code param in redirect, got empty") } if parsed.Query().Get("state") != "random-state" { t.Errorf("expected state=random-state, got %q", parsed.Query().Get("state")) } } func TestAuthorizeCallback_MFAFailed_AuthFailure(t *testing.T) { auth := &mockAuthProvider{ callbackResult: &domain.AuthResult{Username: "alice"}, } mfa := &mockMFAProvider{ required: true, validateErr: domain.ErrMFAFailed, } emitter := &captureEmitter{} sessions := oidc.NewSessionStore() h := &oidc.AuthorizeHandler{ ClientConfig: testClient(), Auth: auth, MFA: mfa, Sessions: sessions, Emitter: emitter, } h.PendingStates().Store("random-state", &oidc.PendingState{ ClientID: "test-client", RedirectURI: "https://app.example.com/callback", PKCEChallenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", PKCEChallengeMethod: "S256", State: "random-state", Scopes: []string{"openid"}, ExpiresAt: time.Now().Add(5 * time.Minute), }) req := httptest.NewRequest(http.MethodGet, "/authorize/callback?code=authelia-code&state=random-state&mfa_token=wrong", nil) w := httptest.NewRecorder() h.ServeHTTPCallback(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401, got %d", w.Code) } found := false for _, ev := range emitter.events { if ev.EventType == telemetry.EventAuthFailure { found = true break } } if !found { t.Error("expected auth_failure telemetry event") } } func TestAuthorizeCallback_MFARequired_RendersChallengeWithoutToken(t *testing.T) { auth := &mockAuthProvider{ callbackResult: &domain.AuthResult{Username: "alice"}, } mfa := &mockMFAProvider{required: true} emitter := &captureEmitter{} sessions := oidc.NewSessionStore() h := &oidc.AuthorizeHandler{ ClientConfig: testClient(), Auth: auth, MFA: mfa, Sessions: sessions, Emitter: emitter, } h.PendingStates().Store("random-state", &oidc.PendingState{ ClientID: "test-client", RedirectURI: "https://app.example.com/callback", PKCEChallenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", PKCEChallengeMethod: "S256", State: "random-state", Scopes: []string{"openid"}, ExpiresAt: time.Now().Add(5 * time.Minute), }) req := httptest.NewRequest(http.MethodGet, "/authorize/callback?code=authelia-code&state=random-state", nil) w := httptest.NewRecorder() h.ServeHTTPCallback(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200 challenge page, got %d (body: %s)", w.Code, w.Body.String()) } body := w.Body.String() for _, want := range []string{"Verify sign-in", "alice", "Test Client", `name="mfa_token"`} { if !strings.Contains(body, want) { t.Errorf("challenge page missing %q in body: %s", want, body) } } if mfa.validateCalls != 0 { t.Errorf("MFA token should not be validated until form submission, got %d calls", mfa.validateCalls) } ps, ok := h.PendingStates().Load("random-state") if !ok { t.Fatal("expected pending state to remain for MFA form submission") } if ps.AuthenticatedUser != "alice" { t.Errorf("AuthenticatedUser: want alice, got %q", ps.AuthenticatedUser) } } func TestAuthorizeCallback_MFASubmission_ValidToken_RedirectsWithCode(t *testing.T) { auth := &mockAuthProvider{} mfa := &mockMFAProvider{required: true} emitter := &captureEmitter{} sessions := oidc.NewSessionStore() h := &oidc.AuthorizeHandler{ ClientConfig: testClient(), Auth: auth, MFA: mfa, Sessions: sessions, Emitter: emitter, } h.PendingStates().Store("random-state", &oidc.PendingState{ ClientID: "test-client", RedirectURI: "https://app.example.com/callback?from=bootstrap", PKCEChallenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", PKCEChallengeMethod: "S256", State: "random-state", Scopes: []string{"openid"}, ExpiresAt: time.Now().Add(5 * time.Minute), AuthenticatedUser: "alice", }) form := url.Values{"state": {"random-state"}, "mfa_token": {"123456"}} req := httptest.NewRequest(http.MethodPost, "/authorize/callback", strings.NewReader(form.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") w := httptest.NewRecorder() h.ServeHTTPCallback(w, req) if w.Code != http.StatusFound { t.Errorf("expected 302 redirect, got %d (body: %s)", w.Code, w.Body.String()) } if mfa.validatedUser != "alice" || mfa.validatedToken != "123456" { t.Errorf("validated MFA: want alice/123456, got %q/%q", mfa.validatedUser, mfa.validatedToken) } loc := w.Header().Get("Location") parsed, err := url.Parse(loc) if err != nil { t.Fatalf("invalid Location header: %v", err) } if parsed.Query().Get("from") != "bootstrap" { t.Errorf("expected original redirect query to be preserved, got %q", loc) } if parsed.Query().Get("code") == "" { t.Error("expected code param in redirect, got empty") } if parsed.Query().Get("state") != "random-state" { t.Errorf("expected state=random-state, got %q", parsed.Query().Get("state")) } if _, ok := h.PendingStates().Load("random-state"); ok { t.Error("expected pending MFA state to be deleted after successful submission") } // KEY-WP-0005-T01: the resulting session must record that MFA was // actually verified in this flow, feeding token.go's assurance claim // (aal2, not aal1). code := parsed.Query().Get("code") sess, ok := sessions.Get(code) if !ok { t.Fatal("expected a PKCE session for the issued code") } if !sess.MFAVerified { t.Error("MFAVerified: want true after a successful MFA submission") } } func TestAuthorizeCallback_MFANotRequired_SessionRecordsMFAVerifiedFalse(t *testing.T) { auth := &mockAuthProvider{ callbackResult: &domain.AuthResult{Username: "alice"}, } mfa := &mockMFAProvider{required: false} emitter := &captureEmitter{} sessions := oidc.NewSessionStore() h := &oidc.AuthorizeHandler{ ClientConfig: testClient(), Auth: auth, MFA: mfa, Sessions: sessions, Emitter: emitter, } h.PendingStates().Store("random-state", &oidc.PendingState{ ClientID: "test-client", RedirectURI: "https://app.example.com/callback", PKCEChallenge: "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM", PKCEChallengeMethod: "S256", State: "random-state", Scopes: []string{"openid"}, ExpiresAt: time.Now().Add(5 * time.Minute), }) req := httptest.NewRequest(http.MethodGet, "/authorize/callback?code=authelia-code&state=random-state", nil) w := httptest.NewRecorder() h.ServeHTTPCallback(w, req) if w.Code != http.StatusFound { t.Fatalf("expected 302 redirect, got %d (body: %s)", w.Code, w.Body.String()) } loc, err := url.Parse(w.Header().Get("Location")) if err != nil { t.Fatalf("invalid Location header: %v", err) } code := loc.Query().Get("code") sess, ok := sessions.Get(code) if !ok { t.Fatal("expected a PKCE session for the issued code") } if sess.MFAVerified { t.Error("MFAVerified: want false when MFA was never required for this flow") } } func TestAuthorizeCallback_MFASubmission_InvalidToken_AuthFailure(t *testing.T) { auth := &mockAuthProvider{} mfa := &mockMFAProvider{ required: true, validateErr: domain.ErrMFAFailed, } emitter := &captureEmitter{} h := &oidc.AuthorizeHandler{ ClientConfig: testClient(), Auth: auth, MFA: mfa, Sessions: oidc.NewSessionStore(), Emitter: emitter, } h.PendingStates().Store("random-state", &oidc.PendingState{ ClientID: "test-client", RedirectURI: "https://app.example.com/callback", PKCEChallenge: "abc", PKCEChallengeMethod: "S256", State: "random-state", Scopes: []string{"openid"}, ExpiresAt: time.Now().Add(5 * time.Minute), AuthenticatedUser: "alice", }) form := url.Values{"state": {"random-state"}, "mfa_token": {"wrong"}} req := httptest.NewRequest(http.MethodPost, "/authorize/callback", strings.NewReader(form.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") w := httptest.NewRecorder() h.ServeHTTPCallback(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401, got %d", w.Code) } if _, ok := h.PendingStates().Load("random-state"); ok { t.Error("expected pending MFA state to be deleted after invalid submission") } found := false for _, ev := range emitter.events { if ev.EventType == telemetry.EventAuthFailure && ev.ErrorType == "mfa_failed" { found = true break } } if !found { t.Error("expected mfa_failed auth_failure telemetry event") } } func TestAuthorizeCallback_AuthProviderFailed_AuthFailure(t *testing.T) { auth := &mockAuthProvider{ callbackErr: domain.ErrAuthFailed, } mfa := &mockMFAProvider{} emitter := &captureEmitter{} sessions := oidc.NewSessionStore() h := &oidc.AuthorizeHandler{ ClientConfig: testClient(), Auth: auth, MFA: mfa, Sessions: sessions, Emitter: emitter, } h.PendingStates().Store("random-state", &oidc.PendingState{ ClientID: "test-client", RedirectURI: "https://app.example.com/callback", State: "random-state", ExpiresAt: time.Now().Add(5 * time.Minute), }) req := httptest.NewRequest(http.MethodGet, "/authorize/callback?code=bad&state=random-state", nil) w := httptest.NewRecorder() h.ServeHTTPCallback(w, req) if w.Code != http.StatusUnauthorized { t.Errorf("expected 401, got %d", w.Code) } found := false for _, ev := range emitter.events { if ev.EventType == telemetry.EventAuthFailure { found = true break } } if !found { t.Error("expected auth_failure telemetry event") } } func TestAuthorizeCallback_EmitsAuthSuccess(t *testing.T) { auth := &mockAuthProvider{ callbackResult: &domain.AuthResult{Username: "bob"}, } mfa := &mockMFAProvider{required: false} emitter := &captureEmitter{} sessions := oidc.NewSessionStore() h := &oidc.AuthorizeHandler{ ClientConfig: testClient(), Auth: auth, MFA: mfa, Sessions: sessions, Emitter: emitter, } h.PendingStates().Store("s1", &oidc.PendingState{ ClientID: "test-client", RedirectURI: "https://app.example.com/callback", PKCEChallenge: "abc", PKCEChallengeMethod: "S256", State: "s1", Scopes: []string{"openid"}, ExpiresAt: time.Now().Add(5 * time.Minute), }) req := httptest.NewRequest(http.MethodGet, "/authorize/callback?code=c&state=s1", nil) w := httptest.NewRecorder() h.ServeHTTPCallback(w, req) found := false for _, ev := range emitter.events { if ev.EventType == telemetry.EventAuthSuccess { found = true break } } if !found { t.Errorf("expected auth_success telemetry event, got events: %v", emitter.events) } } // --------------------------------------------------------------------------- // ServeHTTP dispatch // --------------------------------------------------------------------------- func TestAuthorizeHandler_ServeHTTP_DispatchesToCallback(t *testing.T) { auth := &mockAuthProvider{ callbackResult: &domain.AuthResult{Username: "alice"}, } mfa := &mockMFAProvider{} emitter := &captureEmitter{} h := newAuthorizeHandler(auth, mfa, emitter) // A request to /authorize/callback should not be treated as the initial // authorize request and must not require PKCE params. req := httptest.NewRequest(http.MethodGet, "/authorize/callback?code=x&state=y", nil) w := httptest.NewRecorder() h.ServeHTTP(w, req) // Without a seeded pending state for "y", the callback returns an error. // The important thing is that it is NOT a redirect to Authelia. if w.Code == http.StatusFound { loc := w.Header().Get("Location") if strings.Contains(loc, "authelia") { t.Error("callback path must not redirect to Authelia") } } }