xtablo-source/backend/internal/web/csrf_debug_test.go
Arthur Belleville 389e1bc8b4
feat(02-07): gorilla/csrf integration — mount middleware, wire all forms, env-driven key
- auth.Mount(env, key) wraps csrf.Protect with locked D-14/D-24 options
- auth.LoadKeyFromEnv() reads SESSION_SECRET, hex-decodes, validates 32 bytes; fails fast on error
- ui.CSRFField(token) templ component renders hidden _csrf input
- Layout, LoginPage/Fragment, SignupPage/Fragment, Index all embed @ui.CSRFField(csrfToken)
- Handlers thread csrf.Token(r) into every page/fragment render call
- NewRouter mounts auth.Mount after ResolveSession, before all route groups (D-24)
- main.go calls auth.LoadKeyFromEnv(); logs.Fatalf on missing/invalid SESSION_SECRET
- SESSION_SECRET documented in .env.example with openssl rand -hex 32 instruction
- go.mod: gorilla/csrf v1.7.3 (direct); prior tests updated with getCSRFToken helper
- All Plan 04/05/06 tests updated to acquire and submit valid _csrf tokens
2026-05-14 22:59:06 +02:00

66 lines
1.7 KiB
Go

package web
import (
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"backend/internal/auth"
"backend/internal/db/sqlc"
)
func TestCSRF_Debug(t *testing.T) {
pool, cleanup := setupTestDB(t)
defer cleanup()
q := sqlc.New(pool)
store := auth.NewStore(q)
router := newTestRouter(q, store)
// GET /login and collect cookies
getReq := httptest.NewRequest(http.MethodGet, "/login", nil)
getRec := httptest.NewRecorder()
router.ServeHTTP(getRec, getReq)
t.Logf("GET /login status: %d", getRec.Code)
t.Logf("GET /login cookies:")
for _, c := range getRec.Result().Cookies() {
t.Logf(" %s=%s (httponly=%v, secure=%v)", c.Name, c.Value[:min(len(c.Value), 20)], c.HttpOnly, c.Secure)
}
body := getRec.Body.String()
const needle = `name="_csrf" value="`
idx := strings.Index(body, needle)
if idx == -1 {
t.Fatal("no _csrf hidden input found")
}
rest := body[idx+len(needle):]
end := strings.Index(rest, `"`)
token := rest[:end]
t.Logf("Extracted CSRF token: %s...", token[:min(len(token), 20)])
form := url.Values{"email": {"x@y.com"}, "password": {"correct-horse-12"}, "_csrf": {token}}
postReq := httptest.NewRequest(http.MethodPost, "/login", strings.NewReader(form.Encode()))
postReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
for _, c := range getRec.Result().Cookies() {
t.Logf("Adding cookie to POST: %s", c.Name)
postReq.AddCookie(c)
}
t.Logf("POST cookies count: %d", len(getRec.Result().Cookies()))
postRec := httptest.NewRecorder()
router.ServeHTTP(postRec, postReq)
t.Logf("POST /login status: %d", postRec.Code)
if postRec.Code == 403 {
t.Logf("403 body: %s", postRec.Body.String()[:min(len(postRec.Body.String()), 200)])
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}