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)
}