@absolutejs/auth 0.75.0 → 0.75.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.
@@ -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;
@@ -35,6 +35,7 @@ export declare const agentAuthPlugin: (config?: AgentAuthConfig) => Elysia<"", "
35
35
  scopes_supported: string[];
36
36
  resource_name?: string | undefined;
37
37
  resource_logo_uri?: string | undefined;
38
+ dpop_bound_access_tokens_required?: boolean | undefined;
38
39
  authorization_servers: string[];
39
40
  bearer_methods_supported: string[];
40
41
  };
@@ -149,6 +150,7 @@ export declare const agentAuthRoutes: (config?: AgentAuthConfig) => Elysia<"", "
149
150
  scopes_supported: string[];
150
151
  resource_name?: string | undefined;
151
152
  resource_logo_uri?: string | undefined;
153
+ dpop_bound_access_tokens_required?: boolean | undefined;
152
154
  authorization_servers: string[];
153
155
  bearer_methods_supported: string[];
154
156
  };
package/dist/index.d.ts CHANGED
@@ -3759,6 +3759,7 @@ export declare const createAuthApplications: <UserType>(configuration: AuthConfi
3759
3759
  scopes_supported: string[];
3760
3760
  resource_name?: string | undefined;
3761
3761
  resource_logo_uri?: string | undefined;
3762
+ dpop_bound_access_tokens_required?: boolean | undefined;
3762
3763
  authorization_servers: string[];
3763
3764
  bearer_methods_supported: string[];
3764
3765
  };
@@ -3991,7 +3992,7 @@ export { oidcProviderRoutes } from './oidc/routes';
3991
3992
  export { generateSigningKey, jwkThumbprint, signJwt, signingVerificationKeys, toPublicJwk, verifyJwt, verifyJwtWithKeys } from './oidc/keys';
3992
3993
  export type { SigningKey, SigningKeyIdentity } from './oidc/keys';
3993
3994
  export { extractDpopNonceClaim, mintDpopNonce, verifyDpopNonce, verifyDpopProof } from './oidc/dpop';
3994
- export type { DpopResult } from './oidc/dpop';
3995
+ export type { DpopJti, DpopResult } from './oidc/dpop';
3995
3996
  export { CLIENT_ASSERTION_TYPE, verifyClientAssertion, verifyJwtSignedByClient } from './oidc/clientAuth';
3996
3997
  export { parseSignedRequestObject } from './oidc/jar';
3997
3998
  export type { JarParseResult } from './oidc/jar';
package/dist/index.js CHANGED
@@ -3260,6 +3260,7 @@ var DEFAULT_AGENT_RESOURCE_METADATA_ROUTE = "/.well-known/oauth-protected-resour
3260
3260
  var agentProtectedResourceMetadata = (config) => ({
3261
3261
  authorization_servers: [config.authorizationServer],
3262
3262
  bearer_methods_supported: ["header"],
3263
+ ...config.dpopBoundAccessTokensRequired === true ? { dpop_bound_access_tokens_required: true } : {},
3263
3264
  ...config.logoUri === undefined ? {} : { resource_logo_uri: config.logoUri },
3264
3265
  ...config.resourceName === undefined ? {} : { resource_name: config.resourceName },
3265
3266
  resource: config.resource,
@@ -6191,8 +6192,8 @@ var createMfaGate = ({ getUserId, mfaStore }) => async (user) => isMfaEnrolled(a
6191
6192
 
6192
6193
  // src/nativePush.ts
6193
6194
  import { Elysia as Elysia17, t as t11 } from "elysia";
6194
- var DEFAULT_PUSH_ROUTE = "/auth/push";
6195
6195
  var DEFAULT_NATIVE_PUSH_ROUTE = "/auth/mobile/push";
6196
+ var DEFAULT_PUSH_ROUTE = "/auth/push";
6196
6197
  var requireServerText = (value, field) => {
6197
6198
  const normalized = value.trim();
6198
6199
  if (!normalized)
@@ -6246,9 +6247,11 @@ var pushRoutes = ({
6246
6247
  }
6247
6248
  return true;
6248
6249
  };
6250
+ const installationIdSchema = t11.Optional(t11.String({ maxLength: 128, minLength: 1 }));
6251
+ const localeSchema = t11.Optional(t11.String({ maxLength: 64, minLength: 1 }));
6249
6252
  const commonRegistrationFields = {
6250
- installationId: t11.Optional(t11.String({ maxLength: 128, minLength: 1 })),
6251
- locale: t11.Optional(t11.String({ maxLength: 64, minLength: 1 }))
6253
+ installationId: installationIdSchema,
6254
+ locale: localeSchema
6252
6255
  };
6253
6256
  const registrationBody = t11.Union([
6254
6257
  t11.Object({
@@ -6268,18 +6271,20 @@ var pushRoutes = ({
6268
6271
  })
6269
6272
  })
6270
6273
  ]);
6274
+ const requirePushPrincipal = ({
6275
+ pushPrincipal,
6276
+ status
6277
+ }) => pushPrincipal ? undefined : status("Unauthorized", "User is not authenticated");
6271
6278
  const registrationOptions = {
6272
- body: registrationBody,
6273
- beforeHandle: ({
6274
- pushPrincipal,
6275
- status
6276
- }) => pushPrincipal ? undefined : status("Unauthorized", "User is not authenticated")
6279
+ beforeHandle: requirePushPrincipal,
6280
+ body: registrationBody
6277
6281
  };
6282
+ const removalBody = t11.Object({
6283
+ installationId: t11.String({ maxLength: 128, minLength: 1 })
6284
+ });
6278
6285
  const removalOptions = {
6279
- body: t11.Object({
6280
- installationId: t11.String({ maxLength: 128, minLength: 1 })
6281
- }),
6282
- beforeHandle: registrationOptions.beforeHandle
6286
+ beforeHandle: requirePushPrincipal,
6287
+ body: removalBody
6283
6288
  };
6284
6289
  return new Elysia17({ name: "@absolutejs/auth/push" }).use(sessionStore()).guard({
6285
6290
  cookie: t11.Cookie({ user_session_id: userSessionIdTypebox }),
@@ -8086,6 +8091,7 @@ var verifyCertificateBoundToken = async ({
8086
8091
  // src/oidc/dpop.ts
8087
8092
  init_constants();
8088
8093
  var DEFAULT_MAX_AGE_MS = 60000;
8094
+ var MAX_JTI_LENGTH = 128;
8089
8095
  var SECONDS_TO_MS = 1000;
8090
8096
  var NONCE_WINDOW_SECONDS = 120;
8091
8097
  var NONCE_WINDOW_MS = NONCE_WINDOW_SECONDS * MILLISECONDS_IN_A_SECOND;
@@ -8126,17 +8132,38 @@ var verifyDpopNonce = async ({
8126
8132
  const candidates = await Promise.all(Array.from({ length: NONCE_PREVIOUS_WINDOWS_ACCEPTED + 1 }, (_, offset) => hmacSha2562(secret, String(currentWindow - offset))));
8127
8133
  return candidates.some((expected) => expected === nonce);
8128
8134
  };
8129
- var decodeHeader = (segment) => JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
8130
- var normalizeHtu = (value) => {
8135
+ var decodeHeader = (segment) => {
8136
+ try {
8137
+ const value = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
8138
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
8139
+ } catch {
8140
+ return;
8141
+ }
8142
+ };
8143
+ var normalizeHtu = (value, proofClaim = false) => {
8131
8144
  try {
8132
8145
  const url = new URL(String(value));
8146
+ if (url.username || url.password || proofClaim && (url.search.length > 0 || url.hash.length > 0))
8147
+ return;
8133
8148
  return `${url.origin}${url.pathname}`;
8134
8149
  } catch {
8135
- return "";
8150
+ return;
8136
8151
  }
8137
8152
  };
8153
+ var isPublicEs256Jwk = (value) => {
8154
+ if (typeof value !== "object" || value === null || Array.isArray(value))
8155
+ return false;
8156
+ const kty = Reflect.get(value, "kty");
8157
+ const crv = Reflect.get(value, "crv");
8158
+ const xCoordinate = Reflect.get(value, "x");
8159
+ const yCoordinate = Reflect.get(value, "y");
8160
+ const privateComponent = Reflect.get(value, "d");
8161
+ const alg = Reflect.get(value, "alg");
8162
+ return kty === "EC" && crv === "P-256" && typeof xCoordinate === "string" && xCoordinate.length > 0 && typeof yCoordinate === "string" && yCoordinate.length > 0 && privateComponent === undefined && (alg === undefined || alg === "ES256");
8163
+ };
8138
8164
  var verifyDpopProof = async ({
8139
8165
  accessToken,
8166
+ consumeJti,
8140
8167
  htm,
8141
8168
  htu,
8142
8169
  isUsedJti,
@@ -8146,14 +8173,20 @@ var verifyDpopProof = async ({
8146
8173
  }) => {
8147
8174
  if (proof === undefined)
8148
8175
  return;
8149
- const [headerSegment] = proof.split(".");
8176
+ const segments = proof.split(".");
8177
+ if (segments.length !== 3)
8178
+ return;
8179
+ const [headerSegment] = segments;
8150
8180
  if (headerSegment === undefined)
8151
8181
  return;
8152
8182
  const header = decodeHeader(headerSegment);
8153
- if (header?.typ !== "dpop+jwt" || header.alg !== "ES256" || header.jwk === undefined) {
8183
+ const publicJwk = header === undefined ? undefined : Reflect.get(header, "jwk");
8184
+ if (header === undefined || Reflect.get(header, "typ") !== "dpop+jwt" || Reflect.get(header, "alg") !== "ES256" || !isPublicEs256Jwk(publicJwk)) {
8154
8185
  return;
8155
8186
  }
8156
- const verified = await verifyJwt(proof, header.jwk);
8187
+ const verified = await verifyJwt(proof, publicJwk).catch(() => {
8188
+ return;
8189
+ });
8157
8190
  if (verified === undefined)
8158
8191
  return;
8159
8192
  const { payload } = verified;
@@ -8163,14 +8196,19 @@ var verifyDpopProof = async ({
8163
8196
  return;
8164
8197
  }
8165
8198
  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) {
8199
+ const claimedHtu = normalizeHtu(payload.htu, true);
8200
+ const expectedHtu = normalizeHtu(htu);
8201
+ const jti = typeof payload.jti === "string" ? payload.jti : undefined;
8202
+ 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
8203
  return;
8168
8204
  }
8169
- const jti = typeof payload.jti === "string" ? payload.jti : undefined;
8170
- if (jti !== undefined && isUsedJti !== undefined && await isUsedJti(jti)) {
8205
+ const jkt = await jwkThumbprint(publicJwk);
8206
+ if (isUsedJti !== undefined && await isUsedJti(jti)) {
8171
8207
  return;
8172
8208
  }
8173
- return { jkt: await jwkThumbprint(header.jwk), jti };
8209
+ if (consumeJti !== undefined && !await consumeJti({ expiresAt: iatMs + maxAgeMs, jkt, jti }))
8210
+ return;
8211
+ return { jkt, jti };
8174
8212
  };
8175
8213
 
8176
8214
  // src/oidc/logout.ts
@@ -8909,7 +8947,7 @@ var oidcProviderRoutes = (config) => {
8909
8947
  "dpop-nonce": fresh,
8910
8948
  "www-authenticate": 'DPoP error="use_dpop_nonce"'
8911
8949
  },
8912
- status: HTTP_UNAUTHORIZED4
8950
+ status: HTTP_BAD_REQUEST3
8913
8951
  });
8914
8952
  };
8915
8953
  const grantAuthorizationCode = async (client, body, dpop, clientCertThumbprint) => {
@@ -36606,6 +36644,7 @@ var readAudience = (audience) => {
36606
36644
  };
36607
36645
  var createOidcAgentCredentialVerifier = ({
36608
36646
  issuer,
36647
+ consumeDpopJti,
36609
36648
  isUsedDpopJti,
36610
36649
  maxDpopAgeMs,
36611
36650
  publicJwk,
@@ -36634,6 +36673,7 @@ var createOidcAgentCredentialVerifier = ({
36634
36673
  return;
36635
36674
  const proof = await verifyDpopProof({
36636
36675
  accessToken: token,
36676
+ consumeJti: consumeDpopJti,
36637
36677
  htm: request.method,
36638
36678
  htu: request.url,
36639
36679
  isUsedJti: isUsedDpopJti,
@@ -40973,5 +41013,5 @@ export {
40973
41013
  ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV
40974
41014
  };
40975
41015
 
40976
- //# debugId=A9E81CE91544BE6B64756E2164756E21
41016
+ //# debugId=6230674639E5BA6B64756E2164756E21
40977
41017
  //# sourceMappingURL=index.js.map