package jwt import ( "context" "net/http" "strings" "time" "forge.cadoles.com/cadoles/bouncer/internal/auth" "forge.cadoles.com/cadoles/bouncer/internal/jwk" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) const DefaultAcceptableSkew = 5 * time.Minute type Authenticator struct { keys jwk.Set issuer string acceptableSkew time.Duration } // Authenticate implements auth.Authenticator. func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth.User, error) { ctx = logger.With(r.Context(), logger.F("remoteAddr", r.RemoteAddr)) authorization := r.Header.Get("Authorization") if authorization == "" { return nil, errors.WithStack(auth.ErrUnauthenticated) } rawToken := strings.TrimPrefix(authorization, "Bearer ") if rawToken == "" { return nil, errors.WithStack(auth.ErrUnauthenticated) } token, err := parseToken(ctx, a.keys, a.issuer, rawToken, a.acceptableSkew) if err != nil { return nil, errors.WithStack(err) } rawRole, exists := token.Get(keyRole) if !exists { return nil, errors.New("could not find 'thumbprint' claim") } role, ok := rawRole.(string) if !ok { return nil, errors.Errorf("unexpected '%s' claim value: '%v'", keyRole, rawRole) } if !isValidRole(role) { return nil, errors.Errorf("invalid role '%s'", role) } user := &User{ subject: token.Subject(), role: Role(role), } return user, nil } func NewAuthenticator(keys jwk.Set, issuer string, acceptableSkew time.Duration) *Authenticator { return &Authenticator{ keys: keys, issuer: issuer, acceptableSkew: acceptableSkew, } } var _ auth.Authenticator = &Authenticator{}