func ValidateWithJWKS()

in server/gcpcredential/validate.go [140:205]


func ValidateWithJWKS(jwks *JWKS, credentials []string, expectedAudience string) ([]string, error) {
	// For JWT validation - finds the JWK that corresponds to the tokens Key ID and parses it into its respective key type.
	keyFunc := func(token *jwt.Token) (any, error) {
		kid, ok := token.Header["kid"]
		if !ok {
			return nil, fmt.Errorf("token missing Key ID")
		}

		for _, k := range jwks.Keys {
			if kid == k.Kid {
				alg, ok := token.Header["alg"]
				if !ok {
					return nil, errors.New("no signing algorithm specified in token")
				}

				switch alg {
				case "RS256":
					return rsaPubKey(k)
				case "ES256":
					return ecdsaPubKey(k)
				default:
					return nil, fmt.Errorf("unsupported signing algorithm %v, expext RS256 or ES256", alg)
				}
			}
		}

		return nil, errors.New("no matching key found")
	}

	// Validates a Google-issued ID token per guidance at https://developers.google.com/identity/sign-in/web/backend-auth#verify-the-integrity-of-the-id-token.
	validator := func(token string) (map[string]any, error) {
		// Check the signature.
		claims := jwt.MapClaims{}
		_, err := jwt.ParseWithClaims(token, claims, keyFunc)
		if err != nil {
			return nil, err
		}

		// Check the audience.
		audience := claims["aud"]
		if audience != expectedAudience {
			return nil, fmt.Errorf("unexpected audience: %v, token %s", audience, token)
		}

		// Check the issuer.
		issuer := claims["iss"]
		if issuer != "accounts.google.com" && issuer != "https://accounts.google.com" {
			return nil, fmt.Errorf("invalid issuer: %v, token %s", issuer, token)
		}

		// Check the expiration.
		// Numbers need to be converted to float64 first to avoid panicking (https://stackoverflow.com/a/29690346).
		exp, ok := claims["exp"].(float64)
		if !ok {
			return nil, errors.New("unable to convert exp claim to float64")
		}

		if time.Now().Unix() > int64(exp) {
			return nil, errors.New("token is expired")
		}

		return claims, nil
	}

	return validateAndParse(credentials, validator)
}