Files
Andrew S Erwin b9721ff13f
release gate / check (push) Failing after 8m35s
initial commit on Federted servers
2026-10-19 16:23:55 -05:00

114 lines
4.0 KiB
Go

package auth
import (
"github.com/golang-jwt/jwt/v5"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestSessionLifetimeAndVerification(t *testing.T) {
token, e := SignSession(SessionUser{ID: "alice"}, "test-secret")
if e != nil {
t.Fatal(e)
}
s, e := VerifySession(token, "test-secret")
if e != nil || s.ID != "alice" {
t.Fatalf("session: %v %v", s, e)
}
var claims SessionClaims
_, _, e = jwt.NewParser().ParseUnverified(token, &claims)
if e != nil {
t.Fatal(e)
}
if d := claims.ExpiresAt.Sub(claims.IssuedAt.Time); d != 30*24*time.Hour {
t.Fatalf("token lifetime: %v", d)
}
if _, e = VerifySession(token, "wrong-secret"); e == nil {
t.Fatal("accepted wrong secret")
}
expired := jwt.NewWithClaims(jwt.SigningMethodHS256, SessionClaims{UserID: "alice", RegisteredClaims: jwt.RegisteredClaims{Issuer: "computer", ExpiresAt: jwt.NewNumericDate(time.Now().Add(-time.Hour))}})
v, _ := expired.SignedString([]byte("test-secret"))
if _, e = VerifySession(v, "test-secret"); e == nil {
t.Fatal("accepted expired token")
}
token256 := jwt.NewWithClaims(jwt.SigningMethodHS384, SessionClaims{UserID: "alice", RegisteredClaims: jwt.RegisteredClaims{Issuer: "computer", ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour))}})
v, _ = token256.SignedString([]byte("test-secret"))
if _, e = VerifySession(v, "test-secret"); e == nil {
t.Fatal("accepted unexpected algorithm")
}
}
func signTestToken(t *testing.T, typ string, ttl time.Duration, ver int) string {
t.Helper()
now := time.Now()
tok := jwt.NewWithClaims(jwt.SigningMethodHS256, SessionClaims{
UserID: "alice", Type: typ, Ver: ver,
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "computer",
IssuedAt: jwt.NewNumericDate(now),
ExpiresAt: jwt.NewNumericDate(now.Add(ttl)),
},
})
v, e := tok.SignedString([]byte("test-secret"))
if e != nil {
t.Fatal(e)
}
return v
}
// The Bearer channel accepts only short-lived assertions carrying a token
// version (review SEC-02); the cookie channel accepts only session tokens (or
// legacy tokens issued before typ existed). A 30-day cookie JWT must never
// work as a Bearer credential.
func TestTokenChannelBinding(t *testing.T) {
assertion := signTestToken(t, TokenTypeAssertion, 60*time.Second, 3)
versionlessAssertion := signTestToken(t, TokenTypeAssertion, 60*time.Second, 0)
longAssertion := signTestToken(t, TokenTypeAssertion, time.Hour, 3)
session, e := SignSession(SessionUser{ID: "alice"}, "test-secret")
if e != nil {
t.Fatal(e)
}
legacy := signTestToken(t, "", time.Hour, 0)
// Review SEC-7: cookies now require Ver >= 1. Create a versioned session
// cookie for the channel-binding test.
versionedSession := signTestToken(t, TokenTypeSession, time.Hour, 3)
bearer := func(token string) (*SessionUser, error) {
r := httptest.NewRequest("GET", "/api/me", nil)
r.Header.Set("Authorization", "Bearer "+token)
return ReadSession(r, "test-secret")
}
cookie := func(token string) (*SessionUser, error) {
r := httptest.NewRequest("GET", "/api/me", nil)
r.AddCookie(&http.Cookie{Name: sessionCookieName, Value: token})
return ReadSession(r, "test-secret")
}
if s, e := bearer(assertion); e != nil || s.ID != "alice" || s.Ver != 3 {
t.Fatalf("valid assertion rejected or lost its version: %v %v", s, e)
}
if _, e := bearer(versionlessAssertion); e == nil {
t.Fatal("assertion without token version accepted (revocation bypass)")
}
if _, e := bearer(session); e == nil {
t.Fatal("30-day session token accepted as Bearer assertion")
}
if _, e := bearer(longAssertion); e == nil {
t.Fatal("over-long assertion accepted (exp-iat cap not enforced)")
}
if _, e := bearer(legacy); e == nil {
t.Fatal("typ-less token accepted as Bearer assertion")
}
if s, e := cookie(versionedSession); e != nil || s.ID != "alice" {
t.Fatalf("valid session cookie rejected: %v %v", s, e)
}
if s, e := cookie(legacy); e == nil {
t.Fatalf("legacy typ-less cookie with Ver=0 must be rejected: %v %v", s, e)
}
if _, e := cookie(assertion); e == nil {
t.Fatal("assertion accepted as cookie session")
}
}