@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.
- package/dist/adapters/index.d.mts +1 -1
- package/dist/api/index.d.mts +1 -1
- package/dist/{audit-log-DhRRCMrv.mjs → audit-log-CQnf8nrN.mjs} +442 -46
- package/dist/client/index.d.mts +1 -1
- package/dist/client/plugins/index.d.mts +46 -46
- package/dist/client/plugins/index.mjs +2 -2
- package/dist/client/react/index.d.mts +15 -15
- package/dist/{client-CMsC6-rl.mjs → client-BkpNva-h.mjs} +3 -2
- package/dist/{client-CGV_kj99.d.mts → client-g0OAVuPQ.d.mts} +1 -1
- package/dist/{client-CT-acJeN.d.mts → client-zkWmSxqy.d.mts} +7 -4
- package/dist/cookies/index.d.mts +2 -2
- package/dist/db/index.d.mts +1 -1
- package/dist/{index-Czr_eQFE.d.mts → index-B4HTudhm.d.mts} +6 -6
- package/dist/{index-DYqQWdEv.d.mts → index-BHiF_ZfZ.d.mts} +13 -13
- package/dist/{index-Dmjw6re9.d.mts → index-BX55rZO5.d.mts} +2 -2
- package/dist/{index-Clwezlxm.d.mts → index-BfwCJQfM.d.mts} +8 -8
- package/dist/{index-DGRGLg16.d.mts → index-BhbilPfs.d.mts} +2 -2
- package/dist/{index-JzSy-vFn.d.mts → index-Bi-vEjDm.d.mts} +166 -166
- package/dist/{index-Cl9ITXy1.d.mts → index-BifhxQ8O.d.mts} +10 -10
- package/dist/{index-DWGj7du8.d.mts → index-BiomZttP.d.mts} +19 -19
- package/dist/{index-Hoybqpnt.d.mts → index-BoHrBgTB.d.mts} +9 -9
- package/dist/{index-UDzaYKxq.d.mts → index-C5W_sTlp.d.mts} +9 -9
- package/dist/{index-CLhlur69.d.mts → index-CNyQFga7.d.mts} +6 -6
- package/dist/{index-CZtQ0rUe.d.mts → index-CTjXncwM.d.mts} +113 -113
- package/dist/{index-CGGitykk.d.mts → index-CUUaJp3R.d.mts} +7 -7
- package/dist/{index-D85KXOKz.d.mts → index-CcJjgkwe.d.mts} +3 -3
- package/dist/{index-B-i2ob-C.d.mts → index-Cdfh66xb.d.mts} +9 -9
- package/dist/{index-CHMYUshk.d.mts → index-DBtm_waB.d.mts} +13 -13
- package/dist/{index-DWSbFJWU.d.mts → index-DD_pxX-h.d.mts} +662 -354
- package/dist/{index-BYxt8qOP.d.mts → index-DRoa3BGG.d.mts} +33 -33
- package/dist/{index-D5cyFBEb.d.mts → index-M0IQ_DTL.d.mts} +4 -4
- package/dist/{index-rcUKPBBy.d.mts → index-gRLqvVoc.d.mts} +6 -6
- package/dist/{index-Dt6JhAV4.d.mts → index-i1L-uguJ.d.mts} +6 -6
- package/dist/{index-B6to9CaK.d.mts → index-kBEBsEUr.d.mts} +1 -1
- package/dist/{index-BRUQHEUF.d.mts → index-mDmrBYrk.d.mts} +5 -5
- package/dist/index.d.mts +1 -1
- package/dist/integrations/next-js.d.mts +4 -4
- package/dist/integrations/node.d.mts +1 -1
- package/dist/integrations/react-start.d.mts +2 -2
- package/dist/oauth2/index.d.mts +1 -1
- package/dist/plugins/admin/index.d.mts +2 -2
- package/dist/plugins/anonymous/index.d.mts +2 -2
- package/dist/plugins/audit-log/client.d.mts +23 -23
- package/dist/plugins/audit-log/index.d.mts +23 -23
- package/dist/plugins/audit-log/index.mjs +3 -3
- package/dist/plugins/bearer/index.d.mts +1 -1
- package/dist/plugins/captcha/index.d.mts +1 -1
- package/dist/plugins/custom-session/index.d.mts +2 -2
- package/dist/plugins/device-authorization/index.d.mts +1 -1
- package/dist/plugins/email-otp/index.d.mts +1 -1
- package/dist/plugins/generic-oauth/index.d.mts +2 -2
- package/dist/plugins/haveibeenpwned/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +23 -23
- package/dist/plugins/index.mjs +5 -5
- package/dist/plugins/jwt/index.d.mts +2 -2
- package/dist/plugins/magic-link/index.d.mts +1 -1
- package/dist/plugins/multi-session/index.d.mts +1 -1
- package/dist/plugins/oauth-proxy/index.d.mts +1 -1
- package/dist/plugins/one-tap/index.d.mts +1 -1
- package/dist/plugins/one-time-token/index.d.mts +2 -2
- package/dist/plugins/one-time-token/index.mjs +4 -4
- package/dist/plugins/organization/index.d.mts +2 -2
- package/dist/plugins/passkey/index.d.mts +2 -2
- package/dist/plugins/phone-number/index.d.mts +2 -2
- package/dist/plugins/sentinel/client.d.mts +23 -23
- package/dist/plugins/sentinel/client.mjs +2 -2
- package/dist/plugins/sentinel/index.d.mts +22 -22
- package/dist/plugins/sentinel/index.mjs +3 -3
- package/dist/plugins/two-factor/index.d.mts +2 -2
- package/dist/plugins/username/index.d.mts +1 -1
- package/dist/{registry-GztLf62p.mjs → registry-BjYhRF79.mjs} +69 -2
- package/dist/{sentinel-b8XEetew.mjs → sentinel-BttZLWPD.mjs} +70 -14
- package/dist/src/plugins/audit-log/error-codes.d.ts +7 -0
- package/dist/src/plugins/audit-log/events.d.ts +13 -0
- package/dist/src/plugins/audit-log/index.d.ts +114 -8
- package/dist/src/plugins/audit-log/integrity.d.ts +20 -0
- package/dist/src/plugins/audit-log/redact.d.ts +18 -0
- package/dist/src/plugins/audit-log/remote.d.ts +1 -0
- package/dist/src/plugins/audit-log/schema.d.ts +25 -0
- package/dist/src/plugins/audit-log/types.d.ts +73 -13
- package/dist/src/plugins/sentinel/actions.d.ts +0 -6
- package/dist/src/plugins/sentinel/client.d.ts +3 -0
- package/dist/src/plugins/sentinel/error-codes.d.ts +6 -0
- package/dist/src/plugins/sentinel/index.d.ts +80 -3
- package/dist/src/plugins/sentinel/types.d.ts +1 -1
- package/dist/test-utils/index.d.mts +2 -2
- package/dist/test-utils/index.mjs +4 -4
- package/dist/types/index.d.mts +1 -1
- package/package.json +3 -3
- /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 {
|
|
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-
|
|
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;
|
|
@@ -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
|
-
*
|
|
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
|
-
|
|
133
|
+
retention?: {
|
|
134
|
+
minimumDays?: number | undefined;
|
|
135
|
+
} | undefined;
|
|
103
136
|
/**
|
|
104
|
-
*
|
|
105
|
-
*
|
|
137
|
+
* Signs every event with HMAC-SHA256, so a row that is changed after it
|
|
138
|
+
* is recorded no longer verifies
|
|
106
139
|
*
|
|
107
|
-
*
|
|
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
|
-
|
|
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;
|
|
@@ -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
|
+
};
|