diff --git a/go.mod b/go.mod index 66589b3..ca4687b 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ require github.com/a-h/templ v0.2.707 require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gorilla/securecookie v1.1.2 // indirect + github.com/gorilla/sessions v1.2.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.9.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index acf220e..aaddc24 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= +github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= +github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= +github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= diff --git a/kit/kit.go b/kit/kit.go index ea5c7d9..0a88a2c 100644 --- a/kit/kit.go +++ b/kit/kit.go @@ -3,18 +3,18 @@ package kit import ( "context" "encoding/json" + "fmt" "log/slog" "net/http" "os" "github.com/a-h/templ" + "github.com/gorilla/sessions" ) -type HandlerFunc func(kit *Kit) error +var store *sessions.CookieStore -type Authenticater interface { - Authenticate(http.ResponseWriter, *http.Request) error -} +type HandlerFunc func(kit *Kit) error type ErrorHandlerFunc func(kit *Kit, err error) @@ -50,6 +50,13 @@ func (kit *Kit) Auth() Auth { return value } +// GetSession return a session by its name. GetSession always +// returns a session even if it does not exist. +func (kit *Kit) GetSession(name string) *sessions.Session { + sess, _ := store.Get(kit.Request, name) + return sess +} + // Redirect with HTMX support. func (kit *Kit) Redirect(status int, url string) error { if len(kit.Request.Header.Get("HX-Request")) > 0 { @@ -101,7 +108,7 @@ func Handler(h HandlerFunc) http.HandlerFunc { } type AuthenticationConfig struct { - AuthFunc func(http.ResponseWriter, *http.Request) (Auth, error) + AuthFunc func(*Kit) (Auth, error) RedirectURL string } @@ -112,7 +119,7 @@ func WithAuthentication(config AuthenticationConfig, strict bool) func(http.Hand Response: w, Request: r, } - auth, err := config.AuthFunc(w, r) + auth, err := config.AuthFunc(kit) if err != nil { errorHandler(kit, err) return @@ -121,9 +128,7 @@ func WithAuthentication(config AuthenticationConfig, strict bool) func(http.Hand kit.Redirect(http.StatusSeeOther, config.RedirectURL) return } - ctx := context.WithValue(r.Context(), AuthKey{}, auth) - next.ServeHTTP(w, r.WithContext(ctx)) }) } @@ -140,3 +145,17 @@ func IsProduction() bool { func Env() string { return os.Getenv("APP_ENV") } + +// initialize the store here so the environment variables are +// already initialized. Calling NewCookieStore() from outside of +// a function scope won't work. +func init() { + appSecret := os.Getenv("APP_SECRET") + if len(appSecret) < 32 { + // For security reasons we are calling os.Exit(1) here so Go's panic recover won't + // recover the application without a valid APP_SECRET set. + fmt.Println("invalid APP_SECRET variable. Are you sure you have set the APP_SECRET in your .env file?") + os.Exit(1) + } + store = sessions.NewCookieStore([]byte(appSecret)) +}