package auth import ( "testing" "time" ) const testSecret = "test-secret-at-least-32-bytes-long!" func TestMintAndParseSessionCookie(t *testing.T) { claims := Claims{Sub: "u1", Name: "Alice", Email: "alice@example.com"} cookie, err := MintSessionCookie(claims, []byte(testSecret), time.Hour, true) if err != nil { t.Fatalf("mint: %v", err) } if cookie.Name != SessionCookieName || !cookie.HttpOnly || !cookie.Secure { t.Fatalf("cookie = %+v, want name=%s HttpOnly+Secure", cookie, SessionCookieName) } got, err := ParseSessionCookie(cookie, []byte(testSecret)) if err != nil { t.Fatalf("parse: %v", err) } if got != claims { t.Fatalf("got %+v, want %+v", got, claims) } } func TestParseSessionCookie_Expired(t *testing.T) { cookie, err := MintSessionCookie(Claims{Sub: "u1"}, []byte(testSecret), -time.Hour, false) if err != nil { t.Fatalf("mint: %v", err) } if _, err := ParseSessionCookie(cookie, []byte(testSecret)); err == nil { t.Fatal("expected error for expired cookie") } } func TestParseSessionCookie_Tampered(t *testing.T) { cookie, err := MintSessionCookie(Claims{Sub: "u1"}, []byte(testSecret), time.Hour, false) if err != nil { t.Fatalf("mint: %v", err) } cookie.Value = flipLastChar(cookie.Value) if _, err := ParseSessionCookie(cookie, []byte(testSecret)); err == nil { t.Fatal("expected error for tampered cookie") } } // flipLastChar corrupts a signed token for tamper tests, guaranteeing the // last character actually changes -- blindly overwriting it with a fixed // character (e.g. "x") would occasionally be a no-op if that character // already happened to be there (it's derived from the token's embedded // timestamp, so this isn't as rare as it sounds), silently passing the // test without having tampered with anything. func flipLastChar(s string) string { last := s[len(s)-1] replacement := byte('x') if last == replacement { replacement = 'y' } return s[:len(s)-1] + string(replacement) } func TestParseSessionCookie_WrongSecret(t *testing.T) { cookie, err := MintSessionCookie(Claims{Sub: "u1"}, []byte(testSecret), time.Hour, false) if err != nil { t.Fatalf("mint: %v", err) } if _, err := ParseSessionCookie(cookie, []byte("a-completely-different-secret!!")); err == nil { t.Fatal("expected error for wrong secret") } } func TestMintAndParseTxnCookie(t *testing.T) { txn := TxnState{State: "abc123", CodeVerifier: "verifier-xyz"} cookie, err := MintTxnCookie(txn, []byte(testSecret), false) if err != nil { t.Fatalf("mint: %v", err) } if cookie.Name != TxnCookieName { t.Fatalf("cookie name = %q, want %q", cookie.Name, TxnCookieName) } got, err := ParseTxnCookie(cookie, []byte(testSecret)) if err != nil { t.Fatalf("parse: %v", err) } if got != txn { t.Fatalf("got %+v, want %+v", got, txn) } } func TestClearCookie(t *testing.T) { c := ClearCookie(SessionCookieName, true) if c.Value != "" || c.MaxAge >= 0 { t.Fatalf("ClearCookie = %+v, want empty value and negative MaxAge", c) } }