| 28 | } |
| 29 | |
| 30 | func NewTokenValidator(audience, domain, jwksPath string) (*TokenValidator, error) { |
| 31 | domainURL, err := url.Parse(domain) |
| 32 | if err != nil { |
| 33 | return nil, fmt.Errorf("bad auth.domain URL: %w", err) |
| 34 | } |
| 35 | keys, err := loadPublicKeys(jwksPath) |
| 36 | if err != nil { |
| 37 | return nil, fmt.Errorf("failed to load JWKS file: %w", err) |
| 38 | } |
| 39 | // Auth0 issuer is always the domain URL with trailing "/". |
| 40 | // https://auth0.com/docs/tokens/access-tokens/get-access-tokens#custom-domains-and-the-management-api |
| 41 | expectedIssuer := domainURL.String() + "/" |
| 42 | keyGetter := func(token *jwt.Token) (any, error) { |
| 43 | tokenKeyID, _ := token.Header["kid"].(string) |
| 44 | key, ok := keys[tokenKeyID] |
| 45 | if !ok { |
| 46 | return token, errors.New("unknown token key id") |
| 47 | } |
| 48 | return key, nil |
| 49 | } |
| 50 | return &TokenValidator{ |
| 51 | expectedAudience: audience, |
| 52 | expectedIssuer: expectedIssuer, |
| 53 | keyGetter: keyGetter, |
| 54 | }, nil |
| 55 | } |
| 56 | |
| 57 | func (v *TokenValidator) ValidateRequest(r *http.Request) (string, Identity, error) { |
| 58 | token, err := request.AuthorizationHeaderExtractor.ExtractToken(r) |