diff --git a/src/jwks/remote.ts b/src/jwks/remote.ts index 5c559a0c..fb10e797 100644 --- a/src/jwks/remote.ts +++ b/src/jwks/remote.ts @@ -42,6 +42,11 @@ export interface RemoteJWKSetOptions { * runtime. */ agent?: any + + /** + * Optional headers to be sent with the HTTP request. + */ + headers?: Record } class RemoteJWKSet extends LocalJWKSet { @@ -57,7 +62,7 @@ class RemoteJWKSet extends LocalJWKSet { private _pendingFetch?: Promise - private _options: Pick + private _options: Pick constructor(url: unknown, options?: RemoteJWKSetOptions) { super({ keys: [] }) @@ -68,7 +73,7 @@ class RemoteJWKSet extends LocalJWKSet { throw new TypeError('url must be an instance of URL') } this._url = new URL(url.href) - this._options = { agent: options?.agent } + this._options = { agent: options?.agent, headers: options?.headers } this._timeoutDuration = typeof options?.timeoutDuration === 'number' ? options?.timeoutDuration : 5000 this._cooldownDuration = diff --git a/src/runtime/browser/fetch_jwks.ts b/src/runtime/browser/fetch_jwks.ts index d64a9a6b..a65480ab 100644 --- a/src/runtime/browser/fetch_jwks.ts +++ b/src/runtime/browser/fetch_jwks.ts @@ -1,7 +1,13 @@ import type { FetchFunction } from '../interfaces.d' import { JOSEError, JWKSTimeout } from '../../util/errors.js' -const fetchJwks: FetchFunction = async (url: URL, timeout: number) => { +type AcceptedRequestOptions = Pick + +const fetchJwks: FetchFunction = async ( + url: URL, + timeout: number, + options: AcceptedRequestOptions, +) => { let controller!: AbortController let id!: ReturnType let timedOut = false @@ -16,6 +22,7 @@ const fetchJwks: FetchFunction = async (url: URL, timeout: number) => { const response = await fetch(url.href, { signal: controller ? controller.signal : undefined, redirect: 'manual', + headers: options.headers, }).catch((err) => { if (timedOut) throw new JWKSTimeout() throw err diff --git a/src/runtime/node/fetch_jwks.ts b/src/runtime/node/fetch_jwks.ts index f35eaaf8..3dceb55b 100644 --- a/src/runtime/node/fetch_jwks.ts +++ b/src/runtime/node/fetch_jwks.ts @@ -8,7 +8,7 @@ import type { FetchFunction } from '../interfaces.d' import { JOSEError, JWKSTimeout } from '../../util/errors.js' import { concat, decoder } from '../../lib/buffer_utils.js' -type AcceptedRequestOptions = Pick +type AcceptedRequestOptions = Pick const fetchJwks: FetchFunction = async ( url: URL, @@ -27,10 +27,11 @@ const fetchJwks: FetchFunction = async ( throw new TypeError('Unsupported URL protocol.') } - const { agent } = options + const { agent, headers } = options const req = get(url.href, { agent, timeout, + headers, }) const [response] = <[IncomingMessage]>( diff --git a/test/jwks/remote.test.mjs b/test/jwks/remote.test.mjs index 26690fae..2d1e1b05 100644 --- a/test/jwks/remote.test.mjs +++ b/test/jwks/remote.test.mjs @@ -362,6 +362,22 @@ skipOnUndiciTestSerial('throws on invalid JWKSet', async (t) => { }) }) +skipOnUndiciTestSerial('can have headers configured', async (t) => { + const scope = nock('https://as.example.com', { + reqheaders: { + 'x-custom': 'foo', + }, + }) + .get('/jwks') + .once() + .reply(200, 'null') + + const url = new URL('https://as.example.com/jwks') + const JWKS = createRemoteJWKSet(url, { headers: { 'x-custom': 'foo' } }) + await JWKS().catch(() => {}) + t.true(scope.isDone()) +}) + skipOnUndiciTest('handles ENOTFOUND', async (t) => { nock.enableNetConnect() const url = new URL('https://op.example.com/jwks')