oauth4webapi 2.9.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,7 +44,7 @@ import * as oauth2 from 'oauth4webapi'
44
44
  **`example`** Deno import
45
45
 
46
46
  ```js
47
- import * as oauth2 from 'https://deno.land/x/oauth4webapi@v2.9.0/mod.ts'
47
+ import * as oauth2 from 'https://deno.land/x/oauth4webapi@v2.10.0/mod.ts'
48
48
  ```
49
49
 
50
50
  - Authorization Code Flow - OpenID Connect [source](examples/code.ts), or plain OAuth 2 [source](examples/oauth.ts)
package/build/index.d.ts CHANGED
@@ -161,12 +161,13 @@ export declare const clockTolerance: unique symbol;
161
161
  *
162
162
  * - Expect Type-related issues when passing the inputs through to fetch-like modules, they hardly
163
163
  * ever get their typings inline with actual fetch, you should `@ts-expect-error` them.
164
- * - Returning self-constructed {@link Response} instances prohibits AS-signalled DPoP Nonce caching.
164
+ * - Returning self-constructed {@link Response} instances prohibits AS/RS-signalled DPoP Nonce
165
+ * caching.
165
166
  *
166
167
  * @example
167
168
  *
168
- * Using [sindresorhus/ky](https://github.com/sindresorhus/ky) hooks feature for logging outgoing
169
- * requests and their responses.
169
+ * Using [sindresorhus/ky](https://github.com/sindresorhus/ky) for retries and its hooks feature for
170
+ * logging outgoing requests and their responses.
170
171
  *
171
172
  * ```js
172
173
  * import ky from 'ky'
@@ -200,7 +201,7 @@ export declare const clockTolerance: unique symbol;
200
201
  *
201
202
  * @example
202
203
  *
203
- * Using [nodejs/undici](https://github.com/nodejs/undici) for mocking.
204
+ * Using [nodejs/undici](https://github.com/nodejs/undici) to mock responses in tests.
204
205
  *
205
206
  * ```js
206
207
  * import * as undici from 'undici'
@@ -1183,12 +1184,22 @@ export interface IDToken extends JWTPayload {
1183
1184
  readonly azp?: string;
1184
1185
  readonly [claim: string]: JsonValue | undefined;
1185
1186
  }
1187
+ export interface AuthorizationDetails {
1188
+ readonly type: string;
1189
+ readonly locations?: string[];
1190
+ readonly actions?: string[];
1191
+ readonly datatypes?: string[];
1192
+ readonly privileges?: string[];
1193
+ readonly identifier?: string;
1194
+ readonly [parameter: string]: JsonValue | undefined;
1195
+ }
1186
1196
  export interface TokenEndpointResponse {
1187
1197
  readonly access_token: string;
1188
1198
  readonly expires_in?: number;
1189
1199
  readonly id_token?: string;
1190
1200
  readonly refresh_token?: string;
1191
1201
  readonly scope?: string;
1202
+ readonly authorization_details?: AuthorizationDetails[];
1192
1203
  /**
1193
1204
  * NOTE: because the value is case insensitive it is always returned lowercased
1194
1205
  */
@@ -1201,6 +1212,7 @@ export interface OpenIDTokenEndpointResponse {
1201
1212
  readonly id_token: string;
1202
1213
  readonly refresh_token?: string;
1203
1214
  readonly scope?: string;
1215
+ readonly authorization_details?: AuthorizationDetails[];
1204
1216
  /**
1205
1217
  * NOTE: because the value is case insensitive it is always returned lowercased
1206
1218
  */
@@ -1213,6 +1225,7 @@ export interface OAuth2TokenEndpointResponse {
1213
1225
  readonly id_token?: undefined;
1214
1226
  readonly refresh_token?: string;
1215
1227
  readonly scope?: string;
1228
+ readonly authorization_details?: AuthorizationDetails[];
1216
1229
  /**
1217
1230
  * NOTE: because the value is case insensitive it is always returned lowercased
1218
1231
  */
@@ -1223,6 +1236,7 @@ export interface ClientCredentialsGrantResponse {
1223
1236
  readonly access_token: string;
1224
1237
  readonly expires_in?: number;
1225
1238
  readonly scope?: string;
1239
+ readonly authorization_details?: AuthorizationDetails[];
1226
1240
  /**
1227
1241
  * NOTE: because the value is case insensitive it is always returned lowercased
1228
1242
  */
@@ -1393,11 +1407,12 @@ export interface IntrospectionResponse {
1393
1407
  readonly jti?: string;
1394
1408
  readonly username?: string;
1395
1409
  readonly aud?: string | string[];
1396
- readonly scope: string;
1410
+ readonly scope?: string;
1397
1411
  readonly sub?: string;
1398
1412
  readonly nbf?: number;
1399
1413
  readonly token_type?: string;
1400
1414
  readonly cnf?: ConfirmationClaims;
1415
+ readonly authorization_details?: AuthorizationDetails[];
1401
1416
  readonly [claim: string]: JsonValue | undefined;
1402
1417
  }
1403
1418
  /**
@@ -1596,6 +1611,8 @@ export interface JWTAccessTokenClaims extends JWTPayload {
1596
1611
  readonly iat: number;
1597
1612
  readonly jti: string;
1598
1613
  readonly client_id: string;
1614
+ readonly authorization_details?: AuthorizationDetails[];
1615
+ readonly scope?: string;
1599
1616
  readonly [claim: string]: JsonValue | undefined;
1600
1617
  }
1601
1618
  export interface ValidateJWTAccessTokenOptions extends HttpRequestOptions {
package/build/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  let USER_AGENT;
2
2
  if (typeof navigator === 'undefined' || !navigator.userAgent?.startsWith?.('Mozilla/5.0 ')) {
3
3
  const NAME = 'oauth4webapi';
4
- const VERSION = 'v2.9.0';
4
+ const VERSION = 'v2.10.0';
5
5
  USER_AGENT = `${NAME}/${VERSION}`;
6
6
  }
7
7
  function looseInstanceOf(input, expected) {
@@ -143,14 +143,13 @@ const SUPPORTED_JWS_ALGS = [
143
143
  ];
144
144
  function processDpopNonce(response) {
145
145
  try {
146
- if (response.headers.has('dpop-nonce')) {
147
- const url = new URL(response.url);
148
- dpopNonces.set(url.origin, response.headers.get('dpop-nonce'));
146
+ const nonce = response.headers.get('dpop-nonce');
147
+ if (nonce) {
148
+ dpopNonces.set(new URL(response.url).origin, nonce);
149
149
  }
150
150
  }
151
- finally {
152
- return response;
153
- }
151
+ catch { }
152
+ return response;
154
153
  }
155
154
  function normalizeTyp(value) {
156
155
  return value.toLowerCase().replace(/^application\//, '');
@@ -201,7 +200,7 @@ export async function discoveryRequest(issuerIdentifier, options) {
201
200
  break;
202
201
  case 'oauth2':
203
202
  if (url.pathname === '/') {
204
- url.pathname = `.well-known/oauth-authorization-server`;
203
+ url.pathname = '.well-known/oauth-authorization-server';
205
204
  }
206
205
  else {
207
206
  url.pathname = `.well-known/oauth-authorization-server/${url.pathname}`.replace('//', '/');
@@ -342,21 +341,14 @@ function keyToJws(key) {
342
341
  }
343
342
  }
344
343
  function getClockSkew(client) {
345
- if (client && clockSkew in client) {
346
- if (Number.isFinite(client[clockSkew])) {
347
- return client[clockSkew];
348
- }
349
- }
350
- return 0;
344
+ const skew = client?.[clockSkew];
345
+ return typeof skew === 'number' && Number.isFinite(skew) ? skew : 0;
351
346
  }
352
347
  function getClockTolerance(client) {
353
- if (client && clockTolerance in client) {
354
- const tolerance = client[clockTolerance];
355
- if (Number.isFinite(tolerance) && Math.sign(tolerance) !== -1) {
356
- return tolerance;
357
- }
358
- }
359
- return 30;
348
+ const tolerance = client?.[clockTolerance];
349
+ return typeof tolerance === 'number' && Number.isFinite(tolerance) && Math.sign(tolerance) !== -1
350
+ ? tolerance
351
+ : 30;
360
352
  }
361
353
  function epochTime() {
362
354
  return Math.floor(Date.now() / 1000);
@@ -489,8 +481,8 @@ export async function issueRequestObject(as, client, parameters, privateKey) {
489
481
  resource.length > 1) {
490
482
  claims.resource = resource;
491
483
  }
492
- if (parameters.has('claims')) {
493
- const value = parameters.get('claims');
484
+ let value = parameters.get('claims');
485
+ if (value) {
494
486
  if (value === '[object Object]') {
495
487
  throw new OPE('"claims" parameter must be passed as a UTF-8 encoded JSON');
496
488
  }
@@ -540,24 +532,22 @@ async function dpopProofJwt(headers, options, url, htm, clockSkew, accessToken)
540
532
  headers.set('dpop', proof);
541
533
  }
542
534
  let jwkCache;
543
- async function publicJwk(key) {
544
- jwkCache || (jwkCache = new WeakMap());
545
- if (jwkCache.has(key)) {
546
- return jwkCache.get(key);
547
- }
535
+ async function getSetPublicJwkCache(key) {
548
536
  const { kty, e, n, x, y, crv } = await crypto.subtle.exportKey('jwk', key);
549
537
  const jwk = { kty, e, n, x, y, crv };
550
538
  jwkCache.set(key, jwk);
551
539
  return jwk;
552
540
  }
541
+ async function publicJwk(key) {
542
+ jwkCache || (jwkCache = new WeakMap());
543
+ return jwkCache.get(key) || getSetPublicJwkCache(key);
544
+ }
553
545
  function validateEndpoint(value, endpoint, options) {
554
546
  if (typeof value !== 'string') {
555
547
  if (options?.[useMtlsAlias]) {
556
548
  throw new TypeError(`"as.mtls_endpoint_aliases.${endpoint}" must be a string`);
557
549
  }
558
- else {
559
- throw new TypeError(`"as.${endpoint}" must be a string`);
560
- }
550
+ throw new TypeError(`"as.${endpoint}" must be a string`);
561
551
  }
562
552
  return new URL(value);
563
553
  }
@@ -621,10 +611,10 @@ export function parseWwwAuthenticateChallenges(response) {
621
611
  if (!looseInstanceOf(response, Response)) {
622
612
  throw new TypeError('"response" must be an instance of Response');
623
613
  }
624
- if (!response.headers.has('www-authenticate')) {
614
+ const header = response.headers.get('www-authenticate');
615
+ if (header === null) {
625
616
  return undefined;
626
617
  }
627
- const header = response.headers.get('www-authenticate');
628
618
  const result = [];
629
619
  for (const { 1: scheme, index } of header.matchAll(SCHEMES_REGEXP)) {
630
620
  result.push([scheme, index]);
@@ -791,7 +781,7 @@ async function getPublicSigKeyFromIssuerJwksUri(as, options, header) {
791
781
  }
792
782
  throw new OPE('error when selecting a JWT verification key, no applicable keys found');
793
783
  }
794
- else if (length !== 1) {
784
+ if (length !== 1) {
795
785
  throw new OPE('error when selecting a JWT verification key, multiple applicable keys found, a "kid" JWT Header Parameter is required');
796
786
  }
797
787
  const key = await importJwk(alg, jwk);
@@ -1771,8 +1761,9 @@ function normalizeHtu(htu) {
1771
1761
  url.hash = '';
1772
1762
  return url.href;
1773
1763
  }
1774
- async function validateDPoP(as, request, accessTokenClaims, options) {
1775
- if (!request.headers.has('dpop')) {
1764
+ async function validateDPoP(as, request, accessToken, accessTokenClaims, options) {
1765
+ const header = request.headers.get('dpop');
1766
+ if (header === null) {
1776
1767
  throw new OPE('operation indicated DPoP use but the request has no DPoP HTTP Header');
1777
1768
  }
1778
1769
  if (request.headers.get('authorization')?.toLowerCase().startsWith('dpop ') === false) {
@@ -1782,7 +1773,7 @@ async function validateDPoP(as, request, accessTokenClaims, options) {
1782
1773
  throw new OPE('operation indicated DPoP use but the JWT Access Token has no jkt confirmation claim');
1783
1774
  }
1784
1775
  const clockSkew = getClockSkew(options);
1785
- const proof = await validateJwt(request.headers.get('dpop'), checkSigningAlgorithm.bind(undefined, undefined, as?.dpop_signing_alg_values_supported || SUPPORTED_JWS_ALGS), async ({ jwk, alg }) => {
1776
+ const proof = await validateJwt(header, checkSigningAlgorithm.bind(undefined, undefined, as?.dpop_signing_alg_values_supported || SUPPORTED_JWS_ALGS), async ({ jwk, alg }) => {
1786
1777
  if (!jwk) {
1787
1778
  throw new OPE('DPoP Proof is missing the jwk header parameter');
1788
1779
  }
@@ -1807,7 +1798,6 @@ async function validateDPoP(as, request, accessTokenClaims, options) {
1807
1798
  throw new OPE('DPoP Proof htu mismatch');
1808
1799
  }
1809
1800
  {
1810
- const accessToken = request.headers.get('authorization').split(' ')[1];
1811
1801
  const expected = b64u(await crypto.subtle.digest('SHA-256', encoder.encode(accessToken)));
1812
1802
  if (proof.claims.ath !== expected) {
1813
1803
  throw new OPE('DPoP Proof ath mismatch');
@@ -1856,7 +1846,7 @@ export async function validateJwtAccessToken(as, request, expectedAudience, opti
1856
1846
  throw new OPE('"expectedAudience" must be a non-empty string');
1857
1847
  }
1858
1848
  const authorization = request.headers.get('authorization');
1859
- if (!authorization) {
1849
+ if (authorization === null) {
1860
1850
  throw new OPE('"request" is missing an Authorization HTTP Header');
1861
1851
  }
1862
1852
  let { 0: scheme, 1: accessToken, length } = authorization.split(' ');
@@ -1911,7 +1901,7 @@ export async function validateJwtAccessToken(as, request, expectedAudience, opti
1911
1901
  scheme === 'dpop' ||
1912
1902
  claims.cnf?.jkt !== undefined ||
1913
1903
  request.headers.has('dpop')) {
1914
- await validateDPoP(as, request, claims, options);
1904
+ await validateDPoP(as, request, accessToken, claims, options);
1915
1905
  }
1916
1906
  return claims;
1917
1907
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oauth4webapi",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "description": "OAuth 2 / OpenID Connect for JavaScript Runtimes",
5
5
  "keywords": [
6
6
  "access token",
@@ -67,7 +67,7 @@
67
67
  "devDependencies": {
68
68
  "@koa/cors": "^5.0.0",
69
69
  "@types/koa__cors": "^5.0.0",
70
- "@types/node": "^20.11.15",
70
+ "@types/node": "^20.11.16",
71
71
  "@types/oidc-provider": "^8.4.3",
72
72
  "@types/qunit": "^2.19.10",
73
73
  "archiver": "^6.0.1",
@@ -75,12 +75,12 @@
75
75
  "chrome-launcher": "^1.1.0",
76
76
  "edge-runtime": "^2.5.8",
77
77
  "esbuild": "^0.20.0",
78
- "jose": "^5.2.0",
78
+ "jose": "^5.2.1",
79
79
  "oidc-provider": "^8.4.5",
80
80
  "patch-package": "^8.0.0",
81
- "prettier": "^3.2.4",
81
+ "prettier": "^3.2.5",
82
82
  "prettier-plugin-jsdoc": "^1.3.0",
83
- "puppeteer-core": "^21.10.0",
83
+ "puppeteer-core": "^21.11.0",
84
84
  "qunit": "^2.20.0",
85
85
  "raw-body": "^2.5.2",
86
86
  "selfsigned": "^2.4.1",
@@ -88,7 +88,7 @@
88
88
  "tsx": "^4.7.0",
89
89
  "typedoc": "^0.25.7",
90
90
  "typedoc-plugin-markdown": "^3.17.1",
91
- "typedoc-plugin-mdn-links": "^3.1.14",
91
+ "typedoc-plugin-mdn-links": "^3.1.15",
92
92
  "typescript": "^5.3.3",
93
93
  "undici": "^5.28.2"
94
94
  }