From 3a7b71ae3e023e46da45ae0f9894568d48c09c04 Mon Sep 17 00:00:00 2001 From: Stavros Date: Sun, 25 May 2025 12:38:21 +0300 Subject: [PATCH] feat: generate a unique id for the cookie names based on the domain (#161) * feat: generate a unique id for the cookie names based on the domain * tests: fix tests --- cmd/root.go | 26 ++++++++++++++++++-------- go.mod | 1 + internal/api/api_test.go | 27 +++++++++++++++++++-------- internal/auth/auth.go | 2 +- internal/constants/constants.go | 5 +++++ internal/handlers/handlers.go | 12 ++++++------ internal/types/config.go | 19 +++++++++++-------- internal/utils/utils.go | 16 ++++++++++++++++ 8 files changed, 77 insertions(+), 31 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index d56247d..8212905 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,6 +2,7 @@ package cmd import ( "errors" + "fmt" "os" "strings" "time" @@ -67,6 +68,12 @@ var rootCmd = &cobra.Command{ HandleError(err, "Failed to get upper domain") log.Info().Str("domain", domain).Msg("Using domain for cookie store") + // Generate cookie name + cookieId := utils.GenerateIdentifier(strings.Split(domain, ".")[0]) + sessionCookieName := fmt.Sprintf("%s-%s", constants.SessionCookieName, cookieId) + csrfCookieName := fmt.Sprintf("%s-%s", constants.CsrfCookieName, cookieId) + redirectCookieName := fmt.Sprintf("%s-%s", constants.RedirectCookieName, cookieId) + // Create OAuth config oauthConfig := types.OAuthConfig{ GithubClientId: config.GithubClientId, @@ -93,6 +100,8 @@ var rootCmd = &cobra.Command{ ForgotPasswordMessage: config.FogotPasswordMessage, BackgroundImage: config.BackgroundImage, OAuthAutoRedirect: config.OAuthAutoRedirect, + CsrfCookieName: csrfCookieName, + RedirectCookieName: redirectCookieName, } // Create api config @@ -103,14 +112,15 @@ var rootCmd = &cobra.Command{ // Create auth config authConfig := types.AuthConfig{ - Users: users, - OauthWhitelist: config.OAuthWhitelist, - Secret: config.Secret, - CookieSecure: config.CookieSecure, - SessionExpiry: config.SessionExpiry, - Domain: domain, - LoginTimeout: config.LoginTimeout, - LoginMaxRetries: config.LoginMaxRetries, + Users: users, + OauthWhitelist: config.OAuthWhitelist, + Secret: config.Secret, + CookieSecure: config.CookieSecure, + SessionExpiry: config.SessionExpiry, + Domain: domain, + LoginTimeout: config.LoginTimeout, + LoginMaxRetries: config.LoginMaxRetries, + SessionCookieName: sessionCookieName, } // Create hooks config diff --git a/go.mod b/go.mod index e514646..145712b 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/gin-gonic/gin v1.10.0 github.com/go-playground/validator/v10 v10.26.0 github.com/google/go-querystring v1.1.0 + github.com/google/uuid v1.6.0 github.com/mdp/qrterminal/v3 v3.2.1 github.com/rs/zerolog v1.34.0 github.com/spf13/cobra v1.9.1 diff --git a/internal/api/api_test.go b/internal/api/api_test.go index 23c1baf..8e1352c 100644 --- a/internal/api/api_test.go +++ b/internal/api/api_test.go @@ -28,21 +28,29 @@ var apiConfig = types.APIConfig{ // Simple handlers config for tests var handlersConfig = types.HandlersConfig{ AppURL: "http://localhost:8080", + Domain: "localhost", DisableContinue: false, + CookieSecure: false, Title: "Tinyauth", GenericName: "Generic", ForgotPasswordMessage: "Some message", + CsrfCookieName: "tinyauth-csrf", + RedirectCookieName: "tinyauth-redirect", + BackgroundImage: "https://example.com/image.png", + OAuthAutoRedirect: "none", } // Simple auth config for tests var authConfig = types.AuthConfig{ - Users: types.Users{}, - OauthWhitelist: "", - Secret: "super-secret-api-thing-for-tests", // It is 32 chars long - CookieSecure: false, - SessionExpiry: 3600, - LoginTimeout: 0, - LoginMaxRetries: 0, + Users: types.Users{}, + OauthWhitelist: "", + Secret: "super-secret-api-thing-for-tests", // It is 32 chars long + CookieSecure: false, + SessionExpiry: 3600, + LoginTimeout: 0, + LoginMaxRetries: 0, + SessionCookieName: "tinyauth-session", + Domain: "localhost", } // Simple hooks config for tests @@ -206,6 +214,9 @@ func TestAppContext(t *testing.T) { Title: "Tinyauth", GenericName: "Generic", ForgotPasswordMessage: "Some message", + BackgroundImage: "https://example.com/image.png", + OAuthAutoRedirect: "none", + Domain: "localhost", } // We should get the username back @@ -234,7 +245,7 @@ func TestUserContext(t *testing.T) { // Set the cookie req.AddCookie(&http.Cookie{ - Name: "tinyauth", + Name: "tinyauth-session", Value: cookie, }) diff --git a/internal/auth/auth.go b/internal/auth/auth.go index d593f2f..b416a88 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -45,7 +45,7 @@ func (auth *Auth) GetSession(c *gin.Context) (*sessions.Session, error) { } // Get session - session, err := store.Get(c.Request, "tinyauth") + session, err := store.Get(c.Request, auth.Config.SessionCookieName) if err != nil { log.Error().Err(err).Msg("Failed to get session") return nil, err diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 00b6feb..b01001c 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -21,3 +21,8 @@ type Claims struct { var Version = "development" var CommitHash = "n/a" var BuildTimestamp = "n/a" + +// Cookie names +var SessionCookieName = "tinyauth-session" +var CsrfCookieName = "tinyauth-csrf" +var RedirectCookieName = "tinyauth-redirect" diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 141373e..0966ffe 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -581,7 +581,7 @@ func (h *Handlers) OauthUrlHandler(c *gin.Context) { log.Debug().Msg("Got auth URL") // Set CSRF cookie - c.SetCookie("tinyauth-csrf", state, int(time.Hour.Seconds()), "/", "", h.Config.CookieSecure, true) + c.SetCookie(h.Config.CsrfCookieName, state, int(time.Hour.Seconds()), "/", "", h.Config.CookieSecure, true) // Get redirect URI redirectURI := c.Query("redirect_uri") @@ -589,7 +589,7 @@ func (h *Handlers) OauthUrlHandler(c *gin.Context) { // Set redirect cookie if redirect URI is provided if redirectURI != "" { log.Debug().Str("redirectURI", redirectURI).Msg("Setting redirect cookie") - c.SetCookie("tinyauth-redirect", redirectURI, int(time.Hour.Seconds()), "/", "", h.Config.CookieSecure, true) + c.SetCookie(h.Config.RedirectCookieName, redirectURI, int(time.Hour.Seconds()), "/", "", h.Config.CookieSecure, true) } // Return auth URL @@ -620,7 +620,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) { state := c.Query("state") // Get CSRF cookie - csrfCookie, err := c.Cookie("tinyauth-csrf") + csrfCookie, err := c.Cookie(h.Config.CsrfCookieName) if err != nil { log.Debug().Msg("No CSRF cookie") @@ -638,7 +638,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) { } // Clean up CSRF cookie - c.SetCookie("tinyauth-csrf", "", -1, "/", "", h.Config.CookieSecure, true) + c.SetCookie(h.Config.CsrfCookieName, "", -1, "/", "", h.Config.CookieSecure, true) // Get code code := c.Query("code") @@ -737,7 +737,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) { }) // Check if we have a redirect URI - redirectCookie, err := c.Cookie("tinyauth-redirect") + redirectCookie, err := c.Cookie(h.Config.RedirectCookieName) if err != nil { log.Debug().Msg("No redirect cookie") @@ -762,7 +762,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) { } // Clean up redirect cookie - c.SetCookie("tinyauth-redirect", "", -1, "/", "", h.Config.CookieSecure, true) + c.SetCookie(h.Config.RedirectCookieName, "", -1, "/", "", h.Config.CookieSecure, true) // Redirect to continue with the redirect URI c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/continue?%s", h.Config.AppURL, queries.Encode())) diff --git a/internal/types/config.go b/internal/types/config.go index fdf7bf8..f448e7f 100644 --- a/internal/types/config.go +++ b/internal/types/config.go @@ -48,6 +48,8 @@ type HandlersConfig struct { ForgotPasswordMessage string BackgroundImage string OAuthAutoRedirect string + CsrfCookieName string + RedirectCookieName string } // OAuthConfig is the configuration for the providers @@ -73,14 +75,15 @@ type APIConfig struct { // AuthConfig is the configuration for the auth service type AuthConfig struct { - Users Users - OauthWhitelist string - SessionExpiry int - Secret string - CookieSecure bool - Domain string - LoginTimeout int - LoginMaxRetries int + Users Users + OauthWhitelist string + SessionExpiry int + Secret string + CookieSecure bool + Domain string + LoginTimeout int + LoginMaxRetries int + SessionCookieName string } // HooksConfig is the configuration for the hooks service diff --git a/internal/utils/utils.go b/internal/utils/utils.go index e49075f..9c2a6ab 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -10,6 +10,7 @@ import ( "tinyauth/internal/constants" "tinyauth/internal/types" + "github.com/google/uuid" "github.com/rs/zerolog/log" ) @@ -344,3 +345,18 @@ func SanitizeHeader(header string) string { return -1 }, header) } + +// Generate a static identifier from a string +func GenerateIdentifier(str string) string { + // Create a new UUID + uuid := uuid.NewSHA1(uuid.NameSpaceURL, []byte(str)) + + // Convert the UUID to a string + uuidString := uuid.String() + + // Show the UUID + log.Debug().Str("uuid", uuidString).Msg("Generated UUID") + + // Convert the UUID to a string + return strings.Split(uuidString, "-")[0] +}