Add session package for encrypted cookie management
Uses gorilla/securecookie with keys derived via HKDF. 30-day TTL, HttpOnly, Secure, SameSiteStrict cookies.
This commit is contained in:
204
internal/session/session_test.go
Normal file
204
internal/session/session_test.go
Normal file
@@ -0,0 +1,204 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestManager_CreateAndValidate(t *testing.T) {
|
||||
mgr, err := NewManager("test-signing-key-12345", false)
|
||||
if err != nil {
|
||||
t.Fatalf("NewManager() error = %v", err)
|
||||
}
|
||||
|
||||
// Create a session
|
||||
w := httptest.NewRecorder()
|
||||
if err := mgr.CreateSession(w); err != nil {
|
||||
t.Fatalf("CreateSession() error = %v", err)
|
||||
}
|
||||
|
||||
// Extract the cookie from response
|
||||
resp := w.Result()
|
||||
cookies := resp.Cookies()
|
||||
if len(cookies) == 0 {
|
||||
t.Fatal("CreateSession() did not set a cookie")
|
||||
}
|
||||
|
||||
var sessionCookie *http.Cookie
|
||||
for _, c := range cookies {
|
||||
if c.Name == CookieName {
|
||||
sessionCookie = c
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sessionCookie == nil {
|
||||
t.Fatalf("CreateSession() did not set cookie named %q", CookieName)
|
||||
}
|
||||
|
||||
// Validate the session
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.AddCookie(sessionCookie)
|
||||
|
||||
data, err := mgr.ValidateSession(req)
|
||||
if err != nil {
|
||||
t.Fatalf("ValidateSession() error = %v", err)
|
||||
}
|
||||
|
||||
if !data.Authenticated {
|
||||
t.Error("ValidateSession() returned unauthenticated session")
|
||||
}
|
||||
|
||||
if data.ExpiresAt.Before(time.Now()) {
|
||||
t.Error("ValidateSession() returned already-expired session")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManager_ValidateSession_NoCookie(t *testing.T) {
|
||||
mgr, _ := NewManager("test-signing-key-12345", false)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
|
||||
_, err := mgr.ValidateSession(req)
|
||||
if err == nil {
|
||||
t.Error("ValidateSession() should fail with no cookie")
|
||||
}
|
||||
|
||||
if err != ErrNoSession {
|
||||
t.Errorf("ValidateSession() error = %v, want %v", err, ErrNoSession)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManager_ValidateSession_TamperedCookie(t *testing.T) {
|
||||
mgr, _ := NewManager("test-signing-key-12345", false)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.AddCookie(&http.Cookie{
|
||||
Name: CookieName,
|
||||
Value: "tampered-invalid-cookie-value",
|
||||
})
|
||||
|
||||
_, err := mgr.ValidateSession(req)
|
||||
if err == nil {
|
||||
t.Error("ValidateSession() should fail with tampered cookie")
|
||||
}
|
||||
|
||||
if err != ErrInvalidSession {
|
||||
t.Errorf("ValidateSession() error = %v, want %v", err, ErrInvalidSession)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManager_ValidateSession_WrongKey(t *testing.T) {
|
||||
mgr1, _ := NewManager("signing-key-1", false)
|
||||
mgr2, _ := NewManager("signing-key-2", false)
|
||||
|
||||
// Create session with mgr1
|
||||
w := httptest.NewRecorder()
|
||||
_ = mgr1.CreateSession(w)
|
||||
|
||||
resp := w.Result()
|
||||
var sessionCookie *http.Cookie
|
||||
for _, c := range resp.Cookies() {
|
||||
if c.Name == CookieName {
|
||||
sessionCookie = c
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Try to validate with mgr2 (different key)
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.AddCookie(sessionCookie)
|
||||
|
||||
_, err := mgr2.ValidateSession(req)
|
||||
if err == nil {
|
||||
t.Error("ValidateSession() should fail with different signing key")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManager_ClearSession(t *testing.T) {
|
||||
mgr, _ := NewManager("test-signing-key-12345", false)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
mgr.ClearSession(w)
|
||||
|
||||
resp := w.Result()
|
||||
cookies := resp.Cookies()
|
||||
|
||||
var sessionCookie *http.Cookie
|
||||
for _, c := range cookies {
|
||||
if c.Name == CookieName {
|
||||
sessionCookie = c
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sessionCookie == nil {
|
||||
t.Fatal("ClearSession() did not set a cookie")
|
||||
}
|
||||
|
||||
if sessionCookie.MaxAge != -1 {
|
||||
t.Errorf("ClearSession() cookie MaxAge = %d, want -1", sessionCookie.MaxAge)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManager_IsAuthenticated(t *testing.T) {
|
||||
mgr, _ := NewManager("test-signing-key-12345", false)
|
||||
|
||||
// No session - should return false
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
if mgr.IsAuthenticated(req) {
|
||||
t.Error("IsAuthenticated() should return false with no session")
|
||||
}
|
||||
|
||||
// Create session
|
||||
w := httptest.NewRecorder()
|
||||
_ = mgr.CreateSession(w)
|
||||
|
||||
resp := w.Result()
|
||||
var sessionCookie *http.Cookie
|
||||
for _, c := range resp.Cookies() {
|
||||
if c.Name == CookieName {
|
||||
sessionCookie = c
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// With valid session - should return true
|
||||
req = httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.AddCookie(sessionCookie)
|
||||
|
||||
if !mgr.IsAuthenticated(req) {
|
||||
t.Error("IsAuthenticated() should return true with valid session")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManager_CookieAttributes(t *testing.T) {
|
||||
// Test with secure=true
|
||||
mgr, _ := NewManager("test-key", true)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
_ = mgr.CreateSession(w)
|
||||
|
||||
resp := w.Result()
|
||||
var sessionCookie *http.Cookie
|
||||
for _, c := range resp.Cookies() {
|
||||
if c.Name == CookieName {
|
||||
sessionCookie = c
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !sessionCookie.HttpOnly {
|
||||
t.Error("Cookie should have HttpOnly flag")
|
||||
}
|
||||
|
||||
if !sessionCookie.Secure {
|
||||
t.Error("Cookie should have Secure flag when manager created with secure=true")
|
||||
}
|
||||
|
||||
if sessionCookie.SameSite != http.SameSiteStrictMode {
|
||||
t.Errorf("Cookie SameSite = %v, want %v", sessionCookie.SameSite, http.SameSiteStrictMode)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user