Browse Source

Merge 6e578ceba8 into 4fb3e7810b

pull/4600/merge
kt 23 hours ago committed by GitHub
parent
commit
54a00e4b93
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 267
      docs/enhancements/id-jag-2026-03-02#4600.md

267
docs/enhancements/id-jag-2026-03-02#4600.md

@ -0,0 +1,267 @@
# Dex Enhancement Proposal (DEP) 4600 - 2026-03-02 - Identity Assertion JWT Authorization Grant (ID-JAG)
## Table of Contents
- [Dex Enhancement Proposal (DEP) 4600 - 2026-03-02 - Identity Assertion JWT Authorization Grant (ID-JAG)](#dex-enhancement-proposal-dep-4600---2026-03-02---identity-assertion-jwt-authorization-grant-id-jag)
- [Table of Contents](#table-of-contents)
- [Summary](#summary)
- [Context](#context)
- [Motivation](#motivation)
- [Goals/Pain](#goalspain)
- [Non-goals](#non-goals)
- [Proposal](#proposal)
- [User Experience](#user-experience)
- [Implementation Details/Notes/Constraints](#implementation-detailsnotesconstraints)
- [Risks and Mitigations](#risks-and-mitigations)
- [Alternatives](#alternatives)
- [Future Improvements](#future-improvements)
## Summary
[draft-ietf-oauth-identity-assertion-authz-grant-01] specifies a mechanism
for an application to use an identity assertion to obtain an access token
for a third-party API by coordinating through a common enterprise identity
provider using Token Exchange [RFC 8693] and JWT Profile for OAuth 2.0
Authorization Grants [RFC 7523].
This DEP proposes to extend Dex's existing Token Exchange implementation
to support issuing Identity Assertion JWT Authorization Grants (ID-JAGs),
enabling cross-domain access managed by the enterprise IdP.
[draft-ietf-oauth-identity-assertion-authz-grant-01]: https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/
## Context
- [#2812 DEP for RFC 8693 OAuth 2 Token Exchange]
established the Token Exchange foundation that ID-JAG builds upon.
- [draft-ietf-oauth-identity-assertion-authz-grant-01]
is the IETF Standards Track specification this DEP implements.
- [draft-ietf-oauth-identity-chaining]
is the broader identity chaining specification that ID-JAG profiles.
The specification is authored by A. Parecki (Okta), K. McGuinness, and
B. Campbell (Ping Identity). It is actively being developed within the
IETF OAuth Working Group.
Use cases:
- LLM agents accessing enterprise APIs on behalf of users (Appendix A.3 of the spec)
- Enterprise applications embedding content from third-party apps
- Email/calendaring applications accessing cross-domain resources
Real-world adoption:
- [Okta Cross App Access] is GA, implementing ID-JAG for SaaS-to-SaaS and
AI agent scenarios with a developer tutorial available.
- [Okta AI Agent Token Exchange] (Early Access) uses ID-JAG for AI agents
accessing enterprise APIs on behalf of authenticated users.
- [Keycloak #43971] tracks ID-JAG support as a feature request.
- The upcoming MCP (Model Context Protocol) specification references ID-JAG
for AI agent authorization flows.
[#2812 DEP for RFC 8693 OAuth 2 Token Exchange]: https://github.com/dexidp/dex/pull/2812
[draft-ietf-oauth-identity-chaining]: https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-chaining/
[Okta Cross App Access]: https://developer.okta.com/blog/2026/02/10/xaa-client
[Okta AI Agent Token Exchange]: https://developer.okta.com/docs/guides/ai-agent-token-exchange/authserver/main/
[Keycloak #43971]: https://github.com/keycloak/keycloak/issues/43971
## Motivation
### Goals/Pain
In enterprise environments, applications are configured for SSO through
a common IdP. When one application needs to access a user's data at another
application, the current approach requires either:
1. A direct OAuth flow between apps (bypassing the IdP's visibility and policy)
2. Static API keys or service accounts (security risk)
ID-JAG solves this by letting the IdP broker cross-domain access, maintaining
visibility and policy control.
**Specific goals:**
- Issue ID-JAG tokens via Token Exchange (`requested_token_type=urn:ietf:params:oauth:token-type:id-jag`)
- Support `audience` and `resource` parameters per the specification
- Validate subject token audience against requesting client
- Support configurable policy evaluation for token exchange requests
### Non-goals
- Implementing the Resource Authorization Server role (JWT Bearer Grant / RFC 7523)
is out of scope for this initial DEP. It may be addressed in a follow-up.
- SAML assertion support as `subject_token_type` is deferred.
- Step-up authentication flow is deferred.
## Proposal
### User Experience
End-to-end flow:
```mermaid
sequenceDiagram
participant C as Client (Wiki App)
participant Dex as Dex (IdP AS)
participant RAS as Resource AS (Chat AS)
participant RS as Resource Server (Chat API)
C->>Dex: 1. OIDC Authentication (authorization_code flow)
Dex-->>C: ID Token + optional Refresh Token
C->>Dex: 2. Token Exchange (grant_type=token-exchange)<br/>subject_token=ID Token<br/>requested_token_type=id-jag<br/>audience=https://acme.chat.example/<br/>connector_id=google
Note over Dex: Validate subject_token aud == client_id<br/>Evaluate policy (clientID → allowed audiences)<br/>Issue ID-JAG JWT (typ: oauth-id-jag+jwt)
Dex-->>C: ID-JAG (access_token, token_type=N_A, expires_in=300)
C->>RAS: 3. JWT Bearer Grant (RFC 7523)<br/>grant_type=jwt-bearer, assertion=ID-JAG
Note over RAS: Validate ID-JAG signature (Dex JWKS)<br/>Validate aud == RAS issuer<br/>Issue access token
RAS-->>C: Access Token
C->>RS: 4. API Request with Access Token
RS-->>C: Protected Resource
```
Clients can request ID-JAG tokens from Dex's `/token` endpoint by specifying
`requested_token_type=urn:ietf:params:oauth:token-type:id-jag` in a
Token Exchange request. ID-JAG support is enabled by adding
`urn:ietf:params:oauth:token-type:id-jag` to `oauth2.tokenExchange.tokenTypes`.
When not listed, requests with this `requested_token_type` are rejected,
ensuring no change in behavior for existing deployments.
The request parameters (extending existing Token Exchange):
- `grant_type`: REQUIRED - `urn:ietf:params:oauth:grant-type:token-exchange`
- `subject_token`: REQUIRED - the identity assertion (OpenID Connect ID Token)
- `subject_token_type`: REQUIRED - `urn:ietf:params:oauth:token-type:id_token`.
SAML 2.0 (`urn:ietf:params:oauth:token-type:saml2`) is deferred (see Non-goals).
- `requested_token_type`: REQUIRED - `urn:ietf:params:oauth:token-type:id-jag`
- `audience`: REQUIRED - the Issuer URL of the Resource Authorization Server.
**Note**: The existing Token Exchange implementation uses a Dex-specific `connector_id`
parameter (not part of RFC 8693) for connector selection. The `audience` parameter was
not used in the current implementation despite DEP #2812 originally proposing it for
connector identification. ID-JAG introduces `audience` with its standard RFC 8693
meaning (target Resource AS). This is purely additive and does not affect existing
Token Exchange requests.
- `connector_id`: REQUIRED (Dex extension) - the ID of the Dex connector to verify the
subject token against. This parameter already exists in the current Token Exchange
implementation and is reused as-is.
- `resource`: OPTIONAL - the Resource Identifier of the Resource Server
- `scope`: OPTIONAL - the requested scopes at the Resource Server
The response:
- `access_token`: the ID-JAG JWT (named `access_token` for RFC 8693 compatibility)
- `issued_token_type`: `urn:ietf:params:oauth:token-type:id-jag`
- `token_type`: `N_A` (this is not an OAuth access token)
- `expires_in`: lifetime in seconds (default: 300, configurable independently of ID token
lifetime via `expiry.idJAGTokens`)
- `scope`: OPTIONAL if the issued scope is identical to the requested scope; REQUIRED
otherwise. Per Section 4.3.2 of the specification, policy evaluation at the IdP may
result in different scopes being issued than were requested.
Complete configuration example:
```yaml
oauth2:
grantTypes:
- authorization_code
- urn:ietf:params:oauth:grant-type:token-exchange
tokenExchange:
# List of token types enabled for exchange. Adding id-jag enables ID-JAG support.
# Omitting it (default) disables ID-JAG without affecting other token exchange flows.
# SAML2 (urn:ietf:params:oauth:token-type:saml2) may be added in a future release.
tokenTypes:
- urn:ietf:params:oauth:token-type:id_token
- urn:ietf:params:oauth:token-type:id-jag
expiry:
idTokens: "24h"
idJAGTokens: "5m" # default: 5m; independent of idTokens
staticClients:
- id: wiki-app
name: "Wiki Application"
secret: "wiki-secret"
redirectURIs:
- "https://wiki.example/callback"
# Per-client ID-JAG policy. Clients without this section cannot obtain ID-JAG tokens
# (default-deny). Only audiences and scopes listed here may be requested.
idJAGPolicies:
allowedAudiences:
- "https://chat.example/"
- "https://calendar.example/"
allowedScopes:
- "chat.read"
- "calendar.read"
- id: supermarket-app
name: "Supermarket Application"
secret: "supermarket-secret"
redirectURIs:
- "https://supermarket.example/callback"
idJAGPolicies:
allowedAudiences:
- "https://grocery.store.1/"
- "https://grocery.store.2/"
allowedScopes:
- "eat.bananas"
- "eat.apples"
```
### Implementation Details/Notes/Constraints
- A new `id-jag` branch is added to the existing Token Exchange flow, issuing a signed JWT
per Section 3 of the specification (header `typ: "oauth-id-jag+jwt"`, claims including
`iss`, `sub`, `aud`, `client_id`, `jti`, `exp`, `iat`).
- Per-client `idJAGPolicies` in `staticClients` control which audiences and scopes a
given client may request in an ID-JAG. Clients without `idJAGPolicies` are denied
by default. Dynamically registered clients are currently unsupported for ID-JAG policies;
support via DB-stored policies or CEL expressions (#4601) is deferred to a future release.
- OIDC discovery is extended with `identity_chaining_requested_token_types_supported` per
Section 6 of the specification. When ID-JAG is enabled, Dex includes
`urn:ietf:params:oauth:token-type:id-jag` in this metadata property.
- ID-JAG support is enabled by listing `urn:ietf:params:oauth:token-type:id-jag` in
`oauth2.tokenExchange.tokenTypes`. When not listed (default), requests are rejected,
ensuring no change in behavior for existing deployments.
### Risks and Mitigations
- **Lateral movement risk**: Same as existing Token Exchange. Mitigated by
not issuing refresh tokens, short expiry (5 min recommended), and
policy-based audience restrictions.
- **Token confusion**: The `typ: "oauth-id-jag+jwt"` header and distinct
`issued_token_type` prevent confusion with ID Tokens or access tokens.
- **Replay attack risk**: Server-side `jti` tracking is deferred, so a stolen ID-JAG
can be replayed within its 5-minute lifetime. Short `expires_in` is the only Dex-side
mitigation; Resource Authorization Servers should implement `jti` caching independently.
- **Public client misuse**: Per Section 7.1 of the specification, ID-JAG SHOULD only be
used by confidential clients. Public clients should use the standard authorization code
flow with interactive user consent at the Resource Authorization Server. Dex will enforce
this by rejecting ID-JAG requests from public clients (clients without a secret).
- **Breaking changes**: None. This is purely additive to the existing
Token Exchange implementation. The `audience` parameter is newly introduced
(not previously used in the implementation despite DEP #2812's original proposal),
and `connector_id` already exists.
### Alternatives
- **Wait for spec finalization**: The draft is Standards Track and stable enough
to implement. Okta and Ping Identity (the spec authors) already ship implementations,
and the spec has been adopted by the IETF OAuth WG.
- **External policy engine (OPA/CEL)**: Deferred; config-based policies are sufficient
for now. See #4601 for CEL integration proposal.
## Future Improvements
- Resource Authorization Server role (JWT Bearer Grant / RFC 7523)
accepting ID-JAGs from external IdPs
- SAML 2.0 assertion support as `subject_token_type`
(`urn:ietf:params:oauth:token-type:saml2`)
- CEL-based policy evaluation (#4601) enabling dynamic policies for
dynamically registered clients
- Step-up authentication when authentication context is insufficient
- `actor_token` support for delegation scenarios
- Server-side `jti` tracking to prevent ID-JAG replay attacks
Loading…
Cancel
Save