openid-client
Advanced tools
+7
-0
@@ -5,2 +5,9 @@ # Changelog | ||
| ## [4.1.0](https://github.com/panva/node-openid-client/compare/v4.0.2...v4.1.0) (2020-09-11) | ||
| ### Features | ||
| * OAuth 2.0 DPoP in various relevant API interfaces ([44a0de7](https://github.com/panva/node-openid-client/commit/44a0de7ceb62cabacd62798ac136f1c394907028)) | ||
| ## [4.0.2](https://github.com/panva/node-openid-client/compare/v4.0.1...v4.0.2) (2020-09-11) | ||
@@ -7,0 +14,0 @@ |
+70
-11
@@ -376,3 +376,3 @@ /* eslint-disable max-classes-per-file */ | ||
| checks = {}, | ||
| { exchangeBody, clientAssertionPayload } = {}, | ||
| { exchangeBody, clientAssertionPayload, DPoP } = {}, | ||
| ) { | ||
@@ -467,3 +467,3 @@ let params = pickCb(parameters); | ||
| code_verifier: checks.code_verifier, | ||
| }, { clientAssertionPayload }); | ||
| }, { clientAssertionPayload, DPoP }); | ||
@@ -491,3 +491,3 @@ await this.decryptIdToken(tokenset); | ||
| checks = {}, | ||
| { exchangeBody, clientAssertionPayload } = {}, | ||
| { exchangeBody, clientAssertionPayload, DPoP } = {}, | ||
| ) { | ||
@@ -569,3 +569,3 @@ let params = pickCb(parameters); | ||
| code_verifier: checks.code_verifier, | ||
| }, { clientAssertionPayload }); | ||
| }, { clientAssertionPayload, DPoP }); | ||
| } | ||
@@ -978,3 +978,3 @@ | ||
| */ | ||
| async refresh(refreshToken, { exchangeBody, clientAssertionPayload } = {}) { | ||
| async refresh(refreshToken, { exchangeBody, clientAssertionPayload, DPoP } = {}) { | ||
| let token = refreshToken; | ||
@@ -993,3 +993,3 @@ | ||
| refresh_token: String(token), | ||
| }, { clientAssertionPayload }); | ||
| }, { clientAssertionPayload, DPoP }); | ||
@@ -1023,2 +1023,3 @@ if (tokenset.id_token) { | ||
| tokenType = accessToken instanceof TokenSet ? accessToken.token_type : 'Bearer', | ||
| DPoP, | ||
| } = {}, | ||
@@ -1048,3 +1049,3 @@ ) { | ||
| url: resourceUrl, | ||
| }, { mTLS }); | ||
| }, { mTLS, DPoP }); | ||
| } | ||
@@ -1057,3 +1058,3 @@ | ||
| async userinfo(accessToken, { | ||
| method = 'GET', via = 'header', tokenType, params, | ||
| method = 'GET', via = 'header', tokenType, params, DPoP, | ||
| } = {}) { | ||
@@ -1064,2 +1065,3 @@ assertIssuerConfiguration(this.issuer, 'userinfo_endpoint'); | ||
| method: String(method).toUpperCase(), | ||
| DPoP, | ||
| }; | ||
@@ -1234,3 +1236,3 @@ | ||
| */ | ||
| async grant(body, { clientAssertionPayload } = {}) { | ||
| async grant(body, { clientAssertionPayload, DPoP } = {}) { | ||
| assertIssuerConfiguration(this.issuer, 'token_endpoint'); | ||
@@ -1244,3 +1246,3 @@ const response = await authenticatedPost.call( | ||
| }, | ||
| { clientAssertionPayload }, | ||
| { clientAssertionPayload, DPoP }, | ||
| ); | ||
@@ -1256,3 +1258,3 @@ const responseBody = processResponse(response); | ||
| */ | ||
| async deviceAuthorization(params = {}, { exchangeBody, clientAssertionPayload } = {}) { | ||
| async deviceAuthorization(params = {}, { exchangeBody, clientAssertionPayload, DPoP } = {}) { | ||
| assertIssuerConfiguration(this.issuer, 'device_authorization_endpoint'); | ||
@@ -1285,2 +1287,3 @@ assertIssuerConfiguration(this.issuer, 'token_endpoint'); | ||
| maxAge: params.max_age, | ||
| DPoP, | ||
| }); | ||
@@ -1615,2 +1618,58 @@ } | ||
| /** | ||
| * @name dpopProof | ||
| * @api private | ||
| */ | ||
| function dpopProof(payload, jwk) { | ||
| if (!isPlainObject(payload)) { | ||
| throw new TypeError('payload must be a plain object'); | ||
| } | ||
| let key; | ||
| try { | ||
| key = jose.JWK.asKey(jwk); | ||
| assert(key.type === 'private'); | ||
| } catch (err) { | ||
| throw new TypeError('"DPoP" option must be an asymmetric private key to sign the DPoP Proof JWT with'); | ||
| } | ||
| let { alg } = key; | ||
| if (!alg && this.issuer.dpop_signing_alg_values_supported) { | ||
| const algs = key.algorithms('sign'); | ||
| alg = this.issuer.dpop_signing_alg_values_supported.find((a) => algs.has(a)); | ||
| } | ||
| if (!alg) { | ||
| [alg] = key.algorithms('sign'); | ||
| } | ||
| return jose.JWS.sign({ | ||
| iat: now(), | ||
| jti: random(), | ||
| ...payload, | ||
| }, jwk, { | ||
| alg, | ||
| typ: 'dpop+jwt', | ||
| jwk: pick(key, 'kty', 'crv', 'x', 'y', 'e', 'n'), | ||
| }); | ||
| } | ||
| Object.defineProperty(BaseClient.prototype, 'dpopProof', { | ||
| enumerable: true, | ||
| configurable: true, | ||
| value(...args) { | ||
| process.emitWarning( | ||
| 'The DPoP 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, 'dpopProof', { | ||
| enumerable: true, | ||
| configurable: true, | ||
| value: dpopProof, | ||
| }); | ||
| return this.dpopProof(...args); | ||
| }, | ||
| }); | ||
| module.exports.BaseClient = BaseClient; |
@@ -13,3 +13,3 @@ /* eslint-disable camelcase */ | ||
| constructor({ | ||
| client, exchangeBody, clientAssertionPayload, response, maxAge, | ||
| client, exchangeBody, clientAssertionPayload, response, maxAge, DPoP, | ||
| }) { | ||
@@ -28,2 +28,3 @@ ['verification_uri', 'user_code', 'device_code'].forEach((prop) => { | ||
| instance(this).client = client; | ||
| instance(this).DPoP = DPoP; | ||
| instance(this).maxAge = maxAge; | ||
@@ -54,3 +55,3 @@ instance(this).exchangeBody = exchangeBody; | ||
| }, | ||
| { clientAssertionPayload: instance(this).clientAssertionPayload }, | ||
| { clientAssertionPayload: instance(this).clientAssertionPayload, DPoP: instance(this).DPoP }, | ||
| ); | ||
@@ -57,0 +58,0 @@ |
@@ -125,3 +125,3 @@ const jose = require('jose'); | ||
| async function authenticatedPost(endpoint, opts, { | ||
| clientAssertionPayload, endpointAuthMethod = endpoint, | ||
| clientAssertionPayload, endpointAuthMethod = endpoint, DPoP, | ||
| } = {}) { | ||
@@ -153,3 +153,3 @@ const auth = await authFor.call(this, endpointAuthMethod, { clientAssertionPayload }); | ||
| url: targetUrl, | ||
| }, { mTLS }); | ||
| }, { mTLS, DPoP }); | ||
| } | ||
@@ -156,0 +156,0 @@ |
@@ -25,11 +25,18 @@ const Got = require('got'); | ||
| module.exports = function request(options, { mTLS = false } = {}) { | ||
| module.exports = async function request(options, { mTLS = false, DPoP } = {}) { | ||
| const { url } = options; | ||
| isAbsoluteUrl(url); | ||
| const optsFn = this[HTTP_OPTIONS]; | ||
| let opts; | ||
| let opts = options; | ||
| if (DPoP && 'dpopProof' in this) { | ||
| opts.headers = opts.headers || {}; | ||
| opts.headers.DPoP = this.dpopProof({ | ||
| htu: url, | ||
| htm: options.method, | ||
| }, DPoP); | ||
| } | ||
| if (optsFn) { | ||
| opts = optsFn.call(this, defaultsDeep({}, options, DEFAULT_HTTP_OPTIONS)); | ||
| } else { | ||
| opts = options; | ||
| opts = optsFn.call(this, defaultsDeep({}, opts, DEFAULT_HTTP_OPTIONS)); | ||
| } | ||
@@ -46,2 +53,3 @@ | ||
| } | ||
| return got(opts); | ||
@@ -48,0 +56,0 @@ }; |
+1
-1
| { | ||
| "name": "openid-client", | ||
| "version": "4.0.2", | ||
| "version": "4.1.0", | ||
| "description": "OpenID Connect Relying Party (RP, Client) implementation for Node.js runtime, supports passportjs", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+3
-1
@@ -49,4 +49,5 @@ # openid-client | ||
| - [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] | ||
| Updates to draft specifications (JARM, and FAPI) are released as MINOR library versions, | ||
| 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 | ||
@@ -308,2 +309,3 @@ package.json since breaking changes may be introduced as part of these version updates. | ||
| [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 | ||
| [openid-certified-link]: https://openid.net/certification/ | ||
@@ -310,0 +312,0 @@ [passport-url]: http://passportjs.org |
+30
-7
@@ -12,3 +12,4 @@ /// <reference types="node" /> | ||
| import { URL } from 'url'; | ||
| import { JWKS, JSONWebKeySet } from 'jose'; | ||
| import jose from 'jose'; | ||
| import crypto from 'crypto'; | ||
@@ -19,2 +20,3 @@ export type HttpOptions = GotOptions; | ||
| export type TokenTypeHint = 'access_token' | 'refresh_token' | string; | ||
| export type DPoPInput = crypto.KeyObject | crypto.PrivateKeyInput | jose.JWKRSAKey | jose.JWKECKey | jose.JWKOKPKey; | ||
@@ -187,2 +189,7 @@ /** | ||
| clientAssertionPayload?: object; | ||
| /** | ||
| * Private key to sign the DPoP Proof JWT with. This can be a crypto.KeyObject, crypto.createPrivateKey valid | ||
| * inputs, or a JWK formatted private key. | ||
| */ | ||
| DPoP?: DPoPInput; | ||
| } | ||
@@ -200,2 +207,7 @@ | ||
| clientAssertionPayload?: object; | ||
| /** | ||
| * Private key to sign the DPoP Proof JWT with. This can be a crypto.KeyObject, crypto.createPrivateKey valid | ||
| * inputs, or a JWK formatted private key. | ||
| */ | ||
| DPoP?: DPoPInput; | ||
| } | ||
@@ -215,2 +227,7 @@ | ||
| clientAssertionPayload?: object; | ||
| /** | ||
| * Private key to sign the DPoP Proof JWT with. This can be a crypto.KeyObject, crypto.createPrivateKey valid | ||
| * inputs, or a JWK formatted private key. | ||
| */ | ||
| DPoP?: DPoPInput; | ||
| } | ||
@@ -258,3 +275,3 @@ | ||
| */ | ||
| jwks?: JSONWebKeySet; | ||
| jwks?: jose.JSONWebKeySet; | ||
| /** | ||
@@ -283,2 +300,7 @@ * Initial Access Token to use as a Bearer token during the registration call. | ||
| clientAssertionPayload?: object; | ||
| /** | ||
| * Private key to sign the DPoP Proof JWT with. This can be a crypto.KeyObject, crypto.createPrivateKey valid | ||
| * inputs, or a JWK formatted private key. | ||
| */ | ||
| DPoP?: DPoPInput; | ||
| } | ||
@@ -350,3 +372,3 @@ | ||
| export class Client { | ||
| constructor(metadata: ClientMetadata, jwks?: JSONWebKeySet, options?: ClientOptions); | ||
| constructor(metadata: ClientMetadata, jwks?: jose.JSONWebKeySet, options?: ClientOptions); | ||
| [custom.http_options]: CustomHttpOptionsProvider; | ||
@@ -417,3 +439,3 @@ [custom.clock_tolerance]: number; | ||
| */ | ||
| userinfo(accessToken: TokenSet | string, options?: { method?: 'GET' | 'POST', via?: 'header' | 'body' | 'query', tokenType?: string, params?: object }): Promise<UserinfoResponse>; | ||
| userinfo(accessToken: TokenSet | string, options?: { method?: 'GET' | 'POST', via?: 'header' | 'body' | 'query', tokenType?: string, params?: object, DPoP?: DPoPInput }): Promise<UserinfoResponse>; | ||
@@ -433,2 +455,3 @@ /** | ||
| tokenType?: string | ||
| DPoP?: DPoPInput | ||
| }): CancelableRequest<Response<Buffer>>; | ||
@@ -464,3 +487,3 @@ | ||
| static register(metadata: object, other?: RegisterOther & ClientOptions): Promise<Client>; | ||
| static fromUri(registrationClientUri: string, registrationAccessToken: string, jwks?: JSONWebKeySet, clientOptions?: ClientOptions): Promise<Client>; | ||
| static fromUri(registrationClientUri: string, registrationAccessToken: string, jwks?: jose.JSONWebKeySet, clientOptions?: ClientOptions): Promise<Client>; | ||
| static [custom.http_options]: CustomHttpOptionsProvider; | ||
@@ -516,3 +539,3 @@ | ||
| export interface TypeOfGenericClient<TClient extends Client> { | ||
| new (metadata: ClientMetadata, jwks?: JSONWebKeySet, options?: ClientOptions): TClient; | ||
| new (metadata: ClientMetadata, jwks?: jose.JSONWebKeySet, options?: ClientOptions): TClient; | ||
| [custom.http_options]: CustomHttpOptionsProvider; | ||
@@ -549,3 +572,3 @@ [custom.clock_tolerance]: number; | ||
| */ | ||
| keystore(forceReload?: boolean): Promise<JWKS.KeyStore>; | ||
| keystore(forceReload?: boolean): Promise<jose.JWKS.KeyStore>; | ||
@@ -552,0 +575,0 @@ /** |
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
170151
1.93%3223
2.58%324
0.62%