Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions core/authenticate/authenticators.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,30 @@ func authenticateWithAccessToken(ctx context.Context, s *Service) (Principal, er
}, nil
}

if claims[token.SubTypeClaimsKey] == schema.PATPrincipal {
patID := userID // sub = PAT ID
pat, err := s.userPATService.GetByID(ctx, patID)
if err != nil {
s.log.Debug("failed to get PAT", "err", err)
return Principal{}, errors.ErrUnauthenticated
}
if pat.ExpiresAt.Before(s.Now()) {
s.log.Debug("PAT has expired", "pat_id", patID)
return Principal{}, errors.ErrUnauthenticated
}
currentUser, err := s.userService.GetByID(ctx, pat.UserID)
if err != nil {
s.log.Debug("failed to get PAT owner", "err", err)
return Principal{}, errors.ErrUnauthenticated
}
return Principal{
ID: pat.ID,
Type: schema.PATPrincipal,
PAT: &pat,
User: &currentUser,
}, nil
}

currentUser, err := s.userService.GetByID(ctx, userID)
if err != nil {
s.log.Debug("failed to get user", "err", err)
Expand Down
4 changes: 4 additions & 0 deletions core/authenticate/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ type TokenService interface {

type UserPATService interface {
Validate(ctx context.Context, value string) (patModels.PAT, error)
GetByID(ctx context.Context, id string) (patModels.PAT, error)
}

type Service struct {
Expand Down Expand Up @@ -691,6 +692,9 @@ func (s Service) BuildToken(ctx context.Context, principal Principal, metadata m
if principal.Type == schema.UserPrincipal && s.config.Token.Claims.AddUserEmailClaim {
metadata[token.SubEmailClaimsKey] = principal.User.Email
}
if principal.Type == schema.PATPrincipal && principal.User != nil {
metadata[token.UserIDClaimKey] = principal.User.ID
}
return s.internalTokenService.Build(principal.ID, metadata)
}

Expand Down
1 change: 1 addition & 0 deletions core/authenticate/token/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
SubTypeClaimsKey = "sub_type"
SubEmailClaimsKey = "email"
SessionIDClaimKey = "sid"
UserIDClaimKey = "user_id"
)

type Service struct {
Expand Down
6 changes: 6 additions & 0 deletions core/userpat/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,9 @@ func (v *Validator) Validate(ctx context.Context, value string) (models.PAT, err

return pat, nil
}

// GetByID retrieves a PAT by ID. Used by the access token authenticator
// to reconstruct the PAT principal from JWT claims.
func (v *Validator) GetByID(ctx context.Context, id string) (models.PAT, error) {
return v.repo.GetByID(ctx, id)
}
Loading