@absolutejs/auth 0.57.6 → 0.57.8
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 +4 -0
- package/dist/agents/index.js.map +6 -6
- package/dist/cli/migrate.js +141 -66
- package/dist/cli/migrate.js.map +13 -13
- 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 +360 -144
- package/dist/index.js.map +23 -23
- package/dist/linkedProviders/index.js +269 -111
- package/dist/linkedProviders/index.js.map +3 -3
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/runner.d.ts +11 -2
- package/dist/oidc/index.js.map +2 -2
- 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 +360 -144
- package/dist/server.js.map +24 -24
- package/dist/session/neonStore.d.ts +60 -0
- package/dist/types.d.ts +6 -1
- package/dist/utils.d.ts +1 -1
- package/dist/vault/index.js.map +2 -2
- package/package.json +59 -21
package/dist/server.js
CHANGED
|
@@ -1363,17 +1363,23 @@ var providers = defineProviders({
|
|
|
1363
1363
|
},
|
|
1364
1364
|
apple: {
|
|
1365
1365
|
authorizationUrl: "https://appleid.apple.com/auth/authorize",
|
|
1366
|
+
createAuthorizationURLSearchParams: {
|
|
1367
|
+
response_mode: "form_post"
|
|
1368
|
+
},
|
|
1369
|
+
createClientSecret: (config) => createAppleClientSecret(config),
|
|
1366
1370
|
isOIDC: true,
|
|
1367
1371
|
isRefreshable: true,
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
url: "https://appleid.apple.com/auth/userinfo"
|
|
1372
|
+
revocationRequest: {
|
|
1373
|
+
authIn: "body",
|
|
1374
|
+
encoding: "application/x-www-form-urlencoded",
|
|
1375
|
+
tokenParamName: "token",
|
|
1376
|
+
url: "https://appleid.apple.com/auth/revoke"
|
|
1374
1377
|
},
|
|
1375
1378
|
scopeRequired: false,
|
|
1376
|
-
subject: ["
|
|
1379
|
+
subject: ["sub"],
|
|
1380
|
+
subjectBySource: {
|
|
1381
|
+
idToken: ["sub"]
|
|
1382
|
+
},
|
|
1377
1383
|
subjectType: "string",
|
|
1378
1384
|
tokenRequest: {
|
|
1379
1385
|
authIn: "body",
|
|
@@ -1442,6 +1448,7 @@ var providers = defineProviders({
|
|
|
1442
1448
|
token_type_hint: "refresh_token"
|
|
1443
1449
|
}),
|
|
1444
1450
|
encoding: "application/json",
|
|
1451
|
+
inputSource: "refreshToken",
|
|
1445
1452
|
tokenParamName: "token",
|
|
1446
1453
|
url: (config) => `https://${config.domain}/oauth/revoke`
|
|
1447
1454
|
},
|
|
@@ -2573,6 +2580,7 @@ var providers = defineProviders({
|
|
|
2573
2580
|
token_type_hint: "refresh_token"
|
|
2574
2581
|
}),
|
|
2575
2582
|
encoding: "application/json",
|
|
2583
|
+
inputSource: "refreshToken",
|
|
2576
2584
|
url: "https://www.reddit.com/api/v1/revoke_token"
|
|
2577
2585
|
},
|
|
2578
2586
|
scopeRequired: true,
|
|
@@ -2946,49 +2954,27 @@ var providers = defineProviders({
|
|
|
2946
2954
|
authorizationUrl: "https://account.withings.com/oauth2_user/authorize2",
|
|
2947
2955
|
isOIDC: false,
|
|
2948
2956
|
isRefreshable: true,
|
|
2949
|
-
profileRequest: {
|
|
2950
|
-
authIn: "header",
|
|
2951
|
-
body: async (config) => {
|
|
2952
|
-
const props = await getWithingsProps(config);
|
|
2953
|
-
if (props === undefined)
|
|
2954
|
-
throw new Error("Failed to get Withings search properties");
|
|
2955
|
-
const { nonce, hashedSignature } = props;
|
|
2956
|
-
return [
|
|
2957
|
-
["action", "getuser"],
|
|
2958
|
-
["nonce", nonce],
|
|
2959
|
-
["client_id", config.clientId],
|
|
2960
|
-
["signature", hashedSignature]
|
|
2961
|
-
];
|
|
2962
|
-
},
|
|
2963
|
-
encoding: "application/x-www-form-urlencoded",
|
|
2964
|
-
method: "POST",
|
|
2965
|
-
url: "https://wbsapi.withings.net/v2/oauth2"
|
|
2966
|
-
},
|
|
2967
2957
|
refreshAccessTokenBody: {
|
|
2968
2958
|
action: "requesttoken"
|
|
2969
2959
|
},
|
|
2970
2960
|
revocationRequest: {
|
|
2971
|
-
authIn: "
|
|
2972
|
-
body:
|
|
2973
|
-
const props = await getWithingsProps(config);
|
|
2974
|
-
if (!props)
|
|
2975
|
-
throw new Error("Failed to get Withings props");
|
|
2976
|
-
const { nonce, hashedSignature } = props;
|
|
2977
|
-
return [
|
|
2978
|
-
["action", "revoke"],
|
|
2979
|
-
["client_id", config.clientId],
|
|
2980
|
-
["nonce", nonce],
|
|
2981
|
-
["signature", hashedSignature]
|
|
2982
|
-
];
|
|
2983
|
-
},
|
|
2961
|
+
authIn: "body",
|
|
2962
|
+
body: (config) => getWithingsSignatureParams(config, "revoke"),
|
|
2984
2963
|
encoding: "application/x-www-form-urlencoded",
|
|
2985
|
-
|
|
2986
|
-
|
|
2964
|
+
includeClientCredentials: false,
|
|
2965
|
+
inputSource: "subject",
|
|
2966
|
+
inputType: "number",
|
|
2967
|
+
tokenParamName: "userid",
|
|
2968
|
+
url: "https://wbsapi.withings.net/v2/oauth2",
|
|
2969
|
+
validateResponse: (value) => assertWithingsSuccess(value)
|
|
2987
2970
|
},
|
|
2988
2971
|
scopeDelimiter: ",",
|
|
2989
2972
|
scopeRequired: true,
|
|
2990
2973
|
subject: ["userid"],
|
|
2991
|
-
|
|
2974
|
+
subjectBySource: {
|
|
2975
|
+
tokenResponse: ["body", "userid"]
|
|
2976
|
+
},
|
|
2977
|
+
subjectType: "number",
|
|
2992
2978
|
tokenRequest: {
|
|
2993
2979
|
authIn: "body",
|
|
2994
2980
|
encoding: "application/x-www-form-urlencoded",
|
|
@@ -3180,14 +3166,39 @@ var isPKCEProviderOption = (option) => {
|
|
|
3180
3166
|
const provider = providers[option];
|
|
3181
3167
|
return provider.PKCEMethod !== undefined;
|
|
3182
3168
|
};
|
|
3183
|
-
|
|
3169
|
+
function isProfileOAuth2Client(providerOrClient, maybeClient) {
|
|
3170
|
+
const client = maybeClient ?? providerOrClient;
|
|
3171
|
+
if (maybeClient !== undefined && (typeof providerOrClient !== "string" || !isProfileProviderOption(providerOrClient))) {
|
|
3172
|
+
return false;
|
|
3173
|
+
}
|
|
3174
|
+
return typeof client === "object" && client !== null && "fetchUserProfile" in client && typeof client.fetchUserProfile === "function";
|
|
3175
|
+
}
|
|
3176
|
+
var isProfileProviderOption = (option) => {
|
|
3177
|
+
if (!isValidProviderOption(option))
|
|
3178
|
+
return false;
|
|
3179
|
+
const provider = providers[option];
|
|
3180
|
+
return provider.profileRequest !== undefined;
|
|
3181
|
+
};
|
|
3182
|
+
function isRefreshableOAuth2Client(providerOrClient, maybeClient) {
|
|
3183
|
+
const client = maybeClient ?? providerOrClient;
|
|
3184
|
+
if (maybeClient !== undefined && (typeof providerOrClient !== "string" || !isRefreshableProviderOption(providerOrClient))) {
|
|
3185
|
+
return false;
|
|
3186
|
+
}
|
|
3187
|
+
return typeof client === "object" && client !== null && "refreshAccessToken" in client && typeof client.refreshAccessToken === "function";
|
|
3188
|
+
}
|
|
3184
3189
|
var isRefreshableProviderOption = (option) => {
|
|
3185
3190
|
if (!isValidProviderOption(option))
|
|
3186
3191
|
return false;
|
|
3187
3192
|
const provider = providers[option];
|
|
3188
3193
|
return provider.isRefreshable;
|
|
3189
3194
|
};
|
|
3190
|
-
|
|
3195
|
+
function isRevocableOAuth2Client(providerOrClient, maybeClient) {
|
|
3196
|
+
const client = maybeClient ?? providerOrClient;
|
|
3197
|
+
if (maybeClient !== undefined && (typeof providerOrClient !== "string" || !isRevocableProviderOption(providerOrClient))) {
|
|
3198
|
+
return false;
|
|
3199
|
+
}
|
|
3200
|
+
return typeof client === "object" && client !== null && "resolveRevocationInput" in client && typeof client.resolveRevocationInput === "function" && "revokeToken" in client && typeof client.revokeToken === "function";
|
|
3201
|
+
}
|
|
3191
3202
|
var isRevocableProviderOption = (option) => {
|
|
3192
3203
|
if (!isValidProviderOption(option))
|
|
3193
3204
|
return false;
|
|
@@ -3201,6 +3212,14 @@ var isScopeRequiredProviderOption = (option) => {
|
|
|
3201
3212
|
return provider.scopeRequired;
|
|
3202
3213
|
};
|
|
3203
3214
|
var isValidProviderOption = (option) => Object.hasOwn(providers, option);
|
|
3215
|
+
var readPath = (value, path) => path.reduce((cursor, key) => cursor && typeof cursor === "object" ? Reflect.get(cursor, key) : undefined, value);
|
|
3216
|
+
var assertWithingsSuccess = (value) => {
|
|
3217
|
+
if (!isObject(value) || value.status !== 0) {
|
|
3218
|
+
const status = isObject(value) ? value.status : "invalid response";
|
|
3219
|
+
const detail = isObject(value) && typeof value.error === "string" ? `: ${value.error}` : "";
|
|
3220
|
+
throw new Error(`Withings request failed (${String(status)})${detail}`);
|
|
3221
|
+
}
|
|
3222
|
+
};
|
|
3204
3223
|
var createOAuth2FetchError = async (response) => {
|
|
3205
3224
|
const clone = response.clone();
|
|
3206
3225
|
const prefix = `HTTP ${response.status} ${response.statusText} for ${response.url}`;
|
|
@@ -3234,16 +3253,27 @@ var createOAuth2Request = ({
|
|
|
3234
3253
|
}
|
|
3235
3254
|
oauthHeaders.set("Authorization", `Basic ${encodeBase64(`${clientId}:${clientSecret}`)}`);
|
|
3236
3255
|
}
|
|
3256
|
+
if (body === undefined && authIn !== "body") {
|
|
3257
|
+
return new Request(url, {
|
|
3258
|
+
headers: oauthHeaders,
|
|
3259
|
+
method: "POST"
|
|
3260
|
+
});
|
|
3261
|
+
}
|
|
3237
3262
|
if (encoding === "application/json") {
|
|
3238
3263
|
oauthHeaders.set("Content-Type", "application/json");
|
|
3264
|
+
const jsonBody = body instanceof URLSearchParams ? Object.fromEntries(body.entries()) : { ...body };
|
|
3265
|
+
if (authIn === "body")
|
|
3266
|
+
jsonBody.client_id = clientId;
|
|
3267
|
+
if (authIn === "body" && clientSecret)
|
|
3268
|
+
jsonBody.client_secret = clientSecret;
|
|
3239
3269
|
return new Request(url, {
|
|
3240
|
-
body: JSON.stringify(
|
|
3270
|
+
body: JSON.stringify(jsonBody),
|
|
3241
3271
|
headers: oauthHeaders,
|
|
3242
3272
|
method: "POST"
|
|
3243
3273
|
});
|
|
3244
3274
|
}
|
|
3245
3275
|
oauthHeaders.set("Content-Type", "application/x-www-form-urlencoded");
|
|
3246
|
-
const entries = body instanceof URLSearchParams ? Array.from(body.entries()) : Object.entries(body).filter((entry) => typeof entry[1] === "string");
|
|
3276
|
+
const entries = body instanceof URLSearchParams ? Array.from(body.entries()) : Object.entries(body ?? {}).filter((entry) => typeof entry[1] === "string");
|
|
3247
3277
|
const params = new URLSearchParams(entries);
|
|
3248
3278
|
if (authIn === "body") {
|
|
3249
3279
|
params.set("client_id", clientId);
|
|
@@ -3289,25 +3319,31 @@ var encodeBase64 = (input) => {
|
|
|
3289
3319
|
}
|
|
3290
3320
|
return btoa(raw);
|
|
3291
3321
|
};
|
|
3292
|
-
var
|
|
3322
|
+
var getWithingsSignatureParams = async (config, action) => {
|
|
3293
3323
|
const timestamp = Math.floor(Date.now() / 1000);
|
|
3294
|
-
const
|
|
3295
|
-
const hashedSignature = await hmacSha256(signature, config.clientSecret);
|
|
3324
|
+
const nonceSignature = await hmacSha256(`getnonce,${config.clientId},${timestamp}`, config.clientSecret);
|
|
3296
3325
|
const nonceUrl = new URL("https://wbsapi.withings.net/v2/signature");
|
|
3297
3326
|
nonceUrl.searchParams.set("action", "getnonce");
|
|
3298
3327
|
nonceUrl.searchParams.set("client_id", config.clientId);
|
|
3299
3328
|
nonceUrl.searchParams.set("timestamp", timestamp.toString());
|
|
3300
|
-
nonceUrl.searchParams.set("signature",
|
|
3329
|
+
nonceUrl.searchParams.set("signature", nonceSignature);
|
|
3301
3330
|
const nonceTarget = nonceUrl.toString();
|
|
3302
3331
|
const nonceResponse = await fetch(nonceTarget, { method: "POST" });
|
|
3332
|
+
if (!nonceResponse.ok) {
|
|
3333
|
+
throw await createOAuth2FetchError(nonceResponse);
|
|
3334
|
+
}
|
|
3303
3335
|
const nonceData = await nonceResponse.json();
|
|
3304
|
-
if (nonceData.status === 0) {
|
|
3305
|
-
|
|
3306
|
-
hashedSignature,
|
|
3307
|
-
nonce: nonceData.body.nonce
|
|
3308
|
-
};
|
|
3336
|
+
if (!isObject(nonceData) || nonceData.status !== 0 || !isObject(nonceData.body) || typeof nonceData.body.nonce !== "string" || nonceData.body.nonce.length === 0) {
|
|
3337
|
+
throw new Error("Withings returned an invalid nonce response");
|
|
3309
3338
|
}
|
|
3310
|
-
|
|
3339
|
+
const { nonce } = nonceData.body;
|
|
3340
|
+
const signature = await hmacSha256(`${action},${config.clientId},${nonce}`, config.clientSecret);
|
|
3341
|
+
return {
|
|
3342
|
+
action,
|
|
3343
|
+
client_id: config.clientId,
|
|
3344
|
+
nonce,
|
|
3345
|
+
signature
|
|
3346
|
+
};
|
|
3311
3347
|
};
|
|
3312
3348
|
var hmacSha256 = async (message, secret) => {
|
|
3313
3349
|
const encoder = new TextEncoder;
|
|
@@ -3315,15 +3351,53 @@ var hmacSha256 = async (message, secret) => {
|
|
|
3315
3351
|
const sigBuffer = await crypto.subtle.sign("HMAC", key, encoder.encode(message));
|
|
3316
3352
|
return Array.from(new Uint8Array(sigBuffer)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
3317
3353
|
};
|
|
3354
|
+
var parseOAuth2TokenResponse = (value, accessTokenPath) => {
|
|
3355
|
+
if (!isObject(value)) {
|
|
3356
|
+
throw new Error("OAuth token endpoint returned a non-object response");
|
|
3357
|
+
}
|
|
3358
|
+
const oauthError = Reflect.get(value, "error");
|
|
3359
|
+
if (typeof oauthError === "string" && oauthError.length > 0) {
|
|
3360
|
+
throw new Error(`OAuth token exchange failed: ${oauthError}`);
|
|
3361
|
+
}
|
|
3362
|
+
const response = { ...value };
|
|
3363
|
+
const nestedToken = accessTokenPath ? readPath(value, accessTokenPath) : undefined;
|
|
3364
|
+
if (typeof nestedToken === "string" && nestedToken.length > 0) {
|
|
3365
|
+
response.access_token = nestedToken;
|
|
3366
|
+
}
|
|
3367
|
+
if (typeof response.access_token !== "string" || response.access_token.length === 0) {
|
|
3368
|
+
throw new Error("OAuth token endpoint returned no access_token");
|
|
3369
|
+
}
|
|
3370
|
+
for (const key of ["refresh_token", "token_type", "scope", "id_token"]) {
|
|
3371
|
+
const field = response[key];
|
|
3372
|
+
if (field !== undefined && typeof field !== "string") {
|
|
3373
|
+
throw new Error(`OAuth token endpoint returned invalid ${key}: expected string`);
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
const expiresIn = response.expires_in;
|
|
3377
|
+
if (typeof expiresIn === "string" && expiresIn.trim() !== "") {
|
|
3378
|
+
response.expires_in = Number(expiresIn);
|
|
3379
|
+
}
|
|
3380
|
+
if (response.expires_in !== undefined && (typeof response.expires_in !== "number" || !Number.isFinite(response.expires_in) || response.expires_in < 0)) {
|
|
3381
|
+
throw new Error("OAuth token endpoint returned invalid expires_in: expected a non-negative number");
|
|
3382
|
+
}
|
|
3383
|
+
return response;
|
|
3384
|
+
};
|
|
3385
|
+
var readIdentityKey = (value, key) => {
|
|
3386
|
+
if (Array.isArray(value)) {
|
|
3387
|
+
if (!/^\d+$/.test(key)) {
|
|
3388
|
+
throw new Error(`Invalid identity data shape: expected an array index, got ${key}`);
|
|
3389
|
+
}
|
|
3390
|
+
return value[Number(key)];
|
|
3391
|
+
}
|
|
3392
|
+
if (!isObject(value)) {
|
|
3393
|
+
throw new Error(`Invalid identity data shape: expected object, got ${typeof value}`);
|
|
3394
|
+
}
|
|
3395
|
+
return value[key];
|
|
3396
|
+
};
|
|
3318
3397
|
var extractPropFromIdentity = (identity, keys, propType) => {
|
|
3319
3398
|
let value = identity;
|
|
3320
3399
|
for (const key of keys) {
|
|
3321
|
-
|
|
3322
|
-
value = value[Number(key)];
|
|
3323
|
-
if (!isObject(value)) {
|
|
3324
|
-
throw new Error(`Invalid identity data shape: expected object, got ${typeof value}`);
|
|
3325
|
-
}
|
|
3326
|
-
value = value[key];
|
|
3400
|
+
value = readIdentityKey(value, key);
|
|
3327
3401
|
}
|
|
3328
3402
|
if (propType !== undefined && !isExpectedType(value, propType)) {
|
|
3329
3403
|
throw new Error(`Invalid identity data shape: expected ${propType}, got ${typeof value}`);
|
|
@@ -3358,6 +3432,13 @@ var normalizeProviderIdentity = ({
|
|
|
3358
3432
|
const normalizedIdentity = structuredClone(identity);
|
|
3359
3433
|
return setPropInIdentity(normalizedIdentity, canonicalKeys, subject);
|
|
3360
3434
|
};
|
|
3435
|
+
var DAYS_IN_APPLE_CLIENT_SECRET_LIFETIME = 180;
|
|
3436
|
+
var HOURS_PER_DAY = 24;
|
|
3437
|
+
var MINUTES_PER_HOUR = 60;
|
|
3438
|
+
var SECONDS_PER_MINUTE = 60;
|
|
3439
|
+
var APPLE_CLIENT_SECRET_LIFETIME_SECONDS = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * DAYS_IN_APPLE_CLIENT_SECRET_LIFETIME;
|
|
3440
|
+
var APPLE_ISSUER = "https://appleid.apple.com";
|
|
3441
|
+
var MILLISECONDS_PER_SECOND = 1000;
|
|
3361
3442
|
var createS256CodeChallenge = async (codeVerifier) => {
|
|
3362
3443
|
const data = new TextEncoder().encode(codeVerifier);
|
|
3363
3444
|
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
@@ -3370,20 +3451,40 @@ var createRandomBase64UrlGenerator = (length) => () => {
|
|
|
3370
3451
|
var generateCodeVerifier = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);
|
|
3371
3452
|
var generateState = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);
|
|
3372
3453
|
var base64Url = (input) => encodeBase64(input).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
3454
|
+
var encodeJwtPart = (value) => base64Url(new TextEncoder().encode(JSON.stringify(value)));
|
|
3455
|
+
var createAppleClientSecret = async (credentials) => {
|
|
3456
|
+
const issuedAt = Math.floor(Date.now() / MILLISECONDS_PER_SECOND);
|
|
3457
|
+
const header = encodeJwtPart({
|
|
3458
|
+
alg: "ES256",
|
|
3459
|
+
kid: credentials.keyId,
|
|
3460
|
+
typ: "JWT"
|
|
3461
|
+
});
|
|
3462
|
+
const payload = encodeJwtPart({
|
|
3463
|
+
aud: APPLE_ISSUER,
|
|
3464
|
+
exp: issuedAt + APPLE_CLIENT_SECRET_LIFETIME_SECONDS,
|
|
3465
|
+
iat: issuedAt,
|
|
3466
|
+
iss: credentials.teamId,
|
|
3467
|
+
sub: credentials.clientId
|
|
3468
|
+
});
|
|
3469
|
+
const signingInput = `${header}.${payload}`;
|
|
3470
|
+
const privateKey = await crypto.subtle.importKey("pkcs8", Uint8Array.from(credentials.pkcs8PrivateKey), { name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
|
|
3471
|
+
const signature = await crypto.subtle.sign({ hash: "SHA-256", name: "ECDSA" }, privateKey, new TextEncoder().encode(signingInput));
|
|
3472
|
+
return `${signingInput}.${base64Url(signature)}`;
|
|
3473
|
+
};
|
|
3373
3474
|
var ALG_ES256 = "ES256";
|
|
3374
3475
|
var ALG_RS256 = "RS256";
|
|
3375
3476
|
var CLOCK_SKEW_SECONDS = 60;
|
|
3376
3477
|
var DEFAULT_SCOPES = ["openid", "email", "profile"];
|
|
3377
3478
|
var JWKS_REFETCH_COOLDOWN_MS = 60000;
|
|
3378
3479
|
var JWT_SEGMENT_COUNT = 3;
|
|
3379
|
-
var
|
|
3480
|
+
var MILLISECONDS_PER_SECOND2 = 1000;
|
|
3380
3481
|
var trimTrailingSlash = (value) => value.endsWith("/") ? value.slice(0, -1) : value;
|
|
3381
3482
|
var fetchJson = async (url) => {
|
|
3382
3483
|
const response = await fetch(url, {
|
|
3383
3484
|
headers: { accept: "application/json" }
|
|
3384
3485
|
});
|
|
3385
3486
|
if (!response.ok) {
|
|
3386
|
-
throw
|
|
3487
|
+
throw await createOAuth2FetchError(response);
|
|
3387
3488
|
}
|
|
3388
3489
|
return response.json();
|
|
3389
3490
|
};
|
|
@@ -3414,11 +3515,14 @@ var verifySignature = (key, alg, signingInput, signature) => {
|
|
|
3414
3515
|
var selectKey = (jwks, kid) => jwks.find((jwk) => kid === undefined || jwk.kid === kid);
|
|
3415
3516
|
var assertClaims = (payload, expected) => {
|
|
3416
3517
|
const aud = Reflect.get(payload, "aud");
|
|
3518
|
+
const azp = Reflect.get(payload, "azp");
|
|
3417
3519
|
const exp = Reflect.get(payload, "exp");
|
|
3520
|
+
const iat = Reflect.get(payload, "iat");
|
|
3418
3521
|
const iss = Reflect.get(payload, "iss");
|
|
3522
|
+
const nbf = Reflect.get(payload, "nbf");
|
|
3419
3523
|
const sub = Reflect.get(payload, "sub");
|
|
3420
3524
|
const audiences = Array.isArray(aud) ? aud : [aud];
|
|
3421
|
-
const nowSeconds = Math.floor(Date.now() /
|
|
3525
|
+
const nowSeconds = Math.floor(Date.now() / MILLISECONDS_PER_SECOND2);
|
|
3422
3526
|
if (iss !== expected.issuer) {
|
|
3423
3527
|
throw new Error('id_token "iss" does not match the provider issuer');
|
|
3424
3528
|
}
|
|
@@ -3428,9 +3532,24 @@ var assertClaims = (payload, expected) => {
|
|
|
3428
3532
|
if (!audiences.includes(expected.audience)) {
|
|
3429
3533
|
throw new Error('id_token "aud" does not include the client id');
|
|
3430
3534
|
}
|
|
3535
|
+
if (audiences.some((audience) => typeof audience !== "string") || typeof aud !== "string" && !Array.isArray(aud)) {
|
|
3536
|
+
throw new Error('id_token "aud" must be a string or string array');
|
|
3537
|
+
}
|
|
3538
|
+
if (audiences.length > 1 && (typeof azp !== "string" || azp !== expected.audience)) {
|
|
3539
|
+
throw new Error('id_token with multiple audiences requires matching "azp"');
|
|
3540
|
+
}
|
|
3431
3541
|
if (typeof exp !== "number" || exp + CLOCK_SKEW_SECONDS < nowSeconds) {
|
|
3432
3542
|
throw new Error("id_token has expired");
|
|
3433
3543
|
}
|
|
3544
|
+
if (typeof iat !== "number") {
|
|
3545
|
+
throw new Error('id_token is missing numeric "iat"');
|
|
3546
|
+
}
|
|
3547
|
+
if (iat > nowSeconds + CLOCK_SKEW_SECONDS) {
|
|
3548
|
+
throw new Error('id_token "iat" is in the future');
|
|
3549
|
+
}
|
|
3550
|
+
if (nbf !== undefined && (typeof nbf !== "number" || nbf > nowSeconds + CLOCK_SKEW_SECONDS)) {
|
|
3551
|
+
throw new Error("id_token is not active yet");
|
|
3552
|
+
}
|
|
3434
3553
|
if (expected.nonce !== undefined && Reflect.get(payload, "nonce") !== expected.nonce) {
|
|
3435
3554
|
throw new Error('id_token "nonce" does not match');
|
|
3436
3555
|
}
|
|
@@ -3557,9 +3676,9 @@ var createOIDCClient = async (config) => {
|
|
|
3557
3676
|
method: "POST"
|
|
3558
3677
|
});
|
|
3559
3678
|
if (!response.ok) {
|
|
3560
|
-
throw
|
|
3679
|
+
throw await createOAuth2FetchError(response);
|
|
3561
3680
|
}
|
|
3562
|
-
const tokens = await response.json();
|
|
3681
|
+
const tokens = parseOAuth2TokenResponse(await response.json());
|
|
3563
3682
|
return tokens;
|
|
3564
3683
|
};
|
|
3565
3684
|
const verifyToken = async (idToken, options) => {
|
|
@@ -3605,20 +3724,26 @@ var createOIDCClient = async (config) => {
|
|
|
3605
3724
|
};
|
|
3606
3725
|
var oidcProviderOptions = Object.keys(providers).filter(isOIDCProviderOption);
|
|
3607
3726
|
var pkceProviderOptions = Object.keys(providers).filter(isPKCEProviderOption);
|
|
3727
|
+
var profileProviderOptions = Object.keys(providers).filter(isProfileProviderOption);
|
|
3608
3728
|
var providerOptions = Object.keys(providers).filter(isValidProviderOption);
|
|
3609
3729
|
var refreshableProviderOptions = Object.keys(providers).filter(isRefreshableProviderOption);
|
|
3610
3730
|
var revocableProviderOptions = Object.keys(providers).filter(isRevocableProviderOption);
|
|
3611
3731
|
var scopeRequiredProviderOptions = Object.keys(providers).filter(isScopeRequiredProviderOption);
|
|
3612
|
-
var readPath = (value, path) => path.reduce((cursor, key) => cursor && typeof cursor === "object" ? Reflect.get(cursor, key) : undefined, value);
|
|
3613
3732
|
var buildOAuth2Client = async (meta, config) => {
|
|
3614
3733
|
const isConfigPropertyFunction = (cfgProp) => typeof cfgProp === "function";
|
|
3615
3734
|
const resolveConfigProp = async (cfgProp) => {
|
|
3616
3735
|
const result = isConfigPropertyFunction(cfgProp) ? cfgProp(config) : cfgProp;
|
|
3617
3736
|
return result;
|
|
3618
3737
|
};
|
|
3738
|
+
const resolveClientSecret = async () => {
|
|
3739
|
+
if (meta.createClientSecret) {
|
|
3740
|
+
return resolveConfigProp(meta.createClientSecret);
|
|
3741
|
+
}
|
|
3742
|
+
return hasClientSecret(config) ? config.clientSecret : undefined;
|
|
3743
|
+
};
|
|
3619
3744
|
const authorizationUrl = await resolveConfigProp(meta.authorizationUrl);
|
|
3620
3745
|
const tokenUrl = await resolveConfigProp(meta.tokenRequest.url);
|
|
3621
|
-
|
|
3746
|
+
const client = {
|
|
3622
3747
|
async createAuthorizationUrl(opts) {
|
|
3623
3748
|
const { state, scope = [], searchParams = [], codeVerifier } = opts;
|
|
3624
3749
|
const url = new URL(authorizationUrl);
|
|
@@ -3639,7 +3764,7 @@ var buildOAuth2Client = async (meta, config) => {
|
|
|
3639
3764
|
url.searchParams.set("code_challenge_method", meta.PKCEMethod);
|
|
3640
3765
|
url.searchParams.set("code_challenge", codeChallenge);
|
|
3641
3766
|
}
|
|
3642
|
-
Object.entries(resolveConfigProp(meta.createAuthorizationURLSearchParams) ?? {}).forEach(([key, value]) => url.searchParams.set(key, value));
|
|
3767
|
+
Object.entries(await resolveConfigProp(meta.createAuthorizationURLSearchParams) ?? {}).forEach(([key, value]) => url.searchParams.set(key, value));
|
|
3643
3768
|
searchParams.forEach(([key, value]) => url.searchParams.set(key, value));
|
|
3644
3769
|
return url;
|
|
3645
3770
|
},
|
|
@@ -3672,7 +3797,7 @@ var buildOAuth2Client = async (meta, config) => {
|
|
|
3672
3797
|
if (authIn === "header") {
|
|
3673
3798
|
profileHeaders.Authorization = `Bearer ${accessToken}`;
|
|
3674
3799
|
} else if (authIn === "path") {
|
|
3675
|
-
endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, "")}/${accessToken}`;
|
|
3800
|
+
endpoint.pathname = `${endpoint.pathname.replace(/\/+$/, "")}/${encodeURIComponent(accessToken)}`;
|
|
3676
3801
|
} else {
|
|
3677
3802
|
endpoint.searchParams.append("access_token", accessToken);
|
|
3678
3803
|
}
|
|
@@ -3693,9 +3818,8 @@ var buildOAuth2Client = async (meta, config) => {
|
|
|
3693
3818
|
params.set("grant_type", "refresh_token");
|
|
3694
3819
|
params.set("refresh_token", refreshToken);
|
|
3695
3820
|
const { clientId } = config;
|
|
3696
|
-
|
|
3697
|
-
if (
|
|
3698
|
-
clientSecretValue = config.clientSecret;
|
|
3821
|
+
const clientSecretValue = await resolveClientSecret();
|
|
3822
|
+
if (clientSecretValue) {
|
|
3699
3823
|
params.set("client_id", clientId);
|
|
3700
3824
|
params.set("client_secret", clientSecretValue);
|
|
3701
3825
|
}
|
|
@@ -3710,60 +3834,99 @@ var buildOAuth2Client = async (meta, config) => {
|
|
|
3710
3834
|
const response = await fetch(request);
|
|
3711
3835
|
if (!response.ok)
|
|
3712
3836
|
throw await createOAuth2FetchError(response);
|
|
3713
|
-
return response.json();
|
|
3837
|
+
return parseOAuth2TokenResponse(await response.json());
|
|
3838
|
+
},
|
|
3839
|
+
resolveRevocationInput(context) {
|
|
3840
|
+
const { revocationRequest } = meta;
|
|
3841
|
+
if (!revocationRequest) {
|
|
3842
|
+
throw new Error("Token revocation not defined for this provider");
|
|
3843
|
+
}
|
|
3844
|
+
const inputSource = revocationRequest.inputSource ?? "accessToken";
|
|
3845
|
+
const input = context[inputSource];
|
|
3846
|
+
if (input === undefined) {
|
|
3847
|
+
throw new Error(`Revocation requires ${inputSource}, but it was not provided`);
|
|
3848
|
+
}
|
|
3849
|
+
const expectsNumber = revocationRequest.authIn !== "header" && revocationRequest.inputType === "number";
|
|
3850
|
+
if (expectsNumber && (typeof input !== "number" || !Number.isFinite(input))) {
|
|
3851
|
+
throw new TypeError("This provider requires a numeric revocation input");
|
|
3852
|
+
}
|
|
3853
|
+
if (!expectsNumber && typeof input !== "string") {
|
|
3854
|
+
throw new TypeError("This provider requires a string revocation input");
|
|
3855
|
+
}
|
|
3856
|
+
return input;
|
|
3714
3857
|
},
|
|
3715
|
-
async revokeToken(
|
|
3858
|
+
async revokeToken(input) {
|
|
3716
3859
|
const { revocationRequest } = meta;
|
|
3717
3860
|
if (!revocationRequest) {
|
|
3718
3861
|
throw new Error("Token revocation not defined for this provider");
|
|
3719
3862
|
}
|
|
3720
|
-
|
|
3863
|
+
if (revocationRequest.authIn !== "header" && revocationRequest.inputType === "number" && (typeof input !== "number" || !Number.isFinite(input))) {
|
|
3864
|
+
throw new TypeError("This provider requires a numeric revocation input");
|
|
3865
|
+
}
|
|
3866
|
+
const {
|
|
3867
|
+
url,
|
|
3868
|
+
authIn,
|
|
3869
|
+
body,
|
|
3870
|
+
encoding,
|
|
3871
|
+
headers,
|
|
3872
|
+
includeClientCredentials = true,
|
|
3873
|
+
tokenParamName,
|
|
3874
|
+
validateResponse
|
|
3875
|
+
} = revocationRequest;
|
|
3721
3876
|
const endpoint = await resolveConfigProp(url);
|
|
3722
3877
|
const resolvedBody = await resolveConfigProp(body);
|
|
3723
|
-
const revocationBody = new URLSearchParams(resolvedBody);
|
|
3878
|
+
const revocationBody = resolvedBody === undefined ? undefined : new URLSearchParams(resolvedBody);
|
|
3724
3879
|
const revocationHeaders = new Headers(headers && await resolveConfigProp(headers));
|
|
3725
3880
|
const { clientId } = config;
|
|
3726
|
-
const clientSecret =
|
|
3881
|
+
const clientSecret = await resolveClientSecret();
|
|
3727
3882
|
let request;
|
|
3728
3883
|
if (authIn === "body") {
|
|
3729
|
-
revocationBody
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3884
|
+
const bodyWithToken = revocationBody ?? new URLSearchParams;
|
|
3885
|
+
bodyWithToken.set(tokenParamName, String(input));
|
|
3886
|
+
const hasAuthorizationHeader = revocationHeaders.has("Authorization");
|
|
3887
|
+
if (includeClientCredentials && !hasAuthorizationHeader)
|
|
3888
|
+
bodyWithToken.set("client_id", clientId);
|
|
3889
|
+
if (includeClientCredentials && !hasAuthorizationHeader && clientSecret)
|
|
3890
|
+
bodyWithToken.set("client_secret", clientSecret);
|
|
3733
3891
|
request = createOAuth2Request({
|
|
3734
|
-
authIn: "body",
|
|
3735
|
-
body:
|
|
3892
|
+
authIn: hasAuthorizationHeader || !includeClientCredentials ? "query" : "body",
|
|
3893
|
+
body: bodyWithToken,
|
|
3736
3894
|
clientId,
|
|
3737
3895
|
clientSecret,
|
|
3738
|
-
encoding
|
|
3896
|
+
encoding,
|
|
3739
3897
|
headers: revocationHeaders,
|
|
3740
3898
|
url: endpoint.toString()
|
|
3741
3899
|
});
|
|
3742
3900
|
} else if (authIn === "header") {
|
|
3743
|
-
revocationHeaders.set("Authorization", `Bearer ${
|
|
3901
|
+
revocationHeaders.set("Authorization", `Bearer ${String(input)}`);
|
|
3744
3902
|
request = createOAuth2Request({
|
|
3745
|
-
authIn: "
|
|
3903
|
+
authIn: "query",
|
|
3746
3904
|
body: revocationBody,
|
|
3747
3905
|
clientId,
|
|
3748
|
-
|
|
3749
|
-
encoding: "application/x-www-form-urlencoded",
|
|
3906
|
+
encoding,
|
|
3750
3907
|
headers: revocationHeaders,
|
|
3751
3908
|
url: endpoint.toString()
|
|
3752
3909
|
});
|
|
3753
3910
|
} else {
|
|
3911
|
+
const queryEndpoint = new URL(endpoint);
|
|
3912
|
+
queryEndpoint.searchParams.set(tokenParamName, String(input));
|
|
3754
3913
|
request = createOAuth2Request({
|
|
3755
3914
|
authIn: "query",
|
|
3756
3915
|
body: revocationBody,
|
|
3757
3916
|
clientId,
|
|
3758
|
-
|
|
3759
|
-
encoding: "application/x-www-form-urlencoded",
|
|
3917
|
+
encoding,
|
|
3760
3918
|
headers: revocationHeaders,
|
|
3761
|
-
url:
|
|
3919
|
+
url: queryEndpoint.toString()
|
|
3762
3920
|
});
|
|
3763
3921
|
}
|
|
3764
3922
|
const response = await fetch(request);
|
|
3765
3923
|
if (!response.ok)
|
|
3766
3924
|
throw await createOAuth2FetchError(response);
|
|
3925
|
+
if (validateResponse) {
|
|
3926
|
+
await validateResponse(await response.json().catch(() => {
|
|
3927
|
+
return;
|
|
3928
|
+
}));
|
|
3929
|
+
}
|
|
3767
3930
|
},
|
|
3768
3931
|
async validateAuthorizationCode(opts) {
|
|
3769
3932
|
const { code, codeVerifier } = opts;
|
|
@@ -3789,32 +3952,27 @@ var buildOAuth2Client = async (meta, config) => {
|
|
|
3789
3952
|
authIn,
|
|
3790
3953
|
body: payload,
|
|
3791
3954
|
clientId: config.clientId,
|
|
3792
|
-
clientSecret:
|
|
3955
|
+
clientSecret: await resolveClientSecret(),
|
|
3793
3956
|
encoding,
|
|
3794
3957
|
url: tokenUrl
|
|
3795
3958
|
});
|
|
3796
3959
|
const response = await fetch(request);
|
|
3797
3960
|
if (!response.ok)
|
|
3798
3961
|
throw await createOAuth2FetchError(response);
|
|
3799
|
-
|
|
3800
|
-
if (!tokenResponse || typeof tokenResponse !== "object") {
|
|
3801
|
-
throw new Error("OAuth token endpoint returned a non-object response");
|
|
3802
|
-
}
|
|
3803
|
-
const oauthError = Reflect.get(tokenResponse, "error");
|
|
3804
|
-
if (typeof oauthError === "string" && oauthError.length > 0) {
|
|
3805
|
-
throw new Error(`OAuth token exchange failed: ${oauthError}`);
|
|
3806
|
-
}
|
|
3807
|
-
const nestedToken = meta.accessTokenPath ? readPath(tokenResponse, meta.accessTokenPath) : undefined;
|
|
3808
|
-
if (typeof nestedToken === "string" && nestedToken.length > 0 && tokenResponse && typeof tokenResponse === "object") {
|
|
3809
|
-
tokenResponse.access_token = nestedToken;
|
|
3810
|
-
}
|
|
3811
|
-
const accessToken = Reflect.get(tokenResponse, "access_token");
|
|
3812
|
-
if (typeof accessToken !== "string" || accessToken.length === 0) {
|
|
3813
|
-
throw new Error("OAuth token endpoint returned no access_token");
|
|
3814
|
-
}
|
|
3815
|
-
return tokenResponse;
|
|
3962
|
+
return parseOAuth2TokenResponse(await response.json(), meta.accessTokenPath);
|
|
3816
3963
|
}
|
|
3817
3964
|
};
|
|
3965
|
+
if (!meta.profileRequest) {
|
|
3966
|
+
Reflect.deleteProperty(client, "fetchUserProfile");
|
|
3967
|
+
}
|
|
3968
|
+
if (!meta.isRefreshable) {
|
|
3969
|
+
Reflect.deleteProperty(client, "refreshAccessToken");
|
|
3970
|
+
}
|
|
3971
|
+
if (!meta.revocationRequest) {
|
|
3972
|
+
Reflect.deleteProperty(client, "resolveRevocationInput");
|
|
3973
|
+
Reflect.deleteProperty(client, "revokeToken");
|
|
3974
|
+
}
|
|
3975
|
+
return client;
|
|
3818
3976
|
};
|
|
3819
3977
|
var createCustomOAuth2Client = (providerConfig, credentials) => buildOAuth2Client(providerConfig, credentials);
|
|
3820
3978
|
var createOAuth2Client = (providerName, config) => buildOAuth2Client(providers[providerName], config);
|
|
@@ -5572,7 +5730,15 @@ var instantiateUserSession = async ({
|
|
|
5572
5730
|
providerInstance,
|
|
5573
5731
|
tokenResponse
|
|
5574
5732
|
});
|
|
5575
|
-
const {
|
|
5733
|
+
const {
|
|
5734
|
+
accessToken,
|
|
5735
|
+
oauthSubject: resolvedOAuthSubject,
|
|
5736
|
+
refreshToken,
|
|
5737
|
+
userIdentity
|
|
5738
|
+
} = authorization;
|
|
5739
|
+
const providerMeta = providerConfiguration ?? (isValidProviderOption(authProvider) ? providers[authProvider] : undefined);
|
|
5740
|
+
const extractedSubject = providerMeta ? extractPropFromIdentity(userIdentity, providerMeta.subject, providerMeta.subjectType) : Reflect.get(userIdentity, "sub");
|
|
5741
|
+
const oauthSubject = resolvedOAuthSubject ?? (typeof extractedSubject === "string" || typeof extractedSubject === "number" ? extractedSubject : undefined);
|
|
5576
5742
|
const userSession = validateSession({ session, user_session_id });
|
|
5577
5743
|
const userSessionId = getUserSessionId({
|
|
5578
5744
|
cookieSecure,
|
|
@@ -5589,6 +5755,7 @@ var instantiateUserSession = async ({
|
|
|
5589
5755
|
accessToken,
|
|
5590
5756
|
authenticatedAt: Date.now(),
|
|
5591
5757
|
expiresAt: Date.now() + sessionDurationMs,
|
|
5758
|
+
oauthSubject,
|
|
5592
5759
|
refreshToken,
|
|
5593
5760
|
user
|
|
5594
5761
|
};
|
|
@@ -5598,6 +5765,7 @@ var instantiateUserSession = async ({
|
|
|
5598
5765
|
if (existingUnregistered) {
|
|
5599
5766
|
existingUnregistered.accessToken = accessToken;
|
|
5600
5767
|
existingUnregistered.expiresAt = Date.now() + unregisteredSessionDurationMs;
|
|
5768
|
+
existingUnregistered.oauthSubject = oauthSubject;
|
|
5601
5769
|
existingUnregistered.refreshToken = refreshToken;
|
|
5602
5770
|
existingUnregistered.userIdentity = userIdentity;
|
|
5603
5771
|
return response;
|
|
@@ -5605,6 +5773,7 @@ var instantiateUserSession = async ({
|
|
|
5605
5773
|
unregisteredSession[userSessionId] = {
|
|
5606
5774
|
accessToken,
|
|
5607
5775
|
expiresAt: Date.now() + unregisteredSessionDurationMs,
|
|
5776
|
+
oauthSubject,
|
|
5608
5777
|
refreshToken,
|
|
5609
5778
|
userIdentity
|
|
5610
5779
|
};
|
|
@@ -5653,6 +5822,9 @@ var resolveOAuthAuthorization = async ({
|
|
|
5653
5822
|
accessToken = readOptionalString(withingsBody, "access_token") ?? accessToken;
|
|
5654
5823
|
refreshToken = readOptionalString(withingsBody, "refresh_token") ?? refreshToken;
|
|
5655
5824
|
} else {
|
|
5825
|
+
if (!isProfileOAuth2Client(providerInstance)) {
|
|
5826
|
+
throw new Error(`Provider "${authProvider}" returned no identity and has no profile endpoint`);
|
|
5827
|
+
}
|
|
5656
5828
|
userIdentity = normalizeProviderIdentity({
|
|
5657
5829
|
identity: await providerInstance.fetchUserProfile(accessToken),
|
|
5658
5830
|
providerConfiguration: meta,
|
|
@@ -5660,9 +5832,14 @@ var resolveOAuthAuthorization = async ({
|
|
|
5660
5832
|
});
|
|
5661
5833
|
}
|
|
5662
5834
|
const tokenType = Reflect.get(tokenResponse, "token_type");
|
|
5835
|
+
const oauthSubject = extractPropFromIdentity(userIdentity, meta.subject, meta.subjectType);
|
|
5836
|
+
if (typeof oauthSubject !== "string" && typeof oauthSubject !== "number") {
|
|
5837
|
+
throw new Error(`Provider "${authProvider}" returned an invalid OAuth subject`);
|
|
5838
|
+
}
|
|
5663
5839
|
return {
|
|
5664
5840
|
accessToken,
|
|
5665
5841
|
expiresAt: resolveOAuthTokenExpiresAt(tokenResponse, now),
|
|
5842
|
+
oauthSubject,
|
|
5666
5843
|
refreshToken,
|
|
5667
5844
|
tokenType: typeof tokenType === "string" ? tokenType : undefined,
|
|
5668
5845
|
userIdentity
|
|
@@ -7610,10 +7787,10 @@ var exchangeBackchannelAuth = async ({
|
|
|
7610
7787
|
init_constants();
|
|
7611
7788
|
var CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
|
|
7612
7789
|
var MAX_ASSERTION_LIFETIME_MINUTES = 5;
|
|
7613
|
-
var
|
|
7614
|
-
var MAX_ASSERTION_LIFETIME_MS = MAX_ASSERTION_LIFETIME_MINUTES *
|
|
7790
|
+
var SECONDS_PER_MINUTE2 = 60;
|
|
7791
|
+
var MAX_ASSERTION_LIFETIME_MS = MAX_ASSERTION_LIFETIME_MINUTES * SECONDS_PER_MINUTE2 * MILLISECONDS_IN_A_SECOND;
|
|
7615
7792
|
var jwksCache = new Map;
|
|
7616
|
-
var JWKS_CACHE_TTL_MS =
|
|
7793
|
+
var JWKS_CACHE_TTL_MS = SECONDS_PER_MINUTE2 * MILLISECONDS_IN_A_SECOND;
|
|
7617
7794
|
var JWKS_FETCH_TIMEOUT_SECONDS = 5;
|
|
7618
7795
|
var JWKS_FETCH_TIMEOUT_MS = JWKS_FETCH_TIMEOUT_SECONDS * MILLISECONDS_IN_A_SECOND;
|
|
7619
7796
|
var fetchJwksUri = async (jwksUri) => {
|
|
@@ -11170,6 +11347,7 @@ var callback = ({
|
|
|
11170
11347
|
// src/routes/profile.ts
|
|
11171
11348
|
import { Elysia as Elysia29, t as t22 } from "elysia";
|
|
11172
11349
|
var profile = ({
|
|
11350
|
+
authSessionStore,
|
|
11173
11351
|
clientProviders,
|
|
11174
11352
|
profileRoute = "/oauth2/profile",
|
|
11175
11353
|
onProfileSuccess,
|
|
@@ -11184,9 +11362,6 @@ var profile = ({
|
|
|
11184
11362
|
if (auth_provider.value === undefined) {
|
|
11185
11363
|
return status("Unauthorized", "No auth provider found");
|
|
11186
11364
|
}
|
|
11187
|
-
if (!isValidProviderOption(auth_provider.value)) {
|
|
11188
|
-
return status("Unauthorized", "Invalid provider");
|
|
11189
|
-
}
|
|
11190
11365
|
if (user_session_id.value === undefined) {
|
|
11191
11366
|
return status("Unauthorized", "No user session found");
|
|
11192
11367
|
}
|
|
@@ -11199,7 +11374,14 @@ var profile = ({
|
|
|
11199
11374
|
return status("Unauthorized", resolvedProvider.error);
|
|
11200
11375
|
}
|
|
11201
11376
|
const { clientName, providerInstance } = resolvedProvider.entry;
|
|
11202
|
-
|
|
11377
|
+
if (!isProfileOAuth2Client(providerInstance)) {
|
|
11378
|
+
return status("Not Implemented", "Provider does not expose a profile endpoint");
|
|
11379
|
+
}
|
|
11380
|
+
const userSession = await loadSessionFromSource({
|
|
11381
|
+
authSessionStore,
|
|
11382
|
+
session,
|
|
11383
|
+
userSessionId: user_session_id.value
|
|
11384
|
+
});
|
|
11203
11385
|
if (userSession === undefined) {
|
|
11204
11386
|
return status("Unauthorized", "No user session found");
|
|
11205
11387
|
}
|
|
@@ -11251,9 +11433,6 @@ var refresh = ({
|
|
|
11251
11433
|
if (auth_provider.value === undefined) {
|
|
11252
11434
|
return status("Unauthorized", "No auth provider found");
|
|
11253
11435
|
}
|
|
11254
|
-
if (!isValidProviderOption(auth_provider.value)) {
|
|
11255
|
-
return status("Bad Request", "Invalid provider");
|
|
11256
|
-
}
|
|
11257
11436
|
if (user_session_id.value === undefined) {
|
|
11258
11437
|
return status("Unauthorized", "No user session found");
|
|
11259
11438
|
}
|
|
@@ -11275,7 +11454,7 @@ var refresh = ({
|
|
|
11275
11454
|
return status("Unauthorized", "No user session found");
|
|
11276
11455
|
}
|
|
11277
11456
|
const { refreshToken } = userSession;
|
|
11278
|
-
if (!isRefreshableOAuth2Client(
|
|
11457
|
+
if (!isRefreshableOAuth2Client(providerInstance)) {
|
|
11279
11458
|
return status("Not Implemented", "Provider is not refreshable");
|
|
11280
11459
|
}
|
|
11281
11460
|
if (refreshToken === undefined) {
|
|
@@ -11335,9 +11514,6 @@ var revoke = ({
|
|
|
11335
11514
|
if (auth_provider.value === undefined) {
|
|
11336
11515
|
return status("Unauthorized", "No auth provider found");
|
|
11337
11516
|
}
|
|
11338
|
-
if (!isValidProviderOption(auth_provider.value)) {
|
|
11339
|
-
return status("Bad Request", "Invalid provider");
|
|
11340
|
-
}
|
|
11341
11517
|
if (user_session_id.value === undefined) {
|
|
11342
11518
|
return status("Unauthorized", "No user session found");
|
|
11343
11519
|
}
|
|
@@ -11350,7 +11526,7 @@ var revoke = ({
|
|
|
11350
11526
|
return status("Unauthorized", resolvedProvider.error);
|
|
11351
11527
|
}
|
|
11352
11528
|
const { clientName, providerInstance } = resolvedProvider.entry;
|
|
11353
|
-
if (!isRevocableOAuth2Client(
|
|
11529
|
+
if (!isRevocableOAuth2Client(providerInstance)) {
|
|
11354
11530
|
return status("Not Implemented", "Provider does not support revocation");
|
|
11355
11531
|
}
|
|
11356
11532
|
const userSession = await loadSessionFromSource({
|
|
@@ -11361,16 +11537,21 @@ var revoke = ({
|
|
|
11361
11537
|
if (userSession === undefined) {
|
|
11362
11538
|
return status("Unauthorized", "No user session found");
|
|
11363
11539
|
}
|
|
11364
|
-
const { accessToken } = userSession;
|
|
11540
|
+
const { accessToken, oauthSubject, refreshToken } = userSession;
|
|
11365
11541
|
if (accessToken === undefined) {
|
|
11366
11542
|
return status("Bad Request", "Session has no access token to revoke");
|
|
11367
11543
|
}
|
|
11368
11544
|
try {
|
|
11369
|
-
|
|
11545
|
+
const tokenToRevoke = providerInstance.resolveRevocationInput({
|
|
11546
|
+
accessToken,
|
|
11547
|
+
refreshToken,
|
|
11548
|
+
subject: oauthSubject
|
|
11549
|
+
});
|
|
11550
|
+
await providerInstance.revokeToken(tokenToRevoke);
|
|
11370
11551
|
await onRevocationSuccess?.({
|
|
11371
11552
|
authClient: clientName,
|
|
11372
11553
|
authProvider: auth_provider.value,
|
|
11373
|
-
tokenToRevoke
|
|
11554
|
+
tokenToRevoke
|
|
11374
11555
|
});
|
|
11375
11556
|
return new Response("Token revoked", {
|
|
11376
11557
|
status: 204
|
|
@@ -25210,6 +25391,7 @@ var authSessionsTable = pgTable("auth_sessions", {
|
|
|
25210
25391
|
created_at: timestamp("created_at").notNull().defaultNow(),
|
|
25211
25392
|
expires_at_ms: bigint("expires_at_ms", { mode: "number" }).notNull(),
|
|
25212
25393
|
id: varchar("id", { length: 255 }).primaryKey(),
|
|
25394
|
+
oauth_subject_json: jsonb("oauth_subject_json").$type(),
|
|
25213
25395
|
refresh_token: text("refresh_token"),
|
|
25214
25396
|
updated_at: timestamp("updated_at").notNull().defaultNow(),
|
|
25215
25397
|
user_json: jsonb("user_json").$type().notNull()
|
|
@@ -25219,6 +25401,7 @@ var authUnregisteredSessionsTable = pgTable("auth_unregistered_sessions", {
|
|
|
25219
25401
|
created_at: timestamp("created_at").notNull().defaultNow(),
|
|
25220
25402
|
expires_at_ms: bigint("expires_at_ms", { mode: "number" }).notNull(),
|
|
25221
25403
|
id: varchar("id", { length: 255 }).primaryKey(),
|
|
25404
|
+
oauth_subject_json: jsonb("oauth_subject_json").$type(),
|
|
25222
25405
|
refresh_token: text("refresh_token"),
|
|
25223
25406
|
session_information_json: jsonb("session_information_json").$type(),
|
|
25224
25407
|
updated_at: timestamp("updated_at").notNull().defaultNow(),
|
|
@@ -25236,12 +25419,14 @@ var toSessionData = (row, decodeUser) => ({
|
|
|
25236
25419
|
accessToken: row.access_token ?? undefined,
|
|
25237
25420
|
authenticatedAt: row.authenticated_at_ms ?? undefined,
|
|
25238
25421
|
expiresAt: row.expires_at_ms,
|
|
25422
|
+
oauthSubject: row.oauth_subject_json ?? undefined,
|
|
25239
25423
|
refreshToken: row.refresh_token ?? undefined,
|
|
25240
25424
|
user: cloneUser(decodeUser(row.user_json))
|
|
25241
25425
|
});
|
|
25242
25426
|
var toUnregisteredSessionData = (row) => ({
|
|
25243
25427
|
accessToken: row.access_token ?? undefined,
|
|
25244
25428
|
expiresAt: row.expires_at_ms,
|
|
25429
|
+
oauthSubject: row.oauth_subject_json ?? undefined,
|
|
25245
25430
|
refreshToken: row.refresh_token ?? undefined,
|
|
25246
25431
|
sessionInformation: cloneRecord(row.session_information_json ?? undefined),
|
|
25247
25432
|
userIdentity: cloneRecord(row.user_identity_json ?? undefined)
|
|
@@ -25286,6 +25471,7 @@ var createNeonAuthSessionStore = (databaseUrl, decodeUser) => {
|
|
|
25286
25471
|
authenticated_at_ms: value.authenticatedAt ?? null,
|
|
25287
25472
|
expires_at_ms: value.expiresAt,
|
|
25288
25473
|
id,
|
|
25474
|
+
oauth_subject_json: value.oauthSubject ?? null,
|
|
25289
25475
|
refresh_token: value.refreshToken ?? null,
|
|
25290
25476
|
updated_at: new Date,
|
|
25291
25477
|
user_json: value.user ?? {}
|
|
@@ -25294,6 +25480,7 @@ var createNeonAuthSessionStore = (databaseUrl, decodeUser) => {
|
|
|
25294
25480
|
access_token: value.accessToken ?? null,
|
|
25295
25481
|
authenticated_at_ms: value.authenticatedAt ?? null,
|
|
25296
25482
|
expires_at_ms: value.expiresAt,
|
|
25483
|
+
oauth_subject_json: value.oauthSubject ?? null,
|
|
25297
25484
|
refresh_token: value.refreshToken ?? null,
|
|
25298
25485
|
updated_at: new Date,
|
|
25299
25486
|
user_json: value.user ?? {}
|
|
@@ -25306,6 +25493,7 @@ var createNeonAuthSessionStore = (databaseUrl, decodeUser) => {
|
|
|
25306
25493
|
access_token: value.accessToken ?? null,
|
|
25307
25494
|
expires_at_ms: value.expiresAt,
|
|
25308
25495
|
id,
|
|
25496
|
+
oauth_subject_json: value.oauthSubject ?? null,
|
|
25309
25497
|
refresh_token: value.refreshToken ?? null,
|
|
25310
25498
|
session_information_json: value.sessionInformation ?? null,
|
|
25311
25499
|
updated_at: new Date,
|
|
@@ -25314,6 +25502,7 @@ var createNeonAuthSessionStore = (databaseUrl, decodeUser) => {
|
|
|
25314
25502
|
set: {
|
|
25315
25503
|
access_token: value.accessToken ?? null,
|
|
25316
25504
|
expires_at_ms: value.expiresAt,
|
|
25505
|
+
oauth_subject_json: value.oauthSubject ?? null,
|
|
25317
25506
|
refresh_token: value.refreshToken ?? null,
|
|
25318
25507
|
session_information_json: value.sessionInformation ?? null,
|
|
25319
25508
|
updated_at: new Date,
|
|
@@ -31353,6 +31542,7 @@ var sessionSchema = Type.Object({
|
|
|
31353
31542
|
authenticatedAt: Type.Optional(Type.Number()),
|
|
31354
31543
|
expiresAt: Type.Number(),
|
|
31355
31544
|
impersonator: Type.Optional(impersonatorSchema),
|
|
31545
|
+
oauthSubject: Type.Optional(Type.Union([Type.String(), Type.Number()])),
|
|
31356
31546
|
refreshToken: Type.Optional(Type.String()),
|
|
31357
31547
|
samlLogout: Type.Optional(Type.Object({
|
|
31358
31548
|
connectionId: Type.String(),
|
|
@@ -31364,6 +31554,7 @@ var sessionSchema = Type.Object({
|
|
|
31364
31554
|
var unregisteredSessionSchema = Type.Object({
|
|
31365
31555
|
accessToken: Type.Optional(Type.String()),
|
|
31366
31556
|
expiresAt: Type.Number(),
|
|
31557
|
+
oauthSubject: Type.Optional(Type.Union([Type.String(), Type.Number()])),
|
|
31367
31558
|
refreshToken: Type.Optional(Type.String()),
|
|
31368
31559
|
sessionInformation: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
31369
31560
|
userIdentity: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
|
|
@@ -36948,30 +37139,40 @@ var JOURNAL_DDL = `CREATE TABLE IF NOT EXISTS "auth_migrations" (
|
|
|
36948
37139
|
var isJournalRow = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "id") === "string";
|
|
36949
37140
|
var isBlockName = (value) => Object.hasOwn(blockMigrations, value);
|
|
36950
37141
|
var allBlockNames = () => Object.keys(blockMigrations).filter(isBlockName);
|
|
36951
|
-
var applyOne = async (
|
|
36952
|
-
await
|
|
36953
|
-
await
|
|
37142
|
+
var applyOne = async (client, id, sql2, log) => {
|
|
37143
|
+
await client.query(sql2);
|
|
37144
|
+
await client.query(`INSERT INTO "auth_migrations" ("id", "applied_at_ms") VALUES ($1, $2)`, [id, Date.now()]);
|
|
36954
37145
|
log(`apply ${id}`);
|
|
36955
37146
|
};
|
|
36956
|
-
var runOne = async (
|
|
37147
|
+
var runOne = async (client, id, sql2, applied, result, log) => {
|
|
36957
37148
|
if (applied.has(id)) {
|
|
36958
37149
|
result.skipped.push(id);
|
|
36959
37150
|
log(`skip ${id}`);
|
|
36960
37151
|
return;
|
|
36961
37152
|
}
|
|
36962
|
-
await applyOne(
|
|
37153
|
+
await applyOne(client, id, sql2, log);
|
|
36963
37154
|
result.applied.push(id);
|
|
36964
37155
|
};
|
|
36965
37156
|
var runMigrations = async ({
|
|
36966
37157
|
blocks,
|
|
37158
|
+
client,
|
|
36967
37159
|
databaseUrl,
|
|
36968
37160
|
log = console.log
|
|
36969
37161
|
}) => {
|
|
36970
|
-
|
|
37162
|
+
let ownedPool;
|
|
37163
|
+
let migrationClient;
|
|
37164
|
+
if (client !== undefined) {
|
|
37165
|
+
migrationClient = client;
|
|
37166
|
+
} else {
|
|
37167
|
+
if (databaseUrl === undefined)
|
|
37168
|
+
throw new Error("runMigrations requires databaseUrl or client");
|
|
37169
|
+
ownedPool = new Ln({ connectionString: databaseUrl });
|
|
37170
|
+
migrationClient = ownedPool;
|
|
37171
|
+
}
|
|
36971
37172
|
const result = { applied: [], skipped: [] };
|
|
36972
37173
|
try {
|
|
36973
|
-
await
|
|
36974
|
-
const journal = await
|
|
37174
|
+
await migrationClient.query(JOURNAL_DDL);
|
|
37175
|
+
const journal = await migrationClient.query(`SELECT "id" FROM "auth_migrations"`);
|
|
36975
37176
|
const applied = new Set(journal.rows.filter(isJournalRow).map((row) => row.id));
|
|
36976
37177
|
const selected = blocks ?? allBlockNames();
|
|
36977
37178
|
const flat = selected.flatMap((block) => blockMigrations[block].migrations.map((migration) => ({
|
|
@@ -36980,10 +37181,10 @@ var runMigrations = async ({
|
|
|
36980
37181
|
})));
|
|
36981
37182
|
await flat.reduce(async (prior, item) => {
|
|
36982
37183
|
await prior;
|
|
36983
|
-
return runOne(
|
|
37184
|
+
return runOne(migrationClient, item.id, item.sql, applied, result, log);
|
|
36984
37185
|
}, Promise.resolve());
|
|
36985
37186
|
} finally {
|
|
36986
|
-
await
|
|
37187
|
+
await ownedPool?.end();
|
|
36987
37188
|
}
|
|
36988
37189
|
return result;
|
|
36989
37190
|
};
|
|
@@ -37016,6 +37217,14 @@ var oidcResourceAudienceMigration = {
|
|
|
37016
37217
|
].join(`
|
|
37017
37218
|
`)
|
|
37018
37219
|
};
|
|
37220
|
+
var sessionOAuthSubjectMigration = {
|
|
37221
|
+
id: "0002_oauth_subject",
|
|
37222
|
+
sql: [
|
|
37223
|
+
'ALTER TABLE "auth_sessions" ADD COLUMN IF NOT EXISTS "oauth_subject_json" jsonb;',
|
|
37224
|
+
'ALTER TABLE "auth_unregistered_sessions" ADD COLUMN IF NOT EXISTS "oauth_subject_json" jsonb;'
|
|
37225
|
+
].join(`
|
|
37226
|
+
`)
|
|
37227
|
+
};
|
|
37019
37228
|
var blockMigrations = {
|
|
37020
37229
|
adaptive: initMigration("adaptive", [knownDevicesTable, loginHistoryTable]),
|
|
37021
37230
|
agents: {
|
|
@@ -37084,10 +37293,16 @@ var blockMigrations = {
|
|
|
37084
37293
|
portal: initMigration("portal", [setupSessionsTable]),
|
|
37085
37294
|
roles: initMigration("roles", [rolesTable]),
|
|
37086
37295
|
scim: initMigration("scim", [scimTokensTable]),
|
|
37087
|
-
sessions:
|
|
37088
|
-
|
|
37089
|
-
|
|
37090
|
-
|
|
37296
|
+
sessions: {
|
|
37297
|
+
block: "sessions",
|
|
37298
|
+
migrations: [
|
|
37299
|
+
...initMigration("sessions", [
|
|
37300
|
+
authSessionsTable,
|
|
37301
|
+
authUnregisteredSessionsTable
|
|
37302
|
+
]).migrations,
|
|
37303
|
+
sessionOAuthSubjectMigration
|
|
37304
|
+
]
|
|
37305
|
+
},
|
|
37091
37306
|
sso: initMigration("sso", [ssoConnectionsTable, samlServiceProvidersTable]),
|
|
37092
37307
|
vault: initMigration("vault", [vaultEntriesTable]),
|
|
37093
37308
|
vc: initMigration("vc", [
|
|
@@ -37773,6 +37988,7 @@ var buildAuthApplications = async (configuration) => {
|
|
|
37773
37988
|
resolveAuthIntent
|
|
37774
37989
|
}),
|
|
37775
37990
|
profile({
|
|
37991
|
+
authSessionStore,
|
|
37776
37992
|
clientProviders,
|
|
37777
37993
|
onProfileError,
|
|
37778
37994
|
onProfileSuccess,
|
|
@@ -38042,5 +38258,5 @@ export {
|
|
|
38042
38258
|
auth2 as auth
|
|
38043
38259
|
};
|
|
38044
38260
|
|
|
38045
|
-
//# debugId=
|
|
38261
|
+
//# debugId=52D884157E2599DD64756E2164756E21
|
|
38046
38262
|
//# sourceMappingURL=server.js.map
|