Skip to content

feat: add RFC 8693 token exchange grant - #7759

Open
Thiago-AS wants to merge 43 commits into
mainfrom
PLATFOR-532
Open

feat: add RFC 8693 token exchange grant#7759
Thiago-AS wants to merge 43 commits into
mainfrom
PLATFOR-532

Conversation

@Thiago-AS

Copy link
Copy Markdown
Contributor

Context

Middleware acting on behalf of users (MCP servers, AI agents, internal developer portals, API gateways) currently has to authenticate using a shared machine identity. This loses user attribution in the audit log and grants every user the full set of permissions assigned to that shared identity.

This change adds support for the RFC 8693 Token Exchange grant to the existing OAuth token endpoint. Trusted middleware can present a user's token issued by the organization's OIDC identity provider and receive a short-lived Infisical access token for that same user. The issued token carries the user's actual permissions and is fully attributed to them in the audit log. No refresh token is issued, and delegated tokens cannot access account self-management endpoints.

OAuth applications now explicitly declare their supported grant types. Existing applications and API clients that do not specify any grant types continue using the authorization code flow by default, so this change is fully backward compatible.

Token Exchange applications introduce one additional configuration field: the audience that the organization's identity provider issues for that middleware. This prevents tokens minted for unrelated applications from being exchanged.

The trust anchor is the organization's OIDC SSO configuration rather than per-application issuer settings. As a result, enabling Token Exchange, updating its audience, or rotating a Token Exchange application's client secret all require SSO edit permissions. Deleting a client, rotating its secret, or removing the Token Exchange grant immediately revokes any delegated tokens it has previously issued.

Screenshots

Steps to verify the change

  1. In the UI, create an OAuth application with the Token Exchange flow. Verify that the Audience field is required and that the redirect URI fields are hidden.
  2. Verify that the SSO page displays the "Applications depend on this issuer" callout once a Token Exchange application exists.
  3. Verify that successful exchanges appear in the organization audit log, attributed to the end user, with the acting application included in the metadata.
  4. Run the end-to-end flow below.

Testing end to end locally

A complete walkthrough is available in sso.md under Testing OAuth Token Exchange (RFC 8693). The abbreviated flow is:

make up-dev-oidc     # Starts the default stack plus Keycloak with the seeded Infisical realm
make seed-dev-oidc   # Creates the oidc org, admin@oidc.com / password123!, verified domain, and active OIDC configuration

The seeded realm also includes a second client, infisical-mcp, which represents the middleware.

Sign in to http://localhost:8080/ as admin@oidc.com, navigate to Organization Settings → OAuth Applications, create an application using the Token Exchange flow, set the audience to infisical-mcp, and copy the generated client credentials.

Then run:

CLIENT_ID="<paste>"
CLIENT_SECRET="<paste>"

SUBJECT_TOKEN=$(curl -s -X POST \
  http://localhost:8088/realms/infisical/protocol/openid-connect/token \
  -d grant_type=password -d scope=openid \
  -d client_id=infisical-mcp -d client_secret=infisical-mcp-client-secret \
  -d username=admin@oidc.com -d password='password123!' \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["id_token"])')

curl -s -X POST http://localhost:8080/api/v1/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
  -d subject_token="$SUBJECT_TOKEN" \
  -d subject_token_type=urn:ietf:params:oauth:token-type:id_token \
  | python3 -m json.tool

Use the id_token, not the access token. The seeded Keycloak realm does not include an audience mapper, so its access tokens do not contain an aud claim.

Finally, validate the issued token:

  • GET /api/v1/oauth/validate returns 200.
  • GET /api/v1/user/me/totp returns 403, confirming delegated tokens cannot access account management endpoints.
  • Reading a secret in the oidc organization succeeds.

sso.md also documents the recommended negative test cases, including cross-application replay, invalid signatures, supplying a scope, SSO disabled, MFA required, unregistered grants, and the alias requirement for users who have never signed in through SSO.

Type

  • Fix
  • Feature
  • Improvement
  • Breaking
  • Docs
  • Chore

Checklist

  • Title follows the conventional commit format: type(scope): short description (scope is optional, e.g., fix: prevent crash on sync or fix(api): handle null response).
  • Tested locally
  • Updated docs (if needed)
  • Updated CLAUDE.md files (if needed)
  • Read the contributing guide

@linear

linear Bot commented Aug 21, 2026

Copy link
Copy Markdown

PLATFOR-532

@infisical-review-police

Copy link
Copy Markdown

💬 Discussion in Slack: #pr-review-infisical-7759-feat-add-rfc-8693-token-exchange-grant

Posted by Review Police — reviews, comments, new commits, and CI failures will stream into this channel.

@gitguardian

gitguardian Bot commented Aug 21, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
35864000 Triggered Generic High Entropy Secret 0f1f0cf backend/e2e-test/routes/v1/oauth-token-exchange.spec.ts View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

@greptile-apps

greptile-apps Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds RFC 8693 token exchange backed by organization OIDC configuration and extends OAuth applications with explicit grant registration.

  • Adds subject-token validation, delegated access-token issuance, audit attribution, and client-authority revocation handling.
  • Enables delegated OAuth authentication across permission-bearing API routes while keeping account-management routes unavailable.
  • Adds OAuth client schema, migration, API, frontend, documentation, local OIDC development, and end-to-end test changes.

Confidence Score: 4/5

The PR appears safe to merge, with a non-blocking audit-log usability issue to address.

The token-exchange trust, validation, revocation, and delegated-permission paths include the expected safeguards; the accepted concern is limited to raw identifiers in the new audit event metadata.

Files Needing Attention: backend/src/services/oauth-client/oauth-client-service.ts, backend/src/ee/services/audit-log/audit-log-types.ts

Important Files Changed

Filename Overview
backend/src/services/oauth-client/oauth-token-exchange-fns.ts Implements cached, SSRF-pinned OIDC discovery/JWKS resolution and subject-token claim verification.
backend/src/services/oauth-client/oauth-client-service.ts Adds token exchange, grant validation, delegated session creation, authority-race checks, and audit emission; the new audit metadata lacks labels for some identifiers.
backend/src/server/plugins/auth/inject-identity.ts Recognizes delegated OAuth tokens and distinguishes scoped delegation from explicit full delegation.
backend/src/ee/services/permission/permission-service.ts Intersects scoped OAuth delegation with organization and project abilities while preserving explicit full delegation.
backend/src/db/migrations/20260806120000_add-oauth-client-grant-types.ts Backfills existing OAuth clients with authorization-code and refresh-token grants and adds token-exchange configuration columns.
backend/src/ee/services/audit-log/audit-log-types.ts Defines the token-exchange audit event, including identifier fields that are not all paired with human-readable labels.
frontend/src/pages/organization/SettingsPage/components/OrgOauthClientsTab/OauthClientModal.tsx Adds grant-flow selection and token-exchange audience/MFA configuration to the OAuth client form.
backend/e2e-test/routes/v1/oauth-token-exchange.spec.ts Covers grant registration, endpoint validation, delegation markers, protected account routes, and refresh-token issuance.

Reviews (1): Last reviewed commit: "block from deleting org" | Re-trigger Greptile

Comment thread backend/src/services/oauth-client/oauth-client-service.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6fdf713b4c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread backend/src/services/oauth-client/oauth-client-service.ts Outdated
Comment thread backend/src/services/oauth-client/oauth-token-exchange-fns.ts Outdated
@veria-ai

veria-ai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

PR overview

Adds support for the RFC 8693 OAuth token exchange grant, including use of delegated tokens with dynamic-secret leasing and ACME enrollment workflows.

Four security issues have been addressed, but one significant authorization-lifecycle gap remains. A holder of a full-delegation exchange token can mint infrastructure credentials or reveal ACME enrollment secrets that remain usable after the originating OAuth sessions are revoked, enabling persistent access beyond the intended revocation boundary.

Open issues (1)

Fixed/addressed: 4 · PR risk: 7/10

Comment thread backend/src/server/routes/v1/identity-token-auth-router.ts Outdated
Comment thread backend/src/ee/routes/v1/scim-router.ts
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN, AuthMode.OAUTH]),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium: Minted credentials outlive OAuth revocation

A client with a full-delegation exchange token can create a dynamic-secret lease—potentially returning AWS, database, or Kubernetes credentials—and continue using those credentials after its OAuth sessions are revoked. The same persistence path exists in dynamic-secret-lease-routers/kubernetes-lease-router.ts:53 and through the ACME EAB reveal routes at pki-application-enrollment-routers/acme-enrollment-router.ts:150 and certificate-profiles-router.ts:898, where the EAB secret can register an independently authenticated ACME account. Keep OAuth disabled on these issuance/bootstrap routes, or explicitly revoke the derived leases and accounts whenever the originating OAuth session or client is revoked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant