From a90a7bb4521cb8465a80dea2485cc984485731b5 Mon Sep 17 00:00:00 2001 From: Junwei Zhao Date: Tue, 5 Aug 2025 19:53:03 +1000 Subject: [PATCH] Add logic to share session across configured domains --- config.example.yaml | 9 +++ internal/config/config.go | 5 ++ internal/handlers/handlers.go | 18 ++++- web/login.html | 10 +-- web/redirect.html | 142 ++++++++++++++++++++++++++++++++++ 5 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 web/redirect.html diff --git a/config.example.yaml b/config.example.yaml index c18db6a..559cf6d 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -45,6 +45,14 @@ auth: # Set to false to allow automatic approval for trusted environments require_approval: true + # Cookie domain for session cookies + # Leave empty for single domain (cookies only work on current domain) + # Set to ".yourdomain.com" to share cookies across all subdomains + # Examples: + # - "" (empty) - cookies only work on the exact domain + # - ".example.com" - cookies work on example.com and all subdomains + cookie_domain: "" + # Email allowlist - list of email addresses allowed to register # Leave empty to allow any email address (not recommended for production) allowed_emails: @@ -65,3 +73,4 @@ auth: # - SESSION_SECRET: Session encryption secret # - ALLOWED_EMAILS: Comma-separated list of allowed emails # - ADMIN_EMAIL: Admin email address +# - COOKIE_DOMAIN: Cookie domain for session cookies diff --git a/internal/config/config.go b/internal/config/config.go index 6cce6d5..4defc72 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -41,6 +41,7 @@ type AuthConfig struct { RequireApproval bool `yaml:"require_approval"` AllowedEmails []string `yaml:"allowed_emails"` AdminEmail string `yaml:"admin_email"` + CookieDomain string `yaml:"cookie_domain"` } func Load() (*Config, error) { @@ -75,6 +76,7 @@ func Load() (*Config, error) { SessionSecret: "change-me-in-production", RequireApproval: true, AllowedEmails: []string{}, // Empty means no email restrictions + CookieDomain: "", // Empty means no domain restriction (current domain only) }, } @@ -117,6 +119,9 @@ func Load() (*Config, error) { if adminEmail := os.Getenv("ADMIN_EMAIL"); adminEmail != "" { config.Auth.AdminEmail = adminEmail } + if cookieDomain := os.Getenv("COOKIE_DOMAIN"); cookieDomain != "" { + config.Auth.CookieDomain = cookieDomain + } return config, nil } diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 2b9ff35..90ea040 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -35,6 +35,7 @@ func New(db *database.DB, webAuthn *auth.WebAuthn, config *config.Config) *Handl HttpOnly: true, Secure: false, // Set to true in production with HTTPS SameSite: http.SameSiteLaxMode, + Domain: config.Auth.CookieDomain, // Share cookies across subdomains if configured } return &Handlers{ @@ -393,10 +394,24 @@ func (h *Handlers) Logout(w http.ResponseWriter, r *http.Request) { // AuthCheck implements the nginx auth_request protocol func (h *Handlers) AuthCheck(w http.ResponseWriter, r *http.Request) { - session, _ := h.store.Get(r, "auth-session") + // Debug logging + logrus.Debugf("AuthCheck request from %s", r.RemoteAddr) + logrus.Debugf("AuthCheck headers: %+v", r.Header) + logrus.Debugf("AuthCheck cookies: %+v", r.Cookies()) + + session, err := h.store.Get(r, "auth-session") + if err != nil { + logrus.Errorf("Failed to get auth session: %v", err) + w.WriteHeader(http.StatusUnauthorized) + return + } authenticated, ok := session.Values["authenticated"].(bool) + logrus.Debugf("Session authenticated: %v, ok: %v", authenticated, ok) + logrus.Debugf("Session values: %+v", session.Values) + if !ok || !authenticated { + logrus.Debugf("User not authenticated, returning 401") w.WriteHeader(http.StatusUnauthorized) return } @@ -409,6 +424,7 @@ func (h *Handlers) AuthCheck(w http.ResponseWriter, r *http.Request) { w.Header().Set("X-Auth-User", userEmail) } + logrus.Debugf("User authenticated, returning 200") w.WriteHeader(http.StatusOK) } diff --git a/web/login.html b/web/login.html index 1ecd8aa..c088f47 100644 --- a/web/login.html +++ b/web/login.html @@ -263,13 +263,13 @@ // Prefer 'redirect' over 'rd' if both are present const redirectParam = urlParams.get('redirect'); const rdParam = urlParams.get('rd'); - + console.log('URL params:', { redirect: redirectParam, rd: rdParam, search: window.location.search }); - + return redirectParam || rdParam; } @@ -281,14 +281,14 @@ // Decode the URL if it's encoded const decodedUrl = decodeURIComponent(redirectUrl); console.log('Decoded redirect URL:', decodedUrl); - + // Validate the URL const url = new URL(decodedUrl); console.log('Parsed URL:', url.href); - + // Show a brief message before redirecting showAlert(`Redirecting to ${url.hostname}...`, 'success'); - + setTimeout(() => { console.log('Executing redirect to:', decodedUrl); window.location.href = decodedUrl; diff --git a/web/redirect.html b/web/redirect.html new file mode 100644 index 0000000..98f572d --- /dev/null +++ b/web/redirect.html @@ -0,0 +1,142 @@ + + + + + + Passkey Authentication + + + +
+
+

🔐 Passkey Authentication

+

Checking authentication status...

+
+ + + +