refactor: store redirect URI in tinyauth session cookie

This commit is contained in:
Stavros
2025-04-06 20:37:02 +03:00
parent df849d5a5c
commit bd7e160e10
6 changed files with 21 additions and 27 deletions

View File

@@ -2,7 +2,6 @@ package cmd
import ( import (
"errors" "errors"
"fmt"
"os" "os"
"strings" "strings"
"time" "time"
@@ -94,10 +93,8 @@ var rootCmd = &cobra.Command{
} }
// Create handlers config // Create handlers config
serverConfig := types.HandlersConfig{ handlersConfig := types.HandlersConfig{
AppURL: config.AppURL, AppURL: config.AppURL,
Domain: fmt.Sprintf(".%s", domain),
CookieSecure: config.CookieSecure,
DisableContinue: config.DisableContinue, DisableContinue: config.DisableContinue,
Title: config.Title, Title: config.Title,
GenericName: config.GenericName, GenericName: config.GenericName,
@@ -141,7 +138,7 @@ var rootCmd = &cobra.Command{
hooks := hooks.NewHooks(auth, providers) hooks := hooks.NewHooks(auth, providers)
// Create handlers // Create handlers
handlers := handlers.NewHandlers(serverConfig, auth, hooks, providers, docker) handlers := handlers.NewHandlers(handlersConfig, auth, hooks, providers, docker)
// Create API // Create API
api := api.NewAPI(apiConfig, handlers) api := api.NewAPI(apiConfig, handlers)

View File

@@ -28,8 +28,6 @@ var apiConfig = types.APIConfig{
// Simple handlers config for tests // Simple handlers config for tests
var handlersConfig = types.HandlersConfig{ var handlersConfig = types.HandlersConfig{
AppURL: "http://localhost:8080", AppURL: "http://localhost:8080",
Domain: ".localhost",
CookieSecure: false,
DisableContinue: false, DisableContinue: false,
Title: "Tinyauth", Title: "Tinyauth",
GenericName: "Generic", GenericName: "Generic",

View File

@@ -178,6 +178,7 @@ func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie)
session.Values["provider"] = data.Provider session.Values["provider"] = data.Provider
session.Values["expiry"] = time.Now().Add(time.Duration(sessionExpiry) * time.Second).Unix() session.Values["expiry"] = time.Now().Add(time.Duration(sessionExpiry) * time.Second).Unix()
session.Values["totpPending"] = data.TotpPending session.Values["totpPending"] = data.TotpPending
session.Values["redirectURI"] = data.RedirectURI
// Save session // Save session
err = session.Save(c.Request, c.Writer) err = session.Save(c.Request, c.Writer)
@@ -231,15 +232,17 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error)
cookieProvider := session.Values["provider"] cookieProvider := session.Values["provider"]
cookieExpiry := session.Values["expiry"] cookieExpiry := session.Values["expiry"]
cookieTotpPending := session.Values["totpPending"] cookieTotpPending := session.Values["totpPending"]
cookieRedirectURI := session.Values["redirectURI"]
// Convert interfaces to correct types // Convert interfaces to correct types
username, usernameOk := cookieUsername.(string) username, usernameOk := cookieUsername.(string)
provider, providerOk := cookieProvider.(string) provider, providerOk := cookieProvider.(string)
expiry, expiryOk := cookieExpiry.(int64) expiry, expiryOk := cookieExpiry.(int64)
totpPending, totpPendingOk := cookieTotpPending.(bool) totpPending, totpPendingOk := cookieTotpPending.(bool)
redirectURI, redirectURIOk := cookieRedirectURI.(string)
// Check if the cookie is invalid // Check if the cookie is invalid
if !usernameOk || !providerOk || !expiryOk || !totpPendingOk { if !usernameOk || !providerOk || !expiryOk || !totpPendingOk || !redirectURIOk {
log.Warn().Msg("Session cookie invalid") log.Warn().Msg("Session cookie invalid")
return types.SessionCookie{}, nil return types.SessionCookie{}, nil
} }
@@ -262,6 +265,7 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error)
Username: username, Username: username,
Provider: provider, Provider: provider,
TotpPending: totpPending, TotpPending: totpPending,
RedirectURI: redirectURI,
}, nil }, nil
} }

View File

@@ -248,16 +248,16 @@ func (h *Handlers) LoginHandler(c *gin.Context) {
} }
log.Debug().Msg("Got login request") log.Debug().Msg("Got login request")
// Get client IP for rate limiting // Get client IP for rate limiting
clientIP := c.ClientIP() clientIP := c.ClientIP()
// Create an identifier for rate limiting (username or IP if username doesn't exist yet) // Create an identifier for rate limiting (username or IP if username doesn't exist yet)
rateIdentifier := login.Username rateIdentifier := login.Username
if rateIdentifier == "" { if rateIdentifier == "" {
rateIdentifier = clientIP rateIdentifier = clientIP
} }
// Check if the account is locked due to too many failed attempts // Check if the account is locked due to too many failed attempts
locked, remainingTime := h.Auth.IsAccountLocked(rateIdentifier) locked, remainingTime := h.Auth.IsAccountLocked(rateIdentifier)
if locked { if locked {
@@ -299,7 +299,7 @@ func (h *Handlers) LoginHandler(c *gin.Context) {
} }
log.Debug().Msg("Password correct, checking totp") log.Debug().Msg("Password correct, checking totp")
// Record successful login attempt (will reset failed attempt counter) // Record successful login attempt (will reset failed attempt counter)
h.Auth.RecordLoginAttempt(rateIdentifier, true) h.Auth.RecordLoginAttempt(rateIdentifier, true)
@@ -420,9 +420,6 @@ func (h *Handlers) LogoutHandler(c *gin.Context) {
log.Debug().Msg("Cleaning up redirect cookie") log.Debug().Msg("Cleaning up redirect cookie")
// Clean up redirect cookie if it exists
c.SetCookie("tinyauth_redirect_uri", "", -1, "/", h.Config.Domain, h.Config.CookieSecure, true)
// Return logged out // Return logged out
c.JSON(200, gin.H{ c.JSON(200, gin.H{
"status": 200, "status": 200,
@@ -529,7 +526,9 @@ func (h *Handlers) OauthUrlHandler(c *gin.Context) {
// Set redirect cookie if redirect URI is provided // Set redirect cookie if redirect URI is provided
if redirectURI != "" { if redirectURI != "" {
log.Debug().Str("redirectURI", redirectURI).Msg("Setting redirect cookie") log.Debug().Str("redirectURI", redirectURI).Msg("Setting redirect cookie")
c.SetCookie("tinyauth_redirect_uri", redirectURI, 3600, "/", h.Config.Domain, h.Config.CookieSecure, true) h.Auth.CreateSessionCookie(c, &types.SessionCookie{
RedirectURI: redirectURI,
})
} }
// Tailscale does not have an auth url so we create a random code (does not need to be secure) to avoid caching and send it // Tailscale does not have an auth url so we create a random code (does not need to be secure) to avoid caching and send it
@@ -651,28 +650,25 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) {
log.Debug().Msg("Email whitelisted") log.Debug().Msg("Email whitelisted")
// Create session cookie // Get redirect URI
cookie, err := h.Auth.GetSessionCookie(c)
// Create session cookie (also cleans up redirect cookie)
h.Auth.CreateSessionCookie(c, &types.SessionCookie{ h.Auth.CreateSessionCookie(c, &types.SessionCookie{
Username: email, Username: email,
Provider: providerName.Provider, Provider: providerName.Provider,
}) })
// Get redirect URI
redirectURI, err := c.Cookie("tinyauth_redirect_uri")
// If it is empty it means that no redirect_uri was provided to the login screen so we just log in // If it is empty it means that no redirect_uri was provided to the login screen so we just log in
if err != nil { if err != nil {
c.Redirect(http.StatusPermanentRedirect, h.Config.AppURL) c.Redirect(http.StatusPermanentRedirect, h.Config.AppURL)
} }
log.Debug().Str("redirectURI", redirectURI).Msg("Got redirect URI") log.Debug().Str("redirectURI", cookie.RedirectURI).Msg("Got redirect URI")
// Clean up redirect cookie since we already have the value
c.SetCookie("tinyauth_redirect_uri", "", -1, "/", h.Config.Domain, h.Config.CookieSecure, true)
// Build query // Build query
queries, err := query.Values(types.LoginQuery{ queries, err := query.Values(types.LoginQuery{
RedirectURI: redirectURI, RedirectURI: cookie.RedirectURI,
}) })
log.Debug().Msg("Got redirect query") log.Debug().Msg("Got redirect query")

View File

@@ -40,8 +40,6 @@ type Config struct {
// Server configuration // Server configuration
type HandlersConfig struct { type HandlersConfig struct {
AppURL string AppURL string
Domain string
CookieSecure bool
DisableContinue bool DisableContinue bool
GenericName string GenericName string
Title string Title string

View File

@@ -27,6 +27,7 @@ type SessionCookie struct {
Username string Username string
Provider string Provider string
TotpPending bool TotpPending bool
RedirectURI string
} }
// TinyauthLabels is the labels for the tinyauth container // TinyauthLabels is the labels for the tinyauth container