@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.
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/index.js +66 -35
- package/dist/agents/index.js.map +3 -3
- package/dist/agents/postgresStores.d.ts +586 -1
- package/dist/cli/migrate.js +152 -68
- package/dist/cli/migrate.js.map +6 -6
- package/dist/htmx/index.js +269 -111
- package/dist/htmx/index.js.map +3 -3
- package/dist/index.d.ts +6 -5
- package/dist/index.js +405 -168
- package/dist/index.js.map +12 -12
- package/dist/linkedProviders/index.js +269 -111
- package/dist/linkedProviders/index.js.map +3 -3
- package/dist/providers/clients.d.ts +30 -17
- package/dist/providers/index.js +269 -111
- package/dist/providers/index.js.map +3 -3
- package/dist/routes/profile.d.ts +6 -3
- package/dist/routes/refresh.d.ts +1 -1
- package/dist/routes/revoke.d.ts +1 -1
- package/dist/server.js +399 -168
- package/dist/server.js.map +12 -12
- package/dist/session/neonStore.d.ts +60 -0
- package/dist/types.d.ts +6 -1
- package/dist/utils.d.ts +1 -1
- package/package.json +2 -2
package/dist/htmx/index.js
CHANGED
|
@@ -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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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: ["
|
|
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: "
|
|
1841
|
-
body:
|
|
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
|
-
|
|
1855
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
2191
|
+
var getWithingsSignatureParams = async (config, action) => {
|
|
2162
2192
|
const timestamp = Math.floor(Date.now() / 1000);
|
|
2163
|
-
const
|
|
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",
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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() /
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
2566
|
-
if (
|
|
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(
|
|
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
|
-
|
|
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 =
|
|
2750
|
+
const clientSecret = await resolveClientSecret();
|
|
2596
2751
|
let request;
|
|
2597
2752
|
if (authIn === "body") {
|
|
2598
|
-
revocationBody
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
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:
|
|
2761
|
+
authIn: hasAuthorizationHeader || !includeClientCredentials ? "query" : "body",
|
|
2762
|
+
body: bodyWithToken,
|
|
2605
2763
|
clientId,
|
|
2606
2764
|
clientSecret,
|
|
2607
|
-
encoding
|
|
2765
|
+
encoding,
|
|
2608
2766
|
headers: revocationHeaders,
|
|
2609
2767
|
url: endpoint.toString()
|
|
2610
2768
|
});
|
|
2611
2769
|
} else if (authIn === "header") {
|
|
2612
|
-
revocationHeaders.set("Authorization", `Bearer ${
|
|
2770
|
+
revocationHeaders.set("Authorization", `Bearer ${String(input)}`);
|
|
2613
2771
|
request = createOAuth2Request({
|
|
2614
|
-
authIn: "
|
|
2772
|
+
authIn: "query",
|
|
2615
2773
|
body: revocationBody,
|
|
2616
2774
|
clientId,
|
|
2617
|
-
|
|
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
|
-
|
|
2628
|
-
encoding: "application/x-www-form-urlencoded",
|
|
2786
|
+
encoding,
|
|
2629
2787
|
headers: revocationHeaders,
|
|
2630
|
-
url:
|
|
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:
|
|
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
|
-
|
|
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);
|
|
@@ -2773,5 +2931,5 @@ export {
|
|
|
2773
2931
|
escapeHtml
|
|
2774
2932
|
};
|
|
2775
2933
|
|
|
2776
|
-
//# debugId=
|
|
2934
|
+
//# debugId=6426F711574B25B164756E2164756E21
|
|
2777
2935
|
//# sourceMappingURL=index.js.map
|