package oidc import ( "keycape/internal/domain" "net/http" "net/http/httptest" "net/url" "strings" "testing" ) func TestRecoveryStripsCredentials(t *testing.T) { h := &AuthorizeHandler{AccountPortalURL: "https://users.example"} w := httptest.NewRecorder() h.authenticationFailure(w, httptest.NewRequest("GET", "https://kc.example/authorize/callback?code=secret&state=secret&redirect=https://evil.example", nil)) if w.Code != 303 || w.Header().Get("Location") != "https://users.example/access-recovery" { t.Fatal(w.Code, w.Header()) } if w.Header().Get("Referrer-Policy") != "no-referrer" || strings.Contains(w.Body.String(), "secret") { t.Fatal("callback leaked") } } func TestSharedLogoutRequiresConfirmationAndFixedReturn(t *testing.T) { h := &AccountLogoutHandler{PortalURL: "https://users.example", UpstreamLogoutURL: "https://auth.example/logout", Issuer: "https://kc.example", Logins: NewLoginSessionStore()} session := h.Logins.Create("synthetic", domain.AssuranceAAL1) w := httptest.NewRecorder() h.ServeHTTP(w, httptest.NewRequest("GET", "https://kc.example/account/logout", nil)) if _, ok := h.Logins.Get(session.ID); !ok { t.Fatal("GET logged out") } if w.Code != 200 || w.Header().Get("Location") != "" { t.Fatal("GET must only confirm") } page := w.Body.String() if !strings.Contains(page, "NetKingdom Identity") || !strings.Contains(page, "

Sign out of NetKingdom?

") || !strings.Contains(page, "--paper:#f5f1e8") || !strings.Contains(page, "Georgia,serif") { t.Fatal("logout page left the account-site design") } if !strings.Contains(w.Header().Get("Content-Security-Policy"), "style-src 'unsafe-inline'") { t.Fatal("inline account-site style blocked") } if w.Header().Get("Referrer-Policy") != "same-origin" { t.Fatal("browser POST would lose its Origin") } if !strings.Contains(w.Header().Get("Content-Security-Policy"), "form-action 'self' https://auth.example https://users.example;") { t.Fatal("browser must follow registered sign-out destinations") } c := w.Result().Cookies()[0] if !c.Secure || !c.HttpOnly || c.Domain != "" { t.Fatal("unsafe CSRF cookie") } for _, origin := range []string{"", "https://evil.example", "https://kc.example"} { form := url.Values{"csrf": {c.Value}, "rd": {"https://evil.example"}} r := httptest.NewRequest("POST", "https://kc.example/account/logout", strings.NewReader(form.Encode())) r.Header.Set("Content-Type", "application/x-www-form-urlencoded") r.Header.Set("Origin", origin) r.AddCookie(c) r.AddCookie(&http.Cookie{Name: loginCookieName, Value: session.ID}) w = httptest.NewRecorder() h.ServeHTTP(w, r) if origin != "https://kc.example" { if w.Code != 403 { t.Fatal("bad origin accepted") } continue } if _, ok := h.Logins.Get(session.ID); ok { t.Fatal("local session survived") } if w.Code != 303 { t.Fatal(w.Code) } dest, _ := url.Parse(w.Header().Get("Location")) if dest.Host != "auth.example" || dest.Query().Get("rd") != "https://users.example/logged-out" { t.Fatal("untrusted return") } } r := httptest.NewRequest("POST", "https://kc.example/account/logout", strings.NewReader("csrf=wrong")) r.Header.Set("Origin", "https://kc.example") r.Header.Set("Content-Type", "application/x-www-form-urlencoded") r.AddCookie(c) w = httptest.NewRecorder() h.ServeHTTP(w, r) if w.Code != http.StatusForbidden { t.Fatal("bad csrf accepted") } }