diff --git a/README.md b/README.md index 1d800d63..f1043d9a 100644 --- a/README.md +++ b/README.md @@ -42,12 +42,13 @@ openid-client. - Client Authentication - tls_client_auth - self_signed_tls_client_auth +- [RFC9101 - OAuth 2.0 JWT-Secured Authorization Request (JAR)][feature-jar] +- [RFC9126 - OAuth 2.0 Pushed Authorization Requests (PAR)][feature-par] - [OpenID Connect Session Management 1.0 - draft 28][feature-rp-logout] - RP-Initiated Logout - [Financial-grade API - Part 2: Read and Write API Security Profile (FAPI) - ID2][feature-fapi] - [JWT Secured Authorization Response Mode for OAuth 2.0 (JARM) - ID1][feature-jarm] - [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP) - draft 01][feature-dpop] -- [OAuth 2.0 Pushed Authorization Requests (PAR) - draft 06][feature-par] Updates to draft specifications (DPoP, JARM, and FAPI) are released as MINOR library versions, if you utilize these specification implementations consider using the tilde `~` operator in your @@ -297,7 +298,8 @@ See [Customizing (docs)](https://github.com/panva/node-openid-client/blob/master [feature-jarm]: https://openid.net/specs/openid-financial-api-jarm-ID1.html [feature-fapi]: https://openid.net/specs/openid-financial-api-part-2-ID2.html [feature-dpop]: https://tools.ietf.org/html/draft-ietf-oauth-dpop-01 -[feature-par]: https://tools.ietf.org/html/draft-ietf-oauth-par-06 +[feature-par]: https://www.rfc-editor.org/rfc/rfc9126.html +[feature-jar]: https://www.rfc-editor.org/rfc/rfc9101.html [openid-certified-link]: https://openid.net/certification/ [passport-url]: http://passportjs.org [npm-url]: https://www.npmjs.com/package/openid-client diff --git a/lib/client.js b/lib/client.js index 07573245..0a07d86c 100644 --- a/lib/client.js +++ b/lib/client.js @@ -1550,6 +1550,57 @@ module.exports = (issuer, aadIssValidation = false) => class Client extends Base }); } + /** + * @name pushedAuthorizationRequest + * @api public + */ + async pushedAuthorizationRequest(params = {}, { clientAssertionPayload } = {}) { + assertIssuerConfiguration(this.issuer, 'pushed_authorization_request_endpoint'); + + const body = { + ...('request' in params ? params : authorizationParams.call(this, params)), + client_id: this.client_id, + }; + + const response = await authenticatedPost.call( + this, + 'pushed_authorization_request', + { + responseType: 'json', + form: body, + }, + { clientAssertionPayload, endpointAuthMethod: 'token' }, + ); + const responseBody = processResponse(response, { statusCode: 201 }); + + if (!('expires_in' in responseBody)) { + throw new RPError({ + message: 'expected expires_in in Pushed Authorization Successful Response', + response, + }); + } + if (typeof responseBody.expires_in !== 'number') { + throw new RPError({ + message: 'invalid expires_in value in Pushed Authorization Successful Response', + response, + }); + } + if (!('request_uri' in responseBody)) { + throw new RPError({ + message: 'expected request_uri in Pushed Authorization Successful Response', + response, + }); + } + if (typeof responseBody.request_uri !== 'string') { + throw new RPError({ + message: 'invalid request_uri value in Pushed Authorization Successful Response', + response, + }); + } + + return responseBody; + } + /** * @name issuer * @api public @@ -1660,72 +1711,4 @@ Object.defineProperty(BaseClient.prototype, 'dpopProof', { }, }); -/** - * @name pushedAuthorizationRequest - * @api public - */ -async function pushedAuthorizationRequest(params = {}, { clientAssertionPayload } = {}) { - assertIssuerConfiguration(this.issuer, 'pushed_authorization_request_endpoint'); - - const body = { - ...('request' in params ? params : authorizationParams.call(this, params)), - client_id: this.client_id, - }; - - const response = await authenticatedPost.call( - this, - 'pushed_authorization_request', - { - responseType: 'json', - form: body, - }, - { clientAssertionPayload, endpointAuthMethod: 'token' }, - ); - const responseBody = processResponse(response, { statusCode: 201 }); - - if (!('expires_in' in responseBody)) { - throw new RPError({ - message: 'expected expires_in in Pushed Authorization Successful Response', - response, - }); - } - if (typeof responseBody.expires_in !== 'number') { - throw new RPError({ - message: 'invalid expires_in value in Pushed Authorization Successful Response', - response, - }); - } - if (!('request_uri' in responseBody)) { - throw new RPError({ - message: 'expected request_uri in Pushed Authorization Successful Response', - response, - }); - } - if (typeof responseBody.request_uri !== 'string') { - throw new RPError({ - message: 'invalid request_uri value in Pushed Authorization Successful Response', - response, - }); - } - - return responseBody; -} - -Object.defineProperty(BaseClient.prototype, 'pushedAuthorizationRequest', { - enumerable: true, - configurable: true, - value(...args) { - process.emitWarning( - 'The Pushed Authorization Requests APIs implements an IETF draft. Breaking draft implementations are included as minor versions of the openid-client library, therefore, the ~ semver operator should be used and close attention be payed to library changelog as well as the drafts themselves.', - 'DraftWarning', - ); - Object.defineProperty(BaseClient.prototype, 'pushedAuthorizationRequest', { - enumerable: true, - configurable: true, - value: pushedAuthorizationRequest, - }); - return this.pushedAuthorizationRequest(...args); - }, -}); - module.exports.BaseClient = BaseClient;