···6565 # Default: "Authentication Portal"
6666 name "My Services"
67676868- # Optional domain for the session cookie.
6969- # Required if the Portal and Gate are on different subdomains.
7070- # e.g., if portal is auth.example.com and gate is app.example.com, set this to example.com
7171- # cookie_domain example.com
7272-7368 # Custom UI templates (optional)
7469 ui {
7570 # Path to a custom HTML template for the login page.
···10297 # REQUIRED.
10398 # If the Portal uses a path_prefix (e.g. /auth), append it here (e.g. https://auth.example.com/auth)
10499 portal_url https://auth.example.com
105105-106106- # Optional domain for the session cookie.
107107- # Must match the Portal's cookie_domain if it is set.
108108- # cookie_domain example.com
109100110101 # Client ID for Transparent Refresh (Optional)
111102 # If provided, enables background token refreshing using the shared DB.
+6-18
gate.go
···2727// Gate acts as a middleware that guards endpoints
2828// and validates the session cookie.
2929type Gate struct {
3030- Allow []string `json:"allow,omitempty"`
3131- ClientID string `json:"client_id,omitempty"` // ClientID for session refreshing (e.g. https://example.com/client-metadata.json)
3232- PortalURL string `json:"portal_url,omitempty"` // URL of the auth portal (e.g. http://localhost:8080 or /)
3333- CookieDomain string `json:"cookie_domain,omitempty"` // Optional domain for the session cookie (e.g. example.com)
3434- UI ui.Config `json:"ui,omitempty"` // Custom UI configuration
3030+ Allow []string `json:"allow,omitempty"`
3131+ ClientID string `json:"client_id,omitempty"` // ClientID for session refreshing (e.g. https://example.com/client-metadata.json)
3232+ PortalURL string `json:"portal_url,omitempty"` // URL of the auth portal (e.g. http://localhost:8080 or /)
3333+ UI ui.Config `json:"ui,omitempty"` // Custom UI configuration
35343635 // Dependencies
3736 app *App
···140139 return d.ArgErr()
141140 }
142141 g.PortalURL = d.Val()
143143- case "cookie_domain":
144144- if !d.NextArg() {
145145- return d.ArgErr()
146146- }
147147- g.CookieDomain = d.Val()
148142 case "ui":
149143 for nesting := d.Nesting(); d.NextBlock(nesting); {
150144 switch d.Val() {
···206200 }
207201208202 // Clear local session cookie
209209- http.SetCookie(w, g.sessions.ClearCookie(g.CookieDomain))
203203+ http.SetCookie(w, g.sessions.ClearCookie(strings.Split(host, ":")[0]))
210204211205 portalLogout := fmt.Sprintf("%s/logout?redirect_to=%s", portalURL, url.QueryEscape(currentURL))
212206 http.Redirect(w, r, portalLogout, http.StatusFound)
···229223 // We need to extend expiration.
230224 // Handle lookup might be needed if not in session?
231225 // Sess has Handle.
232232- cookieDomain := g.CookieDomain
233233- if cookieDomain == "" {
234234- cookieDomain = r.Host
235235- }
236236- cookieDomain = strings.Split(cookieDomain, ":")[0]
237237-238226 cookie, err := g.sessions.CreateCookie(
239227 clientSession.Data.AccountDID,
240228 sess.Handle, // Keep handle from old cookie
241229 clientSession.Data.SessionID,
242230 24*7*time.Hour,
243243- cookieDomain,
231231+ strings.Split(r.Host, ":")[0],
244232 )
245233 if err == nil {
246234 http.SetCookie(w, cookie)
+9-33
portal.go
···25252626// Portal is the centralized authentication portal for Path B (Auth Hub).
2727type Portal struct {
2828- Name string `json:"name,omitempty"`
2929- Domain string `json:"domain,omitempty"` // Public domain of the portal (e.g. auth.example.com)
3030- CookieDomain string `json:"cookie_domain,omitempty"` // Optional domain for the session cookie (e.g. example.com)
3131- UI ui.Config `json:"ui,omitempty"` // Custom UI configuration
2828+ Name string `json:"name,omitempty"`
2929+ Domain string `json:"domain,omitempty"` // Public domain of the portal (e.g. auth.example.com)
3030+ UI ui.Config `json:"ui,omitempty"` // Custom UI configuration
32313332 // Paths configuration
3433 PathPrefix string `json:"path_prefix,omitempty"`
···117116 return d.ArgErr()
118117 }
119118 p.Domain = d.Val()
120120- case "cookie_domain":
121121- if !d.NextArg() {
122122- return d.ArgErr()
123123- }
124124- p.CookieDomain = d.Val()
125119 case "path_prefix":
126120 if !d.NextArg() {
127121 return d.ArgErr()
···188182 return nil
189183 }
190184191191- cookieDomain := p.CookieDomain
192192- if cookieDomain == "" {
193193- cookieDomain = p.Domain
194194- }
195195- cookieDomain = strings.Split(cookieDomain, ":")[0]
185185+ reqDomain := strings.Split(p.Domain, ":")[0]
196186197187 // Create Session Cookie
198188 cookie, err := p.sessions.CreateCookie(
···200190 handle,
201191 sessionData.SessionID,
202192 24*7*time.Hour,
203203- cookieDomain,
193193+ reqDomain,
204194 )
205195 if err != nil {
206196 p.logger.Error("failed to create session cookie", zap.Error(err))
···212202213203 appNameForLog := p.Name
214204 if appNameForLog == "" {
215215- appNameForLog = cookieDomain
205205+ appNameForLog = reqDomain
216206 }
217207 p.logger.Info(fmt.Sprintf("@%s (did: %s) has logged in for %s", handle, sessionData.AccountDID.String(), appNameForLog))
218208···241231 h := parsed.Host
242232 if h == p.Domain {
243233 isAllowedDomain = true
244244- } else if p.CookieDomain != "" {
245245- cleanCookieDomain := strings.TrimPrefix(p.CookieDomain, ".")
246246- if h == cleanCookieDomain || strings.HasSuffix(h, "."+cleanCookieDomain) {
247247- isAllowedDomain = true
248248- }
249234 }
250235 }
251236···329314 // Invalidate credential if session exists
330315 sess, err := p.sessions.VerifyCookie(r)
331316332332- cookieDomain := p.CookieDomain
333333- if cookieDomain == "" {
334334- cookieDomain = p.Domain
335335- }
336336- cookieDomain = strings.Split(cookieDomain, ":")[0]
317317+ reqDomain := strings.Split(p.Domain, ":")[0]
337318338319 if err == nil || err == session.ErrExpired {
339320 appNameForLog := p.Name
340321 if appNameForLog == "" {
341341- appNameForLog = cookieDomain
322322+ appNameForLog = reqDomain
342323 }
343324 p.logger.Info(fmt.Sprintf("@%s (did: %s) has logged out for %s", sess.Handle, sess.DID, appNameForLog))
344325···347328 }
348329 }
349330350350- http.SetCookie(w, p.sessions.ClearCookie(cookieDomain))
331331+ http.SetCookie(w, p.sessions.ClearCookie(reqDomain))
351332352333 // Handle redirect_to for logout
353334 redirectTo := r.URL.Query().Get("redirect_to")
···362343 h := parsed.Host
363344 if h == p.Domain {
364345 isAllowedDomain = true
365365- } else if p.CookieDomain != "" {
366366- cleanCookieDomain := strings.TrimPrefix(p.CookieDomain, ".")
367367- if h == cleanCookieDomain || strings.HasSuffix(h, "."+cleanCookieDomain) {
368368- isAllowedDomain = true
369369- }
370346 }
371347 }
372348