@gatwech/auth 1.0.0-beta.24 → 1.0.0-beta.25

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 (90) hide show
  1. package/dist/adapters/index.d.mts +1 -1
  2. package/dist/api/index.d.mts +1 -1
  3. package/dist/{audit-log-DhRRCMrv.mjs → audit-log-CQnf8nrN.mjs} +442 -46
  4. package/dist/client/index.d.mts +1 -1
  5. package/dist/client/plugins/index.d.mts +46 -46
  6. package/dist/client/plugins/index.mjs +2 -2
  7. package/dist/client/react/index.d.mts +15 -15
  8. package/dist/{client-CMsC6-rl.mjs → client-BkpNva-h.mjs} +3 -2
  9. package/dist/{client-CGV_kj99.d.mts → client-g0OAVuPQ.d.mts} +1 -1
  10. package/dist/{client-CT-acJeN.d.mts → client-zkWmSxqy.d.mts} +7 -4
  11. package/dist/cookies/index.d.mts +2 -2
  12. package/dist/db/index.d.mts +1 -1
  13. package/dist/{index-Czr_eQFE.d.mts → index-B4HTudhm.d.mts} +6 -6
  14. package/dist/{index-DYqQWdEv.d.mts → index-BHiF_ZfZ.d.mts} +13 -13
  15. package/dist/{index-Dmjw6re9.d.mts → index-BX55rZO5.d.mts} +2 -2
  16. package/dist/{index-Clwezlxm.d.mts → index-BfwCJQfM.d.mts} +8 -8
  17. package/dist/{index-DGRGLg16.d.mts → index-BhbilPfs.d.mts} +2 -2
  18. package/dist/{index-JzSy-vFn.d.mts → index-Bi-vEjDm.d.mts} +166 -166
  19. package/dist/{index-Cl9ITXy1.d.mts → index-BifhxQ8O.d.mts} +10 -10
  20. package/dist/{index-DWGj7du8.d.mts → index-BiomZttP.d.mts} +19 -19
  21. package/dist/{index-Hoybqpnt.d.mts → index-BoHrBgTB.d.mts} +9 -9
  22. package/dist/{index-UDzaYKxq.d.mts → index-C5W_sTlp.d.mts} +9 -9
  23. package/dist/{index-CLhlur69.d.mts → index-CNyQFga7.d.mts} +6 -6
  24. package/dist/{index-CZtQ0rUe.d.mts → index-CTjXncwM.d.mts} +113 -113
  25. package/dist/{index-CGGitykk.d.mts → index-CUUaJp3R.d.mts} +7 -7
  26. package/dist/{index-D85KXOKz.d.mts → index-CcJjgkwe.d.mts} +3 -3
  27. package/dist/{index-B-i2ob-C.d.mts → index-Cdfh66xb.d.mts} +9 -9
  28. package/dist/{index-CHMYUshk.d.mts → index-DBtm_waB.d.mts} +13 -13
  29. package/dist/{index-DWSbFJWU.d.mts → index-DD_pxX-h.d.mts} +662 -354
  30. package/dist/{index-BYxt8qOP.d.mts → index-DRoa3BGG.d.mts} +33 -33
  31. package/dist/{index-D5cyFBEb.d.mts → index-M0IQ_DTL.d.mts} +4 -4
  32. package/dist/{index-rcUKPBBy.d.mts → index-gRLqvVoc.d.mts} +6 -6
  33. package/dist/{index-Dt6JhAV4.d.mts → index-i1L-uguJ.d.mts} +6 -6
  34. package/dist/{index-B6to9CaK.d.mts → index-kBEBsEUr.d.mts} +1 -1
  35. package/dist/{index-BRUQHEUF.d.mts → index-mDmrBYrk.d.mts} +5 -5
  36. package/dist/index.d.mts +1 -1
  37. package/dist/integrations/next-js.d.mts +4 -4
  38. package/dist/integrations/node.d.mts +1 -1
  39. package/dist/integrations/react-start.d.mts +2 -2
  40. package/dist/oauth2/index.d.mts +1 -1
  41. package/dist/plugins/admin/index.d.mts +2 -2
  42. package/dist/plugins/anonymous/index.d.mts +2 -2
  43. package/dist/plugins/audit-log/client.d.mts +23 -23
  44. package/dist/plugins/audit-log/index.d.mts +23 -23
  45. package/dist/plugins/audit-log/index.mjs +3 -3
  46. package/dist/plugins/bearer/index.d.mts +1 -1
  47. package/dist/plugins/captcha/index.d.mts +1 -1
  48. package/dist/plugins/custom-session/index.d.mts +2 -2
  49. package/dist/plugins/device-authorization/index.d.mts +1 -1
  50. package/dist/plugins/email-otp/index.d.mts +1 -1
  51. package/dist/plugins/generic-oauth/index.d.mts +2 -2
  52. package/dist/plugins/haveibeenpwned/index.d.mts +1 -1
  53. package/dist/plugins/index.d.mts +23 -23
  54. package/dist/plugins/index.mjs +5 -5
  55. package/dist/plugins/jwt/index.d.mts +2 -2
  56. package/dist/plugins/magic-link/index.d.mts +1 -1
  57. package/dist/plugins/multi-session/index.d.mts +1 -1
  58. package/dist/plugins/oauth-proxy/index.d.mts +1 -1
  59. package/dist/plugins/one-tap/index.d.mts +1 -1
  60. package/dist/plugins/one-time-token/index.d.mts +2 -2
  61. package/dist/plugins/one-time-token/index.mjs +4 -4
  62. package/dist/plugins/organization/index.d.mts +2 -2
  63. package/dist/plugins/passkey/index.d.mts +2 -2
  64. package/dist/plugins/phone-number/index.d.mts +2 -2
  65. package/dist/plugins/sentinel/client.d.mts +23 -23
  66. package/dist/plugins/sentinel/client.mjs +2 -2
  67. package/dist/plugins/sentinel/index.d.mts +22 -22
  68. package/dist/plugins/sentinel/index.mjs +3 -3
  69. package/dist/plugins/two-factor/index.d.mts +2 -2
  70. package/dist/plugins/username/index.d.mts +1 -1
  71. package/dist/{registry-GztLf62p.mjs → registry-BjYhRF79.mjs} +69 -2
  72. package/dist/{sentinel-b8XEetew.mjs → sentinel-BttZLWPD.mjs} +70 -14
  73. package/dist/src/plugins/audit-log/error-codes.d.ts +7 -0
  74. package/dist/src/plugins/audit-log/events.d.ts +13 -0
  75. package/dist/src/plugins/audit-log/index.d.ts +114 -8
  76. package/dist/src/plugins/audit-log/integrity.d.ts +20 -0
  77. package/dist/src/plugins/audit-log/redact.d.ts +18 -0
  78. package/dist/src/plugins/audit-log/remote.d.ts +1 -0
  79. package/dist/src/plugins/audit-log/schema.d.ts +25 -0
  80. package/dist/src/plugins/audit-log/types.d.ts +73 -13
  81. package/dist/src/plugins/sentinel/actions.d.ts +0 -6
  82. package/dist/src/plugins/sentinel/client.d.ts +3 -0
  83. package/dist/src/plugins/sentinel/error-codes.d.ts +6 -0
  84. package/dist/src/plugins/sentinel/index.d.ts +80 -3
  85. package/dist/src/plugins/sentinel/types.d.ts +1 -1
  86. package/dist/test-utils/index.d.mts +2 -2
  87. package/dist/test-utils/index.mjs +4 -4
  88. package/dist/types/index.d.mts +1 -1
  89. package/package.json +3 -3
  90. /package/dist/{pow-DvkUopsK.mjs → pow-Vna_j4hx.mjs} +0 -0
@@ -3,9 +3,9 @@ import { t as safeJSONParse } from "./json-D0TBLy4q.mjs";
3
3
  import { n as deleteSessionCookie } from "./cookies-BuvlgcA-.mjs";
4
4
  import { n as mergeSchema } from "./date-CMr04reD.mjs";
5
5
  import { u as sessionMiddleware } from "./session-BB3oqApE.mjs";
6
- import { a as CREDENTIAL_SIGN_IN_PATHS, c as SIGN_IN_PATHS, l as VERIFICATION_PATHS, s as SECURITY_EVENT_TYPES, t as getAuditRecorder, u as hasRole } from "./registry-GztLf62p.mjs";
6
+ import { d as VERIFICATION_PATHS, f as hasRole, l as SECURITY_EVENT_TYPES, o as CREDENTIAL_SIGN_IN_PATHS, t as getAuditRecorder, u as SIGN_IN_PATHS } from "./registry-BjYhRF79.mjs";
7
7
  import { t as getPasswordBreachCount } from "./haveibeenpwned-_3a5amT-.mjs";
8
- import { c as verifySolution, i as createNonce, n as POW_REASON_HEADER, o as parsePowHeader, r as POW_SOLUTION_HEADER, t as POW_CHALLENGE_HEADER } from "./pow-DvkUopsK.mjs";
8
+ import { c as verifySolution, i as createNonce, n as POW_REASON_HEADER, o as parsePowHeader, r as POW_SOLUTION_HEADER, t as POW_CHALLENGE_HEADER } from "./pow-Vna_j4hx.mjs";
9
9
  import { env } from "@gatwech/core/env";
10
10
  import { PayvoAuthError } from "@gatwech/core/error";
11
11
  import { defineErrorCodes } from "@gatwech/core/utils";
@@ -14,6 +14,15 @@ import { createAuthEndpoint, createAuthMiddleware } from "@gatwech/core/api";
14
14
  import * as z$1 from "zod";
15
15
  import { betterFetch } from "@better-fetch/fetch";
16
16
 
17
+ //#region src/plugins/sentinel/error-codes.ts
18
+ const SENTINEL_ERROR_CODES = defineErrorCodes({
19
+ POW_CHALLENGE_REQUIRED: "Challenge required",
20
+ SECURITY_BLOCKED: "Request blocked for security reasons",
21
+ SECURITY_ADMIN_REQUIRED: "Only an administrator can manage security blocks",
22
+ BLOCK_NOT_FOUND: "That block doesn't exist or has ended"
23
+ });
24
+
25
+ //#endregion
17
26
  //#region src/plugins/sentinel/actions.ts
18
27
  const CHALLENGE_TTL_MS = 6e4;
19
28
  /**
@@ -111,12 +120,6 @@ function challengeHeaders(decision) {
111
120
  "access-control-expose-headers": `${POW_CHALLENGE_HEADER}, ${POW_REASON_HEADER}`
112
121
  };
113
122
  }
114
- const SENTINEL_ERROR_CODES = defineErrorCodes({
115
- POW_CHALLENGE_REQUIRED: "Challenge required",
116
- SECURITY_BLOCKED: "Request blocked for security reasons",
117
- SECURITY_ADMIN_REQUIRED: "Only an administrator can manage security blocks",
118
- BLOCK_NOT_FOUND: "That block doesn't exist or has ended"
119
- });
120
123
  /** The error for a decision, or null when the request is allowed */
121
124
  function errorForDecision(decision) {
122
125
  if (decision.outcome === "allow") return null;
@@ -1830,17 +1833,62 @@ const sentinel = (options) => {
1830
1833
  ...normalizedEmailSchema
1831
1834
  } : schema, opts.schema),
1832
1835
  endpoints: {
1833
- listBlocks: createAuthEndpoint("/sentinel/blocks", {
1836
+ listBlocks: createAuthEndpoint("/sentinel/list-blocks", {
1834
1837
  method: "GET",
1835
1838
  use: [sessionMiddleware],
1836
1839
  query: z$1.object({
1837
- limit: z$1.string().or(z$1.number()).optional(),
1838
- offset: z$1.string().or(z$1.number()).optional()
1840
+ limit: z$1.string().or(z$1.number()).meta({ description: "The number of blocks to return. Max 100, defaults to 50" }).optional(),
1841
+ offset: z$1.string().or(z$1.number()).meta({ description: "The offset to start from" }).optional()
1839
1842
  }).optional(),
1840
1843
  metadata: { openapi: {
1841
1844
  operationId: "listSentinelBlocks",
1842
1845
  summary: "List active blocks",
1843
- description: "List the visitors and IP addresses sentinel is blocking, for admins"
1846
+ description: "List the visitors and IP addresses sentinel is blocking, for admins",
1847
+ responses: { 200: {
1848
+ description: "Paginated blocks that haven't ended",
1849
+ content: { "application/json": { schema: {
1850
+ type: "object",
1851
+ properties: {
1852
+ blocks: {
1853
+ type: "array",
1854
+ items: {
1855
+ type: "object",
1856
+ properties: {
1857
+ id: { type: "string" },
1858
+ key: { type: "string" },
1859
+ feature: { type: "string" },
1860
+ kind: {
1861
+ type: "string",
1862
+ enum: ["visitor", "ip"]
1863
+ },
1864
+ identifier: { type: "string" },
1865
+ account: {
1866
+ type: "string",
1867
+ nullable: true
1868
+ },
1869
+ blockedUntil: {
1870
+ type: "string",
1871
+ format: "date-time"
1872
+ },
1873
+ createdAt: {
1874
+ type: "string",
1875
+ format: "date-time"
1876
+ }
1877
+ }
1878
+ }
1879
+ },
1880
+ total: { type: "number" },
1881
+ limit: { type: "number" },
1882
+ offset: { type: "number" }
1883
+ },
1884
+ required: [
1885
+ "blocks",
1886
+ "total",
1887
+ "limit",
1888
+ "offset"
1889
+ ]
1890
+ } } }
1891
+ } }
1844
1892
  } }
1845
1893
  }, async (ctx) => {
1846
1894
  requireAdmin(ctx.context.session.user);
@@ -1860,11 +1908,19 @@ const sentinel = (options) => {
1860
1908
  unblock: createAuthEndpoint("/sentinel/unblock", {
1861
1909
  method: "POST",
1862
1910
  use: [sessionMiddleware],
1863
- body: z$1.object({ id: z$1.string() }),
1911
+ body: z$1.object({ id: z$1.string().meta({ description: "The id of the block to lift, from the list of blocks" }) }),
1864
1912
  metadata: { openapi: {
1865
1913
  operationId: "unblockSentinel",
1866
1914
  summary: "Lift a block",
1867
- description: "Lift a block before its cooldown ends, for admins. Recorded in the audit log."
1915
+ description: "Lift a block before its cooldown ends, for admins. Recorded in the audit log.",
1916
+ responses: { 200: {
1917
+ description: "The block was lifted",
1918
+ content: { "application/json": { schema: {
1919
+ type: "object",
1920
+ properties: { success: { type: "boolean" } },
1921
+ required: ["success"]
1922
+ } } }
1923
+ } }
1868
1924
  } }
1869
1925
  }, async (ctx) => {
1870
1926
  const { user } = ctx.context.session;
@@ -0,0 +1,7 @@
1
+ export declare const AUDIT_LOG_ERROR_CODES: {
2
+ readonly AUDIT_LOG_ACCESS_DENIED: "You don't have access to these audit logs";
3
+ readonly AUDIT_CANNOT_BE_DISABLED: "Core audit events and the local audit store can't be turned off";
4
+ readonly AUDIT_LOG_UNAVAILABLE: "Audit logs are temporarily unavailable";
5
+ readonly AUDIT_RETENTION_TOO_SHORT: "Audit events can't be deleted before the retention minimum has passed";
6
+ readonly AUDIT_SIGNING_SECRET_MISSING: "Audit events must be signed in production: set integrity.secret or PAYVO_AUDIT_SIGNING_SECRET";
7
+ };
@@ -55,6 +55,8 @@ export declare const AUDIT_EVENT_TYPES: {
55
55
  readonly TEAM_MEMBER_REMOVED: "team_member_removed";
56
56
  readonly USER_SIGN_IN_FAILED: "user_sign_in_failed";
57
57
  readonly VERIFICATION_FAILED: "verification_failed";
58
+ readonly AUDIT_READ: "audit_read";
59
+ readonly AUDIT_EVENTS_DELETED: "audit_events_deleted";
58
60
  };
59
61
  /**
60
62
  * Security event types recorded by the sentinel plugin
@@ -78,6 +80,12 @@ export declare const SECURITY_EVENT_TYPES: {
78
80
  };
79
81
  export type AuditEventType = (typeof AUDIT_EVENT_TYPES)[keyof typeof AUDIT_EVENT_TYPES] | (typeof SECURITY_EVENT_TYPES)[keyof typeof SECURITY_EVENT_TYPES];
80
82
  export declare const ALL_EVENT_TYPES: AuditEventType[];
83
+ /**
84
+ * The events that can't be excluded: who signed in or out and who failed to,
85
+ * changes to credentials, roles, bans and sessions, impersonation, reads and
86
+ * deletions of the audit log itself, and every security decision.
87
+ */
88
+ export declare const CORE_EVENT_TYPES: ("user_signed_up" | "user_banned" | "user_unbanned" | "user_deleted" | "user_role_updated" | "user_email_changed" | "user_signed_in" | "user_signed_out" | "session_revoked" | "sessions_revoked_all" | "user_impersonated" | "user_impersonation_stopped" | "password_changed" | "two_factor_enabled" | "two_factor_disabled" | "password_reset_completed" | "user_sign_in_failed" | "verification_failed" | "audit_read" | "audit_events_deleted" | "security_blocked" | "security_allowed" | "security_credential_stuffing" | "security_impossible_travel" | "security_geo_blocked" | "security_bot_blocked" | "security_suspicious_ip" | "security_velocity_exceeded" | "security_free_trial_abuse" | "security_compromised_password" | "security_stale_account" | "security_invalid_email" | "security_unblocked")[];
81
89
  /** Endpoints that complete a sign-in by creating a session */
82
90
  export declare const SIGN_IN_PATHS: string[];
83
91
  /** Sign-in endpoints that check credentials, so a failure is a failed attempt */
@@ -89,3 +97,8 @@ export declare const CREDENTIAL_SIGN_IN_PATHS: string[];
89
97
  export declare const VERIFICATION_PATHS: string[];
90
98
  /** Endpoints whose successful response maps to a single event */
91
99
  export declare const PATH_EVENTS: Record<string, AuditEventType>;
100
+ /**
101
+ * The severity of each event type that isn't `info`. A failed, blocked or
102
+ * challenged event is at least `warning`.
103
+ */
104
+ export declare const EVENT_SEVERITIES: Partial<Record<AuditEventType, "warning" | "critical">>;
@@ -3,12 +3,10 @@ import * as z from "zod";
3
3
  import { type AuditEventType } from "./events";
4
4
  import { schema } from "./schema";
5
5
  import type { AuditEvent, AuditLogOptions } from "./types";
6
+ export * from "./error-codes";
6
7
  export * from "./events";
8
+ export { verifyAuditEvent } from "./integrity";
7
9
  export type * from "./types";
8
- export declare const AUDIT_LOG_ERROR_CODES: {
9
- readonly AUDIT_LOG_ACCESS_DENIED: "You don't have access to these audit logs";
10
- readonly AUDIT_LOG_UNAVAILABLE: "Audit logs are temporarily unavailable";
11
- };
12
10
  export declare const auditLog: (options?: AuditLogOptions | undefined) => {
13
11
  id: "audit-log";
14
12
  init(ctx: AuthContext<import("@gatwech/core").PayvoAuthOptions>): {
@@ -115,6 +113,7 @@ export declare const auditLog: (options?: AuditLogOptions | undefined) => {
115
113
  offset?: string | number | undefined;
116
114
  eventType?: string | undefined;
117
115
  identifier?: string | undefined;
116
+ traceId?: string | undefined;
118
117
  organizationId?: string | undefined;
119
118
  userId?: string | undefined;
120
119
  startDate?: string | undefined;
@@ -178,6 +177,7 @@ export declare const auditLog: (options?: AuditLogOptions | undefined) => {
178
177
  offset: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
179
178
  eventType: z.ZodOptional<z.ZodString>;
180
179
  identifier: z.ZodOptional<z.ZodString>;
180
+ traceId: z.ZodOptional<z.ZodString>;
181
181
  organizationId: z.ZodOptional<z.ZodString>;
182
182
  userId: z.ZodOptional<z.ZodString>;
183
183
  startDate: z.ZodOptional<z.ZodString>;
@@ -242,6 +242,30 @@ export declare const auditLog: (options?: AuditLogOptions | undefined) => {
242
242
  type: string;
243
243
  nullable: boolean;
244
244
  };
245
+ traceId: {
246
+ type: string;
247
+ nullable: boolean;
248
+ };
249
+ requestId: {
250
+ type: string;
251
+ nullable: boolean;
252
+ };
253
+ actorType: {
254
+ type: string;
255
+ enum: string[];
256
+ };
257
+ severity: {
258
+ type: string;
259
+ enum: string[];
260
+ };
261
+ signature: {
262
+ type: string;
263
+ nullable: boolean;
264
+ };
265
+ verified: {
266
+ type: string;
267
+ description: string;
268
+ };
245
269
  createdAt: {
246
270
  type: string;
247
271
  format: string;
@@ -283,6 +307,7 @@ export declare const auditLog: (options?: AuditLogOptions | undefined) => {
283
307
  offset?: string | number | undefined;
284
308
  eventType?: string | undefined;
285
309
  identifier?: string | undefined;
310
+ traceId?: string | undefined;
286
311
  organizationId?: string | undefined;
287
312
  userId?: string | undefined;
288
313
  startDate?: string | undefined;
@@ -305,13 +330,17 @@ export declare const auditLog: (options?: AuditLogOptions | undefined) => {
305
330
  }) | undefined): Promise<[AsResponse] extends [true] ? Response : [ReturnHeaders] extends [true] ? {
306
331
  headers: Headers;
307
332
  response: {
308
- events: AuditEvent[];
333
+ events: (AuditEvent & {
334
+ verified?: boolean | undefined;
335
+ })[];
309
336
  total: number;
310
337
  limit: number;
311
338
  offset: number;
312
339
  };
313
340
  } : {
314
- events: AuditEvent[];
341
+ events: (AuditEvent & {
342
+ verified?: boolean | undefined;
343
+ })[];
315
344
  total: number;
316
345
  limit: number;
317
346
  offset: number;
@@ -346,6 +375,7 @@ export declare const auditLog: (options?: AuditLogOptions | undefined) => {
346
375
  offset: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
347
376
  eventType: z.ZodOptional<z.ZodString>;
348
377
  identifier: z.ZodOptional<z.ZodString>;
378
+ traceId: z.ZodOptional<z.ZodString>;
349
379
  organizationId: z.ZodOptional<z.ZodString>;
350
380
  userId: z.ZodOptional<z.ZodString>;
351
381
  startDate: z.ZodOptional<z.ZodString>;
@@ -410,6 +440,30 @@ export declare const auditLog: (options?: AuditLogOptions | undefined) => {
410
440
  type: string;
411
441
  nullable: boolean;
412
442
  };
443
+ traceId: {
444
+ type: string;
445
+ nullable: boolean;
446
+ };
447
+ requestId: {
448
+ type: string;
449
+ nullable: boolean;
450
+ };
451
+ actorType: {
452
+ type: string;
453
+ enum: string[];
454
+ };
455
+ severity: {
456
+ type: string;
457
+ enum: string[];
458
+ };
459
+ signature: {
460
+ type: string;
461
+ nullable: boolean;
462
+ };
463
+ verified: {
464
+ type: string;
465
+ description: string;
466
+ };
413
467
  createdAt: {
414
468
  type: string;
415
469
  format: string;
@@ -566,6 +620,29 @@ export declare const auditLog: (options?: AuditLogOptions | undefined) => {
566
620
  metadata: {
567
621
  SERVER_ONLY: true;
568
622
  client: boolean;
623
+ openapi: {
624
+ operationId: string;
625
+ summary: string;
626
+ description: string;
627
+ responses: {
628
+ 200: {
629
+ description: string;
630
+ content: {
631
+ "application/json": {
632
+ schema: {
633
+ type: "object";
634
+ properties: {
635
+ deleted: {
636
+ type: string;
637
+ };
638
+ };
639
+ required: string[];
640
+ };
641
+ };
642
+ };
643
+ };
644
+ };
645
+ };
569
646
  };
570
647
  } & {
571
648
  use: any[];
@@ -616,6 +693,26 @@ export declare const auditLog: (options?: AuditLogOptions | undefined) => {
616
693
  type: "json";
617
694
  required: false;
618
695
  };
696
+ traceId: {
697
+ type: "string";
698
+ required: false;
699
+ };
700
+ requestId: {
701
+ type: "string";
702
+ required: false;
703
+ };
704
+ actorType: {
705
+ type: "string";
706
+ required: false;
707
+ };
708
+ severity: {
709
+ type: "string";
710
+ required: false;
711
+ };
712
+ signature: {
713
+ type: "string";
714
+ required: false;
715
+ };
619
716
  createdAt: {
620
717
  type: "date";
621
718
  required: true;
@@ -625,11 +722,12 @@ export declare const auditLog: (options?: AuditLogOptions | undefined) => {
625
722
  };
626
723
  $ERROR_CODES: {
627
724
  readonly AUDIT_LOG_ACCESS_DENIED: "You don't have access to these audit logs";
725
+ readonly AUDIT_CANNOT_BE_DISABLED: "Core audit events and the local audit store can't be turned off";
628
726
  readonly AUDIT_LOG_UNAVAILABLE: "Audit logs are temporarily unavailable";
727
+ readonly AUDIT_RETENTION_TOO_SHORT: "Audit events can't be deleted before the retention minimum has passed";
728
+ readonly AUDIT_SIGNING_SECRET_MISSING: "Audit events must be signed in production: set integrity.secret or PAYVO_AUDIT_SIGNING_SECRET";
629
729
  };
630
730
  options: {
631
- database: boolean;
632
- trackFailedSignIns: boolean;
633
731
  adminRoles: string[];
634
732
  organizationAdminRoles: string[];
635
733
  showAttemptsOnOwnAccount: boolean;
@@ -644,6 +742,14 @@ export declare const auditLog: (options?: AuditLogOptions | undefined) => {
644
742
  } | undefined;
645
743
  } | false | undefined;
646
744
  onEvent?: ((event: AuditEvent) => void | Promise<void>) | undefined;
745
+ retention?: {
746
+ minimumDays?: number | undefined;
747
+ } | undefined;
748
+ integrity?: {
749
+ secret?: string | undefined;
750
+ secrets?: Record<string, string> | undefined;
751
+ } | undefined;
752
+ sensitiveKeys?: string[] | undefined;
647
753
  exclude?: AuditEventType[] | undefined;
648
754
  schema?: import("..").InferOptionSchema<typeof schema> | undefined;
649
755
  };
@@ -0,0 +1,20 @@
1
+ import type { AuditEvent, AuditLogOptions } from "./types";
2
+ /** Signs an event's fields, as `signAuditPayload` does the string */
3
+ export declare function signAuditEvent(event: Omit<AuditEvent, "signature">, secret: string): Promise<string>;
4
+ /**
5
+ * Whether an event's signature matches its fields, so a row that was changed
6
+ * after it was recorded doesn't verify.
7
+ *
8
+ * `integrity.secret` verifies signatures made with the current key, and
9
+ * `integrity.secrets` maps an older version to its key, so keys can be
10
+ * rotated without signing old rows again. An event without a signature, or
11
+ * with a version that has no key, doesn't verify.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const events = await auth.api.getAllAuditLogs({ headers });
16
+ * events.events.every((event) => event.verified);
17
+ * await verifyAuditEvent(event, { secret: process.env.PAYVO_AUDIT_SIGNING_SECRET });
18
+ * ```
19
+ */
20
+ export declare function verifyAuditEvent(event: AuditEvent, integrity: NonNullable<AuditLogOptions["integrity"]>): Promise<boolean>;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Replaces the value of every sensitive key with `"[REDACTED]"`, at any depth
3
+ * and through arrays. The key stays, so the shape of the data is still
4
+ * visible.
5
+ *
6
+ * A key is sensitive when its name, in lower case and without `-` or `_`,
7
+ * is in the built-in list (passwords, tokens, OTPs, secrets, authorization
8
+ * and cookie headers, card data) or in `sensitiveKeys`. The input isn't
9
+ * changed. Only plain objects and arrays are walked; other values are
10
+ * returned as they are.
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * redactAuditData({ user: { id: "1", Access_Token: "abc" } }, [])
15
+ * // { user: { id: "1", Access_Token: "[REDACTED]" } }
16
+ * ```
17
+ */
18
+ export declare function redactAuditData<Value>(value: Value, sensitiveKeys?: readonly string[]): Value;
@@ -5,6 +5,7 @@ export type RemoteQuery = {
5
5
  offset: number;
6
6
  eventType?: string | undefined;
7
7
  identifier?: string | undefined;
8
+ traceId?: string | undefined;
8
9
  userId?: string | undefined;
9
10
  organizationId?: string | undefined;
10
11
  startDate?: string | undefined;
@@ -41,6 +41,31 @@ export declare const schema: {
41
41
  type: "json";
42
42
  required: false;
43
43
  };
44
+ /** The request chain the event belongs to, shared with other services */
45
+ traceId: {
46
+ type: "string";
47
+ required: false;
48
+ };
49
+ /** The single request within the chain */
50
+ requestId: {
51
+ type: "string";
52
+ required: false;
53
+ };
54
+ /** `user`, `admin` or `system` */
55
+ actorType: {
56
+ type: "string";
57
+ required: false;
58
+ };
59
+ /** `info`, `warning` or `critical` */
60
+ severity: {
61
+ type: "string";
62
+ required: false;
63
+ };
64
+ /** `v1:<HMAC-SHA256>` of the event's fields, when signing is configured */
65
+ signature: {
66
+ type: "string";
67
+ required: false;
68
+ };
44
69
  createdAt: {
45
70
  type: "date";
46
71
  required: true;
@@ -29,11 +29,41 @@ export interface AuditEvent {
29
29
  * Never contains passwords, tokens or OTPs
30
30
  */
31
31
  eventData: Record<string, unknown> | null;
32
+ /**
33
+ * The request chain the event belongs to, from the `Trace-Id` or
34
+ * `traceparent` header, or minted for the request. Other services
35
+ * record the same id, so their events can be joined with these.
36
+ *
37
+ * Null for events recorded outside a request.
38
+ */
39
+ traceId: string | null;
40
+ /**
41
+ * The single request within the chain, from the `Request-Id` header, or
42
+ * minted for the request
43
+ */
44
+ requestId: string | null;
45
+ /**
46
+ * Who acted: `user` for the signed-in or signing-in user, `admin` for an
47
+ * admin acting on someone else or impersonating them, `system` without a
48
+ * request
49
+ */
50
+ actorType: "user" | "admin" | "system";
51
+ /**
52
+ * How much the event matters, from the event type and its status
53
+ */
54
+ severity: "info" | "warning" | "critical";
55
+ /**
56
+ * `v1:<HMAC-SHA256>` over the event's other fields, which shows whether
57
+ * the row was changed after it was recorded (see `verifyAuditEvent`)
58
+ *
59
+ * Null when no signing secret was configured.
60
+ */
61
+ signature: string | null;
32
62
  createdAt: Date;
33
63
  }
34
64
  export interface AuditLogOptions {
35
65
  /**
36
- * Send events to the hosted backend
66
+ * Send events to the hosted backend, in addition to the `auditLog` table
37
67
  *
38
68
  * Enabled when an API key and URL are configured, either here or through
39
69
  * `PAYVO_INFRA_API_KEY` and `PAYVO_INFRA_API_URL`. Set to `false` to
@@ -84,12 +114,6 @@ export interface AuditLogOptions {
84
114
  flushIntervalMs?: number | undefined;
85
115
  } | undefined;
86
116
  } | false | undefined;
87
- /**
88
- * Write events to the `auditLog` table
89
- *
90
- * @default true
91
- */
92
- database?: boolean | undefined;
93
117
  /**
94
118
  * Called for every recorded event, e.g. to publish it to a message broker
95
119
  *
@@ -97,16 +121,52 @@ export interface AuditLogOptions {
97
121
  */
98
122
  onEvent?: ((event: AuditEvent) => void | Promise<void>) | undefined;
99
123
  /**
100
- * Event types that should not be recorded
124
+ * How long events are kept at least. Deleting events with
125
+ * `deleteOldEvents` refuses a period shorter than `minimumDays`, and
126
+ * every deletion is recorded as an `audit_events_deleted` event.
127
+ *
128
+ * `minimumDays` is a whole number of at least 1. The right value is a
129
+ * compliance decision, so set it to what your regulator requires.
130
+ *
131
+ * @default { minimumDays: 365 }
101
132
  */
102
- exclude?: AuditEventType[] | undefined;
133
+ retention?: {
134
+ minimumDays?: number | undefined;
135
+ } | undefined;
103
136
  /**
104
- * Record `user_sign_in_failed` events for failed sign-in attempts, and
105
- * `verification_failed` events for wrong one-time codes and tokens
137
+ * Signs every event with HMAC-SHA256, so a row that is changed after it
138
+ * is recorded no longer verifies
106
139
  *
107
- * @default true
140
+ * `secret` signs and verifies; `secrets` maps an older key version to its
141
+ * secret, so keys can be rotated without signing old rows again. Without
142
+ * a secret events aren't signed, and the plugin refuses to start when
143
+ * `NODE_ENV` is `production`.
144
+ *
145
+ * @default { secret: process.env.PAYVO_AUDIT_SIGNING_SECRET }
108
146
  */
109
- trackFailedSignIns?: boolean | undefined;
147
+ integrity?: {
148
+ secret?: string | undefined;
149
+ secrets?: Record<string, string> | undefined;
150
+ } | undefined;
151
+ /**
152
+ * Keys whose values are replaced with `"[REDACTED]"` in an event's
153
+ * `eventData`, at any depth, before it is stored or sent anywhere
154
+ *
155
+ * Passwords, tokens, one-time codes, secrets, authorization and cookie
156
+ * headers and card data are always redacted. Keys are compared in lower
157
+ * case, without `-` or `_`.
158
+ *
159
+ * @default []
160
+ */
161
+ sensitiveKeys?: string[] | undefined;
162
+ /**
163
+ * Event types that should not be recorded
164
+ *
165
+ * The events in `CORE_EVENT_TYPES` can't be excluded, and `auditLog`
166
+ * refuses to start when one is listed here. Events are always written to
167
+ * the `auditLog` table, whatever else `remote` and `onEvent` do with them.
168
+ */
169
+ exclude?: AuditEventType[] | undefined;
110
170
  /**
111
171
  * Roles allowed to read every audit event through
112
172
  * `/audit-log/get-all-audit-logs`
@@ -36,12 +36,6 @@ export declare function executeAction(params: {
36
36
  difficulty: number;
37
37
  metadata?: Record<string, unknown> | undefined;
38
38
  }): Promise<Decision>;
39
- export declare const SENTINEL_ERROR_CODES: {
40
- readonly POW_CHALLENGE_REQUIRED: "Challenge required";
41
- readonly SECURITY_BLOCKED: "Request blocked for security reasons";
42
- readonly SECURITY_ADMIN_REQUIRED: "Only an administrator can manage security blocks";
43
- readonly BLOCK_NOT_FOUND: "That block doesn't exist or has ended";
44
- };
45
39
  /** The error for a decision, or null when the request is allowed */
46
40
  export declare function errorForDecision(decision: Decision): ({
47
41
  status: ("OK" | "CREATED" | "ACCEPTED" | "NO_CONTENT" | "MULTIPLE_CHOICES" | "MOVED_PERMANENTLY" | "FOUND" | "SEE_OTHER" | "NOT_MODIFIED" | "TEMPORARY_REDIRECT" | "BAD_REQUEST" | "UNAUTHORIZED" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "NOT_FOUND" | "METHOD_NOT_ALLOWED" | "NOT_ACCEPTABLE" | "PROXY_AUTHENTICATION_REQUIRED" | "REQUEST_TIMEOUT" | "CONFLICT" | "GONE" | "LENGTH_REQUIRED" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "URI_TOO_LONG" | "UNSUPPORTED_MEDIA_TYPE" | "RANGE_NOT_SATISFIABLE" | "EXPECTATION_FAILED" | "I'M_A_TEAPOT" | "MISDIRECTED_REQUEST" | "UNPROCESSABLE_ENTITY" | "LOCKED" | "FAILED_DEPENDENCY" | "TOO_EARLY" | "UPGRADE_REQUIRED" | "PRECONDITION_REQUIRED" | "TOO_MANY_REQUESTS" | "REQUEST_HEADER_FIELDS_TOO_LARGE" | "UNAVAILABLE_FOR_LEGAL_REASONS" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT" | "HTTP_VERSION_NOT_SUPPORTED" | "VARIANT_ALSO_NEGOTIATES" | "INSUFFICIENT_STORAGE" | "LOOP_DETECTED" | "NOT_EXTENDED" | "NETWORK_AUTHENTICATION_REQUIRED") | import("better-call").Status;
@@ -64,4 +64,7 @@ export declare const sentinelClient: (options?: {
64
64
  onResponse({ response, request }: import("@better-fetch/fetch").ResponseContext): Promise<Response | undefined>;
65
65
  };
66
66
  }[];
67
+ pathMethods: {
68
+ "/sentinel/list-blocks": "GET";
69
+ };
67
70
  };
@@ -0,0 +1,6 @@
1
+ export declare const SENTINEL_ERROR_CODES: {
2
+ readonly POW_CHALLENGE_REQUIRED: "Challenge required";
3
+ readonly SECURITY_BLOCKED: "Request blocked for security reasons";
4
+ readonly SECURITY_ADMIN_REQUIRED: "Only an administrator can manage security blocks";
5
+ readonly BLOCK_NOT_FOUND: "That block doesn't exist or has ended";
6
+ };