@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,8 +1,8 @@
1
1
  import type { AuthSessionSource } from './session/types';
2
2
  import { type AccessTokenPrincipalConfig, type AuthPrincipal } from './principal';
3
- export declare const DEFAULT_PUSH_ROUTE: "/auth/push";
4
- /** @deprecated Use DEFAULT_PUSH_ROUTE. Kept for installed beta clients. */
5
3
  export declare const DEFAULT_NATIVE_PUSH_ROUTE: "/auth/mobile/push";
4
+ /** Canonical cross-platform push route. */
5
+ export declare const DEFAULT_PUSH_ROUTE: "/auth/push";
6
6
  export type PushPlatform = 'apns' | 'fcm' | 'webpush';
7
7
  /** @deprecated Use PushPlatform. */
8
8
  export type NativePushPlatform = PushPlatform;
@@ -217,13 +217,13 @@ export declare const pushRoutes: <UserType>({ accessTokens, authSessionStore, co
217
217
  }>;
218
218
  error: [];
219
219
  }, "delete", "/auth/mobile/push", import("elysia/types").IntersectIfObjectSchema<import("elysia").UnwrapRoute<{
220
- body: import("typebox").TObject<{
221
- installationId: import("typebox").TString;
222
- }>;
223
220
  beforeHandle: ({ pushPrincipal, status }: {
224
221
  pushPrincipal: AuthPrincipal<UserType> | undefined;
225
222
  status: (code: "Unauthorized", body: string) => unknown;
226
223
  }) => unknown;
224
+ body: import("typebox").TObject<{
225
+ installationId: import("typebox").TString;
226
+ }>;
227
227
  }, {}, "/auth/mobile/push">, import("elysia/types").MergeScopedSchemas<{}, {}, import("elysia").UnwrapRoute<{
228
228
  cookie: import("elysia").TCookieObject<{
229
229
  user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
@@ -372,13 +372,13 @@ export declare const pushRoutes: <UserType>({ accessTokens, authSessionStore, co
372
372
  readonly "Network Authentication Required": 511;
373
373
  }[Code] : Code>;
374
374
  defer: (fn: import("elysia/types").AfterResponseHandler<NoInfer<import("elysia/types").IntersectIfObjectSchema<import("elysia").UnwrapRoute<{
375
- body: import("typebox").TObject<{
376
- installationId: import("typebox").TString;
377
- }>;
378
375
  beforeHandle: ({ pushPrincipal, status }: {
379
376
  pushPrincipal: AuthPrincipal<UserType> | undefined;
380
377
  status: (code: "Unauthorized", body: string) => unknown;
381
378
  }) => unknown;
379
+ body: import("typebox").TObject<{
380
+ installationId: import("typebox").TString;
381
+ }>;
382
382
  }, {}, "/auth/mobile/push">, import("elysia/types").MergeScopedSchemas<{}, {}, import("elysia").UnwrapRoute<{
383
383
  cookie: import("elysia").TCookieObject<{
384
384
  user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
@@ -572,13 +572,13 @@ export declare const nativePushRoutes: <UserType>({ accessTokens, authSessionSto
572
572
  }>;
573
573
  error: [];
574
574
  }, "delete", "/auth/mobile/push", import("elysia/types").IntersectIfObjectSchema<import("elysia").UnwrapRoute<{
575
- body: import("typebox").TObject<{
576
- installationId: import("typebox").TString;
577
- }>;
578
575
  beforeHandle: ({ pushPrincipal, status }: {
579
576
  pushPrincipal: AuthPrincipal<UserType> | undefined;
580
577
  status: (code: "Unauthorized", body: string) => unknown;
581
578
  }) => unknown;
579
+ body: import("typebox").TObject<{
580
+ installationId: import("typebox").TString;
581
+ }>;
582
582
  }, {}, "/auth/mobile/push">, import("elysia/types").MergeScopedSchemas<{}, {}, import("elysia").UnwrapRoute<{
583
583
  cookie: import("elysia").TCookieObject<{
584
584
  user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
@@ -727,13 +727,13 @@ export declare const nativePushRoutes: <UserType>({ accessTokens, authSessionSto
727
727
  readonly "Network Authentication Required": 511;
728
728
  }[Code] : Code>;
729
729
  defer: (fn: import("elysia/types").AfterResponseHandler<NoInfer<import("elysia/types").IntersectIfObjectSchema<import("elysia").UnwrapRoute<{
730
- body: import("typebox").TObject<{
731
- installationId: import("typebox").TString;
732
- }>;
733
730
  beforeHandle: ({ pushPrincipal, status }: {
734
731
  pushPrincipal: AuthPrincipal<UserType> | undefined;
735
732
  status: (code: "Unauthorized", body: string) => unknown;
736
733
  }) => unknown;
734
+ body: import("typebox").TObject<{
735
+ installationId: import("typebox").TString;
736
+ }>;
737
737
  }, {}, "/auth/mobile/push">, import("elysia/types").MergeScopedSchemas<{}, {}, import("elysia").UnwrapRoute<{
738
738
  cookie: import("elysia").TCookieObject<{
739
739
  user_session_id: import("typebox").TOptional<import("typebox").TTemplateLiteral<"^.*-.*-.*-.*-.*$">>;
@@ -10,10 +10,14 @@ export declare const verifyDpopNonce: ({ now, nonce, secret }: {
10
10
  }) => Promise<boolean>;
11
11
  export type DpopResult = {
12
12
  jkt: string;
13
- jti?: string;
13
+ jti: string;
14
14
  };
15
- export declare const verifyDpopProof: ({ accessToken, htm, htu, isUsedJti, maxAgeMs, now, proof }: {
15
+ export type DpopJti = DpopResult & {
16
+ expiresAt: number;
17
+ };
18
+ export declare const verifyDpopProof: ({ accessToken, consumeJti, htm, htu, isUsedJti, maxAgeMs, now, proof }: {
16
19
  accessToken?: string;
20
+ consumeJti?: (jti: DpopJti) => boolean | Promise<boolean>;
17
21
  htm: string;
18
22
  htu: string;
19
23
  isUsedJti?: (jti: string) => boolean | Promise<boolean>;
@@ -6,6 +6,7 @@
6
6
  * through the package root.
7
7
  */
8
8
  export type { OidcProviderConfig } from './config';
9
+ export { extractDpopNonceClaim, mintDpopNonce, verifyDpopNonce, verifyDpopProof, type DpopJti, type DpopResult } from './dpop';
9
10
  export { generateSigningKey, jwkThumbprint, signJwt, signingVerificationKeys, toPublicJwk, verifyJwt, verifyJwtWithKeys, type SigningKey, type SigningKeyIdentity } from './keys';
10
11
  export { revokeOAuthClientCredentials } from './operator';
11
12
  export { createNeonAuthorizationCodeStore, createNeonClientAssertionJtiStore, createNeonClientRegistrationTokenStore, createNeonDeviceAuthorizationStore, createNeonInitialAccessTokenStore, createNeonOAuthClientStore, createNeonOidcRefreshTokenStore, createNeonSocketTicketStore, createPostgresAuthorizationCodeStore, createPostgresClientAssertionJtiStore, createPostgresClientRegistrationTokenStore, createPostgresDeviceAuthorizationStore, createPostgresInitialAccessTokenStore, createPostgresOAuthClientStore, createPostgresOidcRefreshTokenStore, createPostgresSocketTicketStore } from './postgresStores';
@@ -181,6 +181,9 @@ var init_crypto = __esm(() => {
181
181
  textDecoder3 = new TextDecoder;
182
182
  });
183
183
 
184
+ // src/oidc/dpop.ts
185
+ init_constants();
186
+
184
187
  // src/oidc/keys.ts
185
188
  var ENCODER = new TextEncoder;
186
189
  var ES256 = { hash: "SHA-256", name: "ECDSA" };
@@ -280,6 +283,128 @@ var verifyJwtWithKeys = async (token, keys) => {
280
283
  return;
281
284
  return verifyJwt(token, key.publicJwk);
282
285
  };
286
+
287
+ // src/oidc/dpop.ts
288
+ var DEFAULT_MAX_AGE_MS = 60000;
289
+ var MAX_JTI_LENGTH = 128;
290
+ var SECONDS_TO_MS = 1000;
291
+ var NONCE_WINDOW_SECONDS = 120;
292
+ var NONCE_WINDOW_MS = NONCE_WINDOW_SECONDS * MILLISECONDS_IN_A_SECOND;
293
+ var NONCE_PREVIOUS_WINDOWS_ACCEPTED = 1;
294
+ var hmacSha256 = async (secret, message) => {
295
+ const encoder = new TextEncoder;
296
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), { hash: "SHA-256", name: "HMAC" }, false, ["sign"]);
297
+ const signature = await crypto.subtle.sign("HMAC", key, encoder.encode(message));
298
+ return Buffer.from(new Uint8Array(signature)).toString("base64url");
299
+ };
300
+ var extractDpopNonceClaim = (proof) => {
301
+ const [, payloadSegment] = proof.split(".");
302
+ if (payloadSegment === undefined)
303
+ return;
304
+ try {
305
+ const payload = JSON.parse(Buffer.from(payloadSegment, "base64url").toString("utf8"));
306
+ if (typeof payload !== "object" || payload === null)
307
+ return;
308
+ const value = Reflect.get(payload, "nonce");
309
+ return typeof value === "string" ? value : undefined;
310
+ } catch {
311
+ return;
312
+ }
313
+ };
314
+ var mintDpopNonce = async ({
315
+ now = Date.now(),
316
+ secret
317
+ }) => {
318
+ const window = Math.floor(now / NONCE_WINDOW_MS);
319
+ return hmacSha256(secret, String(window));
320
+ };
321
+ var verifyDpopNonce = async ({
322
+ now = Date.now(),
323
+ nonce,
324
+ secret
325
+ }) => {
326
+ const currentWindow = Math.floor(now / NONCE_WINDOW_MS);
327
+ const candidates = await Promise.all(Array.from({ length: NONCE_PREVIOUS_WINDOWS_ACCEPTED + 1 }, (_, offset) => hmacSha256(secret, String(currentWindow - offset))));
328
+ return candidates.some((expected) => expected === nonce);
329
+ };
330
+ var decodeHeader = (segment) => {
331
+ try {
332
+ const value = JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
333
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
334
+ } catch {
335
+ return;
336
+ }
337
+ };
338
+ var normalizeHtu = (value, proofClaim = false) => {
339
+ try {
340
+ const url = new URL(String(value));
341
+ if (url.username || url.password || proofClaim && (url.search.length > 0 || url.hash.length > 0))
342
+ return;
343
+ return `${url.origin}${url.pathname}`;
344
+ } catch {
345
+ return;
346
+ }
347
+ };
348
+ var isPublicEs256Jwk = (value) => {
349
+ if (typeof value !== "object" || value === null || Array.isArray(value))
350
+ return false;
351
+ const kty = Reflect.get(value, "kty");
352
+ const crv = Reflect.get(value, "crv");
353
+ const xCoordinate = Reflect.get(value, "x");
354
+ const yCoordinate = Reflect.get(value, "y");
355
+ const privateComponent = Reflect.get(value, "d");
356
+ const alg = Reflect.get(value, "alg");
357
+ return kty === "EC" && crv === "P-256" && typeof xCoordinate === "string" && xCoordinate.length > 0 && typeof yCoordinate === "string" && yCoordinate.length > 0 && privateComponent === undefined && (alg === undefined || alg === "ES256");
358
+ };
359
+ var verifyDpopProof = async ({
360
+ accessToken,
361
+ consumeJti,
362
+ htm,
363
+ htu,
364
+ isUsedJti,
365
+ maxAgeMs = DEFAULT_MAX_AGE_MS,
366
+ now = Date.now(),
367
+ proof
368
+ }) => {
369
+ if (proof === undefined)
370
+ return;
371
+ const segments = proof.split(".");
372
+ if (segments.length !== 3)
373
+ return;
374
+ const [headerSegment] = segments;
375
+ if (headerSegment === undefined)
376
+ return;
377
+ const header = decodeHeader(headerSegment);
378
+ const publicJwk = header === undefined ? undefined : Reflect.get(header, "jwk");
379
+ if (header === undefined || Reflect.get(header, "typ") !== "dpop+jwt" || Reflect.get(header, "alg") !== "ES256" || !isPublicEs256Jwk(publicJwk)) {
380
+ return;
381
+ }
382
+ const verified = await verifyJwt(proof, publicJwk).catch(() => {
383
+ return;
384
+ });
385
+ if (verified === undefined)
386
+ return;
387
+ const { payload } = verified;
388
+ if (accessToken !== undefined) {
389
+ const expectedAth = Buffer.from(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(accessToken))).toString("base64url");
390
+ if (payload.ath !== expectedAth)
391
+ return;
392
+ }
393
+ const iatMs = typeof payload.iat === "number" ? payload.iat * SECONDS_TO_MS : 0;
394
+ const claimedHtu = normalizeHtu(payload.htu, true);
395
+ const expectedHtu = normalizeHtu(htu);
396
+ const jti = typeof payload.jti === "string" ? payload.jti : undefined;
397
+ 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) {
398
+ return;
399
+ }
400
+ const jkt = await jwkThumbprint(publicJwk);
401
+ if (isUsedJti !== undefined && await isUsedJti(jti)) {
402
+ return;
403
+ }
404
+ if (consumeJti !== undefined && !await consumeJti({ expiresAt: iatMs + maxAgeMs, jkt, jti }))
405
+ return;
406
+ return { jkt, jti };
407
+ };
283
408
  // src/oidc/operator.ts
284
409
  var operatorDelete = (store, storeName) => {
285
410
  if (!store.deleteForClient)
@@ -12788,13 +12913,17 @@ var issueSocketTicket = async ({
12788
12913
  export {
12789
12914
  verifyJwtWithKeys,
12790
12915
  verifyJwt,
12916
+ verifyDpopProof,
12917
+ verifyDpopNonce,
12791
12918
  toPublicJwk,
12792
12919
  signingVerificationKeys,
12793
12920
  signJwt,
12794
12921
  revokeOAuthClientCredentials,
12922
+ mintDpopNonce,
12795
12923
  jwkThumbprint,
12796
12924
  issueSocketTicket,
12797
12925
  generateSigningKey,
12926
+ extractDpopNonceClaim,
12798
12927
  createPostgresSocketTicketStore,
12799
12928
  createPostgresOidcRefreshTokenStore,
12800
12929
  createPostgresOAuthClientStore,
@@ -12815,5 +12944,5 @@ export {
12815
12944
  consumeSocketTicket
12816
12945
  };
12817
12946
 
12818
- //# debugId=B37CCAB8EEBB54A964756E2164756E21
12947
+ //# debugId=9914FF0D8636BBE564756E2164756E21
12819
12948
  //# sourceMappingURL=index.js.map