(req *http.Request, verifier TokenVerifier, opts *RequireBearerTokenOptions)
| 97 | } |
| 98 | |
| 99 | func verify(req *http.Request, verifier TokenVerifier, opts *RequireBearerTokenOptions) (_ *TokenInfo, errmsg string, code int) { |
| 100 | // Extract bearer token. |
| 101 | authHeader := req.Header.Get("Authorization") |
| 102 | fields := strings.Fields(authHeader) |
| 103 | if len(fields) != 2 || strings.ToLower(fields[0]) != "bearer" { |
| 104 | return nil, "no bearer token", http.StatusUnauthorized |
| 105 | } |
| 106 | |
| 107 | // Verify the token and get information from it. |
| 108 | tokenInfo, err := verifier(req.Context(), fields[1], req) |
| 109 | if err != nil { |
| 110 | if errors.Is(err, ErrInvalidToken) { |
| 111 | return nil, err.Error(), http.StatusUnauthorized |
| 112 | } |
| 113 | if errors.Is(err, ErrOAuth) { |
| 114 | return nil, err.Error(), http.StatusBadRequest |
| 115 | } |
| 116 | return nil, err.Error(), http.StatusInternalServerError |
| 117 | } |
| 118 | if tokenInfo == nil { |
| 119 | return nil, "token validation failed", http.StatusInternalServerError |
| 120 | } |
| 121 | |
| 122 | // Check scopes. All must be present. |
| 123 | if opts != nil { |
| 124 | // Note: quadratic, but N is small. |
| 125 | for _, s := range opts.Scopes { |
| 126 | if !slices.Contains(tokenInfo.Scopes, s) { |
| 127 | return nil, "insufficient scope", http.StatusForbidden |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | // Check expiration. |
| 133 | if tokenInfo.Expiration.IsZero() { |
| 134 | return nil, "token missing expiration", http.StatusUnauthorized |
| 135 | } |
| 136 | if tokenInfo.Expiration.Before(time.Now()) { |
| 137 | return nil, "token expired", http.StatusUnauthorized |
| 138 | } |
| 139 | return tokenInfo, "", 0 |
| 140 | } |
| 141 | |
| 142 | // ProtectedResourceMetadataHandler returns an http.Handler that serves OAuth 2.0 |
| 143 | // protected resource metadata (RFC 9728) with CORS support. |
searching dependent graphs…