auth: expose more auth info in gin context to support future login redirect capability
This commit is contained in:
+1
-1
@@ -19,7 +19,7 @@ import (
|
|||||||
func Routes(
|
func Routes(
|
||||||
r *gin.RouterGroup,
|
r *gin.RouterGroup,
|
||||||
logger *logging.Logger,
|
logger *logging.Logger,
|
||||||
auth *auth.Auth,
|
auth *auth.Service,
|
||||||
sq *sse.Queue,
|
sq *sse.Queue,
|
||||||
accts *accounts.Store,
|
accts *accounts.Store,
|
||||||
unp *sse.UpdateNotificationPublisher,
|
unp *sse.UpdateNotificationPublisher,
|
||||||
|
|||||||
+74
-47
@@ -19,12 +19,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
Auth struct {
|
Service struct {
|
||||||
log *logging.Logger
|
log *logging.Logger
|
||||||
auth *authentication.Authenticator
|
auth *authentication.Authenticator
|
||||||
accts *accounts.Store
|
accts *accounts.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Auth struct {
|
||||||
|
Identity Identity
|
||||||
|
Found bool
|
||||||
|
Valid bool // eg not expired
|
||||||
|
}
|
||||||
|
|
||||||
Identity struct {
|
Identity struct {
|
||||||
AccessToken string
|
AccessToken string
|
||||||
Claims authentication.AccessTokenClaims
|
Claims authentication.AccessTokenClaims
|
||||||
@@ -37,36 +43,40 @@ type (
|
|||||||
AuthorizationAssertions = response.HandlerFunc
|
AuthorizationAssertions = response.HandlerFunc
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewAuth(
|
func NewService(
|
||||||
logger *logging.Logger,
|
logger *logging.Logger,
|
||||||
auth *authentication.Authenticator,
|
auth *authentication.Authenticator,
|
||||||
accts *accounts.Store,
|
accts *accounts.Store,
|
||||||
) *Auth {
|
) *Service {
|
||||||
return &Auth{
|
return &Service{
|
||||||
log: logger,
|
log: logger,
|
||||||
auth: auth,
|
auth: auth,
|
||||||
accts: accts,
|
accts: accts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Auth) GetAuthenticator() *authentication.Authenticator {
|
func (a *Service) GetAuthenticator() *authentication.Authenticator {
|
||||||
return a.auth
|
return a.auth
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identify will add an Identity to the context that can then be retrieved via GetIdentity.
|
// Identify will add an Auth to the context that can then be retrieved via GetAuth.
|
||||||
func (a *Auth) Identify(c *gin.Context) {
|
func (a *Service) Identify(c *gin.Context) {
|
||||||
if err := a.addIdentity(c); err != nil {
|
auth, err := a.getAuthFromRequest(c)
|
||||||
|
if auth != nil {
|
||||||
|
c.Request = c.Request.WithContext(SetAuth(c, *auth))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
c.Error(err)
|
c.Error(err)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Auth) addIdentity(c *gin.Context) error {
|
func (a *Service) getAuthFromRequest(c *gin.Context) (*Auth, error) {
|
||||||
r := c.Request
|
r := c.Request
|
||||||
|
|
||||||
ck, err := r.Cookie("access_token")
|
ck, err := r.Cookie("access_token")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
@@ -76,50 +86,61 @@ func (a *Auth) addIdentity(c *gin.Context) error {
|
|||||||
claims, err := a.auth.GetAccessTokenClaimsAndExpiration(ctx, accessToken)
|
claims, err := a.auth.GetAccessTokenClaimsAndExpiration(ctx, accessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, consts.ErrNotFound) {
|
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
|
expiration := claims.Expiration
|
||||||
if expiration.Before(time.Now()) {
|
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)
|
user, acct, err := a.accts.GetUserAndAccountByAccessToken(ctx, accessToken)
|
||||||
if err != nil {
|
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{
|
return &Auth{
|
||||||
AccessToken: accessToken,
|
Found: true,
|
||||||
Claims: claims,
|
Valid: true,
|
||||||
User: user,
|
Identity: Identity{
|
||||||
Account: acct,
|
AccessToken: accessToken,
|
||||||
}))
|
Claims: claims,
|
||||||
|
User: user,
|
||||||
return nil
|
Account: acct,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticate should only be used along with and after Identify
|
// Authenticate should only be used along with and after Identify
|
||||||
// Typically used with response.Handler to make a gin.HandlerFunc.
|
// 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...))
|
return response.Handler(a.AuthenticateHandler(assertions...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// See Authenticate.
|
// 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) {
|
return func(c *gin.Context) (response.Response, error) {
|
||||||
id, ok := getIdentity(c)
|
auth, ok := getAuth(c)
|
||||||
if !ok {
|
switch {
|
||||||
return nil, response.Unauthorized().
|
case !ok:
|
||||||
HTML([]byte(`
|
// TODO: would be nice if we redirected to the login page then back to the original destination
|
||||||
<h1>Unauthorized</h1>
|
return response.SeeOther("/"), nil
|
||||||
<a href="/">Return to app</a>
|
case !auth.Found:
|
||||||
`)) // TODO: would be nice to have a better page for this
|
// 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
|
expiration := id.Claims.Expiration
|
||||||
now := time.Now()
|
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
|
// stores auth in context
|
||||||
func SetIdentity(ctx context.Context, id Identity) context.Context {
|
func SetAuth(ctx context.Context, a Auth) context.Context {
|
||||||
c, ok := ctx.(*gin.Context)
|
c, ok := ctx.(*gin.Context)
|
||||||
if ok {
|
if ok {
|
||||||
c.Set(identityKey{}, id)
|
c.Set(authKey{}, a)
|
||||||
ctx = c.Request.Context()
|
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 {
|
func GetIdentity(ctx context.Context) Identity {
|
||||||
id, _ := getIdentity(ctx)
|
a, _ := getAuth(ctx)
|
||||||
return id
|
return a.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIdentity(ctx context.Context) (Identity, bool) {
|
func getAuth(ctx context.Context) (Auth, bool) {
|
||||||
id, ok := ctx.Value(identityKey{}).(Identity)
|
a, ok := ctx.Value(authKey{}).(Auth)
|
||||||
if ok {
|
if ok {
|
||||||
return id, true
|
return a, true
|
||||||
}
|
}
|
||||||
|
|
||||||
c, ok := ctx.(*gin.Context)
|
c, ok := ctx.(*gin.Context)
|
||||||
if !ok {
|
if !ok {
|
||||||
return Identity{}, false
|
return Auth{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
v, ok := c.Get(identityKey{})
|
v, ok := c.Get(authKey{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return Identity{}, false
|
return Auth{}, false
|
||||||
}
|
}
|
||||||
|
|
||||||
id, ok = v.(Identity)
|
a, ok = v.(Auth)
|
||||||
|
|
||||||
return id, ok
|
return a, ok
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -33,7 +33,7 @@ func NewRouter(
|
|||||||
etsy *etsy_platform.Platform,
|
etsy *etsy_platform.Platform,
|
||||||
authr *authentication.Authenticator,
|
authr *authentication.Authenticator,
|
||||||
) *Router {
|
) *Router {
|
||||||
authM := auth.NewAuth(
|
authM := auth.NewService(
|
||||||
logger.WithGroup("auth-middleware"),
|
logger.WithGroup("auth-middleware"),
|
||||||
authr,
|
authr,
|
||||||
accts,
|
accts,
|
||||||
|
|||||||
Reference in New Issue
Block a user