fix(identity-webhook): OIDC composite app_<24hex>_<secret> token exchange#63
Conversation
Replace the incorrect default {issuer}/.well-known/jwks.json with
issuer-relative OIDC discovery (openid-configuration → jwks_uri).
Validate discovery issuer, wrap JWKS fetch errors with the URL, and
keep OIDC_JWKS_URI as an explicit override. Unit tests cover discovery
and leave api_key mode unchanged.
70f732d to
301770e
Compare
56c5708 to
97c538a
Compare
|
Rebased onto retargeted |
Accept Bearer app_<24hex>_<secret> when OIDC_TOKEN_EXCHANGE_BASE_URL is set: RFC 8693 exchange at the app-scoped token endpoint, then jwtVerify the minted access token (same JWKS discovery path as #62). Secret segment is opaque (operator prefixes allowed); reject cs_ / _cs_ client-secret shapes. Harden cache keys (salted HKDF) and log sanitization. Document exchange env vars.
97c538a to
e3a868b
Compare
There was a problem hiding this comment.
Pull request overview
Adds optional support in identity-webhook OIDC mode for composite Bearer app_<24hex>_<secret> credentials by exchanging them for an access token via RFC 8693 token exchange, then verifying the minted JWT, with documentation and tests to match.
Changes:
- Implement composite token parsing + RFC 8693 exchange flow in
createOidcVerifier, including caching and hardened logging. - Add unit tests for composite parsing/URL normalization and the end-to-end exchange→JWT verify path.
- Document new env vars in
README.mdand.env.example.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| README.md | Documents new OIDC token-exchange env vars and composite bearer behavior. |
| identity-webhook/verifiers.mjs | Implements composite key parsing, token exchange, caching, and JWT verification integration. |
| identity-webhook/verifiers.test.mjs | Adds coverage for composite parsing/normalization and exchange-based verification flow. |
| .env.example | Adds example env vars for enabling token exchange and optional M2M credentials. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Expire stale composite-exchange inflight cache entries, require Bearer scheme, reject non-origin token-exchange URLs, expand JWT algs to RS256/ES256, require exp with 60s clock skew, and normalize OIDC issuer consistently for discovery and jwtVerify.
Expire stale composite-exchange inflight cache entries, require Bearer scheme, reject non-origin token-exchange URLs, expand JWT algs to RS256/ES256, require exp with 60s clock skew, and normalize OIDC issuer consistently for discovery and jwtVerify.
|
Addressed Copilot review + related OIDC hardening in 378b79b:
Same commit cherry-picked to pymthouse#6 ( |
…e races Allow jwtVerify to match iss with or without a trailing slash after issuer normalization, and only clear/set composite-exchange cache entries when they still reference the same inflight promise.
|
Addressed the two latest Copilot comments in $(git rev-parse --short HEAD):
|
…extraction Refactor the bearerToken function to enhance token extraction from the authorization header. The new implementation checks for the "Bearer" scheme and handles leading spaces more robustly. Additionally, add tests to cover various cases, including malformed input and trailing spaces, ensuring the function behaves correctly under different scenarios.
…xtraction Update the bearerToken function to utilize a regular expression for more concise and efficient token extraction from the authorization header. This change improves readability and maintains functionality by ensuring proper handling of the "Bearer" scheme and leading spaces.
Address remaining PR #63 Copilot review: reject username/password in OIDC_TOKEN_EXCHANGE_BASE_URL (origin drops them silently), document HTTPS-except-loopback in README/.env.example, and cover RFC 6750 whitespace rejection in bearerToken tests.
|
Addressed remaining Copilot review in e0be4d7:
|
Manual smoke — composite exchange against local clearinghouse stackTested on a stacked branch ( Config
Composite Bearer used( Authorize (identity-webhook){
"status": 200,
"auth_id": "app_a559adff001a1cbc50f53dac:895773a5-7470-4638-bdf9-a2b3d15906b8",
"identity": {
"issuer": "http://identity-webhook:8090",
"client_id": "app_a559adff001a1cbc50f53dac",
"usage_subject": "895773a5-7470-4638-bdf9-a2b3d15906b8",
"usage_subject_type": "external_user_id"
}
}Live LV2V stream (local signer
|
|
peer reviewed by @rickstaa, merging per discussion (change is bug fix, rather than feature) |
Summary
Bearer app_<24hex>_<secret>in OIDC mode whenOIDC_TOKEN_EXCHANGE_BASE_URLis set./api/v1/apps/{clientId}/oidc/token, thenjwtVerifythe minted access token (JWKS via discovery from fix(identity-webhook): resolve JWKS via OIDC discovery #62).cs_/_cs_client-secret shapes..env.example.Behavior
OIDC_TOKEN_EXCHANGE_BASE_URL, OIDC mode stays JWT-only.app_+ 24 lowercase hex +_+ opaque secret (underscore separator for copy/select UX).OIDC_EXCHANGE_M2M_CLIENT_ID+OIDC_EXCHANGE_M2M_CLIENT_SECRET(both or neither).createEndUserVerifierFromEnv(Docker / Compose path).Test plan
cd identity-webhook && npm test(42 passing: discovery, composite exchange,api_keymode)Notes
legacy-envpackaging is not in this PR (deferred).