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 +1 -1
- package/build/index.d.ts +22 -5
- package/build/index.js +30 -40
- package/package.json +6 -6
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.
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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.
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
dpopNonces.set(url.origin,
|
|
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
|
-
|
|
152
|
-
|
|
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 =
|
|
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
|
-
|
|
346
|
-
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
493
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 (
|
|
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.
|
|
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.
|
|
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.
|
|
78
|
+
"jose": "^5.2.1",
|
|
79
79
|
"oidc-provider": "^8.4.5",
|
|
80
80
|
"patch-package": "^8.0.0",
|
|
81
|
-
"prettier": "^3.2.
|
|
81
|
+
"prettier": "^3.2.5",
|
|
82
82
|
"prettier-plugin-jsdoc": "^1.3.0",
|
|
83
|
-
"puppeteer-core": "^21.
|
|
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.
|
|
91
|
+
"typedoc-plugin-mdn-links": "^3.1.15",
|
|
92
92
|
"typescript": "^5.3.3",
|
|
93
93
|
"undici": "^5.28.2"
|
|
94
94
|
}
|