@absolutejs/auth 0.29.0-beta.0 → 0.29.0-beta.2

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/index.js CHANGED
@@ -4452,6 +4452,105 @@ var exchangeDeviceCode = async ({
4452
4452
  return { ...tokenSet, ok: true };
4453
4453
  };
4454
4454
 
4455
+ // src/oidc/clientAuth.ts
4456
+ var CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
4457
+ var MAX_ASSERTION_LIFETIME_MINUTES = 5;
4458
+ var SECONDS_PER_MINUTE = 60;
4459
+ var MAX_ASSERTION_LIFETIME_MS = MAX_ASSERTION_LIFETIME_MINUTES * SECONDS_PER_MINUTE * MILLISECONDS_IN_A_SECOND;
4460
+ var jwksCache = new Map;
4461
+ var JWKS_CACHE_TTL_MS = SECONDS_PER_MINUTE * MILLISECONDS_IN_A_SECOND;
4462
+ var JWKS_FETCH_TIMEOUT_SECONDS = 5;
4463
+ var JWKS_FETCH_TIMEOUT_MS = JWKS_FETCH_TIMEOUT_SECONDS * MILLISECONDS_IN_A_SECOND;
4464
+ var fetchJwksUri = async (jwksUri) => {
4465
+ const cached = jwksCache.get(jwksUri);
4466
+ if (cached && Date.now() - cached.fetchedAt < JWKS_CACHE_TTL_MS) {
4467
+ return cached.jwks;
4468
+ }
4469
+ try {
4470
+ const response = await fetch(jwksUri, {
4471
+ signal: AbortSignal.timeout(JWKS_FETCH_TIMEOUT_MS)
4472
+ });
4473
+ if (!response.ok)
4474
+ return;
4475
+ const body = await response.json();
4476
+ if (!Array.isArray(body.keys))
4477
+ return;
4478
+ jwksCache.set(jwksUri, { fetchedAt: Date.now(), jwks: body.keys });
4479
+ return body.keys;
4480
+ } catch {
4481
+ return;
4482
+ }
4483
+ };
4484
+ var resolveClientJwks = async (client) => {
4485
+ if (client.jwks && client.jwks.length > 0)
4486
+ return client.jwks;
4487
+ if (client.jwksUri !== undefined)
4488
+ return fetchJwksUri(client.jwksUri);
4489
+ return;
4490
+ };
4491
+ var verifyAgainstAny = async (assertion, candidates) => {
4492
+ for (const jwk of candidates) {
4493
+ const verified = await verifyJwt(assertion, jwk);
4494
+ if (verified !== undefined)
4495
+ return verified;
4496
+ }
4497
+ return;
4498
+ };
4499
+ var verifyClientAssertion = async ({
4500
+ assertion,
4501
+ expectedAudience,
4502
+ jtiStore,
4503
+ resolveClient
4504
+ }) => {
4505
+ const [, payloadSegment] = assertion.split(".");
4506
+ if (payloadSegment === undefined)
4507
+ return;
4508
+ let payload;
4509
+ try {
4510
+ const parsed = JSON.parse(Buffer.from(payloadSegment, "base64url").toString("utf8"));
4511
+ if (typeof parsed !== "object" || parsed === null)
4512
+ return;
4513
+ payload = parsed;
4514
+ } catch {
4515
+ return;
4516
+ }
4517
+ const {
4518
+ aud,
4519
+ exp,
4520
+ iss,
4521
+ jti,
4522
+ sub
4523
+ } = payload;
4524
+ if (typeof iss !== "string" || typeof sub !== "string" || iss !== sub || typeof exp !== "number") {
4525
+ return;
4526
+ }
4527
+ const audMatches = typeof aud === "string" && aud === expectedAudience || Array.isArray(aud) && aud.includes(expectedAudience);
4528
+ if (!audMatches)
4529
+ return;
4530
+ const expMs = exp * MILLISECONDS_IN_A_SECOND;
4531
+ const now = Date.now();
4532
+ if (expMs <= now || expMs - now > MAX_ASSERTION_LIFETIME_MS) {
4533
+ return;
4534
+ }
4535
+ const client = await resolveClient(iss);
4536
+ if (client === undefined)
4537
+ return;
4538
+ const candidates = await resolveClientJwks(client);
4539
+ if (candidates === undefined || candidates.length === 0)
4540
+ return;
4541
+ const verified = await verifyAgainstAny(assertion, candidates);
4542
+ if (verified === undefined)
4543
+ return;
4544
+ if (jtiStore !== undefined) {
4545
+ if (typeof jti !== "string")
4546
+ return;
4547
+ const fresh = await jtiStore.recordIfFresh(client.clientId, jti, expMs);
4548
+ if (!fresh)
4549
+ return;
4550
+ }
4551
+ return client;
4552
+ };
4553
+
4455
4554
  // src/oidc/dpop.ts
4456
4555
  var DEFAULT_MAX_AGE_MS = 60000;
4457
4556
  var SECONDS_TO_MS = 1000;
@@ -4496,6 +4595,138 @@ var verifyDpopProof = async ({
4496
4595
  return { jkt: await jwkThumbprint(header.jwk), jti };
4497
4596
  };
4498
4597
 
4598
+ // src/oidc/logout.ts
4599
+ var BACKCHANNEL_LOGOUT_EVENT = "http://schemas.openid.net/event/backchannel-logout";
4600
+ var buildLogoutClaims = ({
4601
+ clientId,
4602
+ issuer,
4603
+ now,
4604
+ sub
4605
+ }) => ({
4606
+ aud: clientId,
4607
+ events: { [BACKCHANNEL_LOGOUT_EVENT]: {} },
4608
+ iat: Math.floor(now / MILLISECONDS_IN_A_SECOND),
4609
+ iss: issuer,
4610
+ jti: crypto.randomUUID(),
4611
+ sub
4612
+ });
4613
+ var resolvePostLogoutRedirect = ({
4614
+ client,
4615
+ requestedUri
4616
+ }) => {
4617
+ if (requestedUri === undefined)
4618
+ return;
4619
+ const allow = client.postLogoutRedirectUris ?? [];
4620
+ return allow.includes(requestedUri) ? requestedUri : undefined;
4621
+ };
4622
+ var verifyIdTokenHint = async ({
4623
+ config,
4624
+ idTokenHint
4625
+ }) => {
4626
+ const verified = await verifyJwt(idTokenHint, config.signingKey.publicJwk);
4627
+ const payload = verified?.payload;
4628
+ if (payload === undefined || typeof payload.sub !== "string" || typeof payload.aud !== "string" || payload.iss !== config.issuer) {
4629
+ return;
4630
+ }
4631
+ return { audClientId: payload.aud, sub: payload.sub };
4632
+ };
4633
+ var BACKCHANNEL_TIMEOUT_SECONDS = 5;
4634
+ var DEFAULT_BACKCHANNEL_TIMEOUT_MS = BACKCHANNEL_TIMEOUT_SECONDS * MILLISECONDS_IN_A_SECOND;
4635
+ var errorMessage = (error) => error instanceof Error ? error.message : String(error);
4636
+ var statusFromError = (error) => {
4637
+ if (!(error instanceof Error))
4638
+ return;
4639
+ const match = /returned (\d+)/.exec(error.message);
4640
+ return match?.[1] === undefined ? undefined : Number(match[1]);
4641
+ };
4642
+ var mintLogoutToken = async ({
4643
+ clientId,
4644
+ config,
4645
+ now = Date.now(),
4646
+ sub
4647
+ }) => signJwt(buildLogoutClaims({ clientId, issuer: config.issuer, now, sub }), config.signingKey);
4648
+ var postLogoutToken = async ({
4649
+ endpointUrl,
4650
+ fetchImpl,
4651
+ logoutToken,
4652
+ timeoutMs
4653
+ }) => {
4654
+ const response = await fetchImpl(endpointUrl, {
4655
+ body: new URLSearchParams({ logout_token: logoutToken }).toString(),
4656
+ headers: {
4657
+ "content-type": "application/x-www-form-urlencoded"
4658
+ },
4659
+ method: "POST",
4660
+ signal: AbortSignal.timeout(timeoutMs)
4661
+ });
4662
+ if (!response.ok) {
4663
+ throw new Error(`Back-channel logout returned ${response.status}`);
4664
+ }
4665
+ };
4666
+ var deliverLogoutToOne = async ({
4667
+ clientId,
4668
+ config,
4669
+ endpointUrl,
4670
+ fetchImpl,
4671
+ logoutToken,
4672
+ onError,
4673
+ timeoutMs,
4674
+ userId
4675
+ }) => {
4676
+ try {
4677
+ await postLogoutToken({ endpointUrl, fetchImpl, logoutToken, timeoutMs });
4678
+ } catch (error) {
4679
+ const delivery = {
4680
+ attempts: 1,
4681
+ clientId,
4682
+ createdAt: Date.now(),
4683
+ endpointUrl,
4684
+ id: crypto.randomUUID(),
4685
+ lastError: errorMessage(error),
4686
+ lastStatus: statusFromError(error),
4687
+ logoutToken,
4688
+ userId
4689
+ };
4690
+ await config.logoutDeliveryStore?.recordFailure(delivery);
4691
+ await onError?.(delivery);
4692
+ }
4693
+ };
4694
+ var fanOutBackchannelLogout = async ({
4695
+ config,
4696
+ fetchImpl = globalThis.fetch,
4697
+ now = Date.now(),
4698
+ onError,
4699
+ skipClientId,
4700
+ timeoutMs = DEFAULT_BACKCHANNEL_TIMEOUT_MS,
4701
+ userId
4702
+ }) => {
4703
+ const clientIds = await config.refreshTokenStore.listClientIdsForUser(userId);
4704
+ const targets = await Promise.all(clientIds.filter((clientId) => clientId !== skipClientId).map(async (clientId) => {
4705
+ const client = await config.clientStore.findClient(clientId);
4706
+ return client?.backchannelLogoutUri === undefined ? undefined : { client, endpointUrl: client.backchannelLogoutUri };
4707
+ }));
4708
+ const reachable = targets.filter((target) => target !== undefined);
4709
+ await Promise.all(reachable.map(async ({ client, endpointUrl }) => {
4710
+ const logoutToken = await mintLogoutToken({
4711
+ clientId: client.clientId,
4712
+ config,
4713
+ now,
4714
+ sub: userId
4715
+ });
4716
+ await deliverLogoutToOne({
4717
+ clientId: client.clientId,
4718
+ config,
4719
+ endpointUrl,
4720
+ fetchImpl,
4721
+ logoutToken,
4722
+ onError,
4723
+ timeoutMs,
4724
+ userId
4725
+ });
4726
+ }));
4727
+ return reachable.map(({ client }) => client.clientId);
4728
+ };
4729
+
4499
4730
  // src/oidc/routes.ts
4500
4731
  var HTTP_OK2 = 200;
4501
4732
  var HTTP_BAD_REQUEST2 = 400;
@@ -4549,6 +4780,7 @@ var oidcProviderRoutes = (config) => {
4549
4780
  const revokeRoute = `${oidcRoute}/revoke`;
4550
4781
  const deviceAuthorizationRoute = `${oidcRoute}/device_authorization`;
4551
4782
  const deviceApproveRoute = `${oidcRoute}/device/decision`;
4783
+ const endSessionRoute = `${oidcRoute}/end_session`;
4552
4784
  const tokenUrl = `${issuer}${oidcRoute}/token`;
4553
4785
  const authenticateClient = async (clientId, clientSecret) => {
4554
4786
  const client = await clientStore.findClient(clientId);
@@ -4561,6 +4793,28 @@ var oidcProviderRoutes = (config) => {
4561
4793
  const matches = await constantTimeEqual(await hashToken(clientSecret), client.hashedSecret);
4562
4794
  return matches ? client : undefined;
4563
4795
  };
4796
+ const authenticateTokenClient = async ({
4797
+ basicClientId,
4798
+ basicClientSecret,
4799
+ bodyClientAssertion,
4800
+ bodyClientAssertionType,
4801
+ bodyClientId,
4802
+ bodyClientSecret
4803
+ }) => {
4804
+ if (bodyClientAssertion !== undefined && bodyClientAssertionType === CLIENT_ASSERTION_TYPE) {
4805
+ return verifyClientAssertion({
4806
+ assertion: bodyClientAssertion,
4807
+ expectedAudience: tokenUrl,
4808
+ jtiStore: config.clientAssertionJtiStore,
4809
+ resolveClient: clientStore.findClient
4810
+ });
4811
+ }
4812
+ const clientId = bodyClientId ?? basicClientId;
4813
+ const clientSecret = bodyClientSecret ?? basicClientSecret;
4814
+ if (clientId === undefined)
4815
+ return;
4816
+ return authenticateClient(clientId, clientSecret);
4817
+ };
4564
4818
  const grantAuthorizationCode = async (client, body, dpop) => {
4565
4819
  const {
4566
4820
  code,
@@ -4692,8 +4946,11 @@ var oidcProviderRoutes = (config) => {
4692
4946
  }
4693
4947
  const discovery = {
4694
4948
  authorization_endpoint: `${issuer}${authorizeRoute}`,
4949
+ backchannel_logout_session_supported: false,
4950
+ backchannel_logout_supported: true,
4695
4951
  code_challenge_methods_supported: ["S256"],
4696
4952
  dpop_signing_alg_values_supported: ["ES256"],
4953
+ end_session_endpoint: `${issuer}${endSessionRoute}`,
4697
4954
  grant_types_supported: grantTypes,
4698
4955
  id_token_signing_alg_values_supported: ["ES256"],
4699
4956
  introspection_endpoint: `${issuer}${introspectRoute}`,
@@ -4706,12 +4963,55 @@ var oidcProviderRoutes = (config) => {
4706
4963
  token_endpoint_auth_methods_supported: [
4707
4964
  "client_secret_basic",
4708
4965
  "client_secret_post",
4709
- "none"
4710
- ]
4966
+ "none",
4967
+ "private_key_jwt"
4968
+ ],
4969
+ token_endpoint_auth_signing_alg_values_supported: ["ES256"]
4711
4970
  };
4712
4971
  if (config.deviceAuthorizationStore) {
4713
4972
  discovery.device_authorization_endpoint = `${issuer}${deviceAuthorizationRoute}`;
4714
4973
  }
4974
+ const handleEndSession = async ({
4975
+ cookie,
4976
+ inMemorySession,
4977
+ query
4978
+ }) => {
4979
+ const hint = query.id_token_hint === undefined ? undefined : await verifyIdTokenHint({
4980
+ config,
4981
+ idTokenHint: query.id_token_hint
4982
+ });
4983
+ const clientId = hint?.audClientId ?? query.client_id;
4984
+ const client = clientId === undefined ? undefined : await config.clientStore.findClient(clientId);
4985
+ const userSession = await loadSessionFromSource({
4986
+ authSessionStore,
4987
+ session: inMemorySession,
4988
+ userSessionId: cookie.value
4989
+ });
4990
+ const resolvedSub = hint?.sub ?? (userSession === undefined ? undefined : getUserId(userSession.user));
4991
+ await clearSession({
4992
+ authSessionStore,
4993
+ cookie,
4994
+ inMemorySession
4995
+ });
4996
+ if (resolvedSub !== undefined) {
4997
+ await fanOutBackchannelLogout({
4998
+ config,
4999
+ skipClientId: clientId,
5000
+ userId: resolvedSub
5001
+ });
5002
+ }
5003
+ const redirectUri = client === undefined ? undefined : resolvePostLogoutRedirect({
5004
+ client,
5005
+ requestedUri: query.post_logout_redirect_uri
5006
+ });
5007
+ if (redirectUri === undefined) {
5008
+ return jsonResponse({ ok: true }, HTTP_OK2);
5009
+ }
5010
+ const url = new URL(redirectUri);
5011
+ if (query.state !== undefined)
5012
+ url.searchParams.set("state", query.state);
5013
+ return redirectTo(url.toString());
5014
+ };
4715
5015
  return new Elysia15().use(sessionStore()).get(authorizeRoute, async ({ cookie: { user_session_id }, query, request, store }) => {
4716
5016
  const {
4717
5017
  client_id: clientId,
@@ -4791,12 +5091,14 @@ var oidcProviderRoutes = (config) => {
4791
5091
  })
4792
5092
  }).post(tokenRoute, async ({ body, headers }) => {
4793
5093
  const basic = readBasicAuth2(headers.authorization);
4794
- const clientId = basic.clientId ?? body.client_id;
4795
- const clientSecret = basic.clientSecret ?? body.client_secret;
4796
- if (clientId === undefined) {
4797
- return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
4798
- }
4799
- const client = await authenticateClient(clientId, clientSecret);
5094
+ const client = await authenticateTokenClient({
5095
+ basicClientId: basic.clientId,
5096
+ basicClientSecret: basic.clientSecret,
5097
+ bodyClientAssertion: body.client_assertion,
5098
+ bodyClientAssertionType: body.client_assertion_type,
5099
+ bodyClientId: body.client_id,
5100
+ bodyClientSecret: body.client_secret
5101
+ });
4800
5102
  if (client === undefined) {
4801
5103
  return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
4802
5104
  }
@@ -4816,6 +5118,8 @@ var oidcProviderRoutes = (config) => {
4816
5118
  }, {
4817
5119
  body: t12.Object({
4818
5120
  audience: t12.Optional(t12.String()),
5121
+ client_assertion: t12.Optional(t12.String()),
5122
+ client_assertion_type: t12.Optional(t12.String()),
4819
5123
  client_id: t12.Optional(t12.String()),
4820
5124
  client_secret: t12.Optional(t12.String()),
4821
5125
  code: t12.Optional(t12.String()),
@@ -4944,6 +5248,42 @@ var oidcProviderRoutes = (config) => {
4944
5248
  cookie: t12.Cookie({
4945
5249
  user_session_id: t12.Optional(userSessionIdTypebox)
4946
5250
  })
5251
+ }).get(endSessionRoute, async ({
5252
+ cookie: { user_session_id },
5253
+ query,
5254
+ store
5255
+ }) => handleEndSession({
5256
+ cookie: user_session_id,
5257
+ inMemorySession: store.session,
5258
+ query
5259
+ }), {
5260
+ cookie: t12.Cookie({
5261
+ user_session_id: t12.Optional(userSessionIdTypebox)
5262
+ }),
5263
+ query: t12.Object({
5264
+ client_id: t12.Optional(t12.String()),
5265
+ id_token_hint: t12.Optional(t12.String()),
5266
+ post_logout_redirect_uri: t12.Optional(t12.String()),
5267
+ state: t12.Optional(t12.String())
5268
+ })
5269
+ }).post(endSessionRoute, async ({
5270
+ body,
5271
+ cookie: { user_session_id },
5272
+ store
5273
+ }) => handleEndSession({
5274
+ cookie: user_session_id,
5275
+ inMemorySession: store.session,
5276
+ query: body
5277
+ }), {
5278
+ body: t12.Object({
5279
+ client_id: t12.Optional(t12.String()),
5280
+ id_token_hint: t12.Optional(t12.String()),
5281
+ post_logout_redirect_uri: t12.Optional(t12.String()),
5282
+ state: t12.Optional(t12.String())
5283
+ }),
5284
+ cookie: t12.Cookie({
5285
+ user_session_id: t12.Optional(userSessionIdTypebox)
5286
+ })
4947
5287
  }).get(jwksRoute, () => ({ keys: [toPublicJwk(signingKey)] })).get("/.well-known/openid-configuration", () => discovery);
4948
5288
  };
4949
5289
 
@@ -7986,8 +8326,8 @@ var attemptOnce = async ({
7986
8326
  }
7987
8327
  return response.status;
7988
8328
  };
7989
- var errorMessage = (error) => error instanceof Error ? error.message : String(error);
7990
- var statusFromError = (error) => {
8329
+ var errorMessage2 = (error) => error instanceof Error ? error.message : String(error);
8330
+ var statusFromError2 = (error) => {
7991
8331
  if (!(error instanceof Error))
7992
8332
  return;
7993
8333
  const match = /returned (\d+)/.exec(error.message);
@@ -8007,8 +8347,8 @@ var persistFailure = async ({
8007
8347
  createdAt: Date.now(),
8008
8348
  endpointUrl: endpoint.url,
8009
8349
  envelope,
8010
- lastError: errorMessage(lastError),
8011
- lastStatus: statusFromError(lastError)
8350
+ lastError: errorMessage2(lastError),
8351
+ lastStatus: statusFromError2(lastError)
8012
8352
  };
8013
8353
  await deliveryStore.recordFailure(record);
8014
8354
  };
@@ -20083,6 +20423,7 @@ var createPostgresApiKeyStore = (db) => ({
20083
20423
  }
20084
20424
  });
20085
20425
  // src/oidc/inMemoryStores.ts
20426
+ var DEFAULT_LIST_LIMIT = 100;
20086
20427
  var createInMemoryAuthorizationCodeStore = () => {
20087
20428
  const codes = new Map;
20088
20429
  return {
@@ -20096,6 +20437,23 @@ var createInMemoryAuthorizationCodeStore = () => {
20096
20437
  }
20097
20438
  };
20098
20439
  };
20440
+ var createInMemoryClientAssertionJtiStore = () => {
20441
+ const seen = new Map;
20442
+ return {
20443
+ recordIfFresh: async (clientId, jti, expiresAt) => {
20444
+ const now = Date.now();
20445
+ for (const [key, expiry] of seen) {
20446
+ if (expiry < now)
20447
+ seen.delete(key);
20448
+ }
20449
+ const composite = `${clientId}|${jti}`;
20450
+ if (seen.has(composite))
20451
+ return false;
20452
+ seen.set(composite, expiresAt);
20453
+ return true;
20454
+ }
20455
+ };
20456
+ };
20099
20457
  var createInMemoryDeviceAuthorizationStore = () => {
20100
20458
  const byDeviceCode = new Map;
20101
20459
  return {
@@ -20123,6 +20481,18 @@ var createInMemoryDeviceAuthorizationStore = () => {
20123
20481
  }
20124
20482
  };
20125
20483
  };
20484
+ var createInMemoryLogoutDeliveryStore = () => {
20485
+ const failures = new Map;
20486
+ return {
20487
+ listFailed: async (limit = DEFAULT_LIST_LIMIT) => Array.from(failures.values()).sort((left, right) => right.createdAt - left.createdAt).slice(0, limit),
20488
+ recordFailure: async (delivery) => {
20489
+ failures.set(delivery.id, delivery);
20490
+ },
20491
+ removeFailure: async (deliveryId) => {
20492
+ failures.delete(deliveryId);
20493
+ }
20494
+ };
20495
+ };
20126
20496
  var createInMemoryOAuthClientStore = (clients) => {
20127
20497
  const registry = new Map(clients.map((client) => [client.clientId, client]));
20128
20498
  return {
@@ -20144,17 +20514,38 @@ var createInMemoryOidcRefreshTokenStore = () => {
20144
20514
  }
20145
20515
  },
20146
20516
  getToken: async (tokenHash) => tokens.get(tokenHash),
20517
+ listClientIdsForUser: async (userId) => {
20518
+ const now = Date.now();
20519
+ const active = Array.from(tokens.values()).filter((token) => token.userId === userId && token.expiresAt > now);
20520
+ return Array.from(new Set(active.map((token) => token.clientId)));
20521
+ },
20147
20522
  saveToken: async (token) => {
20148
20523
  tokens.set(token.tokenHash, { ...token });
20149
20524
  }
20150
20525
  };
20151
20526
  };
20152
20527
  // src/oidc/postgresStores.ts
20528
+ var URL_LENGTH = 2048;
20529
+ var DEFAULT_LIST_LIMIT2 = 100;
20153
20530
  var ID_LENGTH7 = 255;
20531
+ var oauthClientAssertionJtisTable = pgTable("auth_oauth_client_assertion_jtis", {
20532
+ client_id: varchar("client_id", { length: ID_LENGTH7 }).notNull(),
20533
+ composite_key: varchar("composite_key", {
20534
+ length: ID_LENGTH7
20535
+ }).primaryKey(),
20536
+ expires_at_ms: bigint("expires_at_ms", { mode: "number" }).notNull(),
20537
+ jti: varchar("jti", { length: ID_LENGTH7 }).notNull()
20538
+ });
20154
20539
  var oauthClientsTable = pgTable("auth_oauth_clients", {
20540
+ backchannel_logout_uri: varchar("backchannel_logout_uri", {
20541
+ length: URL_LENGTH
20542
+ }),
20155
20543
  client_id: varchar("client_id", { length: ID_LENGTH7 }).primaryKey(),
20156
20544
  hashed_secret: varchar("hashed_secret", { length: ID_LENGTH7 }),
20545
+ jwks_json: jsonb("jwks_json").$type(),
20546
+ jwks_uri: varchar("jwks_uri", { length: URL_LENGTH }),
20157
20547
  name: varchar("name", { length: ID_LENGTH7 }).notNull(),
20548
+ post_logout_redirect_uris: text("post_logout_redirect_uris").array(),
20158
20549
  redirect_uris: text("redirect_uris").array().notNull(),
20159
20550
  scopes: text("scopes").array().notNull()
20160
20551
  });
@@ -20184,6 +20575,17 @@ var oauthDeviceAuthorizationsTable = pgTable("auth_oauth_device_authorizations",
20184
20575
  user_code: varchar("user_code", { length: 16 }).notNull().unique(),
20185
20576
  user_sub: varchar("user_sub", { length: ID_LENGTH7 })
20186
20577
  });
20578
+ var oauthLogoutDeliveriesTable = pgTable("auth_oauth_logout_deliveries", {
20579
+ attempts: bigint("attempts", { mode: "number" }).notNull(),
20580
+ client_id: varchar("client_id", { length: ID_LENGTH7 }).notNull(),
20581
+ created_at_ms: bigint("created_at_ms", { mode: "number" }).notNull(),
20582
+ endpoint_url: varchar("endpoint_url", { length: URL_LENGTH }).notNull(),
20583
+ id: varchar("id", { length: ID_LENGTH7 }).primaryKey(),
20584
+ last_error: text("last_error"),
20585
+ last_status: bigint("last_status", { mode: "number" }),
20586
+ logout_token: text("logout_token").notNull(),
20587
+ user_id: varchar("user_id", { length: ID_LENGTH7 }).notNull()
20588
+ });
20187
20589
  var oauthRefreshTokensTable = pgTable("auth_oauth_refresh_tokens", {
20188
20590
  claims_json: jsonb("claims_json").$type(),
20189
20591
  client_id: varchar("client_id", { length: ID_LENGTH7 }).notNull(),
@@ -20195,12 +20597,27 @@ var oauthRefreshTokensTable = pgTable("auth_oauth_refresh_tokens", {
20195
20597
  user_id: varchar("user_id", { length: ID_LENGTH7 }).notNull()
20196
20598
  });
20197
20599
  var toClient2 = (row) => ({
20600
+ backchannelLogoutUri: row.backchannel_logout_uri ?? undefined,
20198
20601
  clientId: row.client_id,
20199
20602
  hashedSecret: row.hashed_secret ?? undefined,
20603
+ jwks: row.jwks_json ?? undefined,
20604
+ jwksUri: row.jwks_uri ?? undefined,
20200
20605
  name: row.name,
20606
+ postLogoutRedirectUris: row.post_logout_redirect_uris ?? undefined,
20201
20607
  redirectUris: row.redirect_uris,
20202
20608
  scopes: row.scopes
20203
20609
  });
20610
+ var toLogoutDelivery = (row) => ({
20611
+ attempts: row.attempts,
20612
+ clientId: row.client_id,
20613
+ createdAt: row.created_at_ms,
20614
+ endpointUrl: row.endpoint_url,
20615
+ id: row.id,
20616
+ lastError: row.last_error ?? undefined,
20617
+ lastStatus: row.last_status ?? undefined,
20618
+ logoutToken: row.logout_token,
20619
+ userId: row.user_id
20620
+ });
20204
20621
  var toCode = (row) => ({
20205
20622
  claims: row.claims_json ?? undefined,
20206
20623
  clientId: row.client_id,
@@ -20259,7 +20676,9 @@ var toRefreshValues = (token) => ({
20259
20676
  user_id: token.userId
20260
20677
  });
20261
20678
  var createNeonAuthorizationCodeStore = (databaseUrl) => createPostgresAuthorizationCodeStore(createNeonDatabase(databaseUrl));
20679
+ var createNeonClientAssertionJtiStore = (databaseUrl) => createPostgresClientAssertionJtiStore(createNeonDatabase(databaseUrl));
20262
20680
  var createNeonDeviceAuthorizationStore = (databaseUrl) => createPostgresDeviceAuthorizationStore(createNeonDatabase(databaseUrl));
20681
+ var createNeonLogoutDeliveryStore = (databaseUrl) => createPostgresLogoutDeliveryStore(createNeonDatabase(databaseUrl));
20263
20682
  var createNeonOAuthClientStore = (databaseUrl) => createPostgresOAuthClientStore(createNeonDatabase(databaseUrl));
20264
20683
  var createNeonOidcRefreshTokenStore = (databaseUrl) => createPostgresOidcRefreshTokenStore(createNeonDatabase(databaseUrl));
20265
20684
  var createPostgresAuthorizationCodeStore = (db) => ({
@@ -20271,6 +20690,23 @@ var createPostgresAuthorizationCodeStore = (db) => ({
20271
20690
  await db.insert(oauthCodesTable).values(toCodeValues(code));
20272
20691
  }
20273
20692
  });
20693
+ var createPostgresClientAssertionJtiStore = (db) => ({
20694
+ recordIfFresh: async (clientId, jti, expiresAt) => {
20695
+ await db.delete(oauthClientAssertionJtisTable).where(lt(oauthClientAssertionJtisTable.expires_at_ms, Date.now()));
20696
+ const compositeKey = `${clientId}|${jti}`;
20697
+ try {
20698
+ await db.insert(oauthClientAssertionJtisTable).values({
20699
+ client_id: clientId,
20700
+ composite_key: compositeKey,
20701
+ expires_at_ms: expiresAt,
20702
+ jti
20703
+ });
20704
+ return true;
20705
+ } catch {
20706
+ return false;
20707
+ }
20708
+ }
20709
+ });
20274
20710
  var createPostgresDeviceAuthorizationStore = (db) => ({
20275
20711
  deleteByDeviceCodeHash: async (deviceCodeHash) => {
20276
20712
  await db.delete(oauthDeviceAuthorizationsTable).where(eq(oauthDeviceAuthorizationsTable.device_code_hash, deviceCodeHash));
@@ -20300,6 +20736,28 @@ var createPostgresDeviceAuthorizationStore = (db) => ({
20300
20736
  await db.update(oauthDeviceAuthorizationsTable).set({ status, user_sub: userSub ?? null }).where(eq(oauthDeviceAuthorizationsTable.device_code_hash, deviceCodeHash));
20301
20737
  }
20302
20738
  });
20739
+ var createPostgresLogoutDeliveryStore = (db) => ({
20740
+ listFailed: async (limit = DEFAULT_LIST_LIMIT2) => {
20741
+ const rows = await db.select().from(oauthLogoutDeliveriesTable).orderBy(desc(oauthLogoutDeliveriesTable.created_at_ms)).limit(limit);
20742
+ return rows.map(toLogoutDelivery);
20743
+ },
20744
+ recordFailure: async (delivery) => {
20745
+ await db.insert(oauthLogoutDeliveriesTable).values({
20746
+ attempts: delivery.attempts,
20747
+ client_id: delivery.clientId,
20748
+ created_at_ms: delivery.createdAt,
20749
+ endpoint_url: delivery.endpointUrl,
20750
+ id: delivery.id,
20751
+ last_error: delivery.lastError ?? null,
20752
+ last_status: delivery.lastStatus ?? null,
20753
+ logout_token: delivery.logoutToken,
20754
+ user_id: delivery.userId
20755
+ });
20756
+ },
20757
+ removeFailure: async (deliveryId) => {
20758
+ await db.delete(oauthLogoutDeliveriesTable).where(eq(oauthLogoutDeliveriesTable.id, deliveryId));
20759
+ }
20760
+ });
20303
20761
  var createPostgresOAuthClientStore = (db) => ({
20304
20762
  findClient: async (clientId) => {
20305
20763
  const [row] = await db.select().from(oauthClientsTable).where(eq(oauthClientsTable.client_id, clientId)).limit(1);
@@ -20318,6 +20776,10 @@ var createPostgresOidcRefreshTokenStore = (db) => ({
20318
20776
  const [row] = await db.select().from(oauthRefreshTokensTable).where(eq(oauthRefreshTokensTable.token_hash, tokenHash)).limit(1);
20319
20777
  return row ? toRefresh(row) : undefined;
20320
20778
  },
20779
+ listClientIdsForUser: async (userId) => {
20780
+ const rows = await db.selectDistinct({ client_id: oauthRefreshTokensTable.client_id }).from(oauthRefreshTokensTable).where(and(eq(oauthRefreshTokensTable.user_id, userId), gt(oauthRefreshTokensTable.expires_at_ms, Date.now())));
20781
+ return rows.map((row) => row.client_id);
20782
+ },
20321
20783
  saveToken: async (token) => {
20322
20784
  await db.insert(oauthRefreshTokensTable).values(toRefreshValues(token));
20323
20785
  }
@@ -21451,11 +21913,11 @@ var createPostgresPasswordlessTokenStore = (db) => ({
21451
21913
  }
21452
21914
  });
21453
21915
  // src/webhooks/inMemoryStore.ts
21454
- var DEFAULT_LIST_LIMIT = 100;
21916
+ var DEFAULT_LIST_LIMIT3 = 100;
21455
21917
  var createInMemoryWebhookDeliveryStore = () => {
21456
21918
  const failures = new Map;
21457
21919
  return {
21458
- listFailed: async (limit = DEFAULT_LIST_LIMIT) => Array.from(failures.values()).sort((left, right) => right.createdAt - left.createdAt).slice(0, limit),
21920
+ listFailed: async (limit = DEFAULT_LIST_LIMIT3) => Array.from(failures.values()).sort((left, right) => right.createdAt - left.createdAt).slice(0, limit),
21459
21921
  recordFailure: async (delivery) => {
21460
21922
  failures.set(delivery.envelope.id, delivery);
21461
21923
  },
@@ -21466,12 +21928,12 @@ var createInMemoryWebhookDeliveryStore = () => {
21466
21928
  };
21467
21929
  // src/webhooks/postgresStore.ts
21468
21930
  var ID_LENGTH15 = 255;
21469
- var URL_LENGTH = 2048;
21470
- var DEFAULT_LIST_LIMIT2 = 100;
21931
+ var URL_LENGTH2 = 2048;
21932
+ var DEFAULT_LIST_LIMIT4 = 100;
21471
21933
  var webhookDeliveriesTable = pgTable("auth_webhook_deliveries", {
21472
21934
  attempts: bigint("attempts", { mode: "number" }).notNull(),
21473
21935
  created_at_ms: bigint("created_at_ms", { mode: "number" }).notNull(),
21474
- endpoint_url: varchar("endpoint_url", { length: URL_LENGTH }).notNull(),
21936
+ endpoint_url: varchar("endpoint_url", { length: URL_LENGTH2 }).notNull(),
21475
21937
  envelope_id: varchar("envelope_id", { length: ID_LENGTH15 }).primaryKey(),
21476
21938
  envelope_json: jsonb("envelope_json").$type().notNull(),
21477
21939
  last_error: text("last_error"),
@@ -21487,7 +21949,7 @@ var toDelivery = (row) => ({
21487
21949
  });
21488
21950
  var createNeonWebhookDeliveryStore = (databaseUrl) => createPostgresWebhookDeliveryStore(createNeonDatabase(databaseUrl));
21489
21951
  var createPostgresWebhookDeliveryStore = (db) => ({
21490
- listFailed: async (limit = DEFAULT_LIST_LIMIT2) => {
21952
+ listFailed: async (limit = DEFAULT_LIST_LIMIT4) => {
21491
21953
  const rows = await db.select().from(webhookDeliveriesTable).orderBy(desc(webhookDeliveriesTable.created_at_ms)).limit(limit);
21492
21954
  return rows.map(toDelivery);
21493
21955
  },
@@ -21742,8 +22204,10 @@ export {
21742
22204
  verifyPkce,
21743
22205
  verifyPassword,
21744
22206
  verifyJwt,
22207
+ verifyIdTokenHint,
21745
22208
  verifyHcaptcha,
21746
22209
  verifyDpopProof,
22210
+ verifyClientAssertion,
21747
22211
  verifyAuditChain,
21748
22212
  verifyApiKey,
21749
22213
  verifyAccessToken,
@@ -21780,6 +22244,7 @@ export {
21780
22244
  resolveSetupSession,
21781
22245
  resolveScimOrganization,
21782
22246
  resolveProviderClientConfiguration,
22247
+ resolvePostLogoutRedirect,
21783
22248
  resolvePermissions,
21784
22249
  resolveOAuthTokenExpiresAt,
21785
22250
  resolveOAuthAuthorization,
@@ -21807,9 +22272,12 @@ export {
21807
22272
  oidcProviderRoutes,
21808
22273
  oidcProviderOptions,
21809
22274
  oauthRefreshTokensTable,
22275
+ oauthLogoutDeliveriesTable,
21810
22276
  oauthDeviceAuthorizationsTable,
21811
22277
  oauthCodesTable,
21812
22278
  oauthClientsTable,
22279
+ oauthClientAssertionJtisTable,
22280
+ mintLogoutToken,
21813
22281
  mfaTotpRoutes,
21814
22282
  mfaRoutes,
21815
22283
  mfaEnrollmentsTable,
@@ -21858,6 +22326,7 @@ export {
21858
22326
  generateEncryptionKey,
21859
22327
  generateBackupCodes,
21860
22328
  fingerprintDevice,
22329
+ fanOutBackchannelLogout,
21861
22330
  extractPropFromIdentity,
21862
22331
  exportAuditCsv,
21863
22332
  exchangeToken,
@@ -21909,11 +22378,13 @@ export {
21909
22378
  createPostgresOidcRefreshTokenStore,
21910
22379
  createPostgresOAuthClientStore,
21911
22380
  createPostgresMfaStore,
22381
+ createPostgresLogoutDeliveryStore,
21912
22382
  createPostgresLoginHistoryStore,
21913
22383
  createPostgresLockoutStore,
21914
22384
  createPostgresKnownDeviceStore,
21915
22385
  createPostgresDeviceAuthorizationStore,
21916
22386
  createPostgresCredentialStore,
22387
+ createPostgresClientAssertionJtiStore,
21917
22388
  createPostgresAuthorizationCodeStore,
21918
22389
  createPostgresAuditSink,
21919
22390
  createPostgresApiKeyStore,
@@ -21935,6 +22406,7 @@ export {
21935
22406
  createNeonOAuthLinkedProviderCredentialResolver,
21936
22407
  createNeonOAuthClientStore,
21937
22408
  createNeonMfaStore,
22409
+ createNeonLogoutDeliveryStore,
21938
22410
  createNeonLoginHistoryStore,
21939
22411
  createNeonLockoutStore,
21940
22412
  createNeonLinkedProviderStores,
@@ -21942,6 +22414,7 @@ export {
21942
22414
  createNeonDeviceAuthorizationStore,
21943
22415
  createNeonDatabase,
21944
22416
  createNeonCredentialStore,
22417
+ createNeonClientAssertionJtiStore,
21945
22418
  createNeonAuthorizationCodeStore,
21946
22419
  createNeonAuthSessionStore,
21947
22420
  createNeonAuditSink,
@@ -21965,12 +22438,14 @@ export {
21965
22438
  createInMemoryOidcRefreshTokenStore,
21966
22439
  createInMemoryOAuthClientStore,
21967
22440
  createInMemoryMfaStore,
22441
+ createInMemoryLogoutDeliveryStore,
21968
22442
  createInMemoryLoginHistoryStore,
21969
22443
  createInMemoryLockoutStore,
21970
22444
  createInMemoryLinkedProviderStores,
21971
22445
  createInMemoryKnownDeviceStore,
21972
22446
  createInMemoryDeviceAuthorizationStore,
21973
22447
  createInMemoryCredentialStore,
22448
+ createInMemoryClientAssertionJtiStore,
21974
22449
  createInMemoryCheckCache,
21975
22450
  createInMemoryAuthorizationCodeStore,
21976
22451
  createInMemoryAuthSessionStore,
@@ -22036,8 +22511,9 @@ export {
22036
22511
  DEFAULT_INVITATION_TTL_MS,
22037
22512
  DEFAULT_CREDENTIAL_SESSION_TTL_MS,
22038
22513
  DEFAULT_BACKUP_CODE_COUNT,
22514
+ CLIENT_ASSERTION_TYPE,
22039
22515
  AuthIdentityConflictError
22040
22516
  };
22041
22517
 
22042
- //# debugId=451C9751C02E5E2164756E2164756E21
22518
+ //# debugId=A9BAB004D30595D664756E2164756E21
22043
22519
  //# sourceMappingURL=index.js.map