Problem Statement
OpenShell gateway OIDC authentication currently assumes the OAuth 2.0 access_token is a JWT that can be verified locally with the issuer's JWKS. OAuth 2.0 also permits opaque access tokens, and some conforming OIDC providers return an opaque access_token alongside a JWT id_token.
The CLI can complete Authorization Code with PKCE and store that token bundle, but protected gateway RPCs reject the opaque bearer token as invalid token. Substituting the id_token is not appropriate because an ID token is evidence for the OIDC client, not a bearer credential for the gateway API.
OpenShell needs a provider-neutral, opt-in way to validate opaque OIDC access tokens without weakening the existing local JWT validation path.
Proposed Design
Add an OIDC access-token validation strategy to gateway configuration:
[openshell.gateway.oidc]
token_validation = "userinfo" # "jwt" remains the default
userinfo_cache_ttl_secs = 30
The behavior would be:
jwt: preserve the current JWKS, issuer, audience, expiry, and claims validation behavior.
userinfo: discover userinfo_endpoint from the issuer metadata and call it with the OAuth access token in the Authorization: Bearer header.
- Accept a standard JSON UserInfo claims object or a signed JWT UserInfo response. Continue to validate signed JWT responses with the existing JWKS, issuer, audience, and expiry checks.
- Require
sub and map the resulting claims into OpenShell's existing provider-neutral Identity representation.
- Do not follow redirects on UserInfo requests so a bearer token cannot be forwarded to another origin.
- Map UserInfo
401 and 403 responses to unauthenticated. Map network failures and other upstream failures to unavailable.
- Cache only successful identities for a short configurable TTL. Key the cache with a SHA-256 token digest; never store or log the plaintext bearer token.
- Keep the mode explicit. Do not automatically fall back from JWT parsing to a network request.
Expose the strategy consistently through gateway TOML, CLI/environment settings, Helm values, startup diagnostics, and the gateway authentication documentation. The implementation should contain no provider-specific branches.
Alternatives Considered
Require every provider to issue JWT access tokens. This preserves local validation but excludes standards-compliant providers that intentionally issue reference or opaque access tokens.
Use the OIDC id_token as the gateway bearer token. This confuses authentication evidence with API authorization. The ID token's audience is the OAuth client, and it should not replace the access token.
Automatically call UserInfo when JWT parsing fails. This makes network behavior dependent on untrusted token syntax, hides configuration mistakes, and can add avoidable load. An explicit strategy is easier to operate and audit.
Add RFC 7662 token introspection in the same change. Introspection is a useful future strategy, but it requires confidential-client credentials and endpoint configuration that are not part of standard OIDC discovery. Keeping it separate makes this proposal smaller and avoids introducing secret distribution into gateway auth.
Standards Alignment
Agent Investigation
- The current gateway path is implemented in
crates/openshell-server/src/auth/oidc.rs, where access tokens are parsed and validated as JWTs against discovered JWKS.
crates/openshell-bootstrap/src/oidc_token.rs stores the OAuth access token and optional refresh token used by the CLI.
crates/openshell-server/src/auth/identity.rs already provides the provider-neutral identity boundary needed by either validation strategy.
docs/reference/gateway-auth.mdx documents Authorization Code with PKCE and the existing gateway bearer-token flow.
- A live compatibility test against a conforming OIDC provider confirmed the failing shape: PKCE login succeeds, the OAuth response contains an opaque access token and JWT ID token, and the discovered UserInfo endpoint returns signed identity claims. A provider-neutral prototype successfully authenticated protected gateway RPCs with that opaque token and preserved the existing JWT validation default.
Test Considerations
- Preserve the existing JWT/JWKS validation tests as the default behavior.
- Add unit tests for JSON and signed-JWT UserInfo responses, rejected tokens, malformed responses, positive caching, and redirect rejection.
- Add configuration and Helm rendering tests for both validation strategies and authentication-only claim settings.
- Verify a protected gateway RPC with an opaque access token against a conforming OIDC provider. Never log or assert plaintext token values.
Checklist
Problem Statement
OpenShell gateway OIDC authentication currently assumes the OAuth 2.0
access_tokenis a JWT that can be verified locally with the issuer's JWKS. OAuth 2.0 also permits opaque access tokens, and some conforming OIDC providers return an opaqueaccess_tokenalongside a JWTid_token.The CLI can complete Authorization Code with PKCE and store that token bundle, but protected gateway RPCs reject the opaque bearer token as
invalid token. Substituting theid_tokenis not appropriate because an ID token is evidence for the OIDC client, not a bearer credential for the gateway API.OpenShell needs a provider-neutral, opt-in way to validate opaque OIDC access tokens without weakening the existing local JWT validation path.
Proposed Design
Add an OIDC access-token validation strategy to gateway configuration:
The behavior would be:
jwt: preserve the current JWKS, issuer, audience, expiry, and claims validation behavior.userinfo: discoveruserinfo_endpointfrom the issuer metadata and call it with the OAuth access token in theAuthorization: Bearerheader.suband map the resulting claims into OpenShell's existing provider-neutralIdentityrepresentation.401and403responses to unauthenticated. Map network failures and other upstream failures to unavailable.Expose the strategy consistently through gateway TOML, CLI/environment settings, Helm values, startup diagnostics, and the gateway authentication documentation. The implementation should contain no provider-specific branches.
Alternatives Considered
Require every provider to issue JWT access tokens. This preserves local validation but excludes standards-compliant providers that intentionally issue reference or opaque access tokens.
Use the OIDC
id_tokenas the gateway bearer token. This confuses authentication evidence with API authorization. The ID token's audience is the OAuth client, and it should not replace the access token.Automatically call UserInfo when JWT parsing fails. This makes network behavior dependent on untrusted token syntax, hides configuration mistakes, and can add avoidable load. An explicit strategy is easier to operate and audit.
Add RFC 7662 token introspection in the same change. Introspection is a useful future strategy, but it requires confidential-client credentials and endpoint configuration that are not part of standard OIDC discovery. Keeping it separate makes this proposal smaller and avoids introducing secret distribution into gateway auth.
Standards Alignment
Agent Investigation
crates/openshell-server/src/auth/oidc.rs, where access tokens are parsed and validated as JWTs against discovered JWKS.crates/openshell-bootstrap/src/oidc_token.rsstores the OAuth access token and optional refresh token used by the CLI.crates/openshell-server/src/auth/identity.rsalready provides the provider-neutral identity boundary needed by either validation strategy.docs/reference/gateway-auth.mdxdocuments Authorization Code with PKCE and the existing gateway bearer-token flow.Test Considerations
Checklist