From d69af6fe28eb93dca8babad520d5e763aff7e6ff Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Wed, 20 Oct 2021 13:56:22 +0200 Subject: [PATCH] refactor: DPoP input must be a private KeyObject or valid crypto.createPrivateKey input BREAKING CHANGE: DPoP option inputs must be a private crypto.KeyObject or a valid crypto.createPrivateKey input. --- docs/README.md | 45 ++++++----- lib/client.js | 141 ++++++++++++++++++++++++++++++----- lib/helpers/request.js | 2 +- package.json | 1 + test/client/dpop.test.js | 128 ++++++++++++++++++++----------- types/index.d.ts | 8 +- types/openid-client-tests.ts | 18 ++--- 7 files changed, 245 insertions(+), 98 deletions(-) diff --git a/docs/README.md b/docs/README.md index e219020e..472d7ba4 100644 --- a/docs/README.md +++ b/docs/README.md @@ -298,9 +298,10 @@ Performs the callback for Authorization Server's authorization response. - `clientAssertionPayload`: `` extra client assertion payload parameters to be sent as part of a client JWT assertion. This is only used when the client's `token_endpoint_auth_method` is either `client_secret_jwt` or `private_key_jwt`. - - `DPoP`: `` | `` When provided the client will send a DPoP Proof JWT to the - Token Endpoint. The value should be a private key to sign a DPoP Proof JWT with. This can be - a crypto.KeyObject, crypto.createPrivateKey valid inputs, or a JWK formatted private key. + - `DPoP`: `` When provided the client will send a DPoP Proof JWT to the + Token Endpoint. The value must be a private key in the form of a crypto.KeyObject, or any + valid crypto.createPrivateKey input. The algorithm is determined[^dpop-exception] by the client + based on the type of key and the issuer metadata. - Returns: `Promise` Parsed token endpoint response as a TokenSet. Tip: If you're using pure @@ -322,9 +323,10 @@ Performs `refresh_token` grant type exchange. - `clientAssertionPayload`: `` extra client assertion payload parameters to be sent as part of a client JWT assertion. This is only used when the client's `token_endpoint_auth_method` is either `client_secret_jwt` or `private_key_jwt`. - - `DPoP`: `` | `` When provided the client will send a DPoP Proof JWT to the - Token Endpoint. The value should be a private key to sign a DPoP Proof JWT with. This can be - a crypto.KeyObject, crypto.createPrivateKey valid inputs, or a JWK formatted private key. + - `DPoP`: `` When provided the client will send a DPoP Proof JWT to the + Token Endpoint. The value must be a private key in the form of a crypto.KeyObject, or any + valid crypto.createPrivateKey input. The algorithm is determined[^dpop-exception] by the client + based on the type of key and the issuer metadata. - Returns: `Promise` Parsed token endpoint response as a TokenSet. --- @@ -345,9 +347,10 @@ will also be checked to match the on in the TokenSet's ID Token. or the `token_type` property from a passed in TokenSet. - `params`: `` additional parameters to send with the userinfo request (as query string when GET, as x-www-form-urlencoded body when POST). - - `DPoP`: `` | `` When provided the client will send a DPoP Proof JWT to the - Userinfo Endpoint. The value should be a private key to sign a DPoP Proof JWT with. This can be - a crypto.KeyObject, crypto.createPrivateKey valid inputs, or a JWK formatted private key. + - `DPoP`: `` When provided the client will send a DPoP Proof JWT to the + Userinfo Endpoint. The value must be a private key in the form of a crypto.KeyObject, or any + valid crypto.createPrivateKey input. The algorithm is determined[^dpop-exception] by the client + based on the type of key and the issuer metadata. - Returns: `Promise` Parsed userinfo response. --- @@ -365,9 +368,10 @@ Fetches an arbitrary resource with the provided Access Token in an Authorization - `method`: `` The HTTP method to use for the request. **Default:** 'GET' - `tokenType`: `` The token type as the Authorization Header scheme. **Default:** 'Bearer' or the `token_type` property from a passed in TokenSet. - - `DPoP`: `` | `` When provided the client will send a DPoP Proof JWT to the - Userinfo Endpoint. The value should be a private key to sign a DPoP Proof JWT with. This can be - a crypto.KeyObject, crypto.createPrivateKey valid inputs, or a JWK formatted private key. + - `DPoP`: `` When provided the client will send a DPoP Proof JWT to the + Userinfo Endpoint. The value must be a private key in the form of a crypto.KeyObject, or any + valid crypto.createPrivateKey input. The algorithm is determined[^dpop-exception] by the client + based on the type of key and the issuer metadata. - Returns: `Promise` Response is a [Got Response](https://github.com/sindresorhus/got/tree/v11.8.0#response) with the `body` property being a `` @@ -385,9 +389,10 @@ Performs an arbitrary `grant_type` exchange at the `token_endpoint`. - `clientAssertionPayload`: `` extra client assertion payload parameters to be sent as part of a client JWT assertion. This is only used when the client's `token_endpoint_auth_method` is either `client_secret_jwt` or `private_key_jwt`. - - `DPoP`: `` | `` When provided the client will send a DPoP Proof JWT to the - Token Endpoint. The value should be a private key to sign a DPoP Proof JWT with. This can be - a crypto.KeyObject, crypto.createPrivateKey valid inputs, or a JWK formatted private key. + - `DPoP`: `` When provided the client will send a DPoP Proof JWT to the + Token Endpoint. The value must be a private key in the form of a crypto.KeyObject, or any + valid crypto.createPrivateKey input. The algorithm is determined[^dpop-exception] by the client + based on the type of key and the issuer metadata. - Returns: `Promise` --- @@ -461,9 +466,10 @@ a handle for subsequent Device Access Token Request polling. - `clientAssertionPayload`: `` extra client assertion payload parameters to be sent as part of a client JWT assertion. This is only used when the client's `token_endpoint_auth_method` is either `client_secret_jwt` or `private_key_jwt`. - - `DPoP`: `` | `` When provided the client will send a DPoP Proof JWT to the - Token Endpoint. The value should be a private key to sign a DPoP Proof JWT with. This can be - a crypto.KeyObject, crypto.createPrivateKey valid inputs, or a JWK formatted private key. + - `DPoP`: `` When provided the client will send a DPoP Proof JWT to the + Token Endpoint. The value must be a private key in the form of a crypto.KeyObject, or any + valid crypto.createPrivateKey input. The algorithm is determined[^dpop-exception] by the client + based on the type of key and the issuer metadata. - Returns: `Promise` --- @@ -1023,3 +1029,6 @@ request instance. [webfinger-discovery]: https://openid.net/specs/openid-connect-discovery-1_0.html#IssuerDiscovery [got-library]: https://github.com/sindresorhus/got/tree/v11.8.0 [client-authentication]: https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication + +[^dpop-exception]: Ed25519, Ed448, and all Elliptic Curve keys have a fixed algorithm. RSA and RSA-PSS keys +look for an algorithm supported by the issuer metadata, if none is found PS256 is used as fallback. diff --git a/lib/client.js b/lib/client.js index 5a5a7333..e514ec2e 100644 --- a/lib/client.js +++ b/lib/client.js @@ -6,8 +6,11 @@ const crypto = require('crypto'); const { strict: assert } = require('assert'); const querystring = require('querystring'); const url = require('url'); +const util = require('util'); +const QuickLRU = require('quick-lru'); const jose = require('jose'); +const jose4 = require('jose4'); const tokenHash = require('oidc-token-hash'); const base64url = require('./helpers/base64url'); @@ -29,6 +32,15 @@ const instance = require('./helpers/weak_cache'); const { authenticatedPost, resolveResponseType, resolveRedirectUri } = require('./helpers/client'); const DeviceFlowHandle = require('./device_flow_handle'); +const [major, minor] = process.version + .substr(1) + .split('.') + .map((str) => parseInt(str, 10)); + +const rsaPssParams = major >= 17 || (major === 16 && minor >= 9); + +const isKeyObject = util.types.isKeyObject || ((obj) => obj && obj instanceof crypto.KeyObject); + function pickCb(input) { return pick(input, ...CALLBACK_PROPERTIES); } @@ -1677,44 +1689,135 @@ Object.defineProperty(BaseClient.prototype, 'validateJARM', { }, }); +const RSPS = /^(?:RS|PS)(?:256|384|512)$/; +function determineRsaAlgorithm(privateKey, privateKeyInput, valuesSupported) { + if (typeof privateKeyInput === 'object' && typeof privateKeyInput.key === 'object' && privateKeyInput.key.alg) { + return privateKeyInput.key.alg; + } + + if (Array.isArray(valuesSupported)) { + let candidates = valuesSupported.filter(RegExp.prototype.test.bind(RSPS)); + if (privateKey.asymmetricKeyType === 'rsa-pss') { + candidates = candidates.filter((value) => value.startsWith('PS')); + } + return ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS384'].find((preferred) => candidates.includes(preferred)); + } + + return 'PS256'; +} + +const p256 = Buffer.from([42, 134, 72, 206, 61, 3, 1, 7]); +const p384 = Buffer.from([43, 129, 4, 0, 34]); +const p521 = Buffer.from([43, 129, 4, 0, 35]); +const secp256k1 = Buffer.from([43, 129, 4, 0, 10]); + +function determineEcAlgorithm(privateKey, privateKeyInput) { + // If input was a JWK + switch (typeof privateKeyInput === 'object' && typeof privateKeyInput.key === 'object' && privateKeyInput.key.crv) { + case 'P-256': return 'ES256'; + case 'secp256k1': return 'ES256K'; + case 'P-384': return 'ES384'; + case 'P-512': return 'ES512'; + default: + break; + } + + const buf = privateKey.export({ format: 'der', type: 'pkcs8' }); + const i = buf[1] < 128 ? 17 : 18; + const len = buf[i]; + const curveOid = buf.slice(i + 1, i + 1 + len); + if (curveOid.equals(p256)) { + return 'ES256'; + } + + if (curveOid.equals(p384)) { + return 'ES384'; + } + if (curveOid.equals(p521)) { + return 'ES512'; + } + + if (curveOid.equals(secp256k1)) { + return 'ES256K'; + } + + throw new TypeError('unsupported DPoP private key curve'); +} + +const jwkCache = new QuickLRU({ maxSize: 100 }); +async function getJwk(privateKey, privateKeyInput) { + if (typeof privateKeyInput === 'object' && typeof privateKeyInput.key === 'object' && privateKeyInput.key.crv) { + return pick(privateKeyInput.key, 'kty', 'crv', 'x', 'y', 'e', 'n'); + } + + if (jwkCache.has(privateKeyInput)) { + return jwkCache.get(privateKeyInput); + } + + const jwk = pick(await jose4.exportJWK(privateKey), 'kty', 'crv', 'x', 'y', 'e', 'n'); + + if (isKeyObject(privateKeyInput)) { + jwkCache.set(privateKeyInput, jwk); + } + + return jwk; +} + /** * @name dpopProof * @api private */ -function dpopProof(payload, jwk, accessToken) { +async function dpopProof(payload, privateKeyInput, accessToken) { 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 privateKey; + if (isKeyObject(privateKeyInput)) { + privateKey = privateKeyInput; + } else { + privateKey = crypto.createPrivateKey(privateKeyInput); } - 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 (privateKey.type !== 'private') { + throw new TypeError('"DPoP" option must be a private key'); + } + let alg; + switch (privateKey.asymmetricKeyType) { + case 'ed25519': + case 'ed448': + alg = 'EdDSA'; + break; + case 'ec': + alg = determineEcAlgorithm(privateKey, privateKeyInput); + break; + case 'rsa': + case rsaPssParams && 'rsa-pss': + alg = determineRsaAlgorithm( + privateKey, + privateKeyInput, + this.issuer.dpop_signing_alg_values_supported, + ); + break; + default: + throw new TypeError('unsupported DPoP private key asymmetric key type'); } if (!alg) { - [alg] = key.algorithms('sign'); + throw new TypeError('could not determine DPoP JWS Algorithm'); } - return jose.JWS.sign({ - iat: now(), - jti: random(), + return new jose4.SignJWT({ ath: accessToken ? base64url.encode(crypto.createHash('sha256').update(accessToken).digest()) : undefined, ...payload, - }, jwk, { + }).setProtectedHeader({ alg, typ: 'dpop+jwt', - jwk: pick(key, 'kty', 'crv', 'x', 'y', 'e', 'n'), - }); + jwk: await getJwk(privateKey, privateKeyInput), + }) + .setIssuedAt() + .setJti(random()) + .sign(privateKey); } Object.defineProperty(BaseClient.prototype, 'dpopProof', { diff --git a/lib/helpers/request.js b/lib/helpers/request.js index 48e0f9af..f1771638 100644 --- a/lib/helpers/request.js +++ b/lib/helpers/request.js @@ -57,7 +57,7 @@ module.exports = async function request(options, { accessToken, mTLS = false, DP if (DPoP && 'dpopProof' in this) { opts.headers = opts.headers || {}; - opts.headers.DPoP = this.dpopProof({ + opts.headers.DPoP = await this.dpopProof({ htu: url, htm: options.method, }, DPoP, accessToken); diff --git a/package.json b/package.json index b16c2ab7..3944dc0a 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "dependencies": { "cacheable-lookup": "^6.0.4", "jose": "^2.0.5", + "jose4": "npm:jose@^4.1.0", "make-error": "^1.3.6", "object-hash": "^2.0.1", "oidc-token-hash": "^5.0.1", diff --git a/test/client/dpop.test.js b/test/client/dpop.test.js index 6d561ff6..97164115 100644 --- a/test/client/dpop.test.js +++ b/test/client/dpop.test.js @@ -1,5 +1,3 @@ -const crypto = require('crypto'); - const { expect } = require('chai'); const nock = require('nock'); const jose = require('jose'); @@ -13,10 +11,12 @@ const issuer = new Issuer({ introspection_endpoint: 'https://op.example.com/token/introspect', revocation_endpoint: 'https://op.example.com/token/revoke', device_authorization_endpoint: 'https://op.example.com/device', - dpop_signing_alg_values_supported: ['PS512', 'PS384', 'PS256'], + dpop_signing_alg_values_supported: ['PS512', 'PS384'], }); -const jwk = jose.JWK.generateSync('RSA'); +const privateKey = jose.JWK.generateSync('EC').keyObject; + +const fail = () => { throw new Error('expected promise to be rejected'); }; describe('DPoP', () => { beforeEach(function () { @@ -36,24 +36,45 @@ describe('DPoP', () => { describe('dpopProof', () => { it('must be passed a payload object', function () { - expect(() => this.client.dpopProof('foo')).to.throw('payload must be a plain object'); + return this.client.dpopProof('foo').then(fail, (err) => { + expect(err.message).to.eql('payload must be a plain object'); + }); }); - it('must be passed a private key', function () { - const msg = '"DPoP" option must be an asymmetric private key to sign the DPoP Proof JWT with'; - expect(() => this.client.dpopProof({}, 'foo')).to.throw(msg); - expect(() => this.client.dpopProof({}, Buffer.from('foo'))).to.throw(msg); - expect(() => this.client.dpopProof({}, jose.JWK.generateSync('oct').toJWK(true))).to.throw(msg); - expect(() => this.client.dpopProof({}, jose.JWK.generateSync('EC').toJWK())).to.throw(msg); - expect(() => this.client.dpopProof({}, jose.JWK.generateSync('EC').toPEM())).to.throw(msg); + it('DPoP Private Key can be passed also as valid createPrivateKey input', async function () { + if (parseInt(process.versions.node, 10) >= 16) { + const jwk = (await jose.JWK.generate('EC')).toJWK(true); + await this.client.dpopProof({}, { format: 'jwk', key: jwk }); + } + + { + const pem = (await jose.JWK.generate('EC')).toPEM(true); + await this.client.dpopProof({}, pem); + await this.client.dpopProof({}, { key: pem, format: 'pem' }); + } + + { + const der = (await jose.JWK.generate('EC')).keyObject.export({ format: 'der', type: 'pkcs8' }); + await this.client.dpopProof({}, { key: der, format: 'der', type: 'pkcs8' }); + } + + { + const der = (await jose.JWK.generate('EC')).keyObject.export({ format: 'der', type: 'sec1' }); + await this.client.dpopProof({}, { key: der, format: 'der', type: 'sec1' }); + } + + { + const der = (await jose.JWK.generate('RSA')).keyObject.export({ format: 'der', type: 'pkcs1' }); + await this.client.dpopProof({}, { key: der, format: 'der', type: 'pkcs1' }); + } }); - it('DPoP Proof JWT w/o ath', function () { - const proof = this.client.dpopProof({ + it('DPoP Proof JWT w/o ath', async function () { + const proof = await this.client.dpopProof({ htu: 'foo', htm: 'bar', baz: true, - }, jose.JWK.generateSync('RSA', 2048)); + }, (await jose.JWK.generate('RSA')).keyObject); const decoded = jose.JWT.decode(proof, { complete: true }); expect(decoded).to.have.nested.property('header.typ', 'dpop+jwt'); expect(decoded).to.have.nested.property('payload.iat'); @@ -65,43 +86,64 @@ describe('DPoP', () => { expect( jose.JWT.decode( - this.client.dpopProof({}, jose.JWK.generateSync('EC')), + await this.client.dpopProof({}, (await jose.JWK.generate('EC')).keyObject), { complete: true }, ), ).to.have.nested.property('header.jwk').that.has.keys('kty', 'crv', 'x', 'y'); - if ('sign' in crypto) { - expect( - jose.JWT.decode( - this.client.dpopProof({}, jose.JWK.generateSync('OKP')), - { complete: true }, - ), - ).to.have.nested.property('header.jwk').that.has.keys('kty', 'crv', 'x'); - } + expect( + jose.JWT.decode( + await this.client.dpopProof({}, (await jose.JWK.generate('OKP')).keyObject), + { complete: true }, + ), + ).to.have.nested.property('header.jwk').that.has.keys('kty', 'crv', 'x'); }); - it('DPoP Proof JWT w/ ath', function () { - const proof = this.client.dpopProof({ + it('DPoP Proof JWT w/ ath', async function () { + const proof = await this.client.dpopProof({ htu: 'foo', htm: 'bar', - }, jose.JWK.generateSync('EC'), 'foo'); + }, (await jose.JWK.generate('EC')).keyObject, 'foo'); const decoded = jose.JWT.decode(proof, { complete: true }); expect(decoded).to.have.nested.property('payload.ath', 'LCa0a2j_xo_5m0U8HTBBNBNCLXBkg7-g-YpeiGJm564'); }); - it('key.alg is used if present', function () { - const proof = this.client.dpopProof({}, jose.JWK.generateSync('RSA', 2048, { alg: 'PS384' })); + it('else this.issuer.dpop_signing_alg_values_supported is used', async function () { + const proof = await this.client.dpopProof({}, (await jose.JWK.generate('RSA', 2048)).keyObject); + // 256 is not supported by the issuer, next one in line is PS384 expect(jose.JWT.decode(proof, { complete: true })).to.have.nested.property('header.alg', 'PS384'); }); - it('else this.issuer.dpop_signing_alg_values_supported is used', function () { - const proof = this.client.dpopProof({}, jose.JWK.generateSync('RSA', 2048)); - expect(jose.JWT.decode(proof, { complete: true })).to.have.nested.property('header.alg', 'PS512'); - }); + it('unless the key dictates an algorithm', async function () { + { + const proof = await this.client.dpopProof({}, (await jose.JWK.generate('OKP', 'Ed25519')).keyObject); + expect(jose.JWT.decode(proof, { complete: true })).to.have.nested.property('header.alg', 'EdDSA'); + } + + { + const proof = await this.client.dpopProof({}, (await jose.JWK.generate('OKP', 'Ed448')).keyObject); + expect(jose.JWT.decode(proof, { complete: true })).to.have.nested.property('header.alg', 'EdDSA'); + } - it('else key.algorithms("sign")[0] is used', function () { - const proof = this.client.dpopProof({}, jose.JWK.generateSync('EC')); - expect(jose.JWT.decode(proof, { complete: true })).to.have.nested.property('header.alg', 'ES256'); + { + const proof = await this.client.dpopProof({}, (await jose.JWK.generate('EC', 'P-256')).keyObject); + expect(jose.JWT.decode(proof, { complete: true })).to.have.nested.property('header.alg', 'ES256'); + } + + { + const proof = await this.client.dpopProof({}, (await jose.JWK.generate('EC', 'secp256k1')).keyObject); + expect(jose.JWT.decode(proof, { complete: true })).to.have.nested.property('header.alg', 'ES256K'); + } + + { + const proof = await this.client.dpopProof({}, (await jose.JWK.generate('EC', 'P-384')).keyObject); + expect(jose.JWT.decode(proof, { complete: true })).to.have.nested.property('header.alg', 'ES384'); + } + + { + const proof = await this.client.dpopProof({}, (await jose.JWK.generate('EC', 'P-521')).keyObject); + expect(jose.JWT.decode(proof, { complete: true })).to.have.nested.property('header.alg', 'ES512'); + } }); }); @@ -109,7 +151,7 @@ describe('DPoP', () => { nock('https://op.example.com') .get('/me').reply(200, { sub: 'foo' }); - await this.client.userinfo('foo', { DPoP: jwk }); + await this.client.userinfo('foo', { DPoP: privateKey }); expect(this.httpOpts).to.have.nested.property('headers.DPoP'); @@ -122,7 +164,7 @@ describe('DPoP', () => { nock('https://rs.example.com') .post('/resource').reply(200, { sub: 'foo' }); - await this.client.requestResource('https://rs.example.com/resource', 'foo', { DPoP: jwk, method: 'POST' }); + await this.client.requestResource('https://rs.example.com/resource', 'foo', { DPoP: privateKey, method: 'POST' }); expect(this.httpOpts).to.have.nested.property('headers.DPoP'); @@ -135,7 +177,7 @@ describe('DPoP', () => { nock('https://op.example.com') .post('/token').reply(200, { access_token: 'foo' }); - await this.client.grant({ grant_type: 'foo' }, { DPoP: jwk }); + await this.client.grant({ grant_type: 'foo' }, { DPoP: privateKey }); expect(this.httpOpts).to.have.nested.property('headers.DPoP'); }); @@ -144,7 +186,7 @@ describe('DPoP', () => { nock('https://op.example.com') .post('/token').reply(200, { access_token: 'foo' }); - await this.client.refresh('foo', { DPoP: jwk }); + await this.client.refresh('foo', { DPoP: privateKey }); expect(this.httpOpts).to.have.nested.property('headers.DPoP'); }); @@ -153,7 +195,7 @@ describe('DPoP', () => { nock('https://op.example.com') .post('/token').reply(200, { access_token: 'foo' }); - await this.client.oauthCallback('foo', { code: 'foo' }, {}, { DPoP: jwk }); + await this.client.oauthCallback('foo', { code: 'foo' }, {}, { DPoP: privateKey }); expect(this.httpOpts).to.have.nested.property('headers.DPoP'); }); @@ -163,7 +205,7 @@ describe('DPoP', () => { .post('/token').reply(200, { access_token: 'foo' }); try { - await this.client.callback('foo', { code: 'foo' }, {}, { DPoP: jwk }); + await this.client.callback('foo', { code: 'foo' }, {}, { DPoP: privateKey }); } catch (err) {} expect(this.httpOpts).to.have.nested.property('headers.DPoP'); @@ -179,7 +221,7 @@ describe('DPoP', () => { interval: 1, }); - const handle = await this.client.deviceAuthorization({}, { DPoP: jwk }); + const handle = await this.client.deviceAuthorization({}, { DPoP: privateKey }); expect(this.httpOpts).not.to.have.nested.property('headers.DPoP'); diff --git a/types/index.d.ts b/types/index.d.ts index da4ba515..c03e4193 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,5 +1,4 @@ /// -/// // TypeScript Version: 3.6 /** @@ -17,12 +16,7 @@ export type HttpOptions = Pick number; export type CustomHttpOptionsProvider = (options: HttpOptions & { url: URL } & UnknownObject) => HttpOptions; export type TokenTypeHint = "access_token" | "refresh_token" | string; -export type DPoPInput = - | crypto.KeyObject - | crypto.PrivateKeyInput - | jose.JWKRSAKey - | jose.JWKECKey - | jose.JWKOKPKey; +export type DPoPInput = crypto.KeyObject | Parameters[0] interface UnknownObject { [key: string]: unknown; diff --git a/types/openid-client-tests.ts b/types/openid-client-tests.ts index 9ce37c84..b6515afe 100644 --- a/types/openid-client-tests.ts +++ b/types/openid-client-tests.ts @@ -1,8 +1,6 @@ import { IncomingMessage } from 'http'; import { generateKeyPairSync } from 'crypto'; -import { JWKECKey } from 'jose' - import { custom, generators, Issuer, Client, Strategy, StrategyVerifyCallback, StrategyOptions, TokenSet, RegisterOther, IssuerMetadata } from './index.d'; import passport from 'passport'; @@ -45,19 +43,19 @@ async (req: IncomingMessage) => { // const { privateKey: keyobject } = generateKeyPairSync('rsa', { modulusLength: 2048 }) - const jwk: JWKECKey = { kty: 'EC', x: 'foo', y: 'bar', d: 'baz', crv: 'P-256' } + const jwk = { kty: 'EC', x: 'foo', y: 'bar', d: 'baz', crv: 'P-256' } client.callback('https://rp.example.com/cb', {}, {}, { DPoP: keyobject }) - client.callback('https://rp.example.com/cb', {}, {}, { DPoP: jwk }) + client.callback('https://rp.example.com/cb', {}, {}, { DPoP: { key: jwk, format: 'jwk' } }) client.oauthCallback('https://rp.example.com/cb', {}, {}, { DPoP: keyobject }) - client.oauthCallback('https://rp.example.com/cb', {}, {}, { DPoP: jwk }) + client.oauthCallback('https://rp.example.com/cb', {}, {}, { DPoP: { key: jwk, format: 'jwk' } }) client.userinfo('token', { DPoP: keyobject }) - client.userinfo('token', { DPoP: jwk }) + client.userinfo('token', { DPoP: { key: jwk, format: 'jwk' } }) client.requestResource('https://rs.example.com/resource', 'token', { DPoP: keyobject }) - client.requestResource('https://rs.example.com/resource', 'token', { DPoP: jwk }) + client.requestResource('https://rs.example.com/resource', 'token', { DPoP: { key: jwk, format: 'jwk' } }) client.pushedAuthorizationRequest() client.pushedAuthorizationRequest({}) @@ -72,7 +70,7 @@ async (req: IncomingMessage) => { client.deviceAuthorization({}, { DPoP: keyobject }) - const handle = await client.deviceAuthorization({}, { DPoP: jwk }) + const handle = await client.deviceAuthorization({}, { DPoP: { key: jwk, format: 'jwk' } }) handle.abort(); handle.poll(); @@ -81,10 +79,10 @@ async (req: IncomingMessage) => { handle.poll({ signal: ac.signal }); client.grant({ grant_type: 'client_credentials' }, { DPoP: keyobject }) - client.grant({ grant_type: 'client_credentials' }, { DPoP: jwk }) + client.grant({ grant_type: 'client_credentials' }, { DPoP: { key: jwk, format: 'jwk' } }) client.refresh('token', { DPoP: keyobject }) - client.refresh('token', { DPoP: jwk }) + client.refresh('token', { DPoP: { key: jwk, format: 'jwk' } }) //