@absolutejs/auth 0.57.7 → 0.57.9

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.
@@ -232,17 +232,23 @@ var providers = defineProviders({
232
232
  },
233
233
  apple: {
234
234
  authorizationUrl: "https://appleid.apple.com/auth/authorize",
235
+ createAuthorizationURLSearchParams: {
236
+ response_mode: "form_post"
237
+ },
238
+ createClientSecret: (config) => createAppleClientSecret(config),
235
239
  isOIDC: true,
236
240
  isRefreshable: true,
237
- PKCEMethod: "S256",
238
- profileRequest: {
239
- authIn: "header",
240
- encoding: "application/json",
241
- method: "GET",
242
- url: "https://appleid.apple.com/auth/userinfo"
241
+ revocationRequest: {
242
+ authIn: "body",
243
+ encoding: "application/x-www-form-urlencoded",
244
+ tokenParamName: "token",
245
+ url: "https://appleid.apple.com/auth/revoke"
243
246
  },
244
247
  scopeRequired: false,
245
- subject: ["id"],
248
+ subject: ["sub"],
249
+ subjectBySource: {
250
+ idToken: ["sub"]
251
+ },
246
252
  subjectType: "string",
247
253
  tokenRequest: {
248
254
  authIn: "body",
@@ -311,6 +317,7 @@ var providers = defineProviders({
311
317
  token_type_hint: "refresh_token"
312
318
  }),
313
319
  encoding: "application/json",
320
+ inputSource: "refreshToken",
314
321
  tokenParamName: "token",
315
322
  url: (config) => `https://${config.domain}/oauth/revoke`
316
323
  },
@@ -1442,6 +1449,7 @@ var providers = defineProviders({
1442
1449
  token_type_hint: "refresh_token"
1443
1450
  }),
1444
1451
  encoding: "application/json",
1452
+ inputSource: "refreshToken",
1445
1453
  url: "https://www.reddit.com/api/v1/revoke_token"
1446
1454
  },
1447
1455
  scopeRequired: true,
@@ -1815,49 +1823,27 @@ var providers = defineProviders({
1815
1823
  authorizationUrl: "https://account.withings.com/oauth2_user/authorize2",
1816
1824
  isOIDC: false,
1817
1825
  isRefreshable: true,
1818
- profileRequest: {
1819
- authIn: "header",
1820
- body: async (config) => {
1821
- const props = await getWithingsProps(config);
1822
- if (props === undefined)
1823
- throw new Error("Failed to get Withings search properties");
1824
- const { nonce, hashedSignature } = props;
1825
- return [
1826
- ["action", "getuser"],
1827
- ["nonce", nonce],
1828
- ["client_id", config.clientId],
1829
- ["signature", hashedSignature]
1830
- ];
1831
- },
1832
- encoding: "application/x-www-form-urlencoded",
1833
- method: "POST",
1834
- url: "https://wbsapi.withings.net/v2/oauth2"
1835
- },
1836
1826
  refreshAccessTokenBody: {
1837
1827
  action: "requesttoken"
1838
1828
  },
1839
1829
  revocationRequest: {
1840
- authIn: "header",
1841
- body: async (config) => {
1842
- const props = await getWithingsProps(config);
1843
- if (!props)
1844
- throw new Error("Failed to get Withings props");
1845
- const { nonce, hashedSignature } = props;
1846
- return [
1847
- ["action", "revoke"],
1848
- ["client_id", config.clientId],
1849
- ["nonce", nonce],
1850
- ["signature", hashedSignature]
1851
- ];
1852
- },
1830
+ authIn: "body",
1831
+ body: (config) => getWithingsSignatureParams(config, "revoke"),
1853
1832
  encoding: "application/x-www-form-urlencoded",
1854
- method: "POST",
1855
- url: "https://wbsapi.withings.net/v2/oauth2"
1833
+ includeClientCredentials: false,
1834
+ inputSource: "subject",
1835
+ inputType: "number",
1836
+ tokenParamName: "userid",
1837
+ url: "https://wbsapi.withings.net/v2/oauth2",
1838
+ validateResponse: (value) => assertWithingsSuccess(value)
1856
1839
  },
1857
1840
  scopeDelimiter: ",",
1858
1841
  scopeRequired: true,
1859
1842
  subject: ["userid"],
1860
- subjectType: "string",
1843
+ subjectBySource: {
1844
+ tokenResponse: ["body", "userid"]
1845
+ },
1846
+ subjectType: "number",
1861
1847
  tokenRequest: {
1862
1848
  authIn: "body",
1863
1849
  encoding: "application/x-www-form-urlencoded",
@@ -2049,14 +2035,39 @@ var isPKCEProviderOption = (option) => {
2049
2035
  const provider = providers[option];
2050
2036
  return provider.PKCEMethod !== undefined;
2051
2037
  };
2052
- var isRefreshableOAuth2Client = (providerName, _client) => isRefreshableProviderOption(providerName);
2038
+ function isProfileOAuth2Client(providerOrClient, maybeClient) {
2039
+ const client = maybeClient ?? providerOrClient;
2040
+ if (maybeClient !== undefined && (typeof providerOrClient !== "string" || !isProfileProviderOption(providerOrClient))) {
2041
+ return false;
2042
+ }
2043
+ return typeof client === "object" && client !== null && "fetchUserProfile" in client && typeof client.fetchUserProfile === "function";
2044
+ }
2045
+ var isProfileProviderOption = (option) => {
2046
+ if (!isValidProviderOption(option))
2047
+ return false;
2048
+ const provider = providers[option];
2049
+ return provider.profileRequest !== undefined;
2050
+ };
2051
+ function isRefreshableOAuth2Client(providerOrClient, maybeClient) {
2052
+ const client = maybeClient ?? providerOrClient;
2053
+ if (maybeClient !== undefined && (typeof providerOrClient !== "string" || !isRefreshableProviderOption(providerOrClient))) {
2054
+ return false;
2055
+ }
2056
+ return typeof client === "object" && client !== null && "refreshAccessToken" in client && typeof client.refreshAccessToken === "function";
2057
+ }
2053
2058
  var isRefreshableProviderOption = (option) => {
2054
2059
  if (!isValidProviderOption(option))
2055
2060
  return false;
2056
2061
  const provider = providers[option];
2057
2062
  return provider.isRefreshable;
2058
2063
  };
2059
- var isRevocableOAuth2Client = (providerName, _client) => isRevocableProviderOption(providerName);
2064
+ function isRevocableOAuth2Client(providerOrClient, maybeClient) {
2065
+ const client = maybeClient ?? providerOrClient;
2066
+ if (maybeClient !== undefined && (typeof providerOrClient !== "string" || !isRevocableProviderOption(providerOrClient))) {
2067
+ return false;
2068
+ }
2069
+ return typeof client === "object" && client !== null && "resolveRevocationInput" in client && typeof client.resolveRevocationInput === "function" && "revokeToken" in client && typeof client.revokeToken === "function";
2070
+ }
2060
2071
  var isRevocableProviderOption = (option) => {
2061
2072
  if (!isValidProviderOption(option))
2062
2073
  return false;
@@ -2070,6 +2081,14 @@ var isScopeRequiredProviderOption = (option) => {
2070
2081
  return provider.scopeRequired;
2071
2082
  };
2072
2083
  var isValidProviderOption = (option) => Object.hasOwn(providers, option);
2084
+ var readPath = (value, path) => path.reduce((cursor, key) => cursor && typeof cursor === "object" ? Reflect.get(cursor, key) : undefined, value);
2085
+ var assertWithingsSuccess = (value) => {
2086
+ if (!isObject(value) || value.status !== 0) {
2087
+ const status = isObject(value) ? value.status : "invalid response";
2088
+ const detail = isObject(value) && typeof value.error === "string" ? `: ${value.error}` : "";
2089
+ throw new Error(`Withings request failed (${String(status)})${detail}`);
2090
+ }
2091
+ };
2073
2092
  var createOAuth2FetchError = async (response) => {
2074
2093
  const clone = response.clone();
2075
2094
  const prefix = `HTTP ${response.status} ${response.statusText} for ${response.url}`;
@@ -2103,16 +2122,27 @@ var createOAuth2Request = ({
2103
2122
  }
2104
2123
  oauthHeaders.set("Authorization", `Basic ${encodeBase64(`${clientId}:${clientSecret}`)}`);
2105
2124
  }
2125
+ if (body === undefined && authIn !== "body") {
2126
+ return new Request(url, {
2127
+ headers: oauthHeaders,
2128
+ method: "POST"
2129
+ });
2130
+ }
2106
2131
  if (encoding === "application/json") {
2107
2132
  oauthHeaders.set("Content-Type", "application/json");
2133
+ const jsonBody = body instanceof URLSearchParams ? Object.fromEntries(body.entries()) : { ...body };
2134
+ if (authIn === "body")
2135
+ jsonBody.client_id = clientId;
2136
+ if (authIn === "body" && clientSecret)
2137
+ jsonBody.client_secret = clientSecret;
2108
2138
  return new Request(url, {
2109
- body: JSON.stringify(body),
2139
+ body: JSON.stringify(jsonBody),
2110
2140
  headers: oauthHeaders,
2111
2141
  method: "POST"
2112
2142
  });
2113
2143
  }
2114
2144
  oauthHeaders.set("Content-Type", "application/x-www-form-urlencoded");
2115
- const entries = body instanceof URLSearchParams ? Array.from(body.entries()) : Object.entries(body).filter((entry) => typeof entry[1] === "string");
2145
+ const entries = body instanceof URLSearchParams ? Array.from(body.entries()) : Object.entries(body ?? {}).filter((entry) => typeof entry[1] === "string");
2116
2146
  const params = new URLSearchParams(entries);
2117
2147
  if (authIn === "body") {
2118
2148
  params.set("client_id", clientId);
@@ -2158,25 +2188,31 @@ var encodeBase64 = (input) => {
2158
2188
  }
2159
2189
  return btoa(raw);
2160
2190
  };
2161
- var getWithingsProps = async (config) => {
2191
+ var getWithingsSignatureParams = async (config, action) => {
2162
2192
  const timestamp = Math.floor(Date.now() / 1000);
2163
- const signature = `getnonce,${config.clientId},${timestamp}`;
2164
- const hashedSignature = await hmacSha256(signature, config.clientSecret);
2193
+ const nonceSignature = await hmacSha256(`getnonce,${config.clientId},${timestamp}`, config.clientSecret);
2165
2194
  const nonceUrl = new URL("https://wbsapi.withings.net/v2/signature");
2166
2195
  nonceUrl.searchParams.set("action", "getnonce");
2167
2196
  nonceUrl.searchParams.set("client_id", config.clientId);
2168
2197
  nonceUrl.searchParams.set("timestamp", timestamp.toString());
2169
- nonceUrl.searchParams.set("signature", hashedSignature);
2198
+ nonceUrl.searchParams.set("signature", nonceSignature);
2170
2199
  const nonceTarget = nonceUrl.toString();
2171
2200
  const nonceResponse = await fetch(nonceTarget, { method: "POST" });
2201
+ if (!nonceResponse.ok) {
2202
+ throw await createOAuth2FetchError(nonceResponse);
2203
+ }
2172
2204
  const nonceData = await nonceResponse.json();
2173
- if (nonceData.status === 0) {
2174
- return {
2175
- hashedSignature,
2176
- nonce: nonceData.body.nonce
2177
- };
2205
+ if (!isObject(nonceData) || nonceData.status !== 0 || !isObject(nonceData.body) || typeof nonceData.body.nonce !== "string" || nonceData.body.nonce.length === 0) {
2206
+ throw new Error("Withings returned an invalid nonce response");
2178
2207
  }
2179
- return;
2208
+ const { nonce } = nonceData.body;
2209
+ const signature = await hmacSha256(`${action},${config.clientId},${nonce}`, config.clientSecret);
2210
+ return {
2211
+ action,
2212
+ client_id: config.clientId,
2213
+ nonce,
2214
+ signature
2215
+ };
2180
2216
  };
2181
2217
  var hmacSha256 = async (message, secret) => {
2182
2218
  const encoder = new TextEncoder;
@@ -2184,15 +2220,53 @@ var hmacSha256 = async (message, secret) => {
2184
2220
  const sigBuffer = await crypto.subtle.sign("HMAC", key, encoder.encode(message));
2185
2221
  return Array.from(new Uint8Array(sigBuffer)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
2186
2222
  };
2223
+ var parseOAuth2TokenResponse = (value, accessTokenPath) => {
2224
+ if (!isObject(value)) {
2225
+ throw new Error("OAuth token endpoint returned a non-object response");
2226
+ }
2227
+ const oauthError = Reflect.get(value, "error");
2228
+ if (typeof oauthError === "string" && oauthError.length > 0) {
2229
+ throw new Error(`OAuth token exchange failed: ${oauthError}`);
2230
+ }
2231
+ const response = { ...value };
2232
+ const nestedToken = accessTokenPath ? readPath(value, accessTokenPath) : undefined;
2233
+ if (typeof nestedToken === "string" && nestedToken.length > 0) {
2234
+ response.access_token = nestedToken;
2235
+ }
2236
+ if (typeof response.access_token !== "string" || response.access_token.length === 0) {
2237
+ throw new Error("OAuth token endpoint returned no access_token");
2238
+ }
2239
+ for (const key of ["refresh_token", "token_type", "scope", "id_token"]) {
2240
+ const field = response[key];
2241
+ if (field !== undefined && typeof field !== "string") {
2242
+ throw new Error(`OAuth token endpoint returned invalid ${key}: expected string`);
2243
+ }
2244
+ }
2245
+ const expiresIn = response.expires_in;
2246
+ if (typeof expiresIn === "string" && expiresIn.trim() !== "") {
2247
+ response.expires_in = Number(expiresIn);
2248
+ }
2249
+ if (response.expires_in !== undefined && (typeof response.expires_in !== "number" || !Number.isFinite(response.expires_in) || response.expires_in < 0)) {
2250
+ throw new Error("OAuth token endpoint returned invalid expires_in: expected a non-negative number");
2251
+ }
2252
+ return response;
2253
+ };
2254
+ var readIdentityKey = (value, key) => {
2255
+ if (Array.isArray(value)) {
2256
+ if (!/^\d+$/.test(key)) {
2257
+ throw new Error(`Invalid identity data shape: expected an array index, got ${key}`);
2258
+ }
2259
+ return value[Number(key)];
2260
+ }
2261
+ if (!isObject(value)) {
2262
+ throw new Error(`Invalid identity data shape: expected object, got ${typeof value}`);
2263
+ }
2264
+ return value[key];
2265
+ };
2187
2266
  var extractPropFromIdentity = (identity, keys, propType) => {
2188
2267
  let value = identity;
2189
2268
  for (const key of keys) {
2190
- if (Array.isArray(value))
2191
- value = value[Number(key)];
2192
- if (!isObject(value)) {
2193
- throw new Error(`Invalid identity data shape: expected object, got ${typeof value}`);
2194
- }
2195
- value = value[key];
2269
+ value = readIdentityKey(value, key);
2196
2270
  }
2197
2271
  if (propType !== undefined && !isExpectedType(value, propType)) {
2198
2272
  throw new Error(`Invalid identity data shape: expected ${propType}, got ${typeof value}`);
@@ -2227,6 +2301,13 @@ var normalizeProviderIdentity = ({
2227
2301
  const normalizedIdentity = structuredClone(identity);
2228
2302
  return setPropInIdentity(normalizedIdentity, canonicalKeys, subject);
2229
2303
  };
2304
+ var DAYS_IN_APPLE_CLIENT_SECRET_LIFETIME = 180;
2305
+ var HOURS_PER_DAY = 24;
2306
+ var MINUTES_PER_HOUR = 60;
2307
+ var SECONDS_PER_MINUTE = 60;
2308
+ var APPLE_CLIENT_SECRET_LIFETIME_SECONDS = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * DAYS_IN_APPLE_CLIENT_SECRET_LIFETIME;
2309
+ var APPLE_ISSUER = "https://appleid.apple.com";
2310
+ var MILLISECONDS_PER_SECOND = 1000;
2230
2311
  var createS256CodeChallenge = async (codeVerifier) => {
2231
2312
  const data = new TextEncoder().encode(codeVerifier);
2232
2313
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
@@ -2239,20 +2320,40 @@ var createRandomBase64UrlGenerator = (length) => () => {
2239
2320
  var generateCodeVerifier = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);
2240
2321
  var generateState = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);
2241
2322
  var base64Url = (input) => encodeBase64(input).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
2323
+ var encodeJwtPart = (value) => base64Url(new TextEncoder().encode(JSON.stringify(value)));
2324
+ var createAppleClientSecret = async (credentials) => {
2325
+ const issuedAt = Math.floor(Date.now() / MILLISECONDS_PER_SECOND);
2326
+ const header = encodeJwtPart({
2327
+ alg: "ES256",
2328
+ kid: credentials.keyId,
2329
+ typ: "JWT"
2330
+ });
2331
+ const payload = encodeJwtPart({
2332
+ aud: APPLE_ISSUER,
2333
+ exp: issuedAt + APPLE_CLIENT_SECRET_LIFETIME_SECONDS,
2334
+ iat: issuedAt,
2335
+ iss: credentials.teamId,
2336
+ sub: credentials.clientId
2337
+ });
2338
+ const signingInput = `${header}.${payload}`;
2339
+ const privateKey = await crypto.subtle.importKey("pkcs8", Uint8Array.from(credentials.pkcs8PrivateKey), { name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
2340
+ const signature = await crypto.subtle.sign({ hash: "SHA-256", name: "ECDSA" }, privateKey, new TextEncoder().encode(signingInput));
2341
+ return `${signingInput}.${base64Url(signature)}`;
2342
+ };
2242
2343
  var ALG_ES256 = "ES256";
2243
2344
  var ALG_RS256 = "RS256";
2244
2345
  var CLOCK_SKEW_SECONDS = 60;
2245
2346
  var DEFAULT_SCOPES = ["openid", "email", "profile"];
2246
2347
  var JWKS_REFETCH_COOLDOWN_MS = 60000;
2247
2348
  var JWT_SEGMENT_COUNT = 3;
2248
- var MILLISECONDS_PER_SECOND = 1000;
2349
+ var MILLISECONDS_PER_SECOND2 = 1000;
2249
2350
  var trimTrailingSlash = (value) => value.endsWith("/") ? value.slice(0, -1) : value;
2250
2351
  var fetchJson = async (url) => {
2251
2352
  const response = await fetch(url, {
2252
2353
  headers: { accept: "application/json" }
2253
2354
  });
2254
2355
  if (!response.ok) {
2255
- throw new Error(`Request to ${url} failed with status ${response.status}`);
2356
+ throw await createOAuth2FetchError(response);
2256
2357
  }
2257
2358
  return response.json();
2258
2359
  };
@@ -2283,11 +2384,14 @@ var verifySignature = (key, alg, signingInput, signature) => {
2283
2384
  var selectKey = (jwks, kid) => jwks.find((jwk) => kid === undefined || jwk.kid === kid);
2284
2385
  var assertClaims = (payload, expected) => {
2285
2386
  const aud = Reflect.get(payload, "aud");
2387
+ const azp = Reflect.get(payload, "azp");
2286
2388
  const exp = Reflect.get(payload, "exp");
2389
+ const iat = Reflect.get(payload, "iat");
2287
2390
  const iss = Reflect.get(payload, "iss");
2391
+ const nbf = Reflect.get(payload, "nbf");
2288
2392
  const sub = Reflect.get(payload, "sub");
2289
2393
  const audiences = Array.isArray(aud) ? aud : [aud];
2290
- const nowSeconds = Math.floor(Date.now() / MILLISECONDS_PER_SECOND);
2394
+ const nowSeconds = Math.floor(Date.now() / MILLISECONDS_PER_SECOND2);
2291
2395
  if (iss !== expected.issuer) {
2292
2396
  throw new Error('id_token "iss" does not match the provider issuer');
2293
2397
  }
@@ -2297,9 +2401,24 @@ var assertClaims = (payload, expected) => {
2297
2401
  if (!audiences.includes(expected.audience)) {
2298
2402
  throw new Error('id_token "aud" does not include the client id');
2299
2403
  }
2404
+ if (audiences.some((audience) => typeof audience !== "string") || typeof aud !== "string" && !Array.isArray(aud)) {
2405
+ throw new Error('id_token "aud" must be a string or string array');
2406
+ }
2407
+ if (audiences.length > 1 && (typeof azp !== "string" || azp !== expected.audience)) {
2408
+ throw new Error('id_token with multiple audiences requires matching "azp"');
2409
+ }
2300
2410
  if (typeof exp !== "number" || exp + CLOCK_SKEW_SECONDS < nowSeconds) {
2301
2411
  throw new Error("id_token has expired");
2302
2412
  }
2413
+ if (typeof iat !== "number") {
2414
+ throw new Error('id_token is missing numeric "iat"');
2415
+ }
2416
+ if (iat > nowSeconds + CLOCK_SKEW_SECONDS) {
2417
+ throw new Error('id_token "iat" is in the future');
2418
+ }
2419
+ if (nbf !== undefined && (typeof nbf !== "number" || nbf > nowSeconds + CLOCK_SKEW_SECONDS)) {
2420
+ throw new Error("id_token is not active yet");
2421
+ }
2303
2422
  if (expected.nonce !== undefined && Reflect.get(payload, "nonce") !== expected.nonce) {
2304
2423
  throw new Error('id_token "nonce" does not match');
2305
2424
  }
@@ -2426,9 +2545,9 @@ var createOIDCClient = async (config) => {
2426
2545
  method: "POST"
2427
2546
  });
2428
2547
  if (!response.ok) {
2429
- throw new Error(`OIDC token request failed with status ${response.status}`);
2548
+ throw await createOAuth2FetchError(response);
2430
2549
  }
2431
- const tokens = await response.json();
2550
+ const tokens = parseOAuth2TokenResponse(await response.json());
2432
2551
  return tokens;
2433
2552
  };
2434
2553
  const verifyToken = async (idToken, options) => {
@@ -2474,20 +2593,26 @@ var createOIDCClient = async (config) => {
2474
2593
  };
2475
2594
  var oidcProviderOptions = Object.keys(providers).filter(isOIDCProviderOption);
2476
2595
  var pkceProviderOptions = Object.keys(providers).filter(isPKCEProviderOption);
2596
+ var profileProviderOptions = Object.keys(providers).filter(isProfileProviderOption);
2477
2597
  var providerOptions = Object.keys(providers).filter(isValidProviderOption);
2478
2598
  var refreshableProviderOptions = Object.keys(providers).filter(isRefreshableProviderOption);
2479
2599
  var revocableProviderOptions = Object.keys(providers).filter(isRevocableProviderOption);
2480
2600
  var scopeRequiredProviderOptions = Object.keys(providers).filter(isScopeRequiredProviderOption);
2481
- var readPath = (value, path) => path.reduce((cursor, key) => cursor && typeof cursor === "object" ? Reflect.get(cursor, key) : undefined, value);
2482
2601
  var buildOAuth2Client = async (meta, config) => {
2483
2602
  const isConfigPropertyFunction = (cfgProp) => typeof cfgProp === "function";
2484
2603
  const resolveConfigProp = async (cfgProp) => {
2485
2604
  const result = isConfigPropertyFunction(cfgProp) ? cfgProp(config) : cfgProp;
2486
2605
  return result;
2487
2606
  };
2607
+ const resolveClientSecret = async () => {
2608
+ if (meta.createClientSecret) {
2609
+ return resolveConfigProp(meta.createClientSecret);
2610
+ }
2611
+ return hasClientSecret(config) ? config.clientSecret : undefined;
2612
+ };
2488
2613
  const authorizationUrl = await resolveConfigProp(meta.authorizationUrl);
2489
2614
  const tokenUrl = await resolveConfigProp(meta.tokenRequest.url);
2490
- return {
2615
+ const client = {
2491
2616
  async createAuthorizationUrl(opts) {
2492
2617
  const { state, scope = [], searchParams = [], codeVerifier } = opts;
2493
2618
  const url = new URL(authorizationUrl);
@@ -2508,7 +2633,7 @@ var buildOAuth2Client = async (meta, config) => {
2508
2633
  url.searchParams.set("code_challenge_method", meta.PKCEMethod);
2509
2634
  url.searchParams.set("code_challenge", codeChallenge);
2510
2635
  }
2511
- Object.entries(resolveConfigProp(meta.createAuthorizationURLSearchParams) ?? {}).forEach(([key, value]) => url.searchParams.set(key, value));
2636
+ Object.entries(await resolveConfigProp(meta.createAuthorizationURLSearchParams) ?? {}).forEach(([key, value]) => url.searchParams.set(key, value));
2512
2637
  searchParams.forEach(([key, value]) => url.searchParams.set(key, value));
2513
2638
  return url;
2514
2639
  },
@@ -2541,7 +2666,7 @@ var buildOAuth2Client = async (meta, config) => {
2541
2666
  if (authIn === "header") {
2542
2667
  profileHeaders.Authorization = `Bearer ${accessToken}`;
2543
2668
  } else if (authIn === "path") {
2544
- endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, "")}/${accessToken}`;
2669
+ endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, "")}/${encodeURIComponent(accessToken)}`;
2545
2670
  } else {
2546
2671
  endpoint.searchParams.append("access_token", accessToken);
2547
2672
  }
@@ -2562,9 +2687,8 @@ var buildOAuth2Client = async (meta, config) => {
2562
2687
  params.set("grant_type", "refresh_token");
2563
2688
  params.set("refresh_token", refreshToken);
2564
2689
  const { clientId } = config;
2565
- let clientSecretValue;
2566
- if (hasClientSecret(config)) {
2567
- clientSecretValue = config.clientSecret;
2690
+ const clientSecretValue = await resolveClientSecret();
2691
+ if (clientSecretValue) {
2568
2692
  params.set("client_id", clientId);
2569
2693
  params.set("client_secret", clientSecretValue);
2570
2694
  }
@@ -2579,60 +2703,99 @@ var buildOAuth2Client = async (meta, config) => {
2579
2703
  const response = await fetch(request);
2580
2704
  if (!response.ok)
2581
2705
  throw await createOAuth2FetchError(response);
2582
- return response.json();
2706
+ return parseOAuth2TokenResponse(await response.json());
2707
+ },
2708
+ resolveRevocationInput(context) {
2709
+ const { revocationRequest } = meta;
2710
+ if (!revocationRequest) {
2711
+ throw new Error("Token revocation not defined for this provider");
2712
+ }
2713
+ const inputSource = revocationRequest.inputSource ?? "accessToken";
2714
+ const input = context[inputSource];
2715
+ if (input === undefined) {
2716
+ throw new Error(`Revocation requires ${inputSource}, but it was not provided`);
2717
+ }
2718
+ const expectsNumber = revocationRequest.authIn !== "header" && revocationRequest.inputType === "number";
2719
+ if (expectsNumber && (typeof input !== "number" || !Number.isFinite(input))) {
2720
+ throw new TypeError("This provider requires a numeric revocation input");
2721
+ }
2722
+ if (!expectsNumber && typeof input !== "string") {
2723
+ throw new TypeError("This provider requires a string revocation input");
2724
+ }
2725
+ return input;
2583
2726
  },
2584
- async revokeToken(token) {
2727
+ async revokeToken(input) {
2585
2728
  const { revocationRequest } = meta;
2586
2729
  if (!revocationRequest) {
2587
2730
  throw new Error("Token revocation not defined for this provider");
2588
2731
  }
2589
- const { url, authIn, body, headers, tokenParamName } = revocationRequest;
2732
+ if (revocationRequest.authIn !== "header" && revocationRequest.inputType === "number" && (typeof input !== "number" || !Number.isFinite(input))) {
2733
+ throw new TypeError("This provider requires a numeric revocation input");
2734
+ }
2735
+ const {
2736
+ url,
2737
+ authIn,
2738
+ body,
2739
+ encoding,
2740
+ headers,
2741
+ includeClientCredentials = true,
2742
+ tokenParamName,
2743
+ validateResponse
2744
+ } = revocationRequest;
2590
2745
  const endpoint = await resolveConfigProp(url);
2591
2746
  const resolvedBody = await resolveConfigProp(body);
2592
- const revocationBody = new URLSearchParams(resolvedBody);
2747
+ const revocationBody = resolvedBody === undefined ? undefined : new URLSearchParams(resolvedBody);
2593
2748
  const revocationHeaders = new Headers(headers && await resolveConfigProp(headers));
2594
2749
  const { clientId } = config;
2595
- const clientSecret = hasClientSecret(config) ? config.clientSecret : undefined;
2750
+ const clientSecret = await resolveClientSecret();
2596
2751
  let request;
2597
2752
  if (authIn === "body") {
2598
- revocationBody.set(tokenParamName, token);
2599
- revocationBody.set("client_id", clientId);
2600
- if (clientSecret)
2601
- revocationBody.set("client_secret", clientSecret);
2753
+ const bodyWithToken = revocationBody ?? new URLSearchParams;
2754
+ bodyWithToken.set(tokenParamName, String(input));
2755
+ const hasAuthorizationHeader = revocationHeaders.has("Authorization");
2756
+ if (includeClientCredentials && !hasAuthorizationHeader)
2757
+ bodyWithToken.set("client_id", clientId);
2758
+ if (includeClientCredentials && !hasAuthorizationHeader && clientSecret)
2759
+ bodyWithToken.set("client_secret", clientSecret);
2602
2760
  request = createOAuth2Request({
2603
- authIn: "body",
2604
- body: revocationBody,
2761
+ authIn: hasAuthorizationHeader || !includeClientCredentials ? "query" : "body",
2762
+ body: bodyWithToken,
2605
2763
  clientId,
2606
2764
  clientSecret,
2607
- encoding: "application/x-www-form-urlencoded",
2765
+ encoding,
2608
2766
  headers: revocationHeaders,
2609
2767
  url: endpoint.toString()
2610
2768
  });
2611
2769
  } else if (authIn === "header") {
2612
- revocationHeaders.set("Authorization", `Bearer ${token}`);
2770
+ revocationHeaders.set("Authorization", `Bearer ${String(input)}`);
2613
2771
  request = createOAuth2Request({
2614
- authIn: "header",
2772
+ authIn: "query",
2615
2773
  body: revocationBody,
2616
2774
  clientId,
2617
- clientSecret,
2618
- encoding: "application/x-www-form-urlencoded",
2775
+ encoding,
2619
2776
  headers: revocationHeaders,
2620
2777
  url: endpoint.toString()
2621
2778
  });
2622
2779
  } else {
2780
+ const queryEndpoint = new URL(endpoint);
2781
+ queryEndpoint.searchParams.set(tokenParamName, String(input));
2623
2782
  request = createOAuth2Request({
2624
2783
  authIn: "query",
2625
2784
  body: revocationBody,
2626
2785
  clientId,
2627
- clientSecret,
2628
- encoding: "application/x-www-form-urlencoded",
2786
+ encoding,
2629
2787
  headers: revocationHeaders,
2630
- url: `${endpoint.toString()}?${tokenParamName}=${token}`
2788
+ url: queryEndpoint.toString()
2631
2789
  });
2632
2790
  }
2633
2791
  const response = await fetch(request);
2634
2792
  if (!response.ok)
2635
2793
  throw await createOAuth2FetchError(response);
2794
+ if (validateResponse) {
2795
+ await validateResponse(await response.json().catch(() => {
2796
+ return;
2797
+ }));
2798
+ }
2636
2799
  },
2637
2800
  async validateAuthorizationCode(opts) {
2638
2801
  const { code, codeVerifier } = opts;
@@ -2658,32 +2821,27 @@ var buildOAuth2Client = async (meta, config) => {
2658
2821
  authIn,
2659
2822
  body: payload,
2660
2823
  clientId: config.clientId,
2661
- clientSecret: hasClientSecret(config) ? config.clientSecret : undefined,
2824
+ clientSecret: await resolveClientSecret(),
2662
2825
  encoding,
2663
2826
  url: tokenUrl
2664
2827
  });
2665
2828
  const response = await fetch(request);
2666
2829
  if (!response.ok)
2667
2830
  throw await createOAuth2FetchError(response);
2668
- const tokenResponse = await response.json();
2669
- if (!tokenResponse || typeof tokenResponse !== "object") {
2670
- throw new Error("OAuth token endpoint returned a non-object response");
2671
- }
2672
- const oauthError = Reflect.get(tokenResponse, "error");
2673
- if (typeof oauthError === "string" && oauthError.length > 0) {
2674
- throw new Error(`OAuth token exchange failed: ${oauthError}`);
2675
- }
2676
- const nestedToken = meta.accessTokenPath ? readPath(tokenResponse, meta.accessTokenPath) : undefined;
2677
- if (typeof nestedToken === "string" && nestedToken.length > 0 && tokenResponse && typeof tokenResponse === "object") {
2678
- tokenResponse.access_token = nestedToken;
2679
- }
2680
- const accessToken = Reflect.get(tokenResponse, "access_token");
2681
- if (typeof accessToken !== "string" || accessToken.length === 0) {
2682
- throw new Error("OAuth token endpoint returned no access_token");
2683
- }
2684
- return tokenResponse;
2831
+ return parseOAuth2TokenResponse(await response.json(), meta.accessTokenPath);
2685
2832
  }
2686
2833
  };
2834
+ if (!meta.profileRequest) {
2835
+ Reflect.deleteProperty(client, "fetchUserProfile");
2836
+ }
2837
+ if (!meta.isRefreshable) {
2838
+ Reflect.deleteProperty(client, "refreshAccessToken");
2839
+ }
2840
+ if (!meta.revocationRequest) {
2841
+ Reflect.deleteProperty(client, "resolveRevocationInput");
2842
+ Reflect.deleteProperty(client, "revokeToken");
2843
+ }
2844
+ return client;
2687
2845
  };
2688
2846
  var createCustomOAuth2Client = (providerConfig, credentials) => buildOAuth2Client(providerConfig, credentials);
2689
2847
  var createOAuth2Client = (providerName, config) => buildOAuth2Client(providers[providerName], config);
@@ -2706,5 +2864,5 @@ export {
2706
2864
  decodeJWT
2707
2865
  };
2708
2866
 
2709
- //# debugId=902C18A45F6C2D7A64756E2164756E21
2867
+ //# debugId=8F4DD1C7459EFF1964756E2164756E21
2710
2868
  //# sourceMappingURL=index.js.map