Add jwt-bearer grant for ID-JAG (Identity Assertion Authorization Grant) support#462
Open
manmohan-shaw-okta wants to merge 1 commit into
Open
Conversation
Adds a built-in `jwt-bearer` grant (urn:ietf:params:oauth:grant-type:jwt-bearer,
RFC 7523) implementing the Identity Assertion Authorization Grant (ID-JAG)
draft, letting this library act as the Resource Authorization Server side of
a Cross App Access exchange.
- lib/utils/jwt-util.js: dependency-free JWS decode/verify (RS256/ES256/PS256)
using only Node's built-in crypto module.
- lib/grant-types/jwt-bearer-grant-type.js: the grant itself - assertion
parsing, typ/alg checks, issuer trust + key resolution, claim validation,
replay protection, user resolution, permission hook, scope narrowing, and
token issuance without ever setting a refresh token.
- lib/handlers/token-handler.js, lib/server.js: wire the grant into the
built-in grantTypes map and thread through the new tokenEndpointUri /
idJagClockSkew / jwtBearerAllowedAlgorithms / jwtBearerAllowPublicClients
options.
- lib/model.js, index.d.ts: new required model hooks (getTrustedIssuer,
getRequestingIssuerKey, getUserFromIdJagAssertion, validateIdJagPermission,
validateJti / isJtiUsed+recordJti) and their TypeScript types.
- docs/guide/{grant-types,model}.md, examples/express-id-jag-server.js: usage
docs and a runnable example.
- test/{unit,integration}/...: unit + integration coverage, including the
full security matrix (type confusion, algorithm confusion, audience/claim
validation, clock skew, replay, scope narrowing, refresh-token suppression).
Member
|
@dhensby how should we proceed with PRs that are geared toward non standard RFCs? The RFC 7523 is a draft and likely become standard but it remains unclear when this will happen. |
Member
|
also, how far is this related to #453 ? |
manmohan-shaw-okta
marked this pull request as ready for review
July 20, 2026 02:53
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a built-in
jwt-bearergrant (urn:ietf:params:oauth:grant-type:jwt-bearer, RFC 7523) implementing the Identity Assertion Authorization Grant (ID-JAG) draft, so this library can act as the Resource Authorization Server side of a Cross App Access exchange: it verifies an ID-JAG assertion minted by an external Identity Provider and, once verified, issues a locally-scoped access token. Minting the ID-JAG itself (the IdP side, RFC 8693 Token Exchange) is out of scope for this grant.The library currently has zero JWT/crypto dependencies, and per
CONTRIBUTING.mdnew tight dependencies shouldn't be introduced without discussion, so signature verification (RS256/ES256/PS256) is implemented using only Node's built-incryptomodule — no new npm dependency.Linked issue(s)
This continues the discussion in #411 (ecosystem-wide ID-JAG tracking + the requirements spec for this library specifically). I have not yet opened a formal tracking Issue for this PR — happy to do so if a maintainer confirms the existing Discussion isn't sufficient per the "no PR without an issue" contribution guideline. Opening this as a Draft for that reason.
Involved parts of the project
lib/utils/jwt-util.js(new): dependency-free JWS decode/verify (RS256/ES256/PS256) using Node's built-incrypto.lib/grant-types/jwt-bearer-grant-type.js(new): the grant itself.lib/handlers/token-handler.js,lib/server.js: register the grant as built-in (notextendedGrantTypes, since this is a registered IETF grant type) and thread through new options:tokenEndpointUri(required — this AS's own RFC 8414 issuer identifier),idJagClockSkew,jwtBearerAllowedAlgorithms,jwtBearerAllowPublicClients.lib/model.js,index.d.ts: 5 new model hooks —getTrustedIssuer,getRequestingIssuerKey,getUserFromIdJagAssertion,validateIdJagPermission,validateJti(orisJtiUsed+recordJti) — only required if this grant is used, following the same conditionally-required pattern as e.g.getRefreshTokenfor therefresh_tokengrant.docs/guide/grant-types.md,docs/guide/model.md,examples/express-id-jag-server.js: usage docs and a runnable example.OAuth2 workflow involved: token endpoint (
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer) only. No changes toauthorize/authenticate.Added tests?
Yes —
test/unit/grant-types/jwt-bearer-grant-type_test.js,test/integration/grant-types/jwt-bearer-grant-type_test.js,test/unit/utils/jwt-util_test.js. The integration suite mints real signed JWTs (RS256/ES256/PS256) and covers the full security matrix:typtype-confusion,algallow-list violations (none,HS256), audience mismatch, expired/future/clock-skew-boundary timestamps, tampered signatures, untrusted issuer, unresolvable key,client_idclaim mismatch, replay (bothvalidateJti()andisJtiUsed()/recordJti()model shapes, plus fail-closed on a throwing replay store), refresh-token suppression, and scope intersection/narrowing.npm run lintandnpm test(528 passing) are clean.OAuth2 standard
draft-ietf-oauth-identity-assertion-authz-grant-03) — the profile this grant implements claim-by-claim (Sections 3.1, 4.4.1, 4.4.3, 8.1, 9 referenced directly in code comments/JSDoc).audis validated against this AS's issuer identifier.OAuthErrorsubclasses (InvalidGrantError,InvalidRequestError,InvalidClientError,InvalidScopeError); no new error classes were needed. Per the draft's error-handling guidance, assertion-validation failures all share one non-specific message so a response can't be used as an oracle for which check failed.Reproduction
See
examples/express-id-jag-server.jsfor a full runnable example (mints a self-signed test assertion and exchanges it end-to-end with no external IdP required —npm install express && node examples/express-id-jag-server.js).Additional note for maintainers
CONTRIBUTING.mdsays to branch fromdevelopment, but that branch is stale (last commit Jan 2026,5.2.2-rc.0) relative tomaster(actively merged into, most recently Jul 2026). This PR targetsmaster; happy to retarget if that's wrong.