2026-07-24 21:20:04 +02:00
|
|
|
package auth
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const testSecret = "test-secret-at-least-32-bytes-long!"
|
|
|
|
|
|
|
|
|
|
func TestMintAndParseSessionCookie(t *testing.T) {
|
2026-07-26 11:55:13 +02:00
|
|
|
claims := Claims{Sub: "u1", Name: "Alice", Email: "alice@example.com", IDToken: "raw-id-token-jwt"}
|
2026-07-24 21:20:04 +02:00
|
|
|
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)
|
|
|
|
|
}
|
2026-07-25 19:33:15 +02:00
|
|
|
cookie.Value = flipLastChar(cookie.Value)
|
2026-07-24 21:20:04 +02:00
|
|
|
if _, err := ParseSessionCookie(cookie, []byte(testSecret)); err == nil {
|
|
|
|
|
t.Fatal("expected error for tampered cookie")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-25 19:33:15 +02:00
|
|
|
// 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)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-24 21:20:04 +02:00
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|