Skip to content

Support separate discovery URL and issuer identifier in NewOIDCDiscoveryKeySet #176

@tsaarni

Description

@tsaarni

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

cap/jwt/keyset.go

Lines 96 to 100 in b72d477

// Ensure that the returned issuer matches what was given by issuer
if p.Issuer != issuer {
return nil, fmt.Errorf("issuer did not match the returned issuer, expected %q got %q",
issuer, p.Issuer)
}

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:

{
    "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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions