Skip to content

Commit

Permalink
feat: allow Record<string, string> and string[][] as parameter arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Apr 26, 2023
1 parent 8540b8d commit 021b85f
Show file tree
Hide file tree
Showing 16 changed files with 186 additions and 67 deletions.
2 changes: 1 addition & 1 deletion docs/functions/clientCredentialsGrantRequest.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Performs a Client Credentials Grant request at the
| :------ | :------ | :------ |
| `as` | [`AuthorizationServer`](../interfaces/AuthorizationServer.md) | Authorization Server Metadata. |
| `client` | [`Client`](../interfaces/Client.md) | Client Metadata. |
| `parameters` | [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams ) | - |
| `parameters` | [`Record`]( https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type )<`string`, `string`\> \| [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams ) \| `string`[][] | - |
| `options?` | [`ClientCredentialsGrantRequestOptions`](../interfaces/ClientCredentialsGrantRequestOptions.md) | - |

#### Returns
Expand Down
2 changes: 1 addition & 1 deletion docs/functions/deviceAuthorizationRequest.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Performs a Device Authorization Request at the
| :------ | :------ | :------ |
| `as` | [`AuthorizationServer`](../interfaces/AuthorizationServer.md) | Authorization Server Metadata. |
| `client` | [`Client`](../interfaces/Client.md) | Client Metadata. |
| `parameters` | [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams ) | Device Authorization Request parameters. |
| `parameters` | [`Record`]( https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type )<`string`, `string`\> \| [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams ) \| `string`[][] | Device Authorization Request parameters. |
| `options?` | [`DeviceAuthorizationRequestOptions`](../interfaces/DeviceAuthorizationRequestOptions.md) | - |

#### Returns
Expand Down
2 changes: 1 addition & 1 deletion docs/functions/issueRequestObject.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Generates a signed JWT-Secured Authorization Request (JAR).
| :------ | :------ | :------ |
| `as` | [`AuthorizationServer`](../interfaces/AuthorizationServer.md) | Authorization Server Metadata. |
| `client` | [`Client`](../interfaces/Client.md) | Client Metadata. |
| `parameters` | [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams ) | - |
| `parameters` | [`Record`]( https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type )<`string`, `string`\> \| [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams ) \| `string`[][] | - |
| `privateKey` | [`CryptoKey`]( https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey ) \| [`PrivateKey`](../interfaces/PrivateKey.md) | Private key to sign the Request Object with. |

#### Returns
Expand Down
2 changes: 1 addition & 1 deletion docs/functions/pushedAuthorizationRequest.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Performs a Pushed Authorization Request at the
| :------ | :------ | :------ |
| `as` | [`AuthorizationServer`](../interfaces/AuthorizationServer.md) | Authorization Server Metadata. |
| `client` | [`Client`](../interfaces/Client.md) | Client Metadata. |
| `parameters` | [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams ) | Authorization Request parameters. |
| `parameters` | [`Record`]( https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type )<`string`, `string`\> \| [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams ) \| `string`[][] | Authorization Request parameters. |
| `options?` | [`PushedAuthorizationRequestOptions`](../interfaces/PushedAuthorizationRequestOptions.md) | - |

#### Returns
Expand Down
2 changes: 1 addition & 1 deletion docs/interfaces/IntrospectionRequestOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

### additionalParameters

`Optional` **additionalParameters**: [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams )
`Optional` **additionalParameters**: [`Record`]( https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type )<`string`, `string`\> \| [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams ) \| `string`[][]

Any additional parameters to send. This cannot override existing parameter values.

Expand Down
2 changes: 1 addition & 1 deletion docs/interfaces/RevocationRequestOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

### additionalParameters

`Optional` **additionalParameters**: [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams )
`Optional` **additionalParameters**: [`Record`]( https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type )<`string`, `string`\> \| [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams ) \| `string`[][]

Any additional parameters to send. This cannot override existing parameter values.

Expand Down
2 changes: 1 addition & 1 deletion docs/interfaces/TokenEndpointRequestOptions.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ ___

### additionalParameters

`Optional` **additionalParameters**: [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams )
`Optional` **additionalParameters**: [`Record`]( https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type )<`string`, `string`\> \| [`URLSearchParams`]( https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams ) \| `string`[][]

Any additional parameters to send. This cannot override existing parameter values.

Expand Down
25 changes: 7 additions & 18 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1255,15 +1255,12 @@ async function jwt(
export async function issueRequestObject(
as: AuthorizationServer,
client: Client,
parameters: URLSearchParams,
parameters: URLSearchParams | Record<string, string> | string[][],
privateKey: CryptoKey | PrivateKey,
) {
assertAs(as)
assertClient(client)

if (!(parameters instanceof URLSearchParams)) {
throw new TypeError('"parameters" must be an instance of URLSearchParams')
}
parameters = new URLSearchParams(parameters)

const { key, kid } = getKeyAndKid(privateKey)
Expand Down Expand Up @@ -1397,16 +1394,12 @@ async function publicJwk(key: CryptoKey) {
export async function pushedAuthorizationRequest(
as: AuthorizationServer,
client: Client,
parameters: URLSearchParams,
parameters: URLSearchParams | Record<string, string> | string[][],
options?: PushedAuthorizationRequestOptions,
): Promise<Response> {
assertAs(as)
assertClient(client)

if (!(parameters instanceof URLSearchParams)) {
throw new TypeError('"parameters" must be an instance of URLSearchParams')
}

if (typeof as.pushed_authorization_request_endpoint !== 'string') {
throw new TypeError('"as.pushed_authorization_request_endpoint" must be a string')
}
Expand Down Expand Up @@ -1996,7 +1989,7 @@ export interface TokenEndpointRequestOptions
AuthenticatedRequestOptions,
DPoPRequestOptions {
/** Any additional parameters to send. This cannot override existing parameter values. */
additionalParameters?: URLSearchParams
additionalParameters?: URLSearchParams | Record<string, string> | string[][]
}

async function tokenEndpointRequest(
Expand Down Expand Up @@ -2566,7 +2559,7 @@ export interface ClientCredentialsGrantRequestOptions
export async function clientCredentialsGrantRequest(
as: AuthorizationServer,
client: Client,
parameters: URLSearchParams,
parameters: URLSearchParams | Record<string, string> | string[][],
options?: ClientCredentialsGrantRequestOptions,
): Promise<Response> {
assertAs(as)
Expand Down Expand Up @@ -2610,7 +2603,7 @@ export async function processClientCredentialsResponse(

export interface RevocationRequestOptions extends HttpRequestOptions, AuthenticatedRequestOptions {
/** Any additional parameters to send. This cannot override existing parameter values. */
additionalParameters?: URLSearchParams
additionalParameters?: URLSearchParams | Record<string, string> | string[][]
}

/**
Expand Down Expand Up @@ -2683,7 +2676,7 @@ export interface IntrospectionRequestOptions
extends HttpRequestOptions,
AuthenticatedRequestOptions {
/** Any additional parameters to send. This cannot override existing parameter values. */
additionalParameters?: URLSearchParams
additionalParameters?: URLSearchParams | Record<string, string> | string[][]
/**
* Request a JWT Response from the
* {@link AuthorizationServer.introspection_endpoint `as.introspection_endpoint`}. Default is
Expand Down Expand Up @@ -3365,16 +3358,12 @@ export interface DeviceAuthorizationRequestOptions
export async function deviceAuthorizationRequest(
as: AuthorizationServer,
client: Client,
parameters: URLSearchParams,
parameters: URLSearchParams | Record<string, string> | string[][],
options?: DeviceAuthorizationRequestOptions,
): Promise<Response> {
assertAs(as)
assertClient(client)

if (!(parameters instanceof URLSearchParams)) {
throw new TypeError('"parameters" must be an instance of URLSearchParams')
}

if (typeof as.device_authorization_endpoint !== 'string') {
throw new TypeError('"as.device_authorization_endpoint" must be a string')
}
Expand Down
42 changes: 23 additions & 19 deletions tap/request_object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,31 @@ export default (QUnit: QUnit) => {
for (const [alg, kp] of Object.entries(keys)) {
test(`issueRequestObject() w/ ${alg}`, async (t) => {
const { privateKey, publicKey } = await kp
const jwt = await lib.issueRequestObject(
issuer,
client,
for (const parameters of [
new URLSearchParams({ response_type: 'code', resource: 'urn:example:resource' }),
{ key: privateKey },
)
{ response_type: 'code', resource: 'urn:example:resource' },
[
['response_type', 'code'],
['resource', 'urn:example:resource'],
],
]) {
const jwt = await lib.issueRequestObject(issuer, client, parameters, { key: privateKey })

const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey)
t.propEqual(protectedHeader, { alg, typ: 'oauth-authz-req+jwt' })
const { exp, iat, nbf, jti, ...claims } = payload
t.equal(typeof exp, 'number')
t.equal(typeof nbf, 'number')
t.equal(typeof iat, 'number')
t.equal(typeof jti, 'string')
t.propEqual(claims, {
iss: client.client_id,
aud: issuer.issuer,
response_type: 'code',
resource: 'urn:example:resource',
client_id: client.client_id,
})
const { payload, protectedHeader } = await jose.jwtVerify(jwt, publicKey)
t.propEqual(protectedHeader, { alg, typ: 'oauth-authz-req+jwt' })
const { exp, iat, nbf, jti, ...claims } = payload
t.equal(typeof exp, 'number')
t.equal(typeof nbf, 'number')
t.equal(typeof iat, 'number')
t.equal(typeof jti, 'string')
t.propEqual(claims, {
iss: client.client_id,
aud: issuer.issuer,
response_type: 'code',
resource: 'urn:example:resource',
client_id: client.client_id,
})
}
})
}

Expand Down
29 changes: 29 additions & 0 deletions test/authorization_code.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ test('authorizationCodeGrantRequest() w/ Extra Parameters', async (t) => {
},
})
.reply(200, { access_token: 'token', token_type: 'Bearer' })
.times(3)

await t.notThrowsAsync(
lib.authorizationCodeGrantRequest(
Expand All @@ -165,6 +166,34 @@ test('authorizationCodeGrantRequest() w/ Extra Parameters', async (t) => {
},
),
)

await t.notThrowsAsync(
lib.authorizationCodeGrantRequest(
tIssuer,
tClient,
cb('code=authorization_code'),
'redirect_uri',
'verifier',
{
additionalParameters: {
resource: 'urn:example:resource',
},
},
),
)

await t.notThrowsAsync(
lib.authorizationCodeGrantRequest(
tIssuer,
tClient,
cb('code=authorization_code'),
'redirect_uri',
'verifier',
{
additionalParameters: [['resource', 'urn:example:resource']],
},
),
)
})

test('authorizationCodeGrantRequest() w/ Custom Headers', async (t) => {
Expand Down
20 changes: 18 additions & 2 deletions test/client_credentials.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,28 @@ test('clientCredentialsGrantRequest()', async (t) => {
},
body(body) {
const params = new URLSearchParams(body)
return params.get('grant_type') === 'client_credentials'
return (
params.get('grant_type') === 'client_credentials' &&
params.get('resource') === 'urn:example:resource'
)
},
})
.reply(200, { access_token: 'token', token_type: 'Bearer' })
.times(3)

await t.notThrowsAsync(lib.clientCredentialsGrantRequest(tIssuer, tClient, new URLSearchParams()))
await t.notThrowsAsync(
lib.clientCredentialsGrantRequest(
tIssuer,
tClient,
new URLSearchParams({ resource: 'urn:example:resource' }),
),
)
await t.notThrowsAsync(
lib.clientCredentialsGrantRequest(tIssuer, tClient, { resource: 'urn:example:resource' }),
)
await t.notThrowsAsync(
lib.clientCredentialsGrantRequest(tIssuer, tClient, [['resource', 'urn:example:resource']]),
)
})

test('clientCredentialsGrantRequest() w/ Extra Parameters', async (t) => {
Expand Down
40 changes: 34 additions & 6 deletions test/device_flow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ test('deviceAuthorizationRequest()', async (t) => {
message: '"as.device_authorization_endpoint" must be a string',
})

await t.throwsAsync(lib.deviceAuthorizationRequest(issuer, tClient, <any>null), {
message: '"parameters" must be an instance of URLSearchParams',
})

const tIssuer: lib.AuthorizationServer = {
...issuer,
device_authorization_endpoint: endpoint('device-1'),
Expand All @@ -44,12 +40,29 @@ test('deviceAuthorizationRequest()', async (t) => {
'user-agent': UA,
},
body(body) {
return new URLSearchParams(body).get('client_id') === client.client_id
const params = new URLSearchParams(body)
return (
params.get('client_id') === client.client_id &&
params.get('resource') === 'urn:example:resource'
)
},
})
.reply(200, '')
.times(3)

await t.notThrowsAsync(lib.deviceAuthorizationRequest(tIssuer, tClient, new URLSearchParams()))
await t.notThrowsAsync(
lib.deviceAuthorizationRequest(
tIssuer,
tClient,
new URLSearchParams({ resource: 'urn:example:resource' }),
),
)
await t.notThrowsAsync(
lib.deviceAuthorizationRequest(tIssuer, tClient, { resource: 'urn:example:resource' }),
)
await t.notThrowsAsync(
lib.deviceAuthorizationRequest(tIssuer, tClient, [['resource', 'urn:example:resource']]),
)
})

test('deviceAuthorizationRequest() w/ Custom Headers', async (t) => {
Expand Down Expand Up @@ -267,12 +280,27 @@ test('deviceCodeGrantRequest() w/ Extra Parameters', async (t) => {
},
})
.reply(200, { access_token: 'token', token_type: 'Bearer' })
.times(3)

await t.notThrowsAsync(
lib.deviceCodeGrantRequest(tIssuer, tClient, 'device_code', {
additionalParameters: new URLSearchParams('resource=urn:example:resource'),
}),
)

await t.notThrowsAsync(
lib.deviceCodeGrantRequest(tIssuer, tClient, 'device_code', {
additionalParameters: {
resource: 'urn:example:resource',
},
}),
)

await t.notThrowsAsync(
lib.deviceCodeGrantRequest(tIssuer, tClient, 'device_code', {
additionalParameters: [['resource', 'urn:example:resource']],
}),
)
})

test('deviceCodeGrantRequest() w/ Custom Headers', async (t) => {
Expand Down
15 changes: 15 additions & 0 deletions test/introspection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,27 @@ test('introspectionRequest() w/ Extra Parameters', async (t) => {
},
})
.reply(200, { access_token: 'token', token_type: 'Bearer' })
.times(3)

await t.notThrowsAsync(
lib.introspectionRequest(tIssuer, tClient, 'token', {
additionalParameters: new URLSearchParams('token_type_hint=access_token'),
}),
)

await t.notThrowsAsync(
lib.introspectionRequest(tIssuer, tClient, 'token', {
additionalParameters: {
token_type_hint: 'access_token',
},
}),
)

await t.notThrowsAsync(
lib.introspectionRequest(tIssuer, tClient, 'token', {
additionalParameters: [['token_type_hint', 'access_token']],
}),
)
})

test('introspectionRequest() w/ Custom Headers', async (t) => {
Expand Down
Loading

0 comments on commit 021b85f

Please sign in to comment.