package middleware import ( "bytes" "context" "errors" "fmt" "io" "time" "ruben/inventory2/internal/consts" "ruben/inventory2/internal/domains/accounts" "ruben/inventory2/internal/domains/authentication" "ruben/inventory2/internal/logging" "ruben/inventory2/internal/server/cookies" "ruben/inventory2/internal/server/response" "github.com/gin-gonic/gin" ) type ( Auth struct { log *logging.Logger auth *authentication.Authenticator newLoginURL LoginURLProviderFunc accts *accounts.Store } Identity struct { AccessToken string Claims authentication.AccessTokenClaims User accounts.OAuthUser Account *accounts.Account } LoginURLProviderFunc = func(ctx context.Context, auth *authentication.Authenticator, targetURI string) (string, error) AuthorizationAssertions = response.HandlerFunc ) func NewAuth( logger *logging.Logger, auth *authentication.Authenticator, newLoginURL LoginURLProviderFunc, accts *accounts.Store, ) *Auth { return &Auth{ log: logger, auth: auth, newLoginURL: newLoginURL, accts: accts, } } // AddIdentityToRequest will add an Identity to the context that can then be retrieved via GetIdentity. func (a *Auth) AddIdentityToRequest(c *gin.Context) { if err := a.addIdentityToRequest(c); err != nil { c.Error(err) c.Abort() } } func (a *Auth) addIdentityToRequest(c *gin.Context) error { r := c.Request ck, err := r.Cookie("access_token") if err != nil { return nil } ctx := r.Context() accessToken := ck.Value claims, err := a.auth.GetAccessTokenClaimsAndExpiration(ctx, accessToken) if err != nil { if errors.Is(err, consts.ErrNotFound) { return nil } return response.Errorf("failed to load authentication details: %w", err) } expiration := claims.Expiration if expiration.Before(time.Now()) { return nil } user, acct, err := a.accts.GetUserAndAccountByAccessToken(ctx, accessToken) if err != nil { return 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 } // Authenticate should only be used along with and after AddIdentityToRequest // Typically used with response.Handler to make a gin.HandlerFunc. func (a *Auth) Authenticate(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(`