Compare commits
2 Commits
bfe6c12a7a
...
b778469365
Author | SHA1 | Date | |
---|---|---|---|
b778469365 | |||
2bbff01c0d |
11
go.mod
11
go.mod
@ -5,22 +5,11 @@ go 1.24.6
|
|||||||
require (
|
require (
|
||||||
github.com/valyala/fasthttp v1.64.0
|
github.com/valyala/fasthttp v1.64.0
|
||||||
golang.org/x/crypto v0.41.0
|
golang.org/x/crypto v0.41.0
|
||||||
zombiezen.com/go/sqlite v1.4.2
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
|
||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
|
||||||
golang.org/x/sys v0.35.0 // indirect
|
golang.org/x/sys v0.35.0 // indirect
|
||||||
modernc.org/libc v1.65.7 // indirect
|
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
|
||||||
modernc.org/memory v1.11.0 // indirect
|
|
||||||
modernc.org/sqlite v1.37.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
49
go.sum
49
go.sum
@ -1,19 +1,7 @@
|
|||||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.64.0 h1:QBygLLQmiAyiXuRhthf0tuRkqAFcrC42dckN2S+N3og=
|
github.com/valyala/fasthttp v1.64.0 h1:QBygLLQmiAyiXuRhthf0tuRkqAFcrC42dckN2S+N3og=
|
||||||
@ -22,42 +10,5 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ
|
|||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
|
||||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
|
||||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
|
||||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
|
||||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
|
||||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
|
||||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
|
||||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
|
||||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
|
||||||
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
|
||||||
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
|
||||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
|
||||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
|
||||||
modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
|
|
||||||
modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
|
||||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
|
||||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
|
||||||
modernc.org/libc v1.65.7 h1:Ia9Z4yzZtWNtUIuiPuQ7Qf7kxYrxP1/jeHZzG8bFu00=
|
|
||||||
modernc.org/libc v1.65.7/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU=
|
|
||||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
|
||||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
|
||||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
|
||||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
|
||||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
|
||||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
|
||||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
|
||||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
|
||||||
modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs=
|
|
||||||
modernc.org/sqlite v1.37.1/go.mod h1:XwdRtsE1MpiBcL54+MbKcaDvcuej+IYSMfLN6gSKV8g=
|
|
||||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
|
||||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
|
||||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
|
||||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
|
||||||
zombiezen.com/go/sqlite v1.4.2 h1:KZXLrBuJ7tKNEm+VJcApLMeQbhmAUOKA5VWS93DfFRo=
|
|
||||||
zombiezen.com/go/sqlite v1.4.2/go.mod h1:5Kd4taTAD4MkBzT25mQ9uaAlLjyR0rFhsR6iINO70jc=
|
|
||||||
|
@ -24,17 +24,20 @@ func Middleware() router.Middleware {
|
|||||||
if existingSess, exists := session.Get(sessionID); exists {
|
if existingSess, exists := session.Get(sessionID); exists {
|
||||||
sess = existingSess
|
sess = existingSess
|
||||||
sess.Touch()
|
sess.Touch()
|
||||||
session.Store(sess)
|
|
||||||
|
|
||||||
if sess.UserID > 0 { // User session
|
if sess.UserID > 0 { // User session
|
||||||
user, err := users.Find(sess.UserID)
|
user, err := users.Find(sess.UserID)
|
||||||
if err == nil && user != nil {
|
if err == nil && user != nil {
|
||||||
ctx.SetUserValue("user", user)
|
ctx.SetUserValue("user", user)
|
||||||
|
} else {
|
||||||
|
// User not found, reset to guest session
|
||||||
|
sess.SetUserID(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
session.Store(sess)
|
||||||
setSessionCookie(ctx, sessionID)
|
setSessionCookie(ctx, sessionID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create guest session if none exists
|
// Create guest session if none exists
|
||||||
if sess == nil {
|
if sess == nil {
|
||||||
@ -89,8 +92,10 @@ func RequireGuest(paths ...string) router.Middleware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func IsAuthenticated(ctx router.Ctx) bool {
|
func IsAuthenticated(ctx router.Ctx) bool {
|
||||||
_, exists := ctx.UserValue("user").(*users.User)
|
if user, ok := ctx.UserValue("user").(*users.User); ok && user != nil {
|
||||||
return exists
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCurrentUser(ctx router.Ctx) *users.User {
|
func GetCurrentUser(ctx router.Ctx) *users.User {
|
||||||
@ -109,24 +114,42 @@ func GetCurrentSession(ctx router.Ctx) *session.Session {
|
|||||||
|
|
||||||
func Login(ctx router.Ctx, user *users.User) {
|
func Login(ctx router.Ctx, user *users.User) {
|
||||||
sess := ctx.UserValue("session").(*session.Session)
|
sess := ctx.UserValue("session").(*session.Session)
|
||||||
sess.RegenerateID()
|
|
||||||
sess.Set("user_id", user.ID)
|
// Update the session to be authenticated
|
||||||
|
sess.SetUserID(user.ID) // This updates the struct field
|
||||||
|
sess.RegenerateID() // Generate new ID for security
|
||||||
sess.SetFlash("success", fmt.Sprintf("Welcome back, %s!", user.Username))
|
sess.SetFlash("success", fmt.Sprintf("Welcome back, %s!", user.Username))
|
||||||
|
|
||||||
|
// Remove any old user_id from session data if it exists
|
||||||
|
sess.Delete("user_id")
|
||||||
|
|
||||||
session.Store(sess)
|
session.Store(sess)
|
||||||
|
|
||||||
|
// Update context values
|
||||||
ctx.SetUserValue("session", sess)
|
ctx.SetUserValue("session", sess)
|
||||||
ctx.SetUserValue("user", user)
|
ctx.SetUserValue("user", user)
|
||||||
|
|
||||||
|
// Update cookie with new session ID
|
||||||
|
setSessionCookie(ctx, sess.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Logout(ctx router.Ctx) {
|
func Logout(ctx router.Ctx) {
|
||||||
sessionID := cookies.GetCookie(ctx, SessionCookieName)
|
sess := ctx.UserValue("session").(*session.Session)
|
||||||
if sessionID != "" {
|
if sess != nil {
|
||||||
session.Delete(sessionID)
|
// Convert back to guest session
|
||||||
|
sess.SetUserID(0) // Reset to guest
|
||||||
|
sess.RegenerateID() // Generate new ID for security
|
||||||
|
|
||||||
|
// Clean up any user-related session data
|
||||||
|
sess.Delete("user_id")
|
||||||
|
|
||||||
|
session.Store(sess)
|
||||||
|
ctx.SetUserValue("session", sess)
|
||||||
|
|
||||||
|
// Update cookie with new session ID
|
||||||
|
setSessionCookie(ctx, sess.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteSessionCookie(ctx)
|
|
||||||
|
|
||||||
ctx.SetUserValue("session", nil)
|
|
||||||
ctx.SetUserValue("user", nil)
|
ctx.SetUserValue("user", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,20 +4,20 @@
|
|||||||
// # Basic Usage
|
// # Basic Usage
|
||||||
//
|
//
|
||||||
// // Generate token and store in session
|
// // Generate token and store in session
|
||||||
// token := csrf.GenerateToken(ctx, sessionManager)
|
// token := csrf.GenerateToken(ctx)
|
||||||
//
|
//
|
||||||
// // In templates - generate hidden input field
|
// // In templates - generate hidden input field
|
||||||
// hiddenField := csrf.HiddenField(ctx, sessionManager)
|
// hiddenField := csrf.HiddenField(ctx)
|
||||||
//
|
//
|
||||||
// // Verify form submission
|
// // Verify form submission
|
||||||
// if !csrf.ValidateToken(ctx, sessionManager, formToken) {
|
// if !csrf.ValidateToken(ctx, formToken) {
|
||||||
// // Handle CSRF validation failure
|
// // Handle CSRF validation failure
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// # Middleware Integration
|
// # Middleware Integration
|
||||||
//
|
//
|
||||||
// // Add CSRF middleware to protected routes
|
// // Add CSRF middleware to protected routes
|
||||||
// r.Use(middleware.CSRF(authManager))
|
// r.Use(csrf.Middleware())
|
||||||
package csrf
|
package csrf
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -25,7 +25,6 @@ import (
|
|||||||
"crypto/subtle"
|
"crypto/subtle"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"dk/internal/router"
|
"dk/internal/router"
|
||||||
"dk/internal/session"
|
"dk/internal/session"
|
||||||
@ -37,11 +36,10 @@ const (
|
|||||||
TokenLength = 32
|
TokenLength = 32
|
||||||
TokenFieldName = "_csrf_token"
|
TokenFieldName = "_csrf_token"
|
||||||
SessionKey = "csrf_token"
|
SessionKey = "csrf_token"
|
||||||
SessionCtxKey = "session" // Same as middleware.SessionKey
|
SessionCtxKey = "session"
|
||||||
CookieName = "_csrf"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetCurrentSession retrieves the session from context (mirrors middleware function)
|
// GetCurrentSession retrieves the session from context
|
||||||
func GetCurrentSession(ctx router.Ctx) *session.Session {
|
func GetCurrentSession(ctx router.Ctx) *session.Session {
|
||||||
if sess, ok := ctx.UserValue(SessionCtxKey).(*session.Session); ok {
|
if sess, ok := ctx.UserValue(SessionCtxKey).(*session.Session); ok {
|
||||||
return sess
|
return sess
|
||||||
@ -49,7 +47,7 @@ func GetCurrentSession(ctx router.Ctx) *session.Session {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateToken creates a new CSRF token and stores it in the session or cookie
|
// GenerateToken creates a new CSRF token and stores it in the session
|
||||||
func GenerateToken(ctx router.Ctx) string {
|
func GenerateToken(ctx router.Ctx) string {
|
||||||
// Generate cryptographically secure random bytes
|
// Generate cryptographically secure random bytes
|
||||||
tokenBytes := make([]byte, TokenLength)
|
tokenBytes := make([]byte, TokenLength)
|
||||||
@ -60,54 +58,43 @@ func GenerateToken(ctx router.Ctx) string {
|
|||||||
|
|
||||||
token := base64.URLEncoding.EncodeToString(tokenBytes)
|
token := base64.URLEncoding.EncodeToString(tokenBytes)
|
||||||
|
|
||||||
// Store token in session if user is authenticated, otherwise use cookie
|
// Store token in session (both guests and authenticated users have sessions)
|
||||||
if session := GetCurrentSession(ctx); session != nil {
|
if sess := GetCurrentSession(ctx); sess != nil {
|
||||||
StoreToken(session, token)
|
StoreToken(sess, token)
|
||||||
} else {
|
session.Store(sess)
|
||||||
// Store in cookie for guest users
|
|
||||||
StoreTokenInCookie(ctx, token)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetToken retrieves the current CSRF token from session or cookie, generating one if needed
|
// GetToken retrieves the current CSRF token from session, generating one if needed
|
||||||
func GetToken(ctx router.Ctx) string {
|
func GetToken(ctx router.Ctx) string {
|
||||||
session := GetCurrentSession(ctx)
|
sess := GetCurrentSession(ctx)
|
||||||
|
if sess == nil {
|
||||||
|
return "" // No session available
|
||||||
|
}
|
||||||
|
|
||||||
if session != nil {
|
// Check for existing token
|
||||||
// Authenticated user - check session first
|
if existingToken := GetStoredToken(sess); existingToken != "" {
|
||||||
if existingToken := GetStoredToken(session); existingToken != "" {
|
|
||||||
return existingToken
|
return existingToken
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Guest user - check cookie first
|
|
||||||
if existingToken := GetTokenFromCookie(ctx); existingToken != "" {
|
|
||||||
return existingToken
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate new token if none exists
|
// Generate new token if none exists
|
||||||
return GenerateToken(ctx)
|
return GenerateToken(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateToken verifies a CSRF token against the stored session or cookie token
|
// ValidateToken verifies a CSRF token against the stored session token
|
||||||
func ValidateToken(ctx router.Ctx, submittedToken string) bool {
|
func ValidateToken(ctx router.Ctx, submittedToken string) bool {
|
||||||
if submittedToken == "" {
|
if submittedToken == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var storedToken string
|
sess := GetCurrentSession(ctx)
|
||||||
session := GetCurrentSession(ctx)
|
if sess == nil {
|
||||||
|
return false // No session
|
||||||
if session != nil {
|
|
||||||
// Authenticated user - get token from session
|
|
||||||
storedToken = GetStoredToken(session)
|
|
||||||
} else {
|
|
||||||
// Guest user - get token from cookie
|
|
||||||
storedToken = GetTokenFromCookie(ctx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storedToken := GetStoredToken(sess)
|
||||||
if storedToken == "" {
|
if storedToken == "" {
|
||||||
return false // No stored token
|
return false // No stored token
|
||||||
}
|
}
|
||||||
@ -133,14 +120,13 @@ func GetStoredToken(sess *session.Session) string {
|
|||||||
|
|
||||||
// RotateToken generates a new token and replaces the old one in the session
|
// RotateToken generates a new token and replaces the old one in the session
|
||||||
func RotateToken(ctx router.Ctx) string {
|
func RotateToken(ctx router.Ctx) string {
|
||||||
session := GetCurrentSession(ctx)
|
sess := GetCurrentSession(ctx)
|
||||||
if session == nil {
|
if sess == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate new token
|
// Generate new token (this will automatically store it)
|
||||||
newToken := GenerateToken(ctx)
|
newToken := GenerateToken(ctx)
|
||||||
|
|
||||||
return newToken
|
return newToken
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,23 +167,9 @@ func ValidateFormToken(ctx router.Ctx) bool {
|
|||||||
return ValidateToken(ctx, string(tokenBytes))
|
return ValidateToken(ctx, string(tokenBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// StoreTokenInCookie stores a CSRF token in a cookie for guest users
|
// GetTokenFromCookie retrieves a CSRF token from cookie (legacy support)
|
||||||
func StoreTokenInCookie(ctx router.Ctx, token string) {
|
|
||||||
cookie := &fasthttp.Cookie{}
|
|
||||||
cookie.SetKey(CookieName)
|
|
||||||
cookie.SetValue(token)
|
|
||||||
cookie.SetHTTPOnly(true)
|
|
||||||
cookie.SetSameSite(fasthttp.CookieSameSiteStrictMode)
|
|
||||||
cookie.SetSecure(false) // Set to true in production with HTTPS
|
|
||||||
cookie.SetExpire(time.Now().Add(24 * time.Hour)) // Expire in 24 hours
|
|
||||||
cookie.SetPath("/")
|
|
||||||
|
|
||||||
ctx.Response.Header.SetCookie(cookie)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTokenFromCookie retrieves a CSRF token from cookie for guest users
|
|
||||||
func GetTokenFromCookie(ctx router.Ctx) string {
|
func GetTokenFromCookie(ctx router.Ctx) string {
|
||||||
return string(ctx.Request.Header.Cookie(CookieName))
|
return string(ctx.Request.Header.Cookie("_csrf"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Middleware returns a middleware function that automatically validates CSRF tokens
|
// Middleware returns a middleware function that automatically validates CSRF tokens
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"dk/internal/auth"
|
"dk/internal/auth"
|
||||||
"dk/internal/csrf"
|
|
||||||
"dk/internal/models/users"
|
"dk/internal/models/users"
|
||||||
"dk/internal/password"
|
"dk/internal/password"
|
||||||
"dk/internal/router"
|
"dk/internal/router"
|
||||||
@ -77,12 +76,7 @@ func processLogin(ctx router.Ctx, _ []string) {
|
|||||||
|
|
||||||
auth.Login(ctx, user)
|
auth.Login(ctx, user)
|
||||||
|
|
||||||
// Transfer CSRF token from cookie to session for authenticated user
|
// CSRF token is already in session, no need to transfer from cookie
|
||||||
if cookieToken := csrf.GetTokenFromCookie(ctx); cookieToken != "" {
|
|
||||||
if sess := ctx.UserValue("session").(*session.Session); sess != nil {
|
|
||||||
csrf.StoreToken(sess, cookieToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Redirect("/", fasthttp.StatusFound)
|
ctx.Redirect("/", fasthttp.StatusFound)
|
||||||
}
|
}
|
||||||
@ -158,28 +152,16 @@ func processRegister(ctx router.Ctx, _ []string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store old session ID before creating new one
|
// Auto-login after registration (this will update the current session)
|
||||||
oldSess := ctx.UserValue("session").(*session.Session)
|
|
||||||
oldSessionID := oldSess.ID
|
|
||||||
|
|
||||||
// Auto-login after registration
|
|
||||||
auth.Login(ctx, user)
|
auth.Login(ctx, user)
|
||||||
|
|
||||||
// Clean up old guest session
|
// Update success message (Login already sets a message, so override it)
|
||||||
session.Delete(oldSessionID)
|
|
||||||
|
|
||||||
// Set success message
|
|
||||||
if sess := ctx.UserValue("session").(*session.Session); sess != nil {
|
if sess := ctx.UserValue("session").(*session.Session); sess != nil {
|
||||||
sess.SetFlash("success", fmt.Sprintf("Greetings, %s!", user.Username))
|
sess.SetFlash("success", fmt.Sprintf("Greetings, %s!", user.Username))
|
||||||
session.Store(sess)
|
session.Store(sess)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer CSRF token from cookie to session for authenticated user
|
// CSRF token is already in session, no need to transfer from cookie
|
||||||
if cookieToken := csrf.GetTokenFromCookie(ctx); cookieToken != "" {
|
|
||||||
if sess := ctx.UserValue("session").(*session.Session); sess != nil {
|
|
||||||
csrf.StoreToken(sess, cookieToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.Redirect("/", fasthttp.StatusFound)
|
ctx.Redirect("/", fasthttp.StatusFound)
|
||||||
}
|
}
|
||||||
|
@ -131,10 +131,13 @@ func (sm *SessionManager) load() {
|
|||||||
if data != nil && data.ExpiresAt > now {
|
if data != nil && data.ExpiresAt > now {
|
||||||
sess := &Session{
|
sess := &Session{
|
||||||
ID: id,
|
ID: id,
|
||||||
UserID: data.UserID,
|
UserID: data.UserID, // Make sure we restore the UserID properly
|
||||||
ExpiresAt: data.ExpiresAt,
|
ExpiresAt: data.ExpiresAt,
|
||||||
Data: data.Data,
|
Data: data.Data,
|
||||||
}
|
}
|
||||||
|
if sess.Data == nil {
|
||||||
|
sess.Data = make(map[string]any)
|
||||||
|
}
|
||||||
sm.sessions[id] = sess
|
sm.sessions[id] = sess
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -155,7 +158,7 @@ func (sm *SessionManager) Save() error {
|
|||||||
sessionsData := make(map[string]*sessionData, len(sm.sessions))
|
sessionsData := make(map[string]*sessionData, len(sm.sessions))
|
||||||
for id, sess := range sm.sessions {
|
for id, sess := range sm.sessions {
|
||||||
sessionsData[id] = &sessionData{
|
sessionsData[id] = &sessionData{
|
||||||
UserID: sess.UserID,
|
UserID: sess.UserID, // Save the actual UserID from the struct
|
||||||
ExpiresAt: sess.ExpiresAt,
|
ExpiresAt: sess.ExpiresAt,
|
||||||
Data: sess.Data,
|
Data: sess.Data,
|
||||||
}
|
}
|
||||||
|
@ -84,6 +84,11 @@ func (s *Session) RegenerateID() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetUserID updates the session's user ID (for login/logout)
|
||||||
|
func (s *Session) SetUserID(userID int) {
|
||||||
|
s.UserID = userID
|
||||||
|
}
|
||||||
|
|
||||||
// generateID creates a random session ID
|
// generateID creates a random session ID
|
||||||
func generateID() string {
|
func generateID() string {
|
||||||
bytes := make([]byte, IDLength)
|
bytes := make([]byte, IDLength)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user