@absolutejs/auth 0.75.0 → 0.75.1

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.
@@ -1,4 +1,5 @@
1
1
  import { type SigningKeyIdentity } from '../oidc/keys';
2
+ import { type DpopJti } from '../oidc/dpop';
2
3
  import type { AgentCredentialVerifier } from './types';
3
4
  type OidcAgentCredentialVerifierKeys = {
4
5
  publicJwk: JsonWebKey;
@@ -10,8 +11,9 @@ type OidcAgentCredentialVerifierKeys = {
10
11
  /** Adapter for Absolute Auth's RFC 9068-style JWT access tokens. It validates
11
12
  * signature, issuer, audience, expiry, and extracts the OAuth client as the
12
13
  * agent identity while retaining `sub` as the authorizing user. */
13
- export declare const createOidcAgentCredentialVerifier: ({ issuer, isUsedDpopJti, maxDpopAgeMs, publicJwk, publicKeys, requireDpop, resource }: {
14
+ export declare const createOidcAgentCredentialVerifier: ({ issuer, consumeDpopJti, isUsedDpopJti, maxDpopAgeMs, publicJwk, publicKeys, requireDpop, resource }: {
14
15
  issuer: string;
16
+ consumeDpopJti?: (jti: DpopJti) => boolean | Promise<boolean>;
15
17
  isUsedDpopJti?: (jti: string) => boolean | Promise<boolean>;
16
18
  maxDpopAgeMs?: number;
17
19
  requireDpop?: boolean;
package/dist/index.d.ts CHANGED
@@ -3991,7 +3991,7 @@ export { oidcProviderRoutes } from './oidc/routes';
3991
3991
  export { generateSigningKey, jwkThumbprint, signJwt, signingVerificationKeys, toPublicJwk, verifyJwt, verifyJwtWithKeys } from './oidc/keys';
3992
3992
  export type { SigningKey, SigningKeyIdentity } from './oidc/keys';
3993
3993
  export { extractDpopNonceClaim, mintDpopNonce, verifyDpopNonce, verifyDpopProof } from './oidc/dpop';
3994
- export type { DpopResult } from './oidc/dpop';
3994
+ export type { DpopJti, DpopResult } from './oidc/dpop';
3995
3995
  export { CLIENT_ASSERTION_TYPE, verifyClientAssertion, verifyJwtSignedByClient } from './oidc/clientAuth';
3996
3996
  export { parseSignedRequestObject } from './oidc/jar';
3997
3997
  export type { JarParseResult } from './oidc/jar';
package/dist/index.js CHANGED
@@ -6191,8 +6191,8 @@ var createMfaGate = ({ getUserId, mfaStore }) => async (user) => isMfaEnrolled(a
6191
6191
 
6192
6192
  // src/nativePush.ts
6193
6193
  import { Elysia as Elysia17, t as t11 } from "elysia";
6194
- var DEFAULT_PUSH_ROUTE = "/auth/push";
6195
6194
  var DEFAULT_NATIVE_PUSH_ROUTE = "/auth/mobile/push";
6195
+ var DEFAULT_PUSH_ROUTE = "/auth/push";
6196
6196
  var requireServerText = (value, field) => {
6197
6197
  const normalized = value.trim();
6198
6198
  if (!normalized)
@@ -6246,9 +6246,11 @@ var pushRoutes = ({
6246
6246
  }
6247
6247
  return true;
6248
6248
  };
6249
+ const installationIdSchema = t11.Optional(t11.String({ maxLength: 128, minLength: 1 }));
6250
+ const localeSchema = t11.Optional(t11.String({ maxLength: 64, minLength: 1 }));
6249
6251
  const commonRegistrationFields = {
6250
- installationId: t11.Optional(t11.String({ maxLength: 128, minLength: 1 })),
6251
- locale: t11.Optional(t11.String({ maxLength: 64, minLength: 1 }))
6252
+ installationId: installationIdSchema,
6253
+ locale: localeSchema
6252
6254
  };
6253
6255
  const registrationBody = t11.Union([
6254
6256
  t11.Object({
@@ -6268,18 +6270,20 @@ var pushRoutes = ({
6268
6270
  })
6269
6271
  })
6270
6272
  ]);
6273
+ const requirePushPrincipal = ({
6274
+ pushPrincipal,
6275
+ status
6276
+ }) => pushPrincipal ? undefined : status("Unauthorized", "User is not authenticated");
6271
6277
  const registrationOptions = {
6272
- body: registrationBody,
6273
- beforeHandle: ({
6274
- pushPrincipal,
6275
- status
6276
- }) => pushPrincipal ? undefined : status("Unauthorized", "User is not authenticated")
6278
+ beforeHandle: requirePushPrincipal,
6279
+ body: registrationBody
6277
6280
  };
6281
+ const removalBody = t11.Object({
6282
+ installationId: t11.String({ maxLength: 128, minLength: 1 })
6283
+ });
6278
6284
  const removalOptions = {
6279
- body: t11.Object({
6280
- installationId: t11.String({ maxLength: 128, minLength: 1 })
6281
- }),
6282
- beforeHandle: registrationOptions.beforeHandle
6285
+ beforeHandle: requirePushPrincipal,
6286
+ body: removalBody
6283
6287
  };
6284
6288
  return new Elysia17({ name: "@absolutejs/auth/push" }).use(sessionStore()).guard({
6285
6289
  cookie: t11.Cookie({ user_session_id: userSessionIdTypebox }),
@@ -8086,6 +8090,7 @@ var verifyCertificateBoundToken = async ({
8086
8090
  // src/oidc/dpop.ts
8087
8091
  init_constants();
8088
8092
  var DEFAULT_MAX_AGE_MS = 60000;
8093
+ var MAX_JTI_LENGTH = 128;
8089
8094
  var SECONDS_TO_MS = 1000;
8090
8095
  var NONCE_WINDOW_SECONDS = 120;
8091
8096
  var NONCE_WINDOW_MS = NONCE_WINDOW_SECONDS * MILLISECONDS_IN_A_SECOND;
@@ -8126,17 +8131,38 @@ var verifyDpopNonce = async ({
8126
8131
  const candidates = await Promise.all(Array.from({ length: NONCE_PREVIOUS_WINDOWS_ACCEPTED + 1 }, (_, offset) => hmacSha2562(secret, String(currentWindow - offset))));
8127
8132
  return candidates.some((expected) => expected === nonce);
8128
8133
  };
8129
- var decodeHeader = (segment) => JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
8130
- var normalizeHtu = (value) => {
8134
+ var decodeHeader = (segment) => {
8135
+ try {
8136
+ const value = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
8137
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
8138
+ } catch {
8139
+ return;
8140
+ }
8141
+ };
8142
+ var normalizeHtu = (value, proofClaim = false) => {
8131
8143
  try {
8132
8144
  const url = new URL(String(value));
8145
+ if (url.username || url.password || proofClaim && (url.search.length > 0 || url.hash.length > 0))
8146
+ return;
8133
8147
  return `${url.origin}${url.pathname}`;
8134
8148
  } catch {
8135
- return "";
8149
+ return;
8136
8150
  }
8137
8151
  };
8152
+ var isPublicEs256Jwk = (value) => {
8153
+ if (typeof value !== "object" || value === null || Array.isArray(value))
8154
+ return false;
8155
+ const kty = Reflect.get(value, "kty");
8156
+ const crv = Reflect.get(value, "crv");
8157
+ const xCoordinate = Reflect.get(value, "x");
8158
+ const yCoordinate = Reflect.get(value, "y");
8159
+ const privateComponent = Reflect.get(value, "d");
8160
+ const alg = Reflect.get(value, "alg");
8161
+ return kty === "EC" && crv === "P-256" && typeof xCoordinate === "string" && xCoordinate.length > 0 && typeof yCoordinate === "string" && yCoordinate.length > 0 && privateComponent === undefined && (alg === undefined || alg === "ES256");
8162
+ };
8138
8163
  var verifyDpopProof = async ({
8139
8164
  accessToken,
8165
+ consumeJti,
8140
8166
  htm,
8141
8167
  htu,
8142
8168
  isUsedJti,
@@ -8146,14 +8172,20 @@ var verifyDpopProof = async ({
8146
8172
  }) => {
8147
8173
  if (proof === undefined)
8148
8174
  return;
8149
- const [headerSegment] = proof.split(".");
8175
+ const segments = proof.split(".");
8176
+ if (segments.length !== 3)
8177
+ return;
8178
+ const [headerSegment] = segments;
8150
8179
  if (headerSegment === undefined)
8151
8180
  return;
8152
8181
  const header = decodeHeader(headerSegment);
8153
- if (header?.typ !== "dpop+jwt" || header.alg !== "ES256" || header.jwk === undefined) {
8182
+ const publicJwk = header === undefined ? undefined : Reflect.get(header, "jwk");
8183
+ if (header === undefined || Reflect.get(header, "typ") !== "dpop+jwt" || Reflect.get(header, "alg") !== "ES256" || !isPublicEs256Jwk(publicJwk)) {
8154
8184
  return;
8155
8185
  }
8156
- const verified = await verifyJwt(proof, header.jwk);
8186
+ const verified = await verifyJwt(proof, publicJwk).catch(() => {
8187
+ return;
8188
+ });
8157
8189
  if (verified === undefined)
8158
8190
  return;
8159
8191
  const { payload } = verified;
@@ -8163,14 +8195,19 @@ var verifyDpopProof = async ({
8163
8195
  return;
8164
8196
  }
8165
8197
  const iatMs = typeof payload.iat === "number" ? payload.iat * SECONDS_TO_MS : 0;
8166
- if (payload.htm !== htm || normalizeHtu(payload.htu) !== normalizeHtu(htu) || iatMs === 0 || Math.abs(now - iatMs) > maxAgeMs) {
8198
+ const claimedHtu = normalizeHtu(payload.htu, true);
8199
+ const expectedHtu = normalizeHtu(htu);
8200
+ const jti = typeof payload.jti === "string" ? payload.jti : undefined;
8201
+ if (payload.htm !== htm || claimedHtu === undefined || expectedHtu === undefined || claimedHtu !== expectedHtu || iatMs === 0 || Math.abs(now - iatMs) > maxAgeMs || jti === undefined || jti.length === 0 || jti.length > MAX_JTI_LENGTH) {
8167
8202
  return;
8168
8203
  }
8169
- const jti = typeof payload.jti === "string" ? payload.jti : undefined;
8170
- if (jti !== undefined && isUsedJti !== undefined && await isUsedJti(jti)) {
8204
+ const jkt = await jwkThumbprint(publicJwk);
8205
+ if (isUsedJti !== undefined && await isUsedJti(jti)) {
8171
8206
  return;
8172
8207
  }
8173
- return { jkt: await jwkThumbprint(header.jwk), jti };
8208
+ if (consumeJti !== undefined && !await consumeJti({ expiresAt: iatMs + maxAgeMs, jkt, jti }))
8209
+ return;
8210
+ return { jkt, jti };
8174
8211
  };
8175
8212
 
8176
8213
  // src/oidc/logout.ts
@@ -8909,7 +8946,7 @@ var oidcProviderRoutes = (config) => {
8909
8946
  "dpop-nonce": fresh,
8910
8947
  "www-authenticate": 'DPoP error="use_dpop_nonce"'
8911
8948
  },
8912
- status: HTTP_UNAUTHORIZED4
8949
+ status: HTTP_BAD_REQUEST3
8913
8950
  });
8914
8951
  };
8915
8952
  const grantAuthorizationCode = async (client, body, dpop, clientCertThumbprint) => {
@@ -36606,6 +36643,7 @@ var readAudience = (audience) => {
36606
36643
  };
36607
36644
  var createOidcAgentCredentialVerifier = ({
36608
36645
  issuer,
36646
+ consumeDpopJti,
36609
36647
  isUsedDpopJti,
36610
36648
  maxDpopAgeMs,
36611
36649
  publicJwk,
@@ -36634,6 +36672,7 @@ var createOidcAgentCredentialVerifier = ({
36634
36672
  return;
36635
36673
  const proof = await verifyDpopProof({
36636
36674
  accessToken: token,
36675
+ consumeJti: consumeDpopJti,
36637
36676
  htm: request.method,
36638
36677
  htu: request.url,
36639
36678
  isUsedJti: isUsedDpopJti,
@@ -40973,5 +41012,5 @@ export {
40973
41012
  ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV
40974
41013
  };
40975
41014
 
40976
- //# debugId=A9E81CE91544BE6B64756E2164756E21
41015
+ //# debugId=1BF002329763FE6764756E2164756E21
40977
41016
  //# sourceMappingURL=index.js.map