@absolutejs/auth 0.35.0 → 0.36.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
@@ -4492,6 +4492,7 @@ var RESERVED_ACCESS_CLAIMS = new Set([
4492
4492
  var buildAccessClaims = ({
4493
4493
  act,
4494
4494
  audience,
4495
+ clientCertThumbprint,
4495
4496
  clientId,
4496
4497
  dpopJkt,
4497
4498
  extraClaims,
@@ -4516,8 +4517,15 @@ var buildAccessClaims = ({
4516
4517
  };
4517
4518
  if (act !== undefined)
4518
4519
  claims.act = act;
4519
- if (dpopJkt !== undefined)
4520
- claims.cnf = { jkt: dpopJkt };
4520
+ if (dpopJkt !== undefined || clientCertThumbprint !== undefined) {
4521
+ const cnf = {};
4522
+ if (dpopJkt !== undefined)
4523
+ cnf.jkt = dpopJkt;
4524
+ if (clientCertThumbprint !== undefined) {
4525
+ cnf["x5t#S256"] = clientCertThumbprint;
4526
+ }
4527
+ claims.cnf = cnf;
4528
+ }
4521
4529
  return claims;
4522
4530
  };
4523
4531
  var exchangeToken = async ({
@@ -4567,6 +4575,7 @@ var exchangeToken = async ({
4567
4575
  var issueTokenSet = async ({
4568
4576
  acr,
4569
4577
  claims,
4578
+ clientCertThumbprint,
4570
4579
  clientId,
4571
4580
  config,
4572
4581
  dpopJkt,
@@ -4584,6 +4593,7 @@ var issueTokenSet = async ({
4584
4593
  sub
4585
4594
  });
4586
4595
  const accessPayload = buildAccessClaims({
4596
+ clientCertThumbprint,
4587
4597
  clientId,
4588
4598
  dpopJkt,
4589
4599
  extraClaims: { ...accessExtra, ...acr === undefined ? {} : { acr } },
@@ -4790,6 +4800,128 @@ var exchangeDeviceCode = async ({
4790
4800
  });
4791
4801
  return { ...tokenSet, ok: true };
4792
4802
  };
4803
+ var AUTH_REQ_ID_BYTES = 32;
4804
+ var DEFAULT_BACKCHANNEL_TTL_MINUTES = 10;
4805
+ var DEFAULT_BACKCHANNEL_TTL_MS = MILLISECONDS_IN_A_MINUTE * DEFAULT_BACKCHANNEL_TTL_MINUTES;
4806
+ var DEFAULT_BACKCHANNEL_POLL_INTERVAL_SECONDS = 5;
4807
+ var CIBA_GRANT_TYPE = "urn:openid:params:grant-type:ciba";
4808
+ var issueBackchannelAuth = async ({
4809
+ clientId,
4810
+ config,
4811
+ loginHint,
4812
+ bindingMessage,
4813
+ now = Date.now(),
4814
+ requestedScopes
4815
+ }) => {
4816
+ if (!config.backchannelAuthStore || !config.resolveBackchannelUser) {
4817
+ return { error: "invalid_request", ok: false };
4818
+ }
4819
+ const client = await config.clientStore.findClient(clientId);
4820
+ if (!client)
4821
+ return { error: "invalid_client", ok: false };
4822
+ const resolved = await config.resolveBackchannelUser({
4823
+ client,
4824
+ loginHint
4825
+ });
4826
+ if (!resolved)
4827
+ return { error: "unknown_user_id", ok: false };
4828
+ const authReqId = generateSecureToken(AUTH_REQ_ID_BYTES);
4829
+ const ttl = config.backchannelAuthTtlMs ?? DEFAULT_BACKCHANNEL_TTL_MS;
4830
+ const interval = config.backchannelPollIntervalSeconds ?? DEFAULT_BACKCHANNEL_POLL_INTERVAL_SECONDS;
4831
+ await config.backchannelAuthStore.saveBackchannelAuth({
4832
+ authReqId,
4833
+ bindingMessage,
4834
+ clientId,
4835
+ createdAt: now,
4836
+ expiresAt: now + ttl,
4837
+ intervalSeconds: interval,
4838
+ scopes: requestedScopes,
4839
+ status: "pending",
4840
+ userSub: resolved.sub
4841
+ });
4842
+ await config.onBackchannelAuthRequest?.({
4843
+ authReqId,
4844
+ bindingMessage,
4845
+ clientId,
4846
+ scopes: requestedScopes,
4847
+ userSub: resolved.sub
4848
+ });
4849
+ return {
4850
+ auth_req_id: authReqId,
4851
+ expires_in: Math.floor(ttl / MS_PER_SECOND),
4852
+ interval,
4853
+ ok: true
4854
+ };
4855
+ };
4856
+ var decideBackchannel = async (config, authReqId, approval) => {
4857
+ if (!config.backchannelAuthStore) {
4858
+ return { error: "not_configured", ok: false };
4859
+ }
4860
+ const record = await config.backchannelAuthStore.findByAuthReqId(authReqId);
4861
+ if (!record)
4862
+ return { error: "invalid_auth_req_id", ok: false };
4863
+ if (record.expiresAt < Date.now()) {
4864
+ return { error: "expired_token", ok: false };
4865
+ }
4866
+ if (record.status !== "pending") {
4867
+ return { error: "already_decided", ok: false };
4868
+ }
4869
+ await config.backchannelAuthStore.updateStatus(authReqId, approval.status, approval.userSub ?? record.userSub);
4870
+ return { ok: true };
4871
+ };
4872
+ var approveBackchannelAuth = async ({
4873
+ authReqId,
4874
+ config,
4875
+ userSub
4876
+ }) => decideBackchannel(config, authReqId, {
4877
+ status: "approved",
4878
+ userSub
4879
+ });
4880
+ var denyBackchannelAuth = async ({
4881
+ authReqId,
4882
+ config
4883
+ }) => decideBackchannel(config, authReqId, { status: "denied" });
4884
+ var exchangeBackchannelAuth = async ({
4885
+ authReqId,
4886
+ clientCertThumbprint,
4887
+ clientId,
4888
+ config,
4889
+ dpopJkt,
4890
+ now = Date.now()
4891
+ }) => {
4892
+ if (!config.backchannelAuthStore) {
4893
+ return { error: "invalid_grant", ok: false };
4894
+ }
4895
+ const record = await config.backchannelAuthStore.findByAuthReqId(authReqId);
4896
+ if (!record || record.clientId !== clientId) {
4897
+ return { error: "invalid_grant", ok: false };
4898
+ }
4899
+ if (record.expiresAt < now) {
4900
+ await config.backchannelAuthStore.deleteByAuthReqId(authReqId);
4901
+ return { error: "expired_token", ok: false };
4902
+ }
4903
+ if (record.lastPolledAt !== undefined && now - record.lastPolledAt < record.intervalSeconds * MS_PER_SECOND) {
4904
+ return { error: "slow_down", ok: false };
4905
+ }
4906
+ await config.backchannelAuthStore.recordPoll(authReqId, now);
4907
+ if (record.status === "pending") {
4908
+ return { error: "authorization_pending", ok: false };
4909
+ }
4910
+ if (record.status === "denied" || record.userSub === undefined) {
4911
+ return { error: "access_denied", ok: false };
4912
+ }
4913
+ await config.backchannelAuthStore.deleteByAuthReqId(authReqId);
4914
+ const tokenSet = await issueTokenSet({
4915
+ clientCertThumbprint,
4916
+ clientId,
4917
+ config,
4918
+ dpopJkt,
4919
+ now,
4920
+ scopes: record.scopes,
4921
+ sub: record.userSub
4922
+ });
4923
+ return { ...tokenSet, ok: true };
4924
+ };
4793
4925
 
4794
4926
  // src/oidc/clientAuth.ts
4795
4927
  var CLIENT_ASSERTION_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
@@ -4900,6 +5032,55 @@ var verifyJwtSignedByClient = ({
4900
5032
  client
4901
5033
  }) => verifyJwtSignedByClientImpl(client, jwt);
4902
5034
 
5035
+ // src/oidc/mtls.ts
5036
+ var RFC9440_HEADER = "client-cert";
5037
+ var SF_BINARY_PREFIX = ":";
5038
+ var SF_BINARY_SUFFIX = ":";
5039
+ var base64Decode2 = (value) => Uint8Array.from(atob(value), (char) => char.charCodeAt(0));
5040
+ var base64UrlEncode2 = (bytes) => {
5041
+ const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
5042
+ return btoa(binary).replace(/\+/gu, "-").replace(/\//gu, "_").replace(/=+$/u, "");
5043
+ };
5044
+ var computeCertThumbprint = async (derBytes) => {
5045
+ const digest = await crypto.subtle.digest("SHA-256", derBytes);
5046
+ return base64UrlEncode2(new Uint8Array(digest));
5047
+ };
5048
+ var extractRfc9440ClientCert = (headers) => {
5049
+ const raw = headers.get(RFC9440_HEADER);
5050
+ if (raw === null)
5051
+ return;
5052
+ const trimmed = raw.trim();
5053
+ if (!trimmed.startsWith(SF_BINARY_PREFIX) || !trimmed.endsWith(SF_BINARY_SUFFIX) || trimmed.length <= 2) {
5054
+ return;
5055
+ }
5056
+ try {
5057
+ return base64Decode2(trimmed.slice(1, -1));
5058
+ } catch {
5059
+ return;
5060
+ }
5061
+ };
5062
+ var resolveClientCert = async ({
5063
+ extract,
5064
+ headers
5065
+ }) => {
5066
+ if (extract !== undefined)
5067
+ return extract(headers);
5068
+ return extractRfc9440ClientCert(headers);
5069
+ };
5070
+ var verifyCertificateBoundToken = async ({
5071
+ cnfThumbprint,
5072
+ extract,
5073
+ headers
5074
+ }) => {
5075
+ if (cnfThumbprint === undefined)
5076
+ return false;
5077
+ const cert = await resolveClientCert({ extract, headers });
5078
+ if (cert === undefined)
5079
+ return false;
5080
+ const presented = await computeCertThumbprint(cert);
5081
+ return presented === cnfThumbprint;
5082
+ };
5083
+
4903
5084
  // src/oidc/dpop.ts
4904
5085
  var DEFAULT_MAX_AGE_MS = 60000;
4905
5086
  var SECONDS_TO_MS = 1000;
@@ -5517,6 +5698,7 @@ var oidcProviderRoutes = (config) => {
5517
5698
  const introspectRoute = `${oidcRoute}/introspect`;
5518
5699
  const revokeRoute = `${oidcRoute}/revoke`;
5519
5700
  const deviceAuthorizationRoute = `${oidcRoute}/device_authorization`;
5701
+ const backchannelAuthorizationRoute = `${oidcRoute}/bc-authorize`;
5520
5702
  const deviceApproveRoute = `${oidcRoute}/device/decision`;
5521
5703
  const endSessionRoute = `${oidcRoute}/end_session`;
5522
5704
  const parRoute = `${oidcRoute}/par`;
@@ -5535,27 +5717,57 @@ var oidcProviderRoutes = (config) => {
5535
5717
  const matches = await constantTimeEqual(await hashToken(clientSecret), client.hashedSecret);
5536
5718
  return matches ? client : undefined;
5537
5719
  };
5720
+ const tryMtlsAuth = async ({
5721
+ candidate,
5722
+ extract,
5723
+ requestHeaders
5724
+ }) => {
5725
+ const registered = candidate?.tlsCertificateBoundThumbprints ?? [];
5726
+ if (candidate === undefined || registered.length === 0)
5727
+ return;
5728
+ const cert = await resolveClientCert({
5729
+ extract,
5730
+ headers: requestHeaders
5731
+ });
5732
+ if (cert === undefined)
5733
+ return;
5734
+ const presented = await computeCertThumbprint(cert);
5735
+ if (!registered.includes(presented))
5736
+ return;
5737
+ return { client: candidate, clientCertThumbprint: presented };
5738
+ };
5538
5739
  const authenticateTokenClient = async ({
5539
5740
  basicClientId,
5540
5741
  basicClientSecret,
5541
5742
  bodyClientAssertion,
5542
5743
  bodyClientAssertionType,
5543
5744
  bodyClientId,
5544
- bodyClientSecret
5745
+ bodyClientSecret,
5746
+ requestHeaders
5545
5747
  }) => {
5546
5748
  if (bodyClientAssertion !== undefined && bodyClientAssertionType === CLIENT_ASSERTION_TYPE) {
5547
- return verifyClientAssertion({
5749
+ const client2 = await verifyClientAssertion({
5548
5750
  assertion: bodyClientAssertion,
5549
5751
  expectedAudience: tokenUrl,
5550
5752
  jtiStore: config.clientAssertionJtiStore,
5551
5753
  resolveClient: clientStore.findClient
5552
5754
  });
5755
+ return client2 === undefined ? undefined : { client: client2, clientCertThumbprint: undefined };
5553
5756
  }
5554
5757
  const clientId = bodyClientId ?? basicClientId;
5555
- const clientSecret = bodyClientSecret ?? basicClientSecret;
5556
5758
  if (clientId === undefined)
5557
5759
  return;
5558
- return authenticateClient(clientId, clientSecret);
5760
+ const candidate = await clientStore.findClient(clientId);
5761
+ const mtlsResult = await tryMtlsAuth({
5762
+ candidate,
5763
+ extract: config.extractTlsClientCert,
5764
+ requestHeaders
5765
+ });
5766
+ if (mtlsResult !== undefined)
5767
+ return mtlsResult;
5768
+ const clientSecret = bodyClientSecret ?? basicClientSecret;
5769
+ const client = await authenticateClient(clientId, clientSecret);
5770
+ return client === undefined ? undefined : { client, clientCertThumbprint: undefined };
5559
5771
  };
5560
5772
  const dpopNonceChallenge = async (proof) => {
5561
5773
  if (proof === undefined || config.dpopNonce === undefined) {
@@ -5580,7 +5792,7 @@ var oidcProviderRoutes = (config) => {
5580
5792
  status: HTTP_UNAUTHORIZED2
5581
5793
  });
5582
5794
  };
5583
- const grantAuthorizationCode = async (client, body, dpop) => {
5795
+ const grantAuthorizationCode = async (client, body, dpop, clientCertThumbprint) => {
5584
5796
  const {
5585
5797
  code,
5586
5798
  code_verifier: codeVerifier,
@@ -5604,6 +5816,7 @@ var oidcProviderRoutes = (config) => {
5604
5816
  return tokenResponse(await issueTokenSet({
5605
5817
  acr: record.acr,
5606
5818
  claims: record.claims,
5819
+ clientCertThumbprint,
5607
5820
  clientId: client.clientId,
5608
5821
  config,
5609
5822
  dpopJkt: dpopResult?.jkt,
@@ -5612,7 +5825,7 @@ var oidcProviderRoutes = (config) => {
5612
5825
  sub: record.userId
5613
5826
  }));
5614
5827
  };
5615
- const grantRefreshToken = async (client, body, dpop) => {
5828
+ const grantRefreshToken = async (client, body, dpop, clientCertThumbprint) => {
5616
5829
  const presented = body.refresh_token;
5617
5830
  if (presented === undefined) {
5618
5831
  return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
@@ -5634,6 +5847,7 @@ var oidcProviderRoutes = (config) => {
5634
5847
  return tokenResponse(await issueTokenSet({
5635
5848
  acr: record.acr,
5636
5849
  claims: record.claims,
5850
+ clientCertThumbprint,
5637
5851
  clientId: client.clientId,
5638
5852
  config,
5639
5853
  dpopJkt: record.dpopJkt,
@@ -5671,6 +5885,39 @@ var oidcProviderRoutes = (config) => {
5671
5885
  token_type: dpopResult === undefined ? "Bearer" : "DPoP"
5672
5886
  }, HTTP_OK2);
5673
5887
  };
5888
+ const grantBackchannel = async (client, body, dpop, clientCertThumbprint) => {
5889
+ if (config.backchannelAuthStore === undefined) {
5890
+ return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
5891
+ }
5892
+ if (body.auth_req_id === undefined) {
5893
+ return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
5894
+ }
5895
+ const dpopResult = dpop === undefined ? undefined : await verifyDpopProof({
5896
+ htm: "POST",
5897
+ htu: tokenUrl,
5898
+ proof: dpop
5899
+ });
5900
+ if (dpop !== undefined && dpopResult === undefined) {
5901
+ return oauthError2(HTTP_BAD_REQUEST2, "invalid_dpop_proof");
5902
+ }
5903
+ const result = await exchangeBackchannelAuth({
5904
+ authReqId: body.auth_req_id,
5905
+ clientCertThumbprint,
5906
+ clientId: client.clientId,
5907
+ config,
5908
+ dpopJkt: dpopResult?.jkt
5909
+ });
5910
+ if (!result.ok)
5911
+ return oauthError2(HTTP_BAD_REQUEST2, result.error);
5912
+ return jsonResponse({
5913
+ access_token: result.access_token,
5914
+ expires_in: result.expires_in,
5915
+ id_token: result.id_token,
5916
+ refresh_token: result.refresh_token,
5917
+ scope: result.scope,
5918
+ token_type: dpopResult === undefined ? "Bearer" : "DPoP"
5919
+ }, HTTP_OK2);
5920
+ };
5674
5921
  const grantDeviceCode = async (client, body, dpop) => {
5675
5922
  if (config.deviceAuthorizationStore === undefined) {
5676
5923
  return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
@@ -5711,6 +5958,9 @@ var oidcProviderRoutes = (config) => {
5711
5958
  if (config.deviceAuthorizationStore) {
5712
5959
  grantTypes.push("urn:ietf:params:oauth:grant-type:device_code");
5713
5960
  }
5961
+ if (config.backchannelAuthStore) {
5962
+ grantTypes.push(CIBA_GRANT_TYPE);
5963
+ }
5714
5964
  const discovery = {
5715
5965
  authorization_endpoint: `${issuer}${authorizeRoute}`,
5716
5966
  backchannel_logout_session_supported: false,
@@ -5729,12 +5979,14 @@ var oidcProviderRoutes = (config) => {
5729
5979
  response_types_supported: ["code"],
5730
5980
  revocation_endpoint: `${issuer}${revokeRoute}`,
5731
5981
  subject_types_supported: ["public"],
5982
+ tls_client_certificate_bound_access_tokens: true,
5732
5983
  token_endpoint: tokenUrl,
5733
5984
  token_endpoint_auth_methods_supported: [
5734
5985
  "client_secret_basic",
5735
5986
  "client_secret_post",
5736
5987
  "none",
5737
- "private_key_jwt"
5988
+ "private_key_jwt",
5989
+ "self_signed_tls_client_auth"
5738
5990
  ],
5739
5991
  token_endpoint_auth_signing_alg_values_supported: ["ES256"],
5740
5992
  userinfo_endpoint: `${issuer}${userinfoRoute}`
@@ -5742,6 +5994,11 @@ var oidcProviderRoutes = (config) => {
5742
5994
  if (config.deviceAuthorizationStore) {
5743
5995
  discovery.device_authorization_endpoint = `${issuer}${deviceAuthorizationRoute}`;
5744
5996
  }
5997
+ if (config.backchannelAuthStore) {
5998
+ discovery.backchannel_authentication_endpoint = `${issuer}${backchannelAuthorizationRoute}`;
5999
+ discovery.backchannel_token_delivery_modes_supported = ["poll"];
6000
+ discovery.backchannel_user_code_parameter_supported = false;
6001
+ }
5745
6002
  if (config.clientRegistrationTokenStore !== undefined) {
5746
6003
  discovery.registration_endpoint = registrationBaseUrl;
5747
6004
  }
@@ -5936,27 +6193,29 @@ var oidcProviderRoutes = (config) => {
5936
6193
  scope: t12.Optional(t12.String()),
5937
6194
  state: t12.Optional(t12.String())
5938
6195
  })
5939
- }).post(tokenRoute, async ({ body, headers }) => {
6196
+ }).post(tokenRoute, async ({ body, headers, request }) => {
5940
6197
  const basic = readBasicAuth2(headers.authorization);
5941
- const client = await authenticateTokenClient({
6198
+ const auth = await authenticateTokenClient({
5942
6199
  basicClientId: basic.clientId,
5943
6200
  basicClientSecret: basic.clientSecret,
5944
6201
  bodyClientAssertion: body.client_assertion,
5945
6202
  bodyClientAssertionType: body.client_assertion_type,
5946
6203
  bodyClientId: body.client_id,
5947
- bodyClientSecret: body.client_secret
6204
+ bodyClientSecret: body.client_secret,
6205
+ requestHeaders: request.headers
5948
6206
  });
5949
- if (client === undefined) {
6207
+ if (auth === undefined) {
5950
6208
  return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
5951
6209
  }
6210
+ const { client, clientCertThumbprint } = auth;
5952
6211
  const nonceChallenge = await dpopNonceChallenge(headers.dpop);
5953
6212
  if (nonceChallenge !== undefined)
5954
6213
  return nonceChallenge;
5955
6214
  if (body.grant_type === "authorization_code") {
5956
- return grantAuthorizationCode(client, body, headers.dpop);
6215
+ return grantAuthorizationCode(client, body, headers.dpop, clientCertThumbprint);
5957
6216
  }
5958
6217
  if (body.grant_type === "refresh_token") {
5959
- return grantRefreshToken(client, body, headers.dpop);
6218
+ return grantRefreshToken(client, body, headers.dpop, clientCertThumbprint);
5960
6219
  }
5961
6220
  if (body.grant_type === "urn:ietf:params:oauth:grant-type:token-exchange") {
5962
6221
  return grantTokenExchange(client, body, headers.dpop);
@@ -5964,10 +6223,14 @@ var oidcProviderRoutes = (config) => {
5964
6223
  if (body.grant_type === "urn:ietf:params:oauth:grant-type:device_code") {
5965
6224
  return grantDeviceCode(client, body, headers.dpop);
5966
6225
  }
6226
+ if (body.grant_type === CIBA_GRANT_TYPE) {
6227
+ return grantBackchannel(client, body, headers.dpop, clientCertThumbprint);
6228
+ }
5967
6229
  return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
5968
6230
  }, {
5969
6231
  body: t12.Object({
5970
6232
  audience: t12.Optional(t12.String()),
6233
+ auth_req_id: t12.Optional(t12.String()),
5971
6234
  client_assertion: t12.Optional(t12.String()),
5972
6235
  client_assertion_type: t12.Optional(t12.String()),
5973
6236
  client_id: t12.Optional(t12.String()),
@@ -5983,22 +6246,24 @@ var oidcProviderRoutes = (config) => {
5983
6246
  subject_token: t12.Optional(t12.String()),
5984
6247
  subject_token_type: t12.Optional(t12.String())
5985
6248
  })
5986
- }).post(parRoute, async ({ body, headers }) => {
6249
+ }).post(parRoute, async ({ body, headers, request }) => {
5987
6250
  if (config.pushedAuthorizationRequestStore === undefined) {
5988
6251
  return oauthError2(HTTP_NOT_IMPLEMENTED, "unsupported_response_type");
5989
6252
  }
5990
6253
  const basic = readBasicAuth2(headers.authorization);
5991
- const client = await authenticateTokenClient({
6254
+ const auth = await authenticateTokenClient({
5992
6255
  basicClientId: basic.clientId,
5993
6256
  basicClientSecret: basic.clientSecret,
5994
6257
  bodyClientAssertion: body.client_assertion,
5995
6258
  bodyClientAssertionType: body.client_assertion_type,
5996
6259
  bodyClientId: body.client_id,
5997
- bodyClientSecret: body.client_secret
6260
+ bodyClientSecret: body.client_secret,
6261
+ requestHeaders: request.headers
5998
6262
  });
5999
- if (client === undefined) {
6263
+ if (auth === undefined) {
6000
6264
  return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
6001
6265
  }
6266
+ const { client } = auth;
6002
6267
  const isAuthField = (key) => key === "client_assertion" || key === "client_assertion_type" || key === "client_secret";
6003
6268
  const params = Object.fromEntries(Object.entries(body).filter((entry) => typeof entry[1] === "string" && !isAuthField(entry[0])));
6004
6269
  const result = await pushAuthorizationRequest({
@@ -6082,6 +6347,50 @@ var oidcProviderRoutes = (config) => {
6082
6347
  headers: t12.Object({
6083
6348
  authorization: t12.Optional(t12.String())
6084
6349
  })
6350
+ }).post(backchannelAuthorizationRoute, async ({ body, headers }) => {
6351
+ if (config.backchannelAuthStore === undefined) {
6352
+ return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
6353
+ }
6354
+ const basic = readBasicAuth2(headers.authorization);
6355
+ const clientId = body.client_id ?? basic.clientId;
6356
+ const clientSecret = body.client_secret ?? basic.clientSecret;
6357
+ if (clientId === undefined) {
6358
+ return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
6359
+ }
6360
+ const client = await authenticateClient(clientId, clientSecret);
6361
+ if (client === undefined) {
6362
+ return oauthError2(HTTP_UNAUTHORIZED2, "invalid_client");
6363
+ }
6364
+ if (body.login_hint === undefined) {
6365
+ return oauthError2(HTTP_BAD_REQUEST2, "invalid_request");
6366
+ }
6367
+ const requested = body.scope === undefined || body.scope.length === 0 ? client.scopes : body.scope.split(" ").filter((entry) => client.scopes.includes(entry));
6368
+ const result = await issueBackchannelAuth({
6369
+ bindingMessage: body.binding_message,
6370
+ clientId: client.clientId,
6371
+ config,
6372
+ loginHint: body.login_hint,
6373
+ now: Date.now(),
6374
+ requestedScopes: requested
6375
+ });
6376
+ if (!result.ok)
6377
+ return oauthError2(HTTP_BAD_REQUEST2, result.error);
6378
+ return jsonResponse({
6379
+ auth_req_id: result.auth_req_id,
6380
+ expires_in: result.expires_in,
6381
+ interval: result.interval
6382
+ }, HTTP_OK2);
6383
+ }, {
6384
+ body: t12.Object({
6385
+ binding_message: t12.Optional(t12.String()),
6386
+ client_id: t12.Optional(t12.String()),
6387
+ client_secret: t12.Optional(t12.String()),
6388
+ login_hint: t12.Optional(t12.String()),
6389
+ scope: t12.Optional(t12.String())
6390
+ }),
6391
+ headers: t12.Object({
6392
+ authorization: t12.Optional(t12.String())
6393
+ })
6085
6394
  }).post(deviceAuthorizationRoute, async ({ body, headers }) => {
6086
6395
  if (config.deviceAuthorizationStore === undefined) {
6087
6396
  return oauthError2(HTTP_BAD_REQUEST2, "unsupported_grant_type");
@@ -21542,6 +21851,30 @@ var createInMemoryAuthorizationCodeStore = () => {
21542
21851
  }
21543
21852
  };
21544
21853
  };
21854
+ var createInMemoryBackchannelAuthStore = () => {
21855
+ const byAuthReqId = new Map;
21856
+ return {
21857
+ deleteByAuthReqId: async (authReqId) => {
21858
+ byAuthReqId.delete(authReqId);
21859
+ },
21860
+ findByAuthReqId: async (authReqId) => byAuthReqId.get(authReqId),
21861
+ recordPoll: async (authReqId, polledAt) => {
21862
+ const record = byAuthReqId.get(authReqId);
21863
+ if (!record)
21864
+ return;
21865
+ byAuthReqId.set(authReqId, { ...record, lastPolledAt: polledAt });
21866
+ },
21867
+ saveBackchannelAuth: async (request) => {
21868
+ byAuthReqId.set(request.authReqId, { ...request });
21869
+ },
21870
+ updateStatus: async (authReqId, status, userSub) => {
21871
+ const record = byAuthReqId.get(authReqId);
21872
+ if (!record)
21873
+ return;
21874
+ byAuthReqId.set(authReqId, { ...record, status, userSub });
21875
+ }
21876
+ };
21877
+ };
21545
21878
  var createInMemoryClientAssertionJtiStore = () => {
21546
21879
  const seen = new Map;
21547
21880
  return {
@@ -21689,6 +22022,20 @@ var createInMemoryPushedAuthorizationRequestStore = () => {
21689
22022
  var URL_LENGTH = 2048;
21690
22023
  var DEFAULT_LIST_LIMIT2 = 100;
21691
22024
  var ID_LENGTH7 = 255;
22025
+ var oauthBackchannelAuthRequestsTable = pgTable("auth_oauth_backchannel_auth_requests", {
22026
+ auth_req_id: varchar("auth_req_id", { length: ID_LENGTH7 }).primaryKey(),
22027
+ binding_message: text("binding_message"),
22028
+ client_id: varchar("client_id", { length: ID_LENGTH7 }).notNull(),
22029
+ created_at_ms: bigint("created_at_ms", { mode: "number" }).notNull(),
22030
+ expires_at_ms: bigint("expires_at_ms", { mode: "number" }).notNull(),
22031
+ interval_seconds: bigint("interval_seconds", {
22032
+ mode: "number"
22033
+ }).notNull(),
22034
+ last_polled_at_ms: bigint("last_polled_at_ms", { mode: "number" }),
22035
+ scopes: text("scopes").array().notNull(),
22036
+ status: varchar("status", { length: 16 }).notNull(),
22037
+ user_sub: varchar("user_sub", { length: ID_LENGTH7 })
22038
+ });
21692
22039
  var oauthClientAssertionJtisTable = pgTable("auth_oauth_client_assertion_jtis", {
21693
22040
  client_id: varchar("client_id", { length: ID_LENGTH7 }).notNull(),
21694
22041
  composite_key: varchar("composite_key", {
@@ -22054,6 +22401,48 @@ var createPostgresPushedAuthorizationRequestStore = (db) => ({
22054
22401
  });
22055
22402
  }
22056
22403
  });
22404
+ var toBackchannelAuth = (row) => ({
22405
+ authReqId: row.auth_req_id,
22406
+ bindingMessage: row.binding_message ?? undefined,
22407
+ clientId: row.client_id,
22408
+ createdAt: row.created_at_ms,
22409
+ expiresAt: row.expires_at_ms,
22410
+ intervalSeconds: row.interval_seconds,
22411
+ lastPolledAt: row.last_polled_at_ms ?? undefined,
22412
+ scopes: row.scopes,
22413
+ status: row.status,
22414
+ userSub: row.user_sub ?? undefined
22415
+ });
22416
+ var createNeonBackchannelAuthStore = (databaseUrl) => createPostgresBackchannelAuthStore(createNeonDatabase(databaseUrl));
22417
+ var createPostgresBackchannelAuthStore = (db) => ({
22418
+ deleteByAuthReqId: async (authReqId) => {
22419
+ await db.delete(oauthBackchannelAuthRequestsTable).where(eq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId));
22420
+ },
22421
+ findByAuthReqId: async (authReqId) => {
22422
+ const [row] = await db.select().from(oauthBackchannelAuthRequestsTable).where(eq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId)).limit(1);
22423
+ return row ? toBackchannelAuth(row) : undefined;
22424
+ },
22425
+ recordPoll: async (authReqId, polledAt) => {
22426
+ await db.update(oauthBackchannelAuthRequestsTable).set({ last_polled_at_ms: polledAt }).where(eq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId));
22427
+ },
22428
+ saveBackchannelAuth: async (request) => {
22429
+ await db.insert(oauthBackchannelAuthRequestsTable).values({
22430
+ auth_req_id: request.authReqId,
22431
+ binding_message: request.bindingMessage ?? null,
22432
+ client_id: request.clientId,
22433
+ created_at_ms: request.createdAt,
22434
+ expires_at_ms: request.expiresAt,
22435
+ interval_seconds: request.intervalSeconds,
22436
+ last_polled_at_ms: request.lastPolledAt ?? null,
22437
+ scopes: request.scopes,
22438
+ status: request.status,
22439
+ user_sub: request.userSub ?? null
22440
+ });
22441
+ },
22442
+ updateStatus: async (authReqId, status, userSub) => {
22443
+ await db.update(oauthBackchannelAuthRequestsTable).set({ status, user_sub: userSub ?? null }).where(eq(oauthBackchannelAuthRequestsTable.auth_req_id, authReqId));
22444
+ }
22445
+ });
22057
22446
  // src/adaptive/config.ts
22058
22447
  var DEFAULT_HISTORY_LIMIT = 50;
22059
22448
  var DEFAULT_MAX_TRAVEL_KMH = 900;
@@ -23316,6 +23705,7 @@ var blockMigrations = {
23316
23705
  lockout: initMigration("lockout", [lockoutsTable]),
23317
23706
  mfa: initMigration("mfa", [mfaEnrollmentsTable]),
23318
23707
  oidc: initMigration("oidc", [
23708
+ oauthBackchannelAuthRequestsTable,
23319
23709
  oauthClientAssertionJtisTable,
23320
23710
  oauthClientRegistrationTokensTable,
23321
23711
  oauthClientsTable,
@@ -23950,6 +24340,7 @@ export {
23950
24340
  verifyDpopNonce,
23951
24341
  verifyCognitoSha256,
23952
24342
  verifyClientAssertion,
24343
+ verifyCertificateBoundToken,
23953
24344
  verifyAuth0Pbkdf2,
23954
24345
  verifyAuditChain,
23955
24346
  verifyApiKey,
@@ -23998,6 +24389,7 @@ export {
23998
24389
  resolveOAuthAuthorization,
23999
24390
  resolveCookieSecure,
24000
24391
  resolveClientProviderEntry,
24392
+ resolveClientCert,
24001
24393
  resolveAuthHtmxRenderers,
24002
24394
  resolveApiPrincipal,
24003
24395
  removeFromSessionRing,
@@ -24034,6 +24426,7 @@ export {
24034
24426
  oauthClientsTable,
24035
24427
  oauthClientRegistrationTokensTable,
24036
24428
  oauthClientAssertionJtisTable,
24429
+ oauthBackchannelAuthRequestsTable,
24037
24430
  mintLogoutToken,
24038
24431
  mintDpopNonce,
24039
24432
  mfaTotpRoutes,
@@ -24052,6 +24445,7 @@ export {
24052
24445
  jwkThumbprint,
24053
24446
  issueTokenSet,
24054
24447
  issueDeviceAuthorization,
24448
+ issueBackchannelAuth,
24055
24449
  isValidUser,
24056
24450
  isValidProviderOption,
24057
24451
  isUserSessionId,
@@ -24092,17 +24486,20 @@ export {
24092
24486
  fingerprintDevice,
24093
24487
  fetchUserInfo,
24094
24488
  fanOutBackchannelLogout,
24489
+ extractRfc9440ClientCert,
24095
24490
  extractPropFromIdentity,
24096
24491
  extractDpopNonceClaim,
24097
24492
  exportAuditCsv,
24098
24493
  exchangeToken,
24099
24494
  exchangeDeviceCode,
24100
24495
  exchangeClientCredentials,
24496
+ exchangeBackchannelAuth,
24101
24497
  evaluatePassword,
24102
24498
  endImpersonation,
24103
24499
  encryptTotpSecret,
24104
24500
  encryptSecret,
24105
24501
  denyDeviceAuthorization,
24502
+ denyBackchannelAuth,
24106
24503
  deleteWarrant,
24107
24504
  deleteRegisteredClient,
24108
24505
  defineProvidersConfiguration,
@@ -24157,6 +24554,7 @@ export {
24157
24554
  createPostgresCredentialStore,
24158
24555
  createPostgresClientRegistrationTokenStore,
24159
24556
  createPostgresClientAssertionJtiStore,
24557
+ createPostgresBackchannelAuthStore,
24160
24558
  createPostgresAuthorizationCodeStore,
24161
24559
  createPostgresAuditSink,
24162
24560
  createPostgresApiKeyStore,
@@ -24191,6 +24589,7 @@ export {
24191
24589
  createNeonCredentialStore,
24192
24590
  createNeonClientRegistrationTokenStore,
24193
24591
  createNeonClientAssertionJtiStore,
24592
+ createNeonBackchannelAuthStore,
24194
24593
  createNeonAuthorizationCodeStore,
24195
24594
  createNeonAuthSessionStore,
24196
24595
  createNeonAuditSink,
@@ -24227,6 +24626,7 @@ export {
24227
24626
  createInMemoryClientRegistrationTokenStore,
24228
24627
  createInMemoryClientAssertionJtiStore,
24229
24628
  createInMemoryCheckCache,
24629
+ createInMemoryBackchannelAuthStore,
24230
24630
  createInMemoryAuthorizationCodeStore,
24231
24631
  createInMemoryAuthSessionStore,
24232
24632
  createInMemoryAuditSink,
@@ -24246,6 +24646,7 @@ export {
24246
24646
  consumePushedRequest,
24247
24647
  consumeBackupCode,
24248
24648
  constantTimeEqual,
24649
+ computeCertThumbprint,
24249
24650
  complianceRoutes,
24250
24651
  check,
24251
24652
  buildClientProviders,
@@ -24260,6 +24661,7 @@ export {
24260
24661
  assessRisk,
24261
24662
  assessAbuse,
24262
24663
  approveDeviceAuthorization,
24664
+ approveBackchannelAuth,
24263
24665
  apiKeysTable,
24264
24666
  apiKeysRoutes,
24265
24667
  apiClientsTable,
@@ -24297,8 +24699,9 @@ export {
24297
24699
  DEFAULT_CREDENTIAL_SESSION_TTL_MS,
24298
24700
  DEFAULT_BACKUP_CODE_COUNT,
24299
24701
  CLIENT_ASSERTION_TYPE,
24702
+ CIBA_GRANT_TYPE,
24300
24703
  AuthIdentityConflictError
24301
24704
  };
24302
24705
 
24303
- //# debugId=E33462A0DBAD894164756E2164756E21
24706
+ //# debugId=1069DAAF739CAF9F64756E2164756E21
24304
24707
  //# sourceMappingURL=index.js.map