Skip to content

Commit

Permalink
feat: optional headers options for createRemoteJWKSet (#397)
Browse files Browse the repository at this point in the history
Co-authored-by: Filip Skokan <panva.ip@gmail.com>
  • Loading branch information
rolandzwaga and panva committed Apr 26, 2022
1 parent c58c80a commit b4612f5
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 5 deletions.
9 changes: 7 additions & 2 deletions src/jwks/remote.ts
Expand Up @@ -42,6 +42,11 @@ export interface RemoteJWKSetOptions {
* runtime.
*/
agent?: any

/**
* Optional headers to be sent with the HTTP request.
*/
headers?: Record<string, string>
}

class RemoteJWKSet extends LocalJWKSet {
Expand All @@ -57,7 +62,7 @@ class RemoteJWKSet extends LocalJWKSet {

private _pendingFetch?: Promise<unknown>

private _options: Pick<RemoteJWKSetOptions, 'agent'>
private _options: Pick<RemoteJWKSetOptions, 'agent' | 'headers'>

constructor(url: unknown, options?: RemoteJWKSetOptions) {
super({ keys: [] })
Expand All @@ -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 =
Expand Down
9 changes: 8 additions & 1 deletion 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<RequestInit, 'headers'>

const fetchJwks: FetchFunction = async (
url: URL,
timeout: number,
options: AcceptedRequestOptions,
) => {
let controller!: AbortController
let id!: ReturnType<typeof setTimeout>
let timedOut = false
Expand All @@ -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
Expand Down
5 changes: 3 additions & 2 deletions src/runtime/node/fetch_jwks.ts
Expand Up @@ -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<RequestOptions, 'agent'>
type AcceptedRequestOptions = Pick<RequestOptions, 'agent' | 'headers'>

const fetchJwks: FetchFunction = async (
url: URL,
Expand All @@ -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]>(
Expand Down
16 changes: 16 additions & 0 deletions test/jwks/remote.test.mjs
Expand Up @@ -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')
Expand Down

0 comments on commit b4612f5

Please sign in to comment.