@absolutejs/auth 0.27.0 → 0.29.0-beta.0

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
@@ -4297,6 +4297,160 @@ var mcpProtectedResourceMetadata = ({
4297
4297
  scopes_supported: scopes ?? []
4298
4298
  });
4299
4299
  var verifyPkce = async (codeVerifier, codeChallenge) => await hashToken(codeVerifier) === codeChallenge;
4300
+ var inactive = { active: false };
4301
+ var introspectToken = async ({
4302
+ config,
4303
+ hint,
4304
+ now = Date.now(),
4305
+ token
4306
+ }) => {
4307
+ if (hint !== "refresh_token") {
4308
+ const verified = await verifyJwt(token, config.signingKey.publicJwk);
4309
+ const payload = verified?.payload;
4310
+ if (payload !== undefined && typeof payload.sub === "string" && typeof payload.exp === "number" && payload.exp > nowSeconds(now)) {
4311
+ return {
4312
+ active: true,
4313
+ client_id: typeof payload.client_id === "string" ? payload.client_id : "",
4314
+ exp: payload.exp,
4315
+ iat: typeof payload.iat === "number" ? payload.iat : 0,
4316
+ scope: typeof payload.scope === "string" ? payload.scope : "",
4317
+ sub: payload.sub,
4318
+ token_type: "access_token"
4319
+ };
4320
+ }
4321
+ }
4322
+ if (hint !== "access_token") {
4323
+ const refresh = await config.refreshTokenStore.getToken(await hashToken(token));
4324
+ if (refresh && refresh.expiresAt > now) {
4325
+ return {
4326
+ active: true,
4327
+ client_id: refresh.clientId,
4328
+ exp: nowSeconds(refresh.expiresAt),
4329
+ iat: nowSeconds(refresh.createdAt),
4330
+ scope: refresh.scopes.join(" "),
4331
+ sub: refresh.userId,
4332
+ token_type: "refresh_token"
4333
+ };
4334
+ }
4335
+ }
4336
+ return inactive;
4337
+ };
4338
+ var revokeRefreshToken = async (config, token) => {
4339
+ const consumed = await config.refreshTokenStore.consumeToken(await hashToken(token));
4340
+ return consumed !== undefined;
4341
+ };
4342
+ var DEVICE_CODE_BYTES = 32;
4343
+ var USER_CODE_HALF_LENGTH = 4;
4344
+ var USER_CODE_ALPHABET = "BCDFGHJKLMNPQRSTVWXZ23456789";
4345
+ var DEFAULT_DEVICE_CODE_TTL_MINUTES = 15;
4346
+ var DEFAULT_DEVICE_CODE_TTL_MS = MILLISECONDS_IN_A_MINUTE * DEFAULT_DEVICE_CODE_TTL_MINUTES;
4347
+ var DEFAULT_DEVICE_POLL_INTERVAL_SECONDS = 5;
4348
+ var generateUserCode = () => {
4349
+ const length = USER_CODE_HALF_LENGTH * 2;
4350
+ const random = crypto.getRandomValues(new Uint8Array(length));
4351
+ let code = "";
4352
+ for (const byte of random) {
4353
+ code += USER_CODE_ALPHABET[byte % USER_CODE_ALPHABET.length];
4354
+ }
4355
+ return `${code.slice(0, USER_CODE_HALF_LENGTH)}-${code.slice(USER_CODE_HALF_LENGTH)}`;
4356
+ };
4357
+ var issueDeviceAuthorization = async ({
4358
+ clientId,
4359
+ config,
4360
+ now = Date.now(),
4361
+ requestedScopes
4362
+ }) => {
4363
+ if (!config.deviceAuthorizationStore) {
4364
+ throw new Error("oidc.deviceAuthorizationStore is not configured \u2014 cannot start a device flow");
4365
+ }
4366
+ const deviceCode = generateSecureToken(DEVICE_CODE_BYTES);
4367
+ const userCode = generateUserCode();
4368
+ const ttl = config.deviceCodeTtlMs ?? DEFAULT_DEVICE_CODE_TTL_MS;
4369
+ const interval = config.devicePollIntervalSeconds ?? DEFAULT_DEVICE_POLL_INTERVAL_SECONDS;
4370
+ await config.deviceAuthorizationStore.saveDeviceAuthorization({
4371
+ clientId,
4372
+ createdAt: now,
4373
+ deviceCodeHash: await hashToken(deviceCode),
4374
+ expiresAt: now + ttl,
4375
+ intervalSeconds: interval,
4376
+ scopes: requestedScopes,
4377
+ status: "pending",
4378
+ userCode
4379
+ });
4380
+ const verificationUri = `${config.issuer}${config.oidcRoute ?? DEFAULT_OIDC_ROUTE}/device`;
4381
+ return {
4382
+ device_code: deviceCode,
4383
+ expires_in: Math.floor(ttl / MS_PER_SECOND),
4384
+ interval,
4385
+ user_code: userCode,
4386
+ verification_uri: verificationUri,
4387
+ verification_uri_complete: `${verificationUri}?user_code=${encodeURIComponent(userCode)}`
4388
+ };
4389
+ };
4390
+ var decideDeviceAuthorization = async (config, userCode, approval) => {
4391
+ if (!config.deviceAuthorizationStore) {
4392
+ return { error: "not_configured", ok: false };
4393
+ }
4394
+ const record = await config.deviceAuthorizationStore.findByUserCode(userCode);
4395
+ if (!record)
4396
+ return { error: "invalid_user_code", ok: false };
4397
+ if (record.expiresAt < Date.now()) {
4398
+ return { error: "expired_token", ok: false };
4399
+ }
4400
+ if (record.status !== "pending") {
4401
+ return { error: "already_decided", ok: false };
4402
+ }
4403
+ await config.deviceAuthorizationStore.updateStatus(record.deviceCodeHash, approval.status, approval.userSub);
4404
+ return { ok: true };
4405
+ };
4406
+ var approveDeviceAuthorization = async ({
4407
+ config,
4408
+ userCode,
4409
+ userSub
4410
+ }) => decideDeviceAuthorization(config, userCode, {
4411
+ status: "approved",
4412
+ userSub
4413
+ });
4414
+ var denyDeviceAuthorization = async ({
4415
+ config,
4416
+ userCode
4417
+ }) => decideDeviceAuthorization(config, userCode, { status: "denied" });
4418
+ var exchangeDeviceCode = async ({
4419
+ clientId,
4420
+ config,
4421
+ deviceCode,
4422
+ dpopJkt,
4423
+ now = Date.now()
4424
+ }) => {
4425
+ if (!config.deviceAuthorizationStore) {
4426
+ return { error: "invalid_grant", ok: false };
4427
+ }
4428
+ const deviceCodeHash = await hashToken(deviceCode);
4429
+ const record = await config.deviceAuthorizationStore.findByDeviceCodeHash(deviceCodeHash);
4430
+ if (!record || record.clientId !== clientId) {
4431
+ return { error: "invalid_grant", ok: false };
4432
+ }
4433
+ if (record.expiresAt < now) {
4434
+ await config.deviceAuthorizationStore.deleteByDeviceCodeHash(deviceCodeHash);
4435
+ return { error: "expired_token", ok: false };
4436
+ }
4437
+ if (record.status === "pending") {
4438
+ return { error: "authorization_pending", ok: false };
4439
+ }
4440
+ if (record.status === "denied" || record.userSub === undefined) {
4441
+ return { error: "access_denied", ok: false };
4442
+ }
4443
+ await config.deviceAuthorizationStore.deleteByDeviceCodeHash(deviceCodeHash);
4444
+ const tokenSet = await issueTokenSet({
4445
+ clientId,
4446
+ config,
4447
+ dpopJkt,
4448
+ now,
4449
+ scopes: record.scopes,
4450
+ sub: record.userSub
4451
+ });
4452
+ return { ...tokenSet, ok: true };
4453
+ };
4300
4454
 
4301
4455
  // src/oidc/dpop.ts
4302
4456
  var DEFAULT_MAX_AGE_MS = 60000;
@@ -4391,6 +4545,10 @@ var oidcProviderRoutes = (config) => {
4391
4545
  const authorizeRoute = `${oidcRoute}/authorize`;
4392
4546
  const tokenRoute = `${oidcRoute}/token`;
4393
4547
  const jwksRoute = `${oidcRoute}/jwks`;
4548
+ const introspectRoute = `${oidcRoute}/introspect`;
4549
+ const revokeRoute = `${oidcRoute}/revoke`;
4550
+ const deviceAuthorizationRoute = `${oidcRoute}/device_authorization`;
4551
+ const deviceApproveRoute = `${oidcRoute}/device/decision`;
4394
4552
  const tokenUrl = `${issuer}${oidcRoute}/token`;
4395
4553
  const authenticateClient = async (clientId, clientSecret) => {
4396
4554
  const client = await clientStore.findClient(clientId);
@@ -4492,19 +4650,57 @@ var oidcProviderRoutes = (config) => {
4492
4650
  token_type: dpopResult === undefined ? "Bearer" : "DPoP"
4493
4651
  }, HTTP_OK2);
4494
4652
  };
4653
+ const grantDeviceCode = async (client, body, dpop) => {
4654
+ if (config.deviceAuthorizationStore === undefined) {
4655
+ return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
4656
+ }
4657
+ if (body.device_code === undefined) {
4658
+ return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
4659
+ }
4660
+ const dpopResult = dpop === undefined ? undefined : await verifyDpopProof({
4661
+ htm: "POST",
4662
+ htu: tokenUrl,
4663
+ proof: dpop
4664
+ });
4665
+ if (dpop !== undefined && dpopResult === undefined) {
4666
+ return oauthError2(HTTP_BAD_REQUEST2, "invalid_dpop_proof");
4667
+ }
4668
+ const result = await exchangeDeviceCode({
4669
+ clientId: client.clientId,
4670
+ config,
4671
+ deviceCode: body.device_code,
4672
+ dpopJkt: dpopResult?.jkt
4673
+ });
4674
+ if (!result.ok)
4675
+ return oauthError2(HTTP_BAD_REQUEST2, result.error);
4676
+ return jsonResponse({
4677
+ access_token: result.access_token,
4678
+ expires_in: result.expires_in,
4679
+ id_token: result.id_token,
4680
+ refresh_token: result.refresh_token,
4681
+ scope: result.scope,
4682
+ token_type: dpopResult === undefined ? "Bearer" : "DPoP"
4683
+ }, HTTP_OK2);
4684
+ };
4685
+ const grantTypes = [
4686
+ "authorization_code",
4687
+ "refresh_token",
4688
+ "urn:ietf:params:oauth:grant-type:token-exchange"
4689
+ ];
4690
+ if (config.deviceAuthorizationStore) {
4691
+ grantTypes.push("urn:ietf:params:oauth:grant-type:device_code");
4692
+ }
4495
4693
  const discovery = {
4496
4694
  authorization_endpoint: `${issuer}${authorizeRoute}`,
4497
4695
  code_challenge_methods_supported: ["S256"],
4498
4696
  dpop_signing_alg_values_supported: ["ES256"],
4499
- grant_types_supported: [
4500
- "authorization_code",
4501
- "refresh_token",
4502
- "urn:ietf:params:oauth:grant-type:token-exchange"
4503
- ],
4697
+ grant_types_supported: grantTypes,
4504
4698
  id_token_signing_alg_values_supported: ["ES256"],
4699
+ introspection_endpoint: `${issuer}${introspectRoute}`,
4505
4700
  issuer,
4506
4701
  jwks_uri: `${issuer}${jwksRoute}`,
4507
4702
  response_types_supported: ["code"],
4703
+ revocation_endpoint: `${issuer}${revokeRoute}`,
4508
4704
  subject_types_supported: ["public"],
4509
4705
  token_endpoint: tokenUrl,
4510
4706
  token_endpoint_auth_methods_supported: [
@@ -4513,6 +4709,9 @@ var oidcProviderRoutes = (config) => {
4513
4709
  "none"
4514
4710
  ]
4515
4711
  };
4712
+ if (config.deviceAuthorizationStore) {
4713
+ discovery.device_authorization_endpoint = `${issuer}${deviceAuthorizationRoute}`;
4714
+ }
4516
4715
  return new Elysia15().use(sessionStore()).get(authorizeRoute, async ({ cookie: { user_session_id }, query, request, store }) => {
4517
4716
  const {
4518
4717
  client_id: clientId,
@@ -4610,6 +4809,9 @@ var oidcProviderRoutes = (config) => {
4610
4809
  if (body.grant_type === "urn:ietf:params:oauth:grant-type:token-exchange") {
4611
4810
  return grantTokenExchange(client, body, headers.dpop);
4612
4811
  }
4812
+ if (body.grant_type === "urn:ietf:params:oauth:grant-type:device_code") {
4813
+ return grantDeviceCode(client, body, headers.dpop);
4814
+ }
4613
4815
  return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
4614
4816
  }, {
4615
4817
  body: t12.Object({
@@ -4618,6 +4820,7 @@ var oidcProviderRoutes = (config) => {
4618
4820
  client_secret: t12.Optional(t12.String()),
4619
4821
  code: t12.Optional(t12.String()),
4620
4822
  code_verifier: t12.Optional(t12.String()),
4823
+ device_code: t12.Optional(t12.String()),
4621
4824
  grant_type: t12.Optional(t12.String()),
4622
4825
  redirect_uri: t12.Optional(t12.String()),
4623
4826
  refresh_token: t12.Optional(t12.String()),
@@ -4626,6 +4829,121 @@ var oidcProviderRoutes = (config) => {
4626
4829
  subject_token: t12.Optional(t12.String()),
4627
4830
  subject_token_type: t12.Optional(t12.String())
4628
4831
  })
4832
+ }).post(introspectRoute, async ({ body, headers }) => {
4833
+ const basic = readBasicAuth2(headers.authorization);
4834
+ const clientId = body.client_id ?? basic.clientId;
4835
+ const clientSecret = body.client_secret ?? basic.clientSecret;
4836
+ if (clientId === undefined) {
4837
+ return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
4838
+ }
4839
+ const client = await authenticateClient(clientId, clientSecret);
4840
+ if (client === undefined) {
4841
+ return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
4842
+ }
4843
+ const result = await introspectToken({
4844
+ config,
4845
+ hint: body.token_type_hint === "access_token" || body.token_type_hint === "refresh_token" ? body.token_type_hint : undefined,
4846
+ now: Date.now(),
4847
+ token: body.token
4848
+ });
4849
+ return jsonResponse(result, HTTP_OK2);
4850
+ }, {
4851
+ body: t12.Object({
4852
+ client_id: t12.Optional(t12.String()),
4853
+ client_secret: t12.Optional(t12.String()),
4854
+ token: t12.String(),
4855
+ token_type_hint: t12.Optional(t12.String())
4856
+ }),
4857
+ headers: t12.Object({
4858
+ authorization: t12.Optional(t12.String())
4859
+ })
4860
+ }).post(revokeRoute, async ({ body, headers }) => {
4861
+ const basic = readBasicAuth2(headers.authorization);
4862
+ const clientId = body.client_id ?? basic.clientId;
4863
+ const clientSecret = body.client_secret ?? basic.clientSecret;
4864
+ if (clientId === undefined) {
4865
+ return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
4866
+ }
4867
+ const client = await authenticateClient(clientId, clientSecret);
4868
+ if (client === undefined) {
4869
+ return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
4870
+ }
4871
+ if (body.token_type_hint !== "access_token") {
4872
+ await revokeRefreshToken(config, body.token);
4873
+ }
4874
+ return new Response(null, { status: HTTP_OK2 });
4875
+ }, {
4876
+ body: t12.Object({
4877
+ client_id: t12.Optional(t12.String()),
4878
+ client_secret: t12.Optional(t12.String()),
4879
+ token: t12.String(),
4880
+ token_type_hint: t12.Optional(t12.String())
4881
+ }),
4882
+ headers: t12.Object({
4883
+ authorization: t12.Optional(t12.String())
4884
+ })
4885
+ }).post(deviceAuthorizationRoute, async ({ body, headers }) => {
4886
+ if (config.deviceAuthorizationStore === undefined) {
4887
+ return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
4888
+ }
4889
+ const basic = readBasicAuth2(headers.authorization);
4890
+ const clientId = body.client_id ?? basic.clientId;
4891
+ const clientSecret = body.client_secret ?? basic.clientSecret;
4892
+ if (clientId === undefined) {
4893
+ return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
4894
+ }
4895
+ const client = await authenticateClient(clientId, clientSecret);
4896
+ if (client === undefined) {
4897
+ return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
4898
+ }
4899
+ const requested = body.scope === undefined || body.scope.length === 0 ? client.scopes : body.scope.split(" ").filter((entry) => client.scopes.includes(entry));
4900
+ const response = await issueDeviceAuthorization({
4901
+ clientId: client.clientId,
4902
+ config,
4903
+ now: Date.now(),
4904
+ requestedScopes: requested
4905
+ });
4906
+ return jsonResponse(response, HTTP_OK2);
4907
+ }, {
4908
+ body: t12.Object({
4909
+ client_id: t12.Optional(t12.String()),
4910
+ client_secret: t12.Optional(t12.String()),
4911
+ scope: t12.Optional(t12.String())
4912
+ }),
4913
+ headers: t12.Object({
4914
+ authorization: t12.Optional(t12.String())
4915
+ })
4916
+ }).post(deviceApproveRoute, async ({ body, cookie: { user_session_id }, store }) => {
4917
+ if (config.deviceAuthorizationStore === undefined) {
4918
+ return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
4919
+ }
4920
+ const userSession = await loadSessionFromSource({
4921
+ authSessionStore,
4922
+ session: store.session,
4923
+ userSessionId: user_session_id.value
4924
+ });
4925
+ if (userSession === undefined) {
4926
+ return oauthError2(HTTP_UNAUTHORIZED2, "login_required");
4927
+ }
4928
+ const result = body.action === "deny" ? await denyDeviceAuthorization({
4929
+ config,
4930
+ userCode: body.user_code
4931
+ }) : await approveDeviceAuthorization({
4932
+ config,
4933
+ userCode: body.user_code,
4934
+ userSub: getUserId(userSession.user)
4935
+ });
4936
+ if (!result.ok)
4937
+ return oauthError2(HTTP_BAD_REQUEST2, result.error);
4938
+ return jsonResponse({ ok: true }, HTTP_OK2);
4939
+ }, {
4940
+ body: t12.Object({
4941
+ action: t12.Optional(t12.Union([t12.Literal("approve"), t12.Literal("deny")])),
4942
+ user_code: t12.String()
4943
+ }),
4944
+ cookie: t12.Cookie({
4945
+ user_session_id: t12.Optional(userSessionIdTypebox)
4946
+ })
4629
4947
  }).get(jwksRoute, () => ({ keys: [toPublicJwk(signingKey)] })).get("/.well-known/openid-configuration", () => discovery);
4630
4948
  };
4631
4949
 
@@ -7602,6 +7920,14 @@ var webauthnRoutes = ({
7602
7920
 
7603
7921
  // src/webhooks/config.ts
7604
7922
  var DEFAULT_TIMEOUT_SECONDS = 5;
7923
+ var DEFAULT_RETRY_ATTEMPTS = 3;
7924
+ var DEFAULT_RETRY_INITIAL_DELAY_MS = MILLISECONDS_IN_A_SECOND;
7925
+ var DEFAULT_RETRY_BACKOFF_MULTIPLIER = 2;
7926
+ var DEFAULT_WEBHOOK_RETRY = {
7927
+ attempts: DEFAULT_RETRY_ATTEMPTS,
7928
+ backoffMultiplier: DEFAULT_RETRY_BACKOFF_MULTIPLIER,
7929
+ initialDelayMs: DEFAULT_RETRY_INITIAL_DELAY_MS
7930
+ };
7605
7931
  var DEFAULT_WEBHOOK_TIMEOUT_MS = MILLISECONDS_IN_A_SECOND * DEFAULT_TIMEOUT_SECONDS;
7606
7932
 
7607
7933
  // src/webhooks/sign.ts
@@ -7634,12 +7960,145 @@ var verifyWebhookSignature = async ({
7634
7960
  };
7635
7961
 
7636
7962
  // src/webhooks/dispatcher.ts
7963
+ var defaultSleep = (delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs));
7964
+ var attemptOnce = async ({
7965
+ envelope,
7966
+ endpoint,
7967
+ fetchImpl,
7968
+ payload,
7969
+ signature,
7970
+ timeoutMs,
7971
+ timestamp
7972
+ }) => {
7973
+ const response = await fetchImpl(endpoint.url, {
7974
+ body: payload,
7975
+ headers: {
7976
+ "content-type": "application/json",
7977
+ "webhook-id": envelope.id,
7978
+ "webhook-signature": signature,
7979
+ "webhook-timestamp": timestamp
7980
+ },
7981
+ method: "POST",
7982
+ signal: AbortSignal.timeout(timeoutMs)
7983
+ });
7984
+ if (!response.ok) {
7985
+ throw new Error(`Webhook delivery returned ${response.status}`);
7986
+ }
7987
+ return response.status;
7988
+ };
7989
+ var errorMessage = (error) => error instanceof Error ? error.message : String(error);
7990
+ var statusFromError = (error) => {
7991
+ if (!(error instanceof Error))
7992
+ return;
7993
+ const match = /returned (\d+)/.exec(error.message);
7994
+ return match?.[1] === undefined ? undefined : Number(match[1]);
7995
+ };
7996
+ var persistFailure = async ({
7997
+ attempts,
7998
+ deliveryStore,
7999
+ endpoint,
8000
+ envelope,
8001
+ lastError
8002
+ }) => {
8003
+ if (deliveryStore === undefined)
8004
+ return;
8005
+ const record = {
8006
+ attempts,
8007
+ createdAt: Date.now(),
8008
+ endpointUrl: endpoint.url,
8009
+ envelope,
8010
+ lastError: errorMessage(lastError),
8011
+ lastStatus: statusFromError(lastError)
8012
+ };
8013
+ await deliveryStore.recordFailure(record);
8014
+ };
8015
+ var tryDeliverThenBackoff = async ({
8016
+ attempt,
8017
+ endpoint,
8018
+ envelope,
8019
+ fetchImpl,
8020
+ payload,
8021
+ retry,
8022
+ signature,
8023
+ sleep,
8024
+ timeoutMs,
8025
+ timestamp
8026
+ }) => {
8027
+ try {
8028
+ await attemptOnce({
8029
+ endpoint,
8030
+ envelope,
8031
+ fetchImpl,
8032
+ payload,
8033
+ signature,
8034
+ timeoutMs,
8035
+ timestamp
8036
+ });
8037
+ return;
8038
+ } catch (error) {
8039
+ const isLastAttempt = attempt >= retry.attempts - 1;
8040
+ await (isLastAttempt ? Promise.resolve() : sleep(retry.initialDelayMs * retry.backoffMultiplier ** attempt));
8041
+ return error;
8042
+ }
8043
+ };
8044
+ var deliverToEndpoint = async ({
8045
+ deliveryStore,
8046
+ endpoint,
8047
+ envelope,
8048
+ fetchImpl,
8049
+ onDeliveryError,
8050
+ payload,
8051
+ retry,
8052
+ signature,
8053
+ sleep,
8054
+ timeoutMs,
8055
+ timestamp
8056
+ }) => {
8057
+ let lastError;
8058
+ for (let attempt = 0;attempt < retry.attempts; attempt++) {
8059
+ const error = await tryDeliverThenBackoff({
8060
+ attempt,
8061
+ endpoint,
8062
+ envelope,
8063
+ fetchImpl,
8064
+ payload,
8065
+ retry,
8066
+ signature,
8067
+ sleep,
8068
+ timeoutMs,
8069
+ timestamp
8070
+ });
8071
+ if (error === undefined)
8072
+ return;
8073
+ lastError = error;
8074
+ }
8075
+ await onDeliveryError?.({
8076
+ endpoint,
8077
+ error: lastError,
8078
+ event: envelope
8079
+ });
8080
+ await persistFailure({
8081
+ attempts: retry.attempts,
8082
+ deliveryStore,
8083
+ endpoint,
8084
+ envelope,
8085
+ lastError
8086
+ });
8087
+ };
7637
8088
  var createWebhookDispatcher = ({
8089
+ deliveryStore,
7638
8090
  endpoints,
7639
8091
  fetch: fetchImpl = globalThis.fetch,
7640
8092
  onDeliveryError,
8093
+ retry,
8094
+ sleep = defaultSleep,
7641
8095
  timeoutMs = DEFAULT_WEBHOOK_TIMEOUT_MS
7642
8096
  }) => {
8097
+ const effectiveRetry = {
8098
+ attempts: retry?.attempts ?? DEFAULT_WEBHOOK_RETRY.attempts,
8099
+ backoffMultiplier: retry?.backoffMultiplier ?? DEFAULT_WEBHOOK_RETRY.backoffMultiplier,
8100
+ initialDelayMs: retry?.initialDelayMs ?? DEFAULT_WEBHOOK_RETRY.initialDelayMs
8101
+ };
7643
8102
  const dispatch = async (event) => {
7644
8103
  const envelope = {
7645
8104
  createdAt: Date.now(),
@@ -7649,35 +8108,26 @@ var createWebhookDispatcher = ({
7649
8108
  };
7650
8109
  const payload = JSON.stringify(envelope);
7651
8110
  const timestamp = Math.floor(Date.now() / MILLISECONDS_IN_A_SECOND).toString();
7652
- await Promise.all(endpoints.map(async (endpoint) => {
7653
- try {
7654
- const signature = await signWebhook({
7655
- id: envelope.id,
7656
- payload,
7657
- secret: endpoint.secret,
7658
- timestamp
7659
- });
7660
- const response = await fetchImpl(endpoint.url, {
7661
- body: payload,
7662
- headers: {
7663
- "content-type": "application/json",
7664
- "webhook-id": envelope.id,
7665
- "webhook-signature": signature,
7666
- "webhook-timestamp": timestamp
7667
- },
7668
- method: "POST",
7669
- signal: AbortSignal.timeout(timeoutMs)
7670
- });
7671
- if (!response.ok) {
7672
- throw new Error(`Webhook delivery returned ${response.status}`);
7673
- }
7674
- } catch (error) {
7675
- await onDeliveryError?.({
7676
- endpoint,
7677
- error,
7678
- event: envelope
7679
- });
7680
- }
8111
+ await Promise.all(endpoints.filter((endpoint) => endpoint.events === undefined || endpoint.events.includes(event.type)).map(async (endpoint) => {
8112
+ const signature = await signWebhook({
8113
+ id: envelope.id,
8114
+ payload,
8115
+ secret: endpoint.secret,
8116
+ timestamp
8117
+ });
8118
+ await deliverToEndpoint({
8119
+ deliveryStore,
8120
+ endpoint,
8121
+ envelope,
8122
+ fetchImpl,
8123
+ onDeliveryError,
8124
+ payload,
8125
+ retry: effectiveRetry,
8126
+ signature,
8127
+ sleep,
8128
+ timeoutMs,
8129
+ timestamp
8130
+ });
7681
8131
  }));
7682
8132
  };
7683
8133
  return dispatch;
@@ -19646,6 +20096,33 @@ var createInMemoryAuthorizationCodeStore = () => {
19646
20096
  }
19647
20097
  };
19648
20098
  };
20099
+ var createInMemoryDeviceAuthorizationStore = () => {
20100
+ const byDeviceCode = new Map;
20101
+ return {
20102
+ deleteByDeviceCodeHash: async (deviceCodeHash) => {
20103
+ byDeviceCode.delete(deviceCodeHash);
20104
+ },
20105
+ findByDeviceCodeHash: async (deviceCodeHash) => byDeviceCode.get(deviceCodeHash),
20106
+ findByUserCode: async (userCode) => {
20107
+ for (const record of byDeviceCode.values()) {
20108
+ if (record.userCode === userCode)
20109
+ return record;
20110
+ }
20111
+ return;
20112
+ },
20113
+ saveDeviceAuthorization: async (deviceAuthorization) => {
20114
+ byDeviceCode.set(deviceAuthorization.deviceCodeHash, {
20115
+ ...deviceAuthorization
20116
+ });
20117
+ },
20118
+ updateStatus: async (deviceCodeHash, status, userSub) => {
20119
+ const record = byDeviceCode.get(deviceCodeHash);
20120
+ if (!record)
20121
+ return;
20122
+ byDeviceCode.set(deviceCodeHash, { ...record, status, userSub });
20123
+ }
20124
+ };
20125
+ };
19649
20126
  var createInMemoryOAuthClientStore = (clients) => {
19650
20127
  const registry = new Map(clients.map((client) => [client.clientId, client]));
19651
20128
  return {
@@ -19666,6 +20143,7 @@ var createInMemoryOidcRefreshTokenStore = () => {
19666
20143
  tokens.delete(hash);
19667
20144
  }
19668
20145
  },
20146
+ getToken: async (tokenHash) => tokens.get(tokenHash),
19669
20147
  saveToken: async (token) => {
19670
20148
  tokens.set(token.tokenHash, { ...token });
19671
20149
  }
@@ -19693,6 +20171,19 @@ var oauthCodesTable = pgTable("auth_oauth_codes", {
19693
20171
  scopes: text("scopes").array().notNull(),
19694
20172
  user_id: varchar("user_id", { length: ID_LENGTH7 }).notNull()
19695
20173
  });
20174
+ var oauthDeviceAuthorizationsTable = pgTable("auth_oauth_device_authorizations", {
20175
+ client_id: varchar("client_id", { length: ID_LENGTH7 }).notNull(),
20176
+ created_at_ms: bigint("created_at_ms", { mode: "number" }).notNull(),
20177
+ device_code_hash: varchar("device_code_hash", {
20178
+ length: ID_LENGTH7
20179
+ }).primaryKey(),
20180
+ expires_at_ms: bigint("expires_at_ms", { mode: "number" }).notNull(),
20181
+ interval_seconds: bigint("interval_seconds", { mode: "number" }).notNull(),
20182
+ scopes: text("scopes").array().notNull(),
20183
+ status: varchar("status", { length: 16 }).notNull(),
20184
+ user_code: varchar("user_code", { length: 16 }).notNull().unique(),
20185
+ user_sub: varchar("user_sub", { length: ID_LENGTH7 })
20186
+ });
19696
20187
  var oauthRefreshTokensTable = pgTable("auth_oauth_refresh_tokens", {
19697
20188
  claims_json: jsonb("claims_json").$type(),
19698
20189
  client_id: varchar("client_id", { length: ID_LENGTH7 }).notNull(),
@@ -19736,6 +20227,17 @@ var toCodeValues = (code) => ({
19736
20227
  scopes: code.scopes,
19737
20228
  user_id: code.userId
19738
20229
  });
20230
+ var toDeviceAuth = (row) => ({
20231
+ clientId: row.client_id,
20232
+ createdAt: row.created_at_ms,
20233
+ deviceCodeHash: row.device_code_hash,
20234
+ expiresAt: row.expires_at_ms,
20235
+ intervalSeconds: row.interval_seconds,
20236
+ scopes: row.scopes,
20237
+ status: row.status,
20238
+ userCode: row.user_code,
20239
+ userSub: row.user_sub ?? undefined
20240
+ });
19739
20241
  var toRefresh = (row) => ({
19740
20242
  claims: row.claims_json ?? undefined,
19741
20243
  clientId: row.client_id,
@@ -19757,6 +20259,7 @@ var toRefreshValues = (token) => ({
19757
20259
  user_id: token.userId
19758
20260
  });
19759
20261
  var createNeonAuthorizationCodeStore = (databaseUrl) => createPostgresAuthorizationCodeStore(createNeonDatabase(databaseUrl));
20262
+ var createNeonDeviceAuthorizationStore = (databaseUrl) => createPostgresDeviceAuthorizationStore(createNeonDatabase(databaseUrl));
19760
20263
  var createNeonOAuthClientStore = (databaseUrl) => createPostgresOAuthClientStore(createNeonDatabase(databaseUrl));
19761
20264
  var createNeonOidcRefreshTokenStore = (databaseUrl) => createPostgresOidcRefreshTokenStore(createNeonDatabase(databaseUrl));
19762
20265
  var createPostgresAuthorizationCodeStore = (db) => ({
@@ -19768,6 +20271,35 @@ var createPostgresAuthorizationCodeStore = (db) => ({
19768
20271
  await db.insert(oauthCodesTable).values(toCodeValues(code));
19769
20272
  }
19770
20273
  });
20274
+ var createPostgresDeviceAuthorizationStore = (db) => ({
20275
+ deleteByDeviceCodeHash: async (deviceCodeHash) => {
20276
+ await db.delete(oauthDeviceAuthorizationsTable).where(eq(oauthDeviceAuthorizationsTable.device_code_hash, deviceCodeHash));
20277
+ },
20278
+ findByDeviceCodeHash: async (deviceCodeHash) => {
20279
+ const [row] = await db.select().from(oauthDeviceAuthorizationsTable).where(eq(oauthDeviceAuthorizationsTable.device_code_hash, deviceCodeHash)).limit(1);
20280
+ return row ? toDeviceAuth(row) : undefined;
20281
+ },
20282
+ findByUserCode: async (userCode) => {
20283
+ const [row] = await db.select().from(oauthDeviceAuthorizationsTable).where(eq(oauthDeviceAuthorizationsTable.user_code, userCode)).limit(1);
20284
+ return row ? toDeviceAuth(row) : undefined;
20285
+ },
20286
+ saveDeviceAuthorization: async (deviceAuthorization) => {
20287
+ await db.insert(oauthDeviceAuthorizationsTable).values({
20288
+ client_id: deviceAuthorization.clientId,
20289
+ created_at_ms: deviceAuthorization.createdAt,
20290
+ device_code_hash: deviceAuthorization.deviceCodeHash,
20291
+ expires_at_ms: deviceAuthorization.expiresAt,
20292
+ interval_seconds: deviceAuthorization.intervalSeconds,
20293
+ scopes: deviceAuthorization.scopes,
20294
+ status: deviceAuthorization.status,
20295
+ user_code: deviceAuthorization.userCode,
20296
+ user_sub: deviceAuthorization.userSub ?? null
20297
+ });
20298
+ },
20299
+ updateStatus: async (deviceCodeHash, status, userSub) => {
20300
+ await db.update(oauthDeviceAuthorizationsTable).set({ status, user_sub: userSub ?? null }).where(eq(oauthDeviceAuthorizationsTable.device_code_hash, deviceCodeHash));
20301
+ }
20302
+ });
19771
20303
  var createPostgresOAuthClientStore = (db) => ({
19772
20304
  findClient: async (clientId) => {
19773
20305
  const [row] = await db.select().from(oauthClientsTable).where(eq(oauthClientsTable.client_id, clientId)).limit(1);
@@ -19782,6 +20314,10 @@ var createPostgresOidcRefreshTokenStore = (db) => ({
19782
20314
  deleteForUser: async (userId) => {
19783
20315
  await db.delete(oauthRefreshTokensTable).where(eq(oauthRefreshTokensTable.user_id, userId));
19784
20316
  },
20317
+ getToken: async (tokenHash) => {
20318
+ const [row] = await db.select().from(oauthRefreshTokensTable).where(eq(oauthRefreshTokensTable.token_hash, tokenHash)).limit(1);
20319
+ return row ? toRefresh(row) : undefined;
20320
+ },
19785
20321
  saveToken: async (token) => {
19786
20322
  await db.insert(oauthRefreshTokensTable).values(toRefreshValues(token));
19787
20323
  }
@@ -20914,6 +21450,62 @@ var createPostgresPasswordlessTokenStore = (db) => ({
20914
21450
  });
20915
21451
  }
20916
21452
  });
21453
+ // src/webhooks/inMemoryStore.ts
21454
+ var DEFAULT_LIST_LIMIT = 100;
21455
+ var createInMemoryWebhookDeliveryStore = () => {
21456
+ const failures = new Map;
21457
+ return {
21458
+ listFailed: async (limit = DEFAULT_LIST_LIMIT) => Array.from(failures.values()).sort((left, right) => right.createdAt - left.createdAt).slice(0, limit),
21459
+ recordFailure: async (delivery) => {
21460
+ failures.set(delivery.envelope.id, delivery);
21461
+ },
21462
+ removeFailure: async (envelopeId) => {
21463
+ failures.delete(envelopeId);
21464
+ }
21465
+ };
21466
+ };
21467
+ // src/webhooks/postgresStore.ts
21468
+ var ID_LENGTH15 = 255;
21469
+ var URL_LENGTH = 2048;
21470
+ var DEFAULT_LIST_LIMIT2 = 100;
21471
+ var webhookDeliveriesTable = pgTable("auth_webhook_deliveries", {
21472
+ attempts: bigint("attempts", { mode: "number" }).notNull(),
21473
+ created_at_ms: bigint("created_at_ms", { mode: "number" }).notNull(),
21474
+ endpoint_url: varchar("endpoint_url", { length: URL_LENGTH }).notNull(),
21475
+ envelope_id: varchar("envelope_id", { length: ID_LENGTH15 }).primaryKey(),
21476
+ envelope_json: jsonb("envelope_json").$type().notNull(),
21477
+ last_error: text("last_error"),
21478
+ last_status: bigint("last_status", { mode: "number" })
21479
+ });
21480
+ var toDelivery = (row) => ({
21481
+ attempts: row.attempts,
21482
+ createdAt: row.created_at_ms,
21483
+ endpointUrl: row.endpoint_url,
21484
+ envelope: row.envelope_json,
21485
+ lastError: row.last_error ?? undefined,
21486
+ lastStatus: row.last_status ?? undefined
21487
+ });
21488
+ var createNeonWebhookDeliveryStore = (databaseUrl) => createPostgresWebhookDeliveryStore(createNeonDatabase(databaseUrl));
21489
+ var createPostgresWebhookDeliveryStore = (db) => ({
21490
+ listFailed: async (limit = DEFAULT_LIST_LIMIT2) => {
21491
+ const rows = await db.select().from(webhookDeliveriesTable).orderBy(desc(webhookDeliveriesTable.created_at_ms)).limit(limit);
21492
+ return rows.map(toDelivery);
21493
+ },
21494
+ recordFailure: async (delivery) => {
21495
+ await db.insert(webhookDeliveriesTable).values({
21496
+ attempts: delivery.attempts,
21497
+ created_at_ms: delivery.createdAt,
21498
+ endpoint_url: delivery.endpointUrl,
21499
+ envelope_id: delivery.envelope.id,
21500
+ envelope_json: delivery.envelope,
21501
+ last_error: delivery.lastError ?? null,
21502
+ last_status: delivery.lastStatus ?? null
21503
+ });
21504
+ },
21505
+ removeFailure: async (envelopeId) => {
21506
+ await db.delete(webhookDeliveriesTable).where(eq(webhookDeliveriesTable.envelope_id, envelopeId));
21507
+ }
21508
+ });
20917
21509
  // src/portal/inMemorySetupSessionStore.ts
20918
21510
  var cloneSession = (value) => ({
20919
21511
  ...value,
@@ -20935,19 +21527,19 @@ var createInMemorySetupSessionStore = () => {
20935
21527
  };
20936
21528
  };
20937
21529
  // src/portal/postgresSetupSessionStore.ts
20938
- var ID_LENGTH15 = 255;
21530
+ var ID_LENGTH16 = 255;
20939
21531
  var setupSessionsTable = pgTable("auth_setup_sessions", {
20940
21532
  capabilities: jsonb("capabilities").$type().notNull().default([]),
20941
21533
  created_at_ms: bigint("created_at_ms", { mode: "number" }).notNull(),
20942
- created_by: varchar("created_by", { length: ID_LENGTH15 }),
21534
+ created_by: varchar("created_by", { length: ID_LENGTH16 }),
20943
21535
  expires_at_ms: bigint("expires_at_ms", { mode: "number" }).notNull(),
20944
21536
  organization_id: varchar("organization_id", {
20945
- length: ID_LENGTH15
21537
+ length: ID_LENGTH16
20946
21538
  }).notNull(),
20947
21539
  setup_session_id: varchar("setup_session_id", {
20948
- length: ID_LENGTH15
21540
+ length: ID_LENGTH16
20949
21541
  }).primaryKey(),
20950
- token_hash: varchar("token_hash", { length: ID_LENGTH15 }).notNull().unique()
21542
+ token_hash: varchar("token_hash", { length: ID_LENGTH16 }).notNull().unique()
20951
21543
  });
20952
21544
  var toSession = (row) => ({
20953
21545
  capabilities: row.capabilities,
@@ -21138,6 +21730,7 @@ var auth = async ({
21138
21730
  };
21139
21731
  export {
21140
21732
  writeWarrant,
21733
+ webhookDeliveriesTable,
21141
21734
  webauthnRoutes,
21142
21735
  webauthnCredentialsTable,
21143
21736
  warrantsTable,
@@ -21182,6 +21775,7 @@ export {
21182
21775
  rolesTable,
21183
21776
  roleRoutes,
21184
21777
  revokeUserSessions,
21778
+ revokeRefreshToken,
21185
21779
  revocableProviderOptions,
21186
21780
  resolveSetupSession,
21187
21781
  resolveScimOrganization,
@@ -21213,6 +21807,7 @@ export {
21213
21807
  oidcProviderRoutes,
21214
21808
  oidcProviderOptions,
21215
21809
  oauthRefreshTokensTable,
21810
+ oauthDeviceAuthorizationsTable,
21216
21811
  oauthCodesTable,
21217
21812
  oauthClientsTable,
21218
21813
  mfaTotpRoutes,
@@ -21230,6 +21825,7 @@ export {
21230
21825
  knownDevicesTable,
21231
21826
  jwkThumbprint,
21232
21827
  issueTokenSet,
21828
+ issueDeviceAuthorization,
21233
21829
  isValidUser,
21234
21830
  isValidProviderOption,
21235
21831
  isUserSessionId,
@@ -21246,6 +21842,7 @@ export {
21246
21842
  isAuthIntent,
21247
21843
  isAnonymousSession,
21248
21844
  inviteToOrganization,
21845
+ introspectToken,
21249
21846
  instantiateUserSession,
21250
21847
  hashToken,
21251
21848
  hashPassword,
@@ -21264,11 +21861,13 @@ export {
21264
21861
  extractPropFromIdentity,
21265
21862
  exportAuditCsv,
21266
21863
  exchangeToken,
21864
+ exchangeDeviceCode,
21267
21865
  exchangeClientCredentials,
21268
21866
  evaluatePassword,
21269
21867
  endImpersonation,
21270
21868
  encryptTotpSecret,
21271
21869
  encryptSecret,
21870
+ denyDeviceAuthorization,
21272
21871
  deleteWarrant,
21273
21872
  defineProvidersConfiguration,
21274
21873
  defineAuthSettings,
@@ -21297,6 +21896,7 @@ export {
21297
21896
  createRiskEngine,
21298
21897
  createRedisLockoutStore,
21299
21898
  createRedisAuthSessionStore,
21899
+ createPostgresWebhookDeliveryStore,
21300
21900
  createPostgresWebAuthnCredentialStore,
21301
21901
  createPostgresWarrantStore,
21302
21902
  createPostgresVaultStore,
@@ -21312,6 +21912,7 @@ export {
21312
21912
  createPostgresLoginHistoryStore,
21313
21913
  createPostgresLockoutStore,
21314
21914
  createPostgresKnownDeviceStore,
21915
+ createPostgresDeviceAuthorizationStore,
21315
21916
  createPostgresCredentialStore,
21316
21917
  createPostgresAuthorizationCodeStore,
21317
21918
  createPostgresAuditSink,
@@ -21320,6 +21921,7 @@ export {
21320
21921
  createPostgresAccessTokenStore,
21321
21922
  createOrganization,
21322
21923
  createOAuthLinkedProviderCredentialResolver,
21924
+ createNeonWebhookDeliveryStore,
21323
21925
  createNeonWebAuthnCredentialStore,
21324
21926
  createNeonWarrantStore,
21325
21927
  createNeonVaultStore,
@@ -21337,6 +21939,7 @@ export {
21337
21939
  createNeonLockoutStore,
21338
21940
  createNeonLinkedProviderStores,
21339
21941
  createNeonKnownDeviceStore,
21942
+ createNeonDeviceAuthorizationStore,
21340
21943
  createNeonDatabase,
21341
21944
  createNeonCredentialStore,
21342
21945
  createNeonAuthorizationCodeStore,
@@ -21349,6 +21952,7 @@ export {
21349
21952
  createMembershipPermissionResolver,
21350
21953
  createLockoutGuard,
21351
21954
  createLinkedProviderCredentialResolver,
21955
+ createInMemoryWebhookDeliveryStore,
21352
21956
  createInMemoryWebAuthnCredentialStore,
21353
21957
  createInMemoryWarrantStore,
21354
21958
  createInMemoryVaultStore,
@@ -21365,6 +21969,7 @@ export {
21365
21969
  createInMemoryLockoutStore,
21366
21970
  createInMemoryLinkedProviderStores,
21367
21971
  createInMemoryKnownDeviceStore,
21972
+ createInMemoryDeviceAuthorizationStore,
21368
21973
  createInMemoryCredentialStore,
21369
21974
  createInMemoryCheckCache,
21370
21975
  createInMemoryAuthorizationCodeStore,
@@ -21396,6 +22001,7 @@ export {
21396
22001
  auditEventsTable,
21397
22002
  assessRisk,
21398
22003
  assessAbuse,
22004
+ approveDeviceAuthorization,
21399
22005
  apiKeysTable,
21400
22006
  apiKeysRoutes,
21401
22007
  apiClientsTable,
@@ -21404,6 +22010,7 @@ export {
21404
22010
  acceptInvitation,
21405
22011
  WEBAUTHN_CHALLENGE_COOKIE,
21406
22012
  DEFAULT_WEBHOOK_TIMEOUT_MS,
22013
+ DEFAULT_WEBHOOK_RETRY,
21407
22014
  DEFAULT_WEBAUTHN_SESSION_TTL_MS,
21408
22015
  DEFAULT_WEBAUTHN_ROUTE,
21409
22016
  DEFAULT_WEBAUTHN_CHALLENGE_TTL_MS,
@@ -21432,5 +22039,5 @@ export {
21432
22039
  AuthIdentityConflictError
21433
22040
  };
21434
22041
 
21435
- //# debugId=CE0246297ACE345E64756E2164756E21
22042
+ //# debugId=451C9751C02E5E2164756E2164756E21
21436
22043
  //# sourceMappingURL=index.js.map