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")
}
}