|
1 | 1 | package oidc
|
2 | 2 |
|
3 | 3 | import (
|
4 |
| - "bytes" |
5 | 4 | "context"
|
6 |
| - "encoding/base64" |
7 | 5 | "encoding/json"
|
8 |
| - "errors" |
9 | 6 | "fmt"
|
10 | 7 | "io"
|
11 | 8 | "net/http"
|
12 |
| - "strings" |
13 | 9 | "time"
|
14 | 10 |
|
15 | 11 | jose "github.com/go-jose/go-jose/v4"
|
@@ -145,18 +141,6 @@ func (p *Provider) newVerifier(keySet KeySet, config *Config) *IDTokenVerifier {
|
145 | 141 | return NewVerifier(p.issuer, keySet, config)
|
146 | 142 | }
|
147 | 143 |
|
148 |
| -func parseJWT(p string) ([]byte, error) { |
149 |
| - parts := strings.Split(p, ".") |
150 |
| - if len(parts) < 2 { |
151 |
| - return nil, fmt.Errorf("oidc: malformed jwt, expected 3 parts got %d", len(parts)) |
152 |
| - } |
153 |
| - payload, err := base64.RawURLEncoding.DecodeString(parts[1]) |
154 |
| - if err != nil { |
155 |
| - return nil, fmt.Errorf("oidc: malformed jwt payload: %v", err) |
156 |
| - } |
157 |
| - return payload, nil |
158 |
| -} |
159 |
| - |
160 | 144 | func contains(sli []string, ele string) bool {
|
161 | 145 | for _, s := range sli {
|
162 | 146 | if s == ele {
|
@@ -219,12 +203,49 @@ func resolveDistributedClaim(ctx context.Context, verifier *IDTokenVerifier, src
|
219 | 203 | //
|
220 | 204 | // token, err := verifier.Verify(ctx, rawIDToken)
|
221 | 205 | func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDToken, error) {
|
222 |
| - // Throw out tokens with invalid claims before trying to verify the token. This lets |
223 |
| - // us do cheap checks before possibly re-syncing keys. |
224 |
| - payload, err := parseJWT(rawIDToken) |
| 206 | + var supportedSigAlgs []jose.SignatureAlgorithm |
| 207 | + for _, alg := range v.config.SupportedSigningAlgs { |
| 208 | + supportedSigAlgs = append(supportedSigAlgs, jose.SignatureAlgorithm(alg)) |
| 209 | + } |
| 210 | + if len(supportedSigAlgs) == 0 { |
| 211 | + // If no algorithms were specified by both the config and discovery, default |
| 212 | + // to the one mandatory algorithm "RS256". |
| 213 | + supportedSigAlgs = []jose.SignatureAlgorithm{jose.RS256} |
| 214 | + } |
| 215 | + if v.config.InsecureSkipSignatureCheck { |
| 216 | + // "none" is a required value to even parse a JWT with the "none" algorithm |
| 217 | + // using go-jose. |
| 218 | + supportedSigAlgs = append(supportedSigAlgs, "none") |
| 219 | + } |
| 220 | + |
| 221 | + // Parse and verify the signature first. This at least forces the user to have |
| 222 | + // a valid, signed ID token before we do any other processing. |
| 223 | + jws, err := jose.ParseSigned(rawIDToken, supportedSigAlgs) |
225 | 224 | if err != nil {
|
226 | 225 | return nil, fmt.Errorf("oidc: malformed jwt: %v", err)
|
227 | 226 | }
|
| 227 | + switch len(jws.Signatures) { |
| 228 | + case 0: |
| 229 | + return nil, fmt.Errorf("oidc: id token not signed") |
| 230 | + case 1: |
| 231 | + default: |
| 232 | + return nil, fmt.Errorf("oidc: multiple signatures on id token not supported") |
| 233 | + } |
| 234 | + sig := jws.Signatures[0] |
| 235 | + |
| 236 | + var payload []byte |
| 237 | + if v.config.InsecureSkipSignatureCheck { |
| 238 | + // Yolo mode. |
| 239 | + payload = jws.UnsafePayloadWithoutVerification() |
| 240 | + } else { |
| 241 | + // The JWT is attached here for the happy path to avoid the verifier from |
| 242 | + // having to parse the JWT twice. |
| 243 | + ctx = context.WithValue(ctx, parsedJWTKey, jws) |
| 244 | + payload, err = v.keySet.VerifySignature(ctx, rawIDToken) |
| 245 | + if err != nil { |
| 246 | + return nil, fmt.Errorf("failed to verify signature: %v", err) |
| 247 | + } |
| 248 | + } |
228 | 249 | var token idToken
|
229 | 250 | if err := json.Unmarshal(payload, &token); err != nil {
|
230 | 251 | return nil, fmt.Errorf("oidc: failed to unmarshal claims: %v", err)
|
@@ -254,6 +275,7 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok
|
254 | 275 | AccessTokenHash: token.AtHash,
|
255 | 276 | claims: payload,
|
256 | 277 | distributedClaims: distributedClaims,
|
| 278 | + sigAlgorithm: sig.Header.Algorithm, |
257 | 279 | }
|
258 | 280 |
|
259 | 281 | // Check issuer.
|
@@ -306,45 +328,6 @@ func (v *IDTokenVerifier) Verify(ctx context.Context, rawIDToken string) (*IDTok
|
306 | 328 | }
|
307 | 329 | }
|
308 | 330 |
|
309 |
| - if v.config.InsecureSkipSignatureCheck { |
310 |
| - return t, nil |
311 |
| - } |
312 |
| - |
313 |
| - var supportedSigAlgs []jose.SignatureAlgorithm |
314 |
| - for _, alg := range v.config.SupportedSigningAlgs { |
315 |
| - supportedSigAlgs = append(supportedSigAlgs, jose.SignatureAlgorithm(alg)) |
316 |
| - } |
317 |
| - if len(supportedSigAlgs) == 0 { |
318 |
| - // If no algorithms were specified by both the config and discovery, default |
319 |
| - // to the one mandatory algorithm "RS256". |
320 |
| - supportedSigAlgs = []jose.SignatureAlgorithm{jose.RS256} |
321 |
| - } |
322 |
| - jws, err := jose.ParseSigned(rawIDToken, supportedSigAlgs) |
323 |
| - if err != nil { |
324 |
| - return nil, fmt.Errorf("oidc: malformed jwt: %v", err) |
325 |
| - } |
326 |
| - |
327 |
| - switch len(jws.Signatures) { |
328 |
| - case 0: |
329 |
| - return nil, fmt.Errorf("oidc: id token not signed") |
330 |
| - case 1: |
331 |
| - default: |
332 |
| - return nil, fmt.Errorf("oidc: multiple signatures on id token not supported") |
333 |
| - } |
334 |
| - sig := jws.Signatures[0] |
335 |
| - t.sigAlgorithm = sig.Header.Algorithm |
336 |
| - |
337 |
| - ctx = context.WithValue(ctx, parsedJWTKey, jws) |
338 |
| - gotPayload, err := v.keySet.VerifySignature(ctx, rawIDToken) |
339 |
| - if err != nil { |
340 |
| - return nil, fmt.Errorf("failed to verify signature: %v", err) |
341 |
| - } |
342 |
| - |
343 |
| - // Ensure that the payload returned by the square actually matches the payload parsed earlier. |
344 |
| - if !bytes.Equal(gotPayload, payload) { |
345 |
| - return nil, errors.New("oidc: internal error, payload parsed did not match previous payload") |
346 |
| - } |
347 |
| - |
348 | 331 | return t, nil
|
349 | 332 | }
|
350 | 333 |
|
|
0 commit comments