openid-client
Advanced tools
Comparing version 5.0.2 to 5.1.0
@@ -15,2 +15,3 @@ const { inspect } = require('util'); | ||
const defaults = require('./helpers/defaults'); | ||
const parseWwwAuthenticate = require('./helpers/www_authenticate_parser'); | ||
const { assertSigningAlgValuesSupport, assertIssuerConfiguration } = require('./helpers/assert'); | ||
@@ -39,2 +40,3 @@ const pick = require('./helpers/pick'); | ||
const rsaPssParams = major >= 17 || (major === 16 && minor >= 9); | ||
const retryAttempt = Symbol(); | ||
@@ -46,11 +48,12 @@ function pickCb(input) { | ||
'code', // OAuth 2.0 | ||
'error', // OAuth 2.0 | ||
'error_description', // OAuth 2.0 | ||
'error_uri', // OAuth 2.0 | ||
'error', // OAuth 2.0 | ||
'expires_in', // OAuth 2.0 | ||
'id_token', // OIDC Core 1.0 | ||
'iss', // draft-ietf-oauth-iss-auth-resp | ||
'response', // FAPI JARM | ||
'session_state', // OIDC Session Management | ||
'state', // OAuth 2.0 | ||
'token_type', // OAuth 2.0 | ||
'session_state', // OIDC Session Management | ||
'response', // FAPI JARM | ||
); | ||
@@ -402,2 +405,21 @@ } | ||
if ('iss' in params) { | ||
assertIssuerConfiguration(this.issuer, 'issuer'); | ||
if (params.iss !== this.issuer.issuer) { | ||
throw new RPError({ | ||
printf: ['iss mismatch, expected %s, got: %s', this.issuer.issuer, params.iss], | ||
params, | ||
}); | ||
} | ||
} else if ( | ||
this.issuer.authorization_response_iss_parameter_supported && | ||
!('id_token' in params) && | ||
!('response' in parameters) | ||
) { | ||
throw new RPError({ | ||
message: 'iss missing from the response', | ||
params, | ||
}); | ||
} | ||
if (params.error) { | ||
@@ -529,2 +551,21 @@ throw new OPError(params); | ||
if ('iss' in params) { | ||
assertIssuerConfiguration(this.issuer, 'issuer'); | ||
if (params.iss !== this.issuer.issuer) { | ||
throw new RPError({ | ||
printf: ['iss mismatch, expected %s, got: %s', this.issuer.issuer, params.iss], | ||
params, | ||
}); | ||
} | ||
} else if ( | ||
this.issuer.authorization_response_iss_parameter_supported && | ||
!('id_token' in params) && | ||
!('response' in parameters) | ||
) { | ||
throw new RPError({ | ||
message: 'iss missing from the response', | ||
params, | ||
}); | ||
} | ||
if (params.error) { | ||
@@ -534,2 +575,10 @@ throw new OPError(params); | ||
if ('id_token' in params) { | ||
throw new RPError({ | ||
message: | ||
'id_token detected in the response, you must use client.callback() instead of client.oauthCallback()', | ||
params, | ||
}); | ||
} | ||
const RESPONSE_TYPE_REQUIRED_PARAMS = { | ||
@@ -578,2 +627,10 @@ code: ['code'], | ||
if ('id_token' in tokenset) { | ||
throw new RPError({ | ||
message: | ||
'id_token detected in the response, you must use client.callback() instead of client.oauthCallback()', | ||
params, | ||
}); | ||
} | ||
if (tokenset.scope && checks.scope && this.fapi()) { | ||
@@ -1074,2 +1131,3 @@ const expected = new Set(checks.scope.split(' ')); | ||
} = {}, | ||
retry, | ||
) { | ||
@@ -1099,3 +1157,3 @@ if (accessToken instanceof TokenSet) { | ||
return request.call( | ||
const response = await request.call( | ||
this, | ||
@@ -1110,2 +1168,20 @@ { | ||
); | ||
const wwwAuthenticate = response.headers['www-authenticate']; | ||
if ( | ||
retry !== retryAttempt && | ||
wwwAuthenticate && | ||
wwwAuthenticate.toLowerCase().startsWith('dpop ') && | ||
parseWwwAuthenticate(wwwAuthenticate).error === 'use_dpop_nonce' | ||
) { | ||
return this.requestResource(resourceUrl, accessToken, { | ||
method, | ||
headers, | ||
body, | ||
DPoP, | ||
tokenType, | ||
}); | ||
} | ||
return response; | ||
} | ||
@@ -1256,11 +1332,6 @@ | ||
const secret = Buffer.alloc( | ||
Math.max(parseInt(alg.substr(-3), 10) >> 3, this.client_secret.length), | ||
); | ||
secret.write(this.client_secret); | ||
return secret; | ||
return new TextEncoder().encode(this.client_secret); | ||
} | ||
async grant(body, { clientAssertionPayload, DPoP } = {}) { | ||
async grant(body, { clientAssertionPayload, DPoP } = {}, retry) { | ||
assertIssuerConfiguration(this.issuer, 'token_endpoint'); | ||
@@ -1276,3 +1347,11 @@ const response = await authenticatedPost.call( | ||
); | ||
const responseBody = processResponse(response); | ||
let responseBody; | ||
try { | ||
responseBody = processResponse(response); | ||
} catch (err) { | ||
if (retry !== retryAttempt && err instanceof OPError && err.error === 'use_dpop_nonce') { | ||
return this.grant(body, { clientAssertionPayload, DPoP }, retryAttempt); | ||
} | ||
throw err; | ||
} | ||
@@ -1739,3 +1818,3 @@ return new TokenSet(responseBody); | ||
process.emitWarning( | ||
'The DPoP APIs implements an IETF draft (https://www.ietf.org/archive/id/draft-ietf-oauth-dpop-03.html). 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.', | ||
'The DPoP APIs implements an IETF draft (https://www.ietf.org/archive/id/draft-ietf-oauth-dpop-04.html). 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', | ||
@@ -1742,0 +1821,0 @@ ); |
@@ -5,13 +5,6 @@ const { STATUS_CODES } = require('http'); | ||
const { OPError } = require('../errors'); | ||
const parseWwwAuthenticate = require('./www_authenticate_parser'); | ||
const REGEXP = /(\w+)=("[^"]*")/g; | ||
const throwAuthenticateErrors = (response) => { | ||
const params = {}; | ||
try { | ||
while (REGEXP.exec(response.headers['www-authenticate']) !== null) { | ||
if (RegExp.$1 && RegExp.$2) { | ||
params[RegExp.$1] = RegExp.$2.slice(1, -1); | ||
} | ||
} | ||
} catch (err) {} | ||
const params = parseWwwAuthenticate(response.headers['www-authenticate']); | ||
@@ -18,0 +11,0 @@ if (params.error) { |
@@ -7,2 +7,4 @@ const assert = require('assert'); | ||
const LRU = require('lru-cache'); | ||
const pkg = require('../../package.json'); | ||
@@ -16,2 +18,3 @@ const { RPError } = require('../errors'); | ||
let DEFAULT_HTTP_OPTIONS; | ||
const NQCHAR = /^[\x21\x23-\x5B\x5D-\x7E]+$/; | ||
@@ -57,2 +60,4 @@ const allowed = [ | ||
const nonces = new LRU({ max: 100 }); | ||
module.exports = async function request(options, { accessToken, mTLS = false, DPoP } = {}) { | ||
@@ -70,2 +75,3 @@ let url; | ||
const nonceKey = `${url.origin}${url.pathname}`; | ||
if (DPoP && 'dpopProof' in this) { | ||
@@ -75,4 +81,5 @@ opts.headers = opts.headers || {}; | ||
{ | ||
htu: url, | ||
htu: url.href, | ||
htm: options.method, | ||
nonce: nonces.get(nonceKey), | ||
}, | ||
@@ -181,8 +188,15 @@ DPoP, | ||
return response; | ||
})().catch((err) => { | ||
Object.defineProperty(err, 'response', { value: response }); | ||
throw err; | ||
}); | ||
})() | ||
.catch((err) => { | ||
if (response) Object.defineProperty(err, 'response', { value: response }); | ||
throw err; | ||
}) | ||
.finally(() => { | ||
const dpopNonce = response && response.headers['dpop-nonce']; | ||
if (dpopNonce && NQCHAR.test(dpopNonce)) { | ||
nonces.set(nonceKey, dpopNonce); | ||
} | ||
}); | ||
}; | ||
module.exports.setDefaults = setDefaults.bind(undefined, allowed); |
@@ -19,3 +19,3 @@ const { inspect } = require('util'); | ||
]; | ||
const AAD_MULTITENANT = Symbol('AAD_MULTITENANT'); | ||
const AAD_MULTITENANT = Symbol(); | ||
const ISSUER_DEFAULTS = { | ||
@@ -22,0 +22,0 @@ claim_types_supported: ['normal'], |
{ | ||
"name": "openid-client", | ||
"version": "5.0.2", | ||
"version": "5.1.0", | ||
"description": "OpenID Connect Relying Party (RP, Client) implementation for Node.js runtime, supports passportjs", | ||
@@ -54,3 +54,3 @@ "keywords": [ | ||
"dependencies": { | ||
"jose": "^4.1.0", | ||
"jose": "^4.1.4", | ||
"lru-cache": "^6.0.0", | ||
@@ -57,0 +57,0 @@ "object-hash": "^2.0.1", |
@@ -48,3 +48,4 @@ # 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 03][feature-dpop] | ||
- [OAuth 2.0 Demonstration of Proof-of-Possession at the Application Layer (DPoP) - draft 04][feature-dpop] | ||
- [OAuth 2.0 Authorization Server Issuer Identification - draft-04][feature-iss] | ||
@@ -280,5 +281,6 @@ Updates to draft specifications (DPoP, JARM, etc) are released as MINOR library versions, | ||
[feature-fapi]: https://openid.net/specs/openid-financial-api-part-2-1_0.html | ||
[feature-dpop]: https://tools.ietf.org/html/draft-ietf-oauth-dpop-03 | ||
[feature-dpop]: https://tools.ietf.org/html/draft-ietf-oauth-dpop-04 | ||
[feature-par]: https://www.rfc-editor.org/rfc/rfc9126.html | ||
[feature-jar]: https://www.rfc-editor.org/rfc/rfc9101.html | ||
[feature-iss]: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-iss-auth-resp-04 | ||
[openid-certified-link]: https://openid.net/certification/ | ||
@@ -285,0 +287,0 @@ [passport-url]: http://passportjs.org |
@@ -385,2 +385,3 @@ /// <reference types="node" /> | ||
interface DeviceFlowPollOptions { | ||
// @ts-ignore | ||
signal?: AbortSignal; | ||
@@ -387,0 +388,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
133311
33
3655
300
Updatedjose@^4.1.4