@absolutejs/auth 0.74.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.
Files changed (47) hide show
  1. package/README.md +21 -6
  2. package/dist/agents/index.js +46 -11
  3. package/dist/agents/index.js.map +4 -4
  4. package/dist/agents/oidcAdapter.d.ts +3 -1
  5. package/dist/apikeys/routes.d.ts +1 -1
  6. package/dist/compliance/routes.d.ts +1 -1
  7. package/dist/credentials/emailVerification.d.ts +1 -1
  8. package/dist/credentials/login.d.ts +1 -1
  9. package/dist/credentials/passwordReset.d.ts +1 -1
  10. package/dist/credentials/register.d.ts +1 -1
  11. package/dist/htmx/configuredRoutes.d.ts +1 -1
  12. package/dist/htmx/routes.d.ts +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +166 -68
  15. package/dist/index.js.map +8 -8
  16. package/dist/mfa/challenge.d.ts +1 -1
  17. package/dist/mfa/management.d.ts +1 -1
  18. package/dist/mfa/sms.d.ts +1 -1
  19. package/dist/mfa/totp.d.ts +1 -1
  20. package/dist/nativePush.d.ts +500 -20
  21. package/dist/oidc/dpop.d.ts +6 -2
  22. package/dist/oidc/index.d.ts +1 -0
  23. package/dist/oidc/index.js +130 -1
  24. package/dist/oidc/index.js.map +4 -3
  25. package/dist/organizations/routes.d.ts +1 -1
  26. package/dist/portal/routes.d.ts +1 -1
  27. package/dist/roles/routes.d.ts +1 -1
  28. package/dist/routes/authorize.d.ts +1 -1
  29. package/dist/routes/callback.d.ts +1 -1
  30. package/dist/routes/profile.d.ts +1 -1
  31. package/dist/routes/refresh.d.ts +1 -1
  32. package/dist/routes/revoke.d.ts +1 -1
  33. package/dist/routes/sessions.d.ts +1 -1
  34. package/dist/routes/signout.d.ts +1 -1
  35. package/dist/routes/userStatus.d.ts +1 -1
  36. package/dist/scim/routes.d.ts +1 -1
  37. package/dist/server.js +164 -68
  38. package/dist/server.js.map +8 -8
  39. package/dist/sso/discoveryRoute.d.ts +1 -1
  40. package/dist/sso/oidcRoutes.d.ts +1 -1
  41. package/dist/sso/samlIdpRoutes.d.ts +1 -1
  42. package/dist/sso/samlRoutes.d.ts +1 -1
  43. package/dist/types.d.ts +3 -1
  44. package/dist/vc/statusListRoutes.d.ts +1 -1
  45. package/dist/vc/vpRoutes.d.ts +1 -1
  46. package/dist/webauthn/routes.d.ts +1 -1
  47. package/package.json +1 -1
@@ -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;
@@ -31,7 +31,7 @@ export declare const apiKeysRoutes: ({ accessTokenStore, accessTokenTtlMs, apiCl
31
31
  status?: number | keyof import("elysia").StatusMap;
32
32
  cookie?: Record<string, import("elysia").BaseCookie>;
33
33
  };
34
- status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
34
+ status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 409 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
35
35
  readonly 100: "Continue";
36
36
  readonly 101: "Switching Protocols";
37
37
  readonly 102: "Processing";
@@ -62,7 +62,7 @@ export declare const complianceRoutes: <UserType>({ authSessionStore, compliance
62
62
  status?: number | keyof import("elysia").StatusMap;
63
63
  cookie?: Record<string, import("elysia").BaseCookie>;
64
64
  };
65
- status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
65
+ status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 409 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
66
66
  readonly 100: "Continue";
67
67
  readonly 101: "Switching Protocols";
68
68
  readonly 102: "Processing";
@@ -46,7 +46,7 @@ export declare const credentialsEmailVerification: <UserType>({ credentialStore,
46
46
  status?: number | keyof import("elysia").StatusMap;
47
47
  cookie?: Record<string, import("elysia").BaseCookie>;
48
48
  };
49
- status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
49
+ status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 409 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
50
50
  readonly 100: "Continue";
51
51
  readonly 101: "Switching Protocols";
52
52
  readonly 102: "Processing";
@@ -41,7 +41,7 @@ export declare const credentialsLogin: <UserType>({ authSessionStore, checkBreac
41
41
  status?: number | keyof import("elysia").StatusMap;
42
42
  cookie?: Record<string, import("elysia").BaseCookie>;
43
43
  };
44
- status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
44
+ status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 409 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
45
45
  readonly 100: "Continue";
46
46
  readonly 101: "Switching Protocols";
47
47
  readonly 102: "Processing";
@@ -52,7 +52,7 @@ export declare const credentialsPasswordReset: <UserType>({ credentialStore, onP
52
52
  status?: number | keyof import("elysia").StatusMap;
53
53
  cookie?: Record<string, import("elysia").BaseCookie>;
54
54
  };
55
- status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
55
+ status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 409 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
56
56
  readonly 100: "Continue";
57
57
  readonly 101: "Switching Protocols";
58
58
  readonly 102: "Processing";
@@ -41,7 +41,7 @@ export declare const credentialsRegister: <UserType>({ authSessionStore, cookieS
41
41
  status?: number | keyof import("elysia").StatusMap;
42
42
  cookie?: Record<string, import("elysia").BaseCookie>;
43
43
  };
44
- status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
44
+ status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 409 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
45
45
  readonly 100: "Continue";
46
46
  readonly 101: "Switching Protocols";
47
47
  readonly 102: "Processing";
@@ -511,7 +511,7 @@ export declare const createConfiguredAuthHtmxRoutes: <UserType>({ authSessionSto
511
511
  status?: number | keyof import("elysia").StatusMap;
512
512
  cookie?: Record<string, import("elysia").BaseCookie>;
513
513
  };
514
- status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
514
+ status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 409 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
515
515
  readonly 100: "Continue";
516
516
  readonly 101: "Switching Protocols";
517
517
  readonly 102: "Processing";
@@ -506,7 +506,7 @@ export declare const createAuthHtmxRoutes: <UserType extends AuthHtmxUser>(confi
506
506
  status?: number | keyof import("elysia").StatusMap;
507
507
  cookie?: Record<string, import("elysia").BaseCookie>;
508
508
  };
509
- status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
509
+ status: <const Code extends number | keyof import("elysia").StatusMap, const T = Code extends 400 | 401 | 403 | 501 | 204 | 404 | 200 | 301 | 302 | 303 | 307 | 308 | 409 | 100 | 101 | 102 | 103 | 201 | 202 | 203 | 205 | 206 | 207 | 208 | 300 | 304 | 402 | 405 | 406 | 407 | 408 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451 | 500 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511 ? {
510
510
  readonly 100: "Continue";
511
511
  readonly 101: "Switching Protocols";
512
512
  readonly 102: "Processing";
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
@@ -6192,23 +6192,100 @@ var createMfaGate = ({ getUserId, mfaStore }) => async (user) => isMfaEnrolled(a
6192
6192
  // src/nativePush.ts
6193
6193
  import { Elysia as Elysia17, t as t11 } from "elysia";
6194
6194
  var DEFAULT_NATIVE_PUSH_ROUTE = "/auth/mobile/push";
6195
+ var DEFAULT_PUSH_ROUTE = "/auth/push";
6195
6196
  var requireServerText = (value, field) => {
6196
6197
  const normalized = value.trim();
6197
6198
  if (!normalized)
6198
- throw new TypeError(`Native push ${field} must not be empty.`);
6199
+ throw new TypeError(`Push ${field} must not be empty.`);
6199
6200
  return normalized;
6200
6201
  };
6201
6202
  var isInstallationOwnershipError = (error) => error instanceof Error && error.name === "PushInstallationOwnershipError";
6202
- var nativePushRoutes = ({
6203
+ var pushRoutes = ({
6203
6204
  accessTokens,
6204
6205
  authSessionStore,
6205
6206
  config
6206
6207
  }) => {
6207
- const resolveTenant = async (principal) => requireServerText(typeof config.tenant === "function" ? await config.tenant(principal) : config.tenant, "native tenant");
6208
+ const resolveTenant = async (principal) => requireServerText(typeof config.tenant === "function" ? await config.tenant(principal) : config.tenant, "tenant");
6208
6209
  const resolveTopics = async (principal) => [
6209
6210
  ...new Set((await config.topics?.(principal) ?? []).map((topic) => requireServerText(topic, "topic")))
6210
6211
  ].sort();
6211
- return new Elysia17({ name: "@absolutejs/auth/native-push" }).use(sessionStore()).guard({
6212
+ const register = async (body, principal) => {
6213
+ try {
6214
+ const registration = await config.registrar.registerInstallation({
6215
+ ...body.installationId ? { installationId: body.installationId } : {},
6216
+ ...body.locale ? { locale: body.locale } : {},
6217
+ ...body.platform === "webpush" ? {
6218
+ platform: body.platform,
6219
+ subscription: body.subscription
6220
+ } : { platform: body.platform, token: body.token },
6221
+ tenant: await resolveTenant(principal),
6222
+ topics: await resolveTopics(principal),
6223
+ userId: principal.subject
6224
+ });
6225
+ return {
6226
+ installationId: requireServerText(registration.installationId, "installation id"),
6227
+ registered: true
6228
+ };
6229
+ } catch (error) {
6230
+ if (isInstallationOwnershipError(error))
6231
+ return null;
6232
+ throw error;
6233
+ }
6234
+ };
6235
+ const remove = async (installationId, principal) => {
6236
+ try {
6237
+ await config.registrar.removeInstallation({
6238
+ installationId,
6239
+ tenant: await resolveTenant(principal),
6240
+ userId: principal.subject
6241
+ });
6242
+ } catch (error) {
6243
+ if (isInstallationOwnershipError(error))
6244
+ return false;
6245
+ throw error;
6246
+ }
6247
+ return true;
6248
+ };
6249
+ const installationIdSchema = t11.Optional(t11.String({ maxLength: 128, minLength: 1 }));
6250
+ const localeSchema = t11.Optional(t11.String({ maxLength: 64, minLength: 1 }));
6251
+ const commonRegistrationFields = {
6252
+ installationId: installationIdSchema,
6253
+ locale: localeSchema
6254
+ };
6255
+ const registrationBody = t11.Union([
6256
+ t11.Object({
6257
+ ...commonRegistrationFields,
6258
+ platform: t11.Union([t11.Literal("apns"), t11.Literal("fcm")]),
6259
+ token: t11.String({ maxLength: 8192, minLength: 1 })
6260
+ }),
6261
+ t11.Object({
6262
+ ...commonRegistrationFields,
6263
+ platform: t11.Literal("webpush"),
6264
+ subscription: t11.Object({
6265
+ endpoint: t11.String({ maxLength: 8192, minLength: 1 }),
6266
+ keys: t11.Object({
6267
+ auth: t11.String({ maxLength: 8192, minLength: 1 }),
6268
+ p256dh: t11.String({ maxLength: 8192, minLength: 1 })
6269
+ })
6270
+ })
6271
+ })
6272
+ ]);
6273
+ const requirePushPrincipal = ({
6274
+ pushPrincipal,
6275
+ status
6276
+ }) => pushPrincipal ? undefined : status("Unauthorized", "User is not authenticated");
6277
+ const registrationOptions = {
6278
+ beforeHandle: requirePushPrincipal,
6279
+ body: registrationBody
6280
+ };
6281
+ const removalBody = t11.Object({
6282
+ installationId: t11.String({ maxLength: 128, minLength: 1 })
6283
+ });
6284
+ const removalOptions = {
6285
+ beforeHandle: requirePushPrincipal,
6286
+ body: removalBody
6287
+ };
6288
+ return new Elysia17({ name: "@absolutejs/auth/push" }).use(sessionStore()).guard({
6212
6289
  cookie: t11.Cookie({ user_session_id: userSessionIdTypebox }),
6213
6290
  schema: "merge"
6214
6291
  }).derive(async ({
@@ -6229,56 +6306,36 @@ var nativePushRoutes = ({
6229
6306
  authorization: headers.authorization,
6230
6307
  config: accessTokens
6231
6308
  });
6232
- return { nativePushPrincipal: principal };
6233
- }).post(DEFAULT_NATIVE_PUSH_ROUTE, {
6234
- body: t11.Object({
6235
- installationId: t11.Optional(t11.String({ maxLength: 128, minLength: 1 })),
6236
- locale: t11.Optional(t11.String({ maxLength: 64, minLength: 1 })),
6237
- platform: t11.Union([t11.Literal("apns"), t11.Literal("fcm")]),
6238
- token: t11.String({ maxLength: 8192, minLength: 1 })
6239
- }),
6240
- beforeHandle: ({ nativePushPrincipal, status }) => nativePushPrincipal ? undefined : status("Unauthorized", "User is not authenticated")
6241
- }, async ({ body, nativePushPrincipal, status }) => {
6242
- if (!nativePushPrincipal)
6309
+ return { pushPrincipal: principal };
6310
+ }).post(DEFAULT_PUSH_ROUTE, registrationOptions, async ({ body, pushPrincipal, status }) => {
6311
+ if (!pushPrincipal)
6243
6312
  return status("Unauthorized", "User is not authenticated");
6244
- let registration;
6245
- try {
6246
- registration = await config.registrar.registerInstallation({
6247
- ...body.installationId ? { installationId: body.installationId } : {},
6248
- ...body.locale ? { locale: body.locale } : {},
6249
- platform: body.platform,
6250
- tenant: await resolveTenant(nativePushPrincipal),
6251
- token: body.token,
6252
- topics: await resolveTopics(nativePushPrincipal),
6253
- userId: nativePushPrincipal.subject
6254
- });
6255
- } catch (error) {
6256
- if (isInstallationOwnershipError(error))
6257
- return status("Conflict", {
6258
- code: "installation-ownership"
6259
- });
6260
- throw error;
6261
- }
6262
- return {
6263
- installationId: requireServerText(registration.installationId, "installation id"),
6264
- registered: true
6265
- };
6266
- }).delete(DEFAULT_NATIVE_PUSH_ROUTE, {
6267
- body: t11.Object({
6268
- installationId: t11.String({ maxLength: 128, minLength: 1 })
6269
- }),
6270
- beforeHandle: ({ nativePushPrincipal, status }) => nativePushPrincipal ? undefined : status("Unauthorized", "User is not authenticated")
6271
- }, async ({ body, nativePushPrincipal, status }) => {
6272
- if (!nativePushPrincipal)
6313
+ const result = await register(body, pushPrincipal);
6314
+ return result ?? status("Conflict", {
6315
+ code: "installation-ownership"
6316
+ });
6317
+ }).post(DEFAULT_NATIVE_PUSH_ROUTE, registrationOptions, async ({ body, pushPrincipal, status }) => {
6318
+ if (!pushPrincipal)
6273
6319
  return status("Unauthorized", "User is not authenticated");
6274
- await config.registrar.removeInstallation({
6275
- installationId: body.installationId,
6276
- tenant: await resolveTenant(nativePushPrincipal),
6277
- userId: nativePushPrincipal.subject
6320
+ const result = await register(body, pushPrincipal);
6321
+ return result ?? status("Conflict", {
6322
+ code: "installation-ownership"
6323
+ });
6324
+ }).delete(DEFAULT_PUSH_ROUTE, removalOptions, async ({ body, pushPrincipal, status }) => {
6325
+ if (!pushPrincipal)
6326
+ return status("Unauthorized", "User is not authenticated");
6327
+ return await remove(body.installationId, pushPrincipal) ? { removed: true } : status("Conflict", {
6328
+ code: "installation-ownership"
6329
+ });
6330
+ }).delete(DEFAULT_NATIVE_PUSH_ROUTE, removalOptions, async ({ body, pushPrincipal, status }) => {
6331
+ if (!pushPrincipal)
6332
+ return status("Unauthorized", "User is not authenticated");
6333
+ return await remove(body.installationId, pushPrincipal) ? { removed: true } : status("Conflict", {
6334
+ code: "installation-ownership"
6278
6335
  });
6279
- return { removed: true };
6280
6336
  });
6281
6337
  };
6338
+ var nativePushRoutes = pushRoutes;
6282
6339
 
6283
6340
  // src/mfa/routes.ts
6284
6341
  import { Elysia as Elysia22 } from "elysia";
@@ -8033,6 +8090,7 @@ var verifyCertificateBoundToken = async ({
8033
8090
  // src/oidc/dpop.ts
8034
8091
  init_constants();
8035
8092
  var DEFAULT_MAX_AGE_MS = 60000;
8093
+ var MAX_JTI_LENGTH = 128;
8036
8094
  var SECONDS_TO_MS = 1000;
8037
8095
  var NONCE_WINDOW_SECONDS = 120;
8038
8096
  var NONCE_WINDOW_MS = NONCE_WINDOW_SECONDS * MILLISECONDS_IN_A_SECOND;
@@ -8073,17 +8131,38 @@ var verifyDpopNonce = async ({
8073
8131
  const candidates = await Promise.all(Array.from({ length: NONCE_PREVIOUS_WINDOWS_ACCEPTED + 1 }, (_, offset) => hmacSha2562(secret, String(currentWindow - offset))));
8074
8132
  return candidates.some((expected) => expected === nonce);
8075
8133
  };
8076
- var decodeHeader = (segment) => JSON.parse(Buffer.from(segment, "base64url").toString("utf8"));
8077
- 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) => {
8078
8143
  try {
8079
8144
  const url = new URL(String(value));
8145
+ if (url.username || url.password || proofClaim && (url.search.length > 0 || url.hash.length > 0))
8146
+ return;
8080
8147
  return `${url.origin}${url.pathname}`;
8081
8148
  } catch {
8082
- return "";
8149
+ return;
8083
8150
  }
8084
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
+ };
8085
8163
  var verifyDpopProof = async ({
8086
8164
  accessToken,
8165
+ consumeJti,
8087
8166
  htm,
8088
8167
  htu,
8089
8168
  isUsedJti,
@@ -8093,14 +8172,20 @@ var verifyDpopProof = async ({
8093
8172
  }) => {
8094
8173
  if (proof === undefined)
8095
8174
  return;
8096
- const [headerSegment] = proof.split(".");
8175
+ const segments = proof.split(".");
8176
+ if (segments.length !== 3)
8177
+ return;
8178
+ const [headerSegment] = segments;
8097
8179
  if (headerSegment === undefined)
8098
8180
  return;
8099
8181
  const header = decodeHeader(headerSegment);
8100
- 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)) {
8101
8184
  return;
8102
8185
  }
8103
- const verified = await verifyJwt(proof, header.jwk);
8186
+ const verified = await verifyJwt(proof, publicJwk).catch(() => {
8187
+ return;
8188
+ });
8104
8189
  if (verified === undefined)
8105
8190
  return;
8106
8191
  const { payload } = verified;
@@ -8110,14 +8195,19 @@ var verifyDpopProof = async ({
8110
8195
  return;
8111
8196
  }
8112
8197
  const iatMs = typeof payload.iat === "number" ? payload.iat * SECONDS_TO_MS : 0;
8113
- 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) {
8114
8202
  return;
8115
8203
  }
8116
- const jti = typeof payload.jti === "string" ? payload.jti : undefined;
8117
- if (jti !== undefined && isUsedJti !== undefined && await isUsedJti(jti)) {
8204
+ const jkt = await jwkThumbprint(publicJwk);
8205
+ if (isUsedJti !== undefined && await isUsedJti(jti)) {
8118
8206
  return;
8119
8207
  }
8120
- return { jkt: await jwkThumbprint(header.jwk), jti };
8208
+ if (consumeJti !== undefined && !await consumeJti({ expiresAt: iatMs + maxAgeMs, jkt, jti }))
8209
+ return;
8210
+ return { jkt, jti };
8121
8211
  };
8122
8212
 
8123
8213
  // src/oidc/logout.ts
@@ -8856,7 +8946,7 @@ var oidcProviderRoutes = (config) => {
8856
8946
  "dpop-nonce": fresh,
8857
8947
  "www-authenticate": 'DPoP error="use_dpop_nonce"'
8858
8948
  },
8859
- status: HTTP_UNAUTHORIZED4
8949
+ status: HTTP_BAD_REQUEST3
8860
8950
  });
8861
8951
  };
8862
8952
  const grantAuthorizationCode = async (client, body, dpop, clientCertThumbprint) => {
@@ -36553,6 +36643,7 @@ var readAudience = (audience) => {
36553
36643
  };
36554
36644
  var createOidcAgentCredentialVerifier = ({
36555
36645
  issuer,
36646
+ consumeDpopJti,
36556
36647
  isUsedDpopJti,
36557
36648
  maxDpopAgeMs,
36558
36649
  publicJwk,
@@ -36581,6 +36672,7 @@ var createOidcAgentCredentialVerifier = ({
36581
36672
  return;
36582
36673
  const proof = await verifyDpopProof({
36583
36674
  accessToken: token,
36675
+ consumeJti: consumeDpopJti,
36584
36676
  htm: request.method,
36585
36677
  htu: request.url,
36586
36678
  isUsedJti: isUsedDpopJti,
@@ -40047,6 +40139,7 @@ var buildAuthApplications = async (configuration) => {
40047
40139
  customProviders,
40048
40140
  mfa,
40049
40141
  nativePush: nativePush2,
40142
+ push,
40050
40143
  verificationProvider,
40051
40144
  passwordless,
40052
40145
  lockout,
@@ -40083,8 +40176,11 @@ var buildAuthApplications = async (configuration) => {
40083
40176
  onRevocationError,
40084
40177
  onSessionCleanup
40085
40178
  } = configuration;
40086
- if (nativePush2 && !oidc)
40087
- throw new Error("nativePush requires the OIDC provider");
40179
+ if (push && nativePush2)
40180
+ throw new Error("Configure `push`, not both `push` and `nativePush`");
40181
+ const pushConfig = push ?? nativePush2;
40182
+ if (pushConfig && !oidc)
40183
+ throw new Error("push requires the OIDC provider");
40088
40184
  if (agentAuth?.agentRegistration !== undefined) {
40089
40185
  if (oidc === undefined) {
40090
40186
  throw new Error("agentAuth.agentRegistration requires the OIDC provider");
@@ -40328,18 +40424,18 @@ var buildAuthApplications = async (configuration) => {
40328
40424
  emit: auditEmit
40329
40425
  }) : new Elysia48
40330
40426
  ]);
40331
- const nativePushApplication = new Elysia48;
40332
- if (nativePush2 && oidcConfig)
40333
- nativePushApplication.use(nativePushRoutes({
40427
+ const pushApplication = new Elysia48;
40428
+ if (pushConfig && oidcConfig)
40429
+ pushApplication.use(pushRoutes({
40334
40430
  accessTokens: {
40335
40431
  oidc: oidcConfig,
40336
40432
  getUser: async (subject) => await getUser(subject) ?? null
40337
40433
  },
40338
40434
  authSessionStore,
40339
- config: nativePush2
40435
+ config: pushConfig
40340
40436
  }));
40341
40437
  const extendedFeatureRoutes = new Elysia48().use([
40342
- nativePushApplication,
40438
+ pushApplication,
40343
40439
  portal ? portalRoutes({ ...portal, emit: auditEmit }) : new Elysia48,
40344
40440
  webauthn ? webauthnRoutes({
40345
40441
  ...webauthn,
@@ -40496,6 +40592,7 @@ export {
40496
40592
  readSessionRing,
40497
40593
  readSessionCookie,
40498
40594
  readAuthSyncPartition,
40595
+ pushRoutes,
40499
40596
  pushAuthorizationRequest,
40500
40597
  pruneInactiveUsers,
40501
40598
  providersFromEnv,
@@ -40886,6 +40983,7 @@ export {
40886
40983
  DEFAULT_SCIM_ROUTE,
40887
40984
  DEFAULT_ROLES_ROUTE,
40888
40985
  DEFAULT_RESET_TOKEN_TTL_MS,
40986
+ DEFAULT_PUSH_ROUTE,
40889
40987
  DEFAULT_PORTAL_ROUTE,
40890
40988
  DEFAULT_PASSWORDLESS_SESSION_TTL_MS,
40891
40989
  DEFAULT_PASSWORDLESS_ROUTE,
@@ -40914,5 +41012,5 @@ export {
40914
41012
  ABSOLUTE_NATIVE_AUTH_CLIENTS_ENV
40915
41013
  };
40916
41014
 
40917
- //# debugId=3448A14AC106260464756E2164756E21
41015
+ //# debugId=1BF002329763FE6764756E2164756E21
40918
41016
  //# sourceMappingURL=index.js.map