hashicorp / hashicorp/cap

Support separate discovery URL and issuer identifier in NewOIDCDiscoveryKeySet

Open
#176 0 comments 2 reactions 0 assignees View on GitHub
Dominant language
Go
Stars
482
Forks
25
Avg merge
1d 20h
Merged PRs (30d)
8

Description

**Is your feature request related to a problem? Please describe.**

The `NewOIDCDiscoveryKeySet()` function fetches the discovery document from `{issuer}/.well-known/openid-configuration` and validates that the `issuer` field returned in the discovery document matches the input issuer parameter exactly.

This validation fails if the client accesses the OIDC endpoint using a different address than the server's configured issuer identifier. For example, a client might access the server via IP address while the server's issuer is configured with a DNS hostname, or clients might use internal service names that are not fully qualified (like `kubernetes.default` in Kubernetes).

**Describe the solution you'd like**

Support alternative validation approaches:
* Allow users to provide the issuer URL and expected issuer identifier as separate parameters.
* Provide an option to disable strict issuer validation.

**Describe alternatives you've considered**

None found.

**Additional context**

The current validation is implemented here https://github.com/hashicorp/cap/blob/b72d477d7f92c08b2dadd2ce2a9c97e45f1ec94c/jwt/keyset.go#L96-L100

Which matches https://datatracker.ietf.org/doc/html/rfc8414#section-3.3

3.3. Authorization Server Metadata Validation

The "issuer" value returned MUST be identical to the authorization
server's issuer identifier value into which the well-known URI string
was inserted to create the URL used to retrieve the metadata. If
these values are not identical, the data contained in the response
MUST NOT be used.

RFC 8414 mandates this validation, though it has no security value: a compromised server can forge any issuer identifier it wants. I assume the validation primarily serves to catch misconfigurations rather than prevent attacks.

**Example scenario: Kubernetes Service Account Token Validation**

I would like to use `NewOIDCDiscoveryKeySet()` for fetching keyset to validate Kubernetes Service Account tokens issued by the Kubernetes API server.

Kubernetes allows applications running inside a Pod to auto-discover the API server address via environment variables:

* `KUBERNETES_SERVICE_HOST` with value `10.96.0.1`
* `KUBERNETES_SERVICE_PORT_HTTPS` with value `443`

When calling `NewOIDCDiscoveryKeySet(ctx, "https://10.96.0.1:443", caPEM)`, the discovery document is fetched successfully but contains:

```json
{
"id_token_signing_alg_values_supported": [
"RS256"
],
"issuer": "https://kubernetes.default.svc.cluster.local",
"jwks_uri": "https://172.20.0.5:6443/openid/v1/jwks",
"response_types_supported": [
"id_token"
],
"subject_types_supported": [
"public"
]
}
```

The validation fails because `https://kubernetes.default.svc.cluster.local` does not match `https://10.96.0.1:443`, even though the server is legitimate and the TLS certificate is valid.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.