@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.
@@ -685,17 +685,23 @@ var providers = defineProviders({
685
685
  },
686
686
  apple: {
687
687
  authorizationUrl: "https://appleid.apple.com/auth/authorize",
688
+ createAuthorizationURLSearchParams: {
689
+ response_mode: "form_post"
690
+ },
691
+ createClientSecret: (config) => createAppleClientSecret(config),
688
692
  isOIDC: true,
689
693
  isRefreshable: true,
690
- PKCEMethod: "S256",
691
- profileRequest: {
692
- authIn: "header",
693
- encoding: "application/json",
694
- method: "GET",
695
- url: "https://appleid.apple.com/auth/userinfo"
694
+ revocationRequest: {
695
+ authIn: "body",
696
+ encoding: "application/x-www-form-urlencoded",
697
+ tokenParamName: "token",
698
+ url: "https://appleid.apple.com/auth/revoke"
696
699
  },
697
700
  scopeRequired: false,
698
- subject: ["id"],
701
+ subject: ["sub"],
702
+ subjectBySource: {
703
+ idToken: ["sub"]
704
+ },
699
705
  subjectType: "string",
700
706
  tokenRequest: {
701
707
  authIn: "body",
@@ -764,6 +770,7 @@ var providers = defineProviders({
764
770
  token_type_hint: "refresh_token"
765
771
  }),
766
772
  encoding: "application/json",
773
+ inputSource: "refreshToken",
767
774
  tokenParamName: "token",
768
775
  url: (config) => `https://${config.domain}/oauth/revoke`
769
776
  },
@@ -1895,6 +1902,7 @@ var providers = defineProviders({
1895
1902
  token_type_hint: "refresh_token"
1896
1903
  }),
1897
1904
  encoding: "application/json",
1905
+ inputSource: "refreshToken",
1898
1906
  url: "https://www.reddit.com/api/v1/revoke_token"
1899
1907
  },
1900
1908
  scopeRequired: true,
@@ -2268,49 +2276,27 @@ var providers = defineProviders({
2268
2276
  authorizationUrl: "https://account.withings.com/oauth2_user/authorize2",
2269
2277
  isOIDC: false,
2270
2278
  isRefreshable: true,
2271
- profileRequest: {
2272
- authIn: "header",
2273
- body: async (config) => {
2274
- const props = await getWithingsProps(config);
2275
- if (props === undefined)
2276
- throw new Error("Failed to get Withings search properties");
2277
- const { nonce, hashedSignature } = props;
2278
- return [
2279
- ["action", "getuser"],
2280
- ["nonce", nonce],
2281
- ["client_id", config.clientId],
2282
- ["signature", hashedSignature]
2283
- ];
2284
- },
2285
- encoding: "application/x-www-form-urlencoded",
2286
- method: "POST",
2287
- url: "https://wbsapi.withings.net/v2/oauth2"
2288
- },
2289
2279
  refreshAccessTokenBody: {
2290
2280
  action: "requesttoken"
2291
2281
  },
2292
2282
  revocationRequest: {
2293
- authIn: "header",
2294
- body: async (config) => {
2295
- const props = await getWithingsProps(config);
2296
- if (!props)
2297
- throw new Error("Failed to get Withings props");
2298
- const { nonce, hashedSignature } = props;
2299
- return [
2300
- ["action", "revoke"],
2301
- ["client_id", config.clientId],
2302
- ["nonce", nonce],
2303
- ["signature", hashedSignature]
2304
- ];
2305
- },
2283
+ authIn: "body",
2284
+ body: (config) => getWithingsSignatureParams(config, "revoke"),
2306
2285
  encoding: "application/x-www-form-urlencoded",
2307
- method: "POST",
2308
- url: "https://wbsapi.withings.net/v2/oauth2"
2286
+ includeClientCredentials: false,
2287
+ inputSource: "subject",
2288
+ inputType: "number",
2289
+ tokenParamName: "userid",
2290
+ url: "https://wbsapi.withings.net/v2/oauth2",
2291
+ validateResponse: (value) => assertWithingsSuccess(value)
2309
2292
  },
2310
2293
  scopeDelimiter: ",",
2311
2294
  scopeRequired: true,
2312
2295
  subject: ["userid"],
2313
- subjectType: "string",
2296
+ subjectBySource: {
2297
+ tokenResponse: ["body", "userid"]
2298
+ },
2299
+ subjectType: "number",
2314
2300
  tokenRequest: {
2315
2301
  authIn: "body",
2316
2302
  encoding: "application/x-www-form-urlencoded",
@@ -2469,6 +2455,7 @@ var providers = defineProviders({
2469
2455
  }
2470
2456
  }
2471
2457
  });
2458
+ var isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && Object.prototype.toString.call(value) === "[object Object]";
2472
2459
  var isOIDCProviderOption = (option) => {
2473
2460
  if (!isValidProviderOption(option))
2474
2461
  return false;
@@ -2481,6 +2468,12 @@ var isPKCEProviderOption = (option) => {
2481
2468
  const provider = providers[option];
2482
2469
  return provider.PKCEMethod !== undefined;
2483
2470
  };
2471
+ var isProfileProviderOption = (option) => {
2472
+ if (!isValidProviderOption(option))
2473
+ return false;
2474
+ const provider = providers[option];
2475
+ return provider.profileRequest !== undefined;
2476
+ };
2484
2477
  var isRefreshableProviderOption = (option) => {
2485
2478
  if (!isValidProviderOption(option))
2486
2479
  return false;
@@ -2500,6 +2493,28 @@ var isScopeRequiredProviderOption = (option) => {
2500
2493
  return provider.scopeRequired;
2501
2494
  };
2502
2495
  var isValidProviderOption = (option) => Object.hasOwn(providers, option);
2496
+ var assertWithingsSuccess = (value) => {
2497
+ if (!isObject(value) || value.status !== 0) {
2498
+ const status = isObject(value) ? value.status : "invalid response";
2499
+ const detail = isObject(value) && typeof value.error === "string" ? `: ${value.error}` : "";
2500
+ throw new Error(`Withings request failed (${String(status)})${detail}`);
2501
+ }
2502
+ };
2503
+ var createOAuth2FetchError = async (response) => {
2504
+ const clone = response.clone();
2505
+ const prefix = `HTTP ${response.status} ${response.statusText} for ${response.url}`;
2506
+ const payload = await response.json().catch(() => null);
2507
+ if (payload && typeof payload === "object" && Object.keys(payload).length) {
2508
+ return new Error(`${prefix}
2509
+ ${JSON.stringify(payload)}`);
2510
+ }
2511
+ const text3 = await clone.text().catch(() => "");
2512
+ if (text3) {
2513
+ return new Error(`${prefix}
2514
+ ${text3}`);
2515
+ }
2516
+ return new Error(prefix);
2517
+ };
2503
2518
  var encodeBase64 = (input) => {
2504
2519
  let raw;
2505
2520
  if (typeof input === "string") {
@@ -2510,25 +2525,31 @@ var encodeBase64 = (input) => {
2510
2525
  }
2511
2526
  return btoa(raw);
2512
2527
  };
2513
- var getWithingsProps = async (config) => {
2528
+ var getWithingsSignatureParams = async (config, action) => {
2514
2529
  const timestamp = Math.floor(Date.now() / 1000);
2515
- const signature = `getnonce,${config.clientId},${timestamp}`;
2516
- const hashedSignature = await hmacSha256(signature, config.clientSecret);
2530
+ const nonceSignature = await hmacSha256(`getnonce,${config.clientId},${timestamp}`, config.clientSecret);
2517
2531
  const nonceUrl = new URL("https://wbsapi.withings.net/v2/signature");
2518
2532
  nonceUrl.searchParams.set("action", "getnonce");
2519
2533
  nonceUrl.searchParams.set("client_id", config.clientId);
2520
2534
  nonceUrl.searchParams.set("timestamp", timestamp.toString());
2521
- nonceUrl.searchParams.set("signature", hashedSignature);
2535
+ nonceUrl.searchParams.set("signature", nonceSignature);
2522
2536
  const nonceTarget = nonceUrl.toString();
2523
2537
  const nonceResponse = await fetch(nonceTarget, { method: "POST" });
2538
+ if (!nonceResponse.ok) {
2539
+ throw await createOAuth2FetchError(nonceResponse);
2540
+ }
2524
2541
  const nonceData = await nonceResponse.json();
2525
- if (nonceData.status === 0) {
2526
- return {
2527
- hashedSignature,
2528
- nonce: nonceData.body.nonce
2529
- };
2542
+ if (!isObject(nonceData) || nonceData.status !== 0 || !isObject(nonceData.body) || typeof nonceData.body.nonce !== "string" || nonceData.body.nonce.length === 0) {
2543
+ throw new Error("Withings returned an invalid nonce response");
2530
2544
  }
2531
- return;
2545
+ const { nonce } = nonceData.body;
2546
+ const signature = await hmacSha256(`${action},${config.clientId},${nonce}`, config.clientSecret);
2547
+ return {
2548
+ action,
2549
+ client_id: config.clientId,
2550
+ nonce,
2551
+ signature
2552
+ };
2532
2553
  };
2533
2554
  var hmacSha256 = async (message, secret) => {
2534
2555
  const encoder = new TextEncoder;
@@ -2536,6 +2557,13 @@ var hmacSha256 = async (message, secret) => {
2536
2557
  const sigBuffer = await crypto.subtle.sign("HMAC", key, encoder.encode(message));
2537
2558
  return Array.from(new Uint8Array(sigBuffer)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
2538
2559
  };
2560
+ var DAYS_IN_APPLE_CLIENT_SECRET_LIFETIME = 180;
2561
+ var HOURS_PER_DAY = 24;
2562
+ var MINUTES_PER_HOUR = 60;
2563
+ var SECONDS_PER_MINUTE = 60;
2564
+ var APPLE_CLIENT_SECRET_LIFETIME_SECONDS = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * DAYS_IN_APPLE_CLIENT_SECRET_LIFETIME;
2565
+ var APPLE_ISSUER = "https://appleid.apple.com";
2566
+ var MILLISECONDS_PER_SECOND = 1000;
2539
2567
  var createRandomBase64UrlGenerator = (length) => () => {
2540
2568
  const buffer = crypto.getRandomValues(new Uint8Array(length));
2541
2569
  return base64Url(buffer);
@@ -2543,8 +2571,29 @@ var createRandomBase64UrlGenerator = (length) => () => {
2543
2571
  var generateCodeVerifier = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);
2544
2572
  var generateState = createRandomBase64UrlGenerator(NUM_GENERATOR_BYTES);
2545
2573
  var base64Url = (input) => encodeBase64(input).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
2574
+ var encodeJwtPart = (value) => base64Url(new TextEncoder().encode(JSON.stringify(value)));
2575
+ var createAppleClientSecret = async (credentials) => {
2576
+ const issuedAt = Math.floor(Date.now() / MILLISECONDS_PER_SECOND);
2577
+ const header = encodeJwtPart({
2578
+ alg: "ES256",
2579
+ kid: credentials.keyId,
2580
+ typ: "JWT"
2581
+ });
2582
+ const payload = encodeJwtPart({
2583
+ aud: APPLE_ISSUER,
2584
+ exp: issuedAt + APPLE_CLIENT_SECRET_LIFETIME_SECONDS,
2585
+ iat: issuedAt,
2586
+ iss: credentials.teamId,
2587
+ sub: credentials.clientId
2588
+ });
2589
+ const signingInput = `${header}.${payload}`;
2590
+ const privateKey = await crypto.subtle.importKey("pkcs8", Uint8Array.from(credentials.pkcs8PrivateKey), { name: "ECDSA", namedCurve: "P-256" }, false, ["sign"]);
2591
+ const signature = await crypto.subtle.sign({ hash: "SHA-256", name: "ECDSA" }, privateKey, new TextEncoder().encode(signingInput));
2592
+ return `${signingInput}.${base64Url(signature)}`;
2593
+ };
2546
2594
  var oidcProviderOptions = Object.keys(providers).filter(isOIDCProviderOption);
2547
2595
  var pkceProviderOptions = Object.keys(providers).filter(isPKCEProviderOption);
2596
+ var profileProviderOptions = Object.keys(providers).filter(isProfileProviderOption);
2548
2597
  var providerOptions = Object.keys(providers).filter(isValidProviderOption);
2549
2598
  var refreshableProviderOptions = Object.keys(providers).filter(isRefreshableProviderOption);
2550
2599
  var revocableProviderOptions = Object.keys(providers).filter(isRevocableProviderOption);
@@ -2893,6 +2942,7 @@ var authSessionsTable = pgTable16("auth_sessions", {
2893
2942
  created_at: timestamp2("created_at").notNull().defaultNow(),
2894
2943
  expires_at_ms: bigint14("expires_at_ms", { mode: "number" }).notNull(),
2895
2944
  id: varchar16("id", { length: 255 }).primaryKey(),
2945
+ oauth_subject_json: jsonb8("oauth_subject_json").$type(),
2896
2946
  refresh_token: text6("refresh_token"),
2897
2947
  updated_at: timestamp2("updated_at").notNull().defaultNow(),
2898
2948
  user_json: jsonb8("user_json").$type().notNull()
@@ -2902,6 +2952,7 @@ var authUnregisteredSessionsTable = pgTable16("auth_unregistered_sessions", {
2902
2952
  created_at: timestamp2("created_at").notNull().defaultNow(),
2903
2953
  expires_at_ms: bigint14("expires_at_ms", { mode: "number" }).notNull(),
2904
2954
  id: varchar16("id", { length: 255 }).primaryKey(),
2955
+ oauth_subject_json: jsonb8("oauth_subject_json").$type(),
2905
2956
  refresh_token: text6("refresh_token"),
2906
2957
  session_information_json: jsonb8("session_information_json").$type(),
2907
2958
  updated_at: timestamp2("updated_at").notNull().defaultNow(),
@@ -3100,30 +3151,40 @@ var JOURNAL_DDL = `CREATE TABLE IF NOT EXISTS "auth_migrations" (
3100
3151
  var isJournalRow = (value) => typeof value === "object" && value !== null && typeof Reflect.get(value, "id") === "string";
3101
3152
  var isBlockName = (value) => Object.hasOwn(blockMigrations, value);
3102
3153
  var allBlockNames = () => Object.keys(blockMigrations).filter(isBlockName);
3103
- var applyOne = async (pool, id, sql, log) => {
3104
- await pool.query(sql);
3105
- await pool.query(`INSERT INTO "auth_migrations" ("id", "applied_at_ms") VALUES ($1, $2)`, [id, Date.now()]);
3154
+ var applyOne = async (client, id, sql, log) => {
3155
+ await client.query(sql);
3156
+ await client.query(`INSERT INTO "auth_migrations" ("id", "applied_at_ms") VALUES ($1, $2)`, [id, Date.now()]);
3106
3157
  log(`apply ${id}`);
3107
3158
  };
3108
- var runOne = async (pool, id, sql, applied, result, log) => {
3159
+ var runOne = async (client, id, sql, applied, result, log) => {
3109
3160
  if (applied.has(id)) {
3110
3161
  result.skipped.push(id);
3111
3162
  log(`skip ${id}`);
3112
3163
  return;
3113
3164
  }
3114
- await applyOne(pool, id, sql, log);
3165
+ await applyOne(client, id, sql, log);
3115
3166
  result.applied.push(id);
3116
3167
  };
3117
3168
  var runMigrations = async ({
3118
3169
  blocks,
3170
+ client,
3119
3171
  databaseUrl,
3120
3172
  log = console.log
3121
3173
  }) => {
3122
- const pool = new Pool({ connectionString: databaseUrl });
3174
+ let ownedPool;
3175
+ let migrationClient;
3176
+ if (client !== undefined) {
3177
+ migrationClient = client;
3178
+ } else {
3179
+ if (databaseUrl === undefined)
3180
+ throw new Error("runMigrations requires databaseUrl or client");
3181
+ ownedPool = new Pool({ connectionString: databaseUrl });
3182
+ migrationClient = ownedPool;
3183
+ }
3123
3184
  const result = { applied: [], skipped: [] };
3124
3185
  try {
3125
- await pool.query(JOURNAL_DDL);
3126
- const journal = await pool.query(`SELECT "id" FROM "auth_migrations"`);
3186
+ await migrationClient.query(JOURNAL_DDL);
3187
+ const journal = await migrationClient.query(`SELECT "id" FROM "auth_migrations"`);
3127
3188
  const applied = new Set(journal.rows.filter(isJournalRow).map((row) => row.id));
3128
3189
  const selected = blocks ?? allBlockNames();
3129
3190
  const flat = selected.flatMap((block) => blockMigrations[block].migrations.map((migration) => ({
@@ -3132,10 +3193,10 @@ var runMigrations = async ({
3132
3193
  })));
3133
3194
  await flat.reduce(async (prior, item) => {
3134
3195
  await prior;
3135
- return runOne(pool, item.id, item.sql, applied, result, log);
3196
+ return runOne(migrationClient, item.id, item.sql, applied, result, log);
3136
3197
  }, Promise.resolve());
3137
3198
  } finally {
3138
- await pool.end();
3199
+ await ownedPool?.end();
3139
3200
  }
3140
3201
  return result;
3141
3202
  };
@@ -3168,6 +3229,14 @@ var oidcResourceAudienceMigration = {
3168
3229
  ].join(`
3169
3230
  `)
3170
3231
  };
3232
+ var sessionOAuthSubjectMigration = {
3233
+ id: "0002_oauth_subject",
3234
+ sql: [
3235
+ 'ALTER TABLE "auth_sessions" ADD COLUMN IF NOT EXISTS "oauth_subject_json" jsonb;',
3236
+ 'ALTER TABLE "auth_unregistered_sessions" ADD COLUMN IF NOT EXISTS "oauth_subject_json" jsonb;'
3237
+ ].join(`
3238
+ `)
3239
+ };
3171
3240
  var blockMigrations = {
3172
3241
  adaptive: initMigration("adaptive", [knownDevicesTable, loginHistoryTable]),
3173
3242
  agents: {
@@ -3236,10 +3305,16 @@ var blockMigrations = {
3236
3305
  portal: initMigration("portal", [setupSessionsTable]),
3237
3306
  roles: initMigration("roles", [rolesTable]),
3238
3307
  scim: initMigration("scim", [scimTokensTable]),
3239
- sessions: initMigration("sessions", [
3240
- authSessionsTable,
3241
- authUnregisteredSessionsTable
3242
- ]),
3308
+ sessions: {
3309
+ block: "sessions",
3310
+ migrations: [
3311
+ ...initMigration("sessions", [
3312
+ authSessionsTable,
3313
+ authUnregisteredSessionsTable
3314
+ ]).migrations,
3315
+ sessionOAuthSubjectMigration
3316
+ ]
3317
+ },
3243
3318
  sso: initMigration("sso", [ssoConnectionsTable, samlServiceProvidersTable]),
3244
3319
  vault: initMigration("vault", [vaultEntriesTable]),
3245
3320
  vc: initMigration("vc", [
@@ -3437,5 +3512,5 @@ var main = async () => {
3437
3512
  };
3438
3513
  await main();
3439
3514
 
3440
- //# debugId=F5FC77C8DEF0189164756E2164756E21
3515
+ //# debugId=D9CB4120E36FA20E64756E2164756E21
3441
3516
  //# sourceMappingURL=migrate.js.map