@cloudflare/workers-oauth-provider 0.8.2 → 0.8.3
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/dist/oauth-provider.d.ts +6 -6
- package/dist/oauth-provider.js +29 -21
- package/package.json +1 -1
package/dist/oauth-provider.d.ts
CHANGED
|
@@ -358,8 +358,9 @@ interface TokenExchangeCallbackResult {
|
|
|
358
358
|
*/
|
|
359
359
|
refreshTokenTTL?: number;
|
|
360
360
|
/**
|
|
361
|
-
*
|
|
362
|
-
*
|
|
361
|
+
* Optional scopes for the new access token. Values outside the scope ceiling
|
|
362
|
+
* for the current grant flow are ignored. If omitted, the effective requested
|
|
363
|
+
* scopes are used.
|
|
363
364
|
*/
|
|
364
365
|
accessTokenScope?: string[];
|
|
365
366
|
}
|
|
@@ -388,12 +389,11 @@ interface TokenExchangeCallbackOptions {
|
|
|
388
389
|
*/
|
|
389
390
|
grantId: string;
|
|
390
391
|
/**
|
|
391
|
-
* List of scopes
|
|
392
|
+
* List of scopes on the underlying authorization grant.
|
|
392
393
|
*/
|
|
393
394
|
scope: string[];
|
|
394
395
|
/**
|
|
395
|
-
*
|
|
396
|
-
* (Will be the same as granted scopes unless client specifically requested a downscoping)
|
|
396
|
+
* Effective scopes selected for this token before applying the callback result.
|
|
397
397
|
*/
|
|
398
398
|
requestedScope: string[];
|
|
399
399
|
/**
|
|
@@ -790,7 +790,7 @@ interface ExchangeTokenOptions {
|
|
|
790
790
|
*/
|
|
791
791
|
subjectToken: string;
|
|
792
792
|
/**
|
|
793
|
-
* Optional
|
|
793
|
+
* Optional requested scopes for the new token. Issued scopes are limited to the subject token's scopes.
|
|
794
794
|
*/
|
|
795
795
|
scope?: string[];
|
|
796
796
|
/**
|
package/dist/oauth-provider.js
CHANGED
|
@@ -990,13 +990,21 @@ var OAuthProviderImpl = class OAuthProviderImpl {
|
|
|
990
990
|
description: "Method not allowed",
|
|
991
991
|
statusCode: 405
|
|
992
992
|
});
|
|
993
|
-
|
|
993
|
+
const contentType = request.headers.get("Content-Type") || "";
|
|
994
994
|
let body = {};
|
|
995
|
-
if (
|
|
995
|
+
if (contentType.split(";")[0].trim().toLowerCase() !== "application/x-www-form-urlencoded") return this.createErrorResponse("invalid_request", {
|
|
996
996
|
description: "Content-Type must be application/x-www-form-urlencoded",
|
|
997
997
|
statusCode: 400
|
|
998
998
|
});
|
|
999
|
-
|
|
999
|
+
let formData;
|
|
1000
|
+
try {
|
|
1001
|
+
formData = await request.formData();
|
|
1002
|
+
} catch {
|
|
1003
|
+
return this.createErrorResponse("invalid_request", {
|
|
1004
|
+
description: "Request body must be valid application/x-www-form-urlencoded data",
|
|
1005
|
+
statusCode: 400
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1000
1008
|
const processedKeys = /* @__PURE__ */ new Set();
|
|
1001
1009
|
for (const [key, value] of formData.entries()) {
|
|
1002
1010
|
if (processedKeys.has(key)) continue;
|
|
@@ -1264,6 +1272,14 @@ var OAuthProviderImpl = class OAuthProviderImpl {
|
|
|
1264
1272
|
} else calculatedChallenge = codeVerifier;
|
|
1265
1273
|
if (calculatedChallenge !== grantData.codeChallenge) return this.createErrorResponse("invalid_grant", { description: "Invalid PKCE code_verifier" });
|
|
1266
1274
|
}
|
|
1275
|
+
const originOnly = !!this.options.resourceMatchOriginOnly;
|
|
1276
|
+
if (body.resource && grantData.resource) {
|
|
1277
|
+
const requestedResources = Array.isArray(body.resource) ? body.resource : [body.resource];
|
|
1278
|
+
const grantedResources = Array.isArray(grantData.resource) ? grantData.resource : [grantData.resource];
|
|
1279
|
+
for (const requested of requestedResources) if (!grantedResources.some((granted) => resourceMatches(requested, granted, originOnly))) return this.createErrorResponse("invalid_target", { description: "Requested resource was not included in the authorization request" });
|
|
1280
|
+
}
|
|
1281
|
+
const audience = parseResourceParameter(body.resource || grantData.resource);
|
|
1282
|
+
if ((body.resource || grantData.resource) && !audience) return this.createErrorResponse("invalid_target", { description: "The resource parameter must be a valid absolute URI without a fragment" });
|
|
1267
1283
|
let accessTokenTTL = this.options.accessTokenTTL;
|
|
1268
1284
|
let refreshTokenTTL = this.options.refreshTokenTTL;
|
|
1269
1285
|
const encryptionKey = await unwrapKeyWithToken(code, grantData.authCodeWrappedKey);
|
|
@@ -1325,14 +1341,6 @@ var OAuthProviderImpl = class OAuthProviderImpl {
|
|
|
1325
1341
|
grantData.expiresAt = expiresAt;
|
|
1326
1342
|
}
|
|
1327
1343
|
await this.saveGrantWithTTL(env, grantKey, grantData, now);
|
|
1328
|
-
const originOnly = !!this.options.resourceMatchOriginOnly;
|
|
1329
|
-
if (body.resource && grantData.resource) {
|
|
1330
|
-
const requestedResources = Array.isArray(body.resource) ? body.resource : [body.resource];
|
|
1331
|
-
const grantedResources = Array.isArray(grantData.resource) ? grantData.resource : [grantData.resource];
|
|
1332
|
-
for (const requested of requestedResources) if (!grantedResources.some((granted) => resourceMatches(requested, granted, originOnly))) return this.createErrorResponse("invalid_target", { description: "Requested resource was not included in the authorization request" });
|
|
1333
|
-
}
|
|
1334
|
-
const audience = parseResourceParameter(body.resource || grantData.resource);
|
|
1335
|
-
if ((body.resource || grantData.resource) && !audience) return this.createErrorResponse("invalid_target", { description: "The resource parameter must be a valid absolute URI without a fragment" });
|
|
1336
1344
|
const tokenResponse = {
|
|
1337
1345
|
access_token: await this.createAccessToken({
|
|
1338
1346
|
userId,
|
|
@@ -1502,7 +1510,7 @@ var OAuthProviderImpl = class OAuthProviderImpl {
|
|
|
1502
1510
|
* `OAuthProviderImpl` is not exposed outside this module, this is still effectively
|
|
1503
1511
|
* module-private.
|
|
1504
1512
|
* @param subjectToken - The subject token to exchange
|
|
1505
|
-
* @param requestedScopes - Optional
|
|
1513
|
+
* @param requestedScopes - Optional requested scopes, limited to the subject token's scopes
|
|
1506
1514
|
* @param requestedResource - Optional resource/audience (must be subset of original if original had resource)
|
|
1507
1515
|
* @param expiresIn - Optional TTL override in seconds
|
|
1508
1516
|
* @param clientInfo - The client making the exchange request
|
|
@@ -1516,7 +1524,7 @@ var OAuthProviderImpl = class OAuthProviderImpl {
|
|
|
1516
1524
|
const grantKey = `grant:${tokenSummary.userId}:${tokenSummary.grantId}`;
|
|
1517
1525
|
const grantData = await env.OAUTH_KV.get(grantKey, { type: "json" });
|
|
1518
1526
|
if (!grantData) throw new OAuthError("invalid_grant", { description: "Grant not found" });
|
|
1519
|
-
let tokenScopes = this.downscope(requestedScopes,
|
|
1527
|
+
let tokenScopes = this.downscope(requestedScopes, tokenSummary.scope);
|
|
1520
1528
|
const originOnly = !!this.options.resourceMatchOriginOnly;
|
|
1521
1529
|
let newAudience = tokenSummary.audience;
|
|
1522
1530
|
if (requestedResource) {
|
|
@@ -1566,7 +1574,7 @@ var OAuthProviderImpl = class OAuthProviderImpl {
|
|
|
1566
1574
|
encryptedAccessTokenProps = tokenResult.encryptedData;
|
|
1567
1575
|
accessTokenEncryptionKey = tokenResult.key;
|
|
1568
1576
|
}
|
|
1569
|
-
if (callbackResult.accessTokenScope) tokenScopes = this.downscope(callbackResult.accessTokenScope,
|
|
1577
|
+
if (callbackResult.accessTokenScope) tokenScopes = this.downscope(callbackResult.accessTokenScope, tokenSummary.scope);
|
|
1570
1578
|
}
|
|
1571
1579
|
}
|
|
1572
1580
|
if (accessTokenTTL < KV_MIN_EXPIRATION_TTL_SECONDS) throw new OAuthError("invalid_request", { description: "Requested token lifetime must be at least 60 seconds" });
|
|
@@ -2210,15 +2218,15 @@ var OAuthProviderImpl = class OAuthProviderImpl {
|
|
|
2210
2218
|
return accessToken;
|
|
2211
2219
|
}
|
|
2212
2220
|
/**
|
|
2213
|
-
*
|
|
2214
|
-
*
|
|
2221
|
+
* Restricts requested scopes to the scopes available for the current flow.
|
|
2222
|
+
* If no scope is requested, all available scopes are returned.
|
|
2215
2223
|
* @param requestedScope - The scope parameter from the request (string or array)
|
|
2216
|
-
* @param
|
|
2217
|
-
* @returns The
|
|
2224
|
+
* @param allowedScopes - The maximum scopes available for the current flow
|
|
2225
|
+
* @returns The requested scopes that are included in the allowed scopes
|
|
2218
2226
|
*/
|
|
2219
|
-
downscope(requestedScope,
|
|
2220
|
-
if (!requestedScope) return
|
|
2221
|
-
return (typeof requestedScope === "string" ? requestedScope.split(" ").filter(Boolean) : requestedScope).filter((scope) =>
|
|
2227
|
+
downscope(requestedScope, allowedScopes) {
|
|
2228
|
+
if (!requestedScope) return allowedScopes;
|
|
2229
|
+
return (typeof requestedScope === "string" ? requestedScope.split(" ").filter(Boolean) : requestedScope).filter((scope) => allowedScopes.includes(scope));
|
|
2222
2230
|
}
|
|
2223
2231
|
/**
|
|
2224
2232
|
* Checks if the global_fetch_strictly_public compatibility flag is enabled.
|