From 5ec4fe67b0fcb8d563c3c002d948cd846521bc99 Mon Sep 17 00:00:00 2001 From: Angel Beltran Date: Wed, 18 Feb 2026 22:28:03 -0700 Subject: [PATCH] auth: expose more auth info in gin context to support future login redirect capability --- server/api/apis.go | 2 +- server/auth/auth.go | 121 +++++++++++++++++++++++++++----------------- server/server.go | 2 +- 3 files changed, 76 insertions(+), 49 deletions(-) diff --git a/server/api/apis.go b/server/api/apis.go index 59b354f..b4b6458 100644 --- a/server/api/apis.go +++ b/server/api/apis.go @@ -19,7 +19,7 @@ import ( func Routes( r *gin.RouterGroup, logger *logging.Logger, - auth *auth.Auth, + auth *auth.Service, sq *sse.Queue, accts *accounts.Store, unp *sse.UpdateNotificationPublisher, diff --git a/server/auth/auth.go b/server/auth/auth.go index b2d99b4..008c68c 100644 --- a/server/auth/auth.go +++ b/server/auth/auth.go @@ -19,12 +19,18 @@ import ( ) type ( - Auth struct { + Service struct { log *logging.Logger auth *authentication.Authenticator accts *accounts.Store } + Auth struct { + Identity Identity + Found bool + Valid bool // eg not expired + } + Identity struct { AccessToken string Claims authentication.AccessTokenClaims @@ -37,36 +43,40 @@ type ( AuthorizationAssertions = response.HandlerFunc ) -func NewAuth( +func NewService( logger *logging.Logger, auth *authentication.Authenticator, accts *accounts.Store, -) *Auth { - return &Auth{ +) *Service { + return &Service{ log: logger, auth: auth, accts: accts, } } -func (a *Auth) GetAuthenticator() *authentication.Authenticator { +func (a *Service) GetAuthenticator() *authentication.Authenticator { return a.auth } -// Identify will add an Identity to the context that can then be retrieved via GetIdentity. -func (a *Auth) Identify(c *gin.Context) { - if err := a.addIdentity(c); err != nil { +// Identify will add an Auth to the context that can then be retrieved via GetAuth. +func (a *Service) Identify(c *gin.Context) { + auth, err := a.getAuthFromRequest(c) + if auth != nil { + c.Request = c.Request.WithContext(SetAuth(c, *auth)) + } + if err != nil { c.Error(err) c.Abort() } } -func (a *Auth) addIdentity(c *gin.Context) error { +func (a *Service) getAuthFromRequest(c *gin.Context) (*Auth, error) { r := c.Request ck, err := r.Cookie("access_token") if err != nil { - return nil + return nil, nil } ctx := r.Context() @@ -76,50 +86,61 @@ func (a *Auth) addIdentity(c *gin.Context) error { claims, err := a.auth.GetAccessTokenClaimsAndExpiration(ctx, accessToken) if err != nil { if errors.Is(err, consts.ErrNotFound) { - return nil + // need to log in, in order to access privilege pages/apis + return &Auth{}, nil } - return response.Errorf("failed to load authentication details: %w", err) + return nil, response.Errorf("failed to load authentication details: %w", err) } expiration := claims.Expiration if expiration.Before(time.Now()) { - return nil + // need to log in, in order to access privilege pages/apis + return &Auth{ + Found: true, + }, nil } user, acct, err := a.accts.GetUserAndAccountByAccessToken(ctx, accessToken) if err != nil { - return response.Errorf("failed to load user and account defails: %w", err) + return nil, response.Errorf("failed to load user and account defails: %w", err) } - c.Request = r.WithContext(SetIdentity(c, Identity{ - AccessToken: accessToken, - Claims: claims, - User: user, - Account: acct, - })) - - return nil + return &Auth{ + Found: true, + Valid: true, + Identity: Identity{ + AccessToken: accessToken, + Claims: claims, + User: user, + Account: acct, + }, + }, nil } // Authenticate should only be used along with and after Identify // Typically used with response.Handler to make a gin.HandlerFunc. -func (a *Auth) Authenticate(assertions ...AuthorizationAssertions) func(c *gin.Context) { +func (a *Service) Authenticate(assertions ...AuthorizationAssertions) func(c *gin.Context) { return response.Handler(a.AuthenticateHandler(assertions...)) } // See Authenticate. -func (a *Auth) AuthenticateHandler(assertions ...AuthorizationAssertions) func(c *gin.Context) (response.Response, error) { +func (a *Service) AuthenticateHandler(assertions ...AuthorizationAssertions) func(c *gin.Context) (response.Response, error) { return func(c *gin.Context) (response.Response, error) { - id, ok := getIdentity(c) - if !ok { - return nil, response.Unauthorized(). - HTML([]byte(` -

Unauthorized

- Return to app - `)) // TODO: would be nice to have a better page for this + auth, ok := getAuth(c) + switch { + case !ok: + // TODO: would be nice if we redirected to the login page then back to the original destination + return response.SeeOther("/"), nil + case !auth.Found: + // TODO: potentially handle differently + return response.SeeOther("/"), nil + case !auth.Valid: + // TODO: potentially handle differently + return response.SeeOther("/"), nil } + id := auth.Identity expiration := id.Claims.Expiration now := time.Now() @@ -151,41 +172,47 @@ func (a *Auth) AuthenticateHandler(assertions ...AuthorizationAssertions) func(c } } -type identityKey struct{} +type authKey struct{} -// stores identity in context -func SetIdentity(ctx context.Context, id Identity) context.Context { +// stores auth in context +func SetAuth(ctx context.Context, a Auth) context.Context { c, ok := ctx.(*gin.Context) if ok { - c.Set(identityKey{}, id) + c.Set(authKey{}, a) ctx = c.Request.Context() } - return context.WithValue(ctx, identityKey{}, id) + return context.WithValue(ctx, authKey{}, a) } -// get identity from context +// get auth from context +func GetAuth(ctx context.Context) Auth { + a, _ := getAuth(ctx) + return a +} + +// a wrapper on GetAuth func GetIdentity(ctx context.Context) Identity { - id, _ := getIdentity(ctx) - return id + a, _ := getAuth(ctx) + return a.Identity } -func getIdentity(ctx context.Context) (Identity, bool) { - id, ok := ctx.Value(identityKey{}).(Identity) +func getAuth(ctx context.Context) (Auth, bool) { + a, ok := ctx.Value(authKey{}).(Auth) if ok { - return id, true + return a, true } c, ok := ctx.(*gin.Context) if !ok { - return Identity{}, false + return Auth{}, false } - v, ok := c.Get(identityKey{}) + v, ok := c.Get(authKey{}) if !ok { - return Identity{}, false + return Auth{}, false } - id, ok = v.(Identity) + a, ok = v.(Auth) - return id, ok + return a, ok } diff --git a/server/server.go b/server/server.go index 0bce216..47f009b 100644 --- a/server/server.go +++ b/server/server.go @@ -33,7 +33,7 @@ func NewRouter( etsy *etsy_platform.Platform, authr *authentication.Authenticator, ) *Router { - authM := auth.NewAuth( + authM := auth.NewService( logger.WithGroup("auth-middleware"), authr, accts,