@rebasepro/server-core 0.2.3 → 0.2.5

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 (116) hide show
  1. package/dist/common/src/collections/default-collections.d.ts +9 -0
  2. package/dist/common/src/collections/index.d.ts +1 -0
  3. package/dist/common/src/util/permissions.d.ts +1 -0
  4. package/dist/{index-BZoAtuqi.js → index-Cr1D21av.js} +11 -3
  5. package/dist/index-Cr1D21av.js.map +1 -0
  6. package/dist/index.es.js +2309 -370
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/index.umd.js +2298 -355
  9. package/dist/index.umd.js.map +1 -1
  10. package/dist/server-core/src/api/logs-routes.d.ts +37 -0
  11. package/dist/server-core/src/api/rest/api-generator.d.ts +6 -0
  12. package/dist/server-core/src/api/types.d.ts +6 -1
  13. package/dist/server-core/src/auth/adapter-middleware.d.ts +7 -3
  14. package/dist/server-core/src/auth/admin-routes.d.ts +3 -3
  15. package/dist/server-core/src/auth/api-keys/api-key-middleware.d.ts +39 -0
  16. package/dist/server-core/src/auth/api-keys/api-key-permission-guard.d.ts +32 -0
  17. package/dist/server-core/src/auth/api-keys/api-key-routes.d.ts +20 -0
  18. package/dist/server-core/src/auth/api-keys/api-key-store.d.ts +35 -0
  19. package/dist/server-core/src/auth/api-keys/api-key-types.d.ts +88 -0
  20. package/dist/server-core/src/auth/api-keys/index.d.ts +17 -0
  21. package/dist/server-core/src/auth/{auth-overrides.d.ts → auth-hooks.d.ts} +69 -11
  22. package/dist/server-core/src/auth/builtin-auth-adapter.d.ts +3 -3
  23. package/dist/server-core/src/auth/index.d.ts +5 -3
  24. package/dist/server-core/src/auth/interfaces.d.ts +93 -3
  25. package/dist/server-core/src/auth/jwt.d.ts +3 -1
  26. package/dist/server-core/src/auth/mfa.d.ts +49 -0
  27. package/dist/server-core/src/auth/middleware.d.ts +7 -0
  28. package/dist/server-core/src/auth/rate-limiter.d.ts +19 -0
  29. package/dist/server-core/src/auth/routes.d.ts +3 -3
  30. package/dist/server-core/src/env.d.ts +6 -0
  31. package/dist/server-core/src/index.d.ts +1 -0
  32. package/dist/server-core/src/init.d.ts +3 -3
  33. package/dist/server-core/src/services/webhook-service.d.ts +29 -0
  34. package/dist/server-core/src/storage/image-transform.d.ts +48 -0
  35. package/dist/server-core/src/storage/index.d.ts +3 -0
  36. package/dist/server-core/src/storage/tus-handler.d.ts +51 -0
  37. package/dist/types/src/controllers/auth.d.ts +4 -26
  38. package/dist/types/src/controllers/client.d.ts +25 -43
  39. package/dist/types/src/controllers/collection_registry.d.ts +1 -1
  40. package/dist/types/src/controllers/data.d.ts +4 -0
  41. package/dist/types/src/controllers/data_driver.d.ts +23 -0
  42. package/dist/types/src/controllers/registry.d.ts +5 -4
  43. package/dist/types/src/rebase_context.d.ts +1 -1
  44. package/dist/types/src/types/auth_adapter.d.ts +5 -60
  45. package/dist/types/src/types/backend.d.ts +2 -2
  46. package/dist/types/src/types/backend_hooks.d.ts +2 -17
  47. package/dist/types/src/types/collections.d.ts +0 -4
  48. package/dist/types/src/types/component_ref.d.ts +1 -1
  49. package/dist/types/src/types/cron.d.ts +1 -1
  50. package/dist/types/src/types/entity_views.d.ts +1 -0
  51. package/dist/types/src/types/export_import.d.ts +1 -1
  52. package/dist/types/src/types/formex.d.ts +2 -2
  53. package/dist/types/src/types/properties.d.ts +9 -7
  54. package/dist/types/src/types/translations.d.ts +28 -12
  55. package/dist/types/src/types/user_management_delegate.d.ts +22 -57
  56. package/dist/types/src/users/index.d.ts +0 -1
  57. package/dist/types/src/users/user.d.ts +0 -1
  58. package/package.json +8 -6
  59. package/src/api/ast-schema-editor.ts +4 -4
  60. package/src/api/errors.ts +16 -7
  61. package/src/api/logs-routes.ts +129 -0
  62. package/src/api/rest/api-generator.ts +42 -2
  63. package/src/api/rest/query-parser.ts +37 -1
  64. package/src/api/types.ts +6 -1
  65. package/src/auth/adapter-middleware.ts +20 -4
  66. package/src/auth/admin-routes.ts +36 -100
  67. package/src/auth/api-keys/api-key-middleware.ts +126 -0
  68. package/src/auth/api-keys/api-key-permission-guard.ts +64 -0
  69. package/src/auth/api-keys/api-key-routes.ts +183 -0
  70. package/src/auth/api-keys/api-key-store.ts +317 -0
  71. package/src/auth/api-keys/api-key-types.ts +94 -0
  72. package/src/auth/api-keys/index.ts +37 -0
  73. package/src/auth/{auth-overrides.ts → auth-hooks.ts} +81 -14
  74. package/src/auth/builtin-auth-adapter.ts +35 -73
  75. package/src/auth/custom-auth-adapter.ts +1 -1
  76. package/src/auth/index.ts +7 -3
  77. package/src/auth/interfaces.ts +111 -3
  78. package/src/auth/jwt.ts +19 -5
  79. package/src/auth/mfa.ts +160 -0
  80. package/src/auth/middleware.ts +20 -1
  81. package/src/auth/rate-limiter.ts +92 -0
  82. package/src/auth/routes.ts +455 -24
  83. package/src/cron/cron-loader.ts +5 -10
  84. package/src/cron/cron-scheduler.ts +11 -12
  85. package/src/cron/cron-store.ts +8 -7
  86. package/src/env.ts +2 -0
  87. package/src/functions/function-loader.ts +6 -9
  88. package/src/index.ts +1 -2
  89. package/src/init.ts +56 -8
  90. package/src/serve-spa.ts +5 -4
  91. package/src/services/webhook-service.ts +155 -0
  92. package/src/storage/image-transform.ts +202 -0
  93. package/src/storage/index.ts +3 -0
  94. package/src/storage/routes.ts +56 -3
  95. package/src/storage/tus-handler.ts +315 -0
  96. package/src/utils/dev-port.ts +14 -0
  97. package/src/utils/logging.ts +9 -7
  98. package/test/admin-routes.test.ts +62 -164
  99. package/test/api-generator.test.ts +0 -1
  100. package/test/api-key-permission-guard.test.ts +132 -0
  101. package/test/ast-schema-editor.test.ts +26 -0
  102. package/test/auth-routes.test.ts +1 -2
  103. package/test/backend-hooks-admin.test.ts +3 -29
  104. package/test/custom-auth-adapter.test.ts +2 -10
  105. package/test/email-templates.test.ts +169 -0
  106. package/test/function-loader.test.ts +124 -0
  107. package/test/jwt.test.ts +4 -2
  108. package/test/mfa.test.ts +197 -0
  109. package/test/middleware.test.ts +10 -5
  110. package/test/webhook-service.test.ts +249 -0
  111. package/vite.config.ts +3 -2
  112. package/dist/index-BZoAtuqi.js.map +0 -1
  113. package/dist/server-core/src/bootstrappers/index.d.ts +0 -0
  114. package/dist/types/src/users/roles.d.ts +0 -22
  115. package/src/bootstrappers/index.ts +0 -1
  116. package/src/singleton.test.ts +0 -28
@@ -0,0 +1,37 @@
1
+ import { Hono } from "hono";
2
+ import type { MiddlewareHandler } from "hono";
3
+ import type { HonoEnv } from "./types";
4
+ export interface LogEntry {
5
+ id: string;
6
+ timestamp: string;
7
+ level: "debug" | "info" | "warn" | "error";
8
+ source: "api" | "auth" | "storage" | "realtime" | "system";
9
+ message: string;
10
+ metadata?: Record<string, unknown>;
11
+ }
12
+ declare class LogRingBuffer {
13
+ private buffer;
14
+ private maxSize;
15
+ private idCounter;
16
+ constructor(maxSize?: number);
17
+ push(entry: Omit<LogEntry, "id">): void;
18
+ query(options: {
19
+ level?: string;
20
+ source?: string;
21
+ search?: string;
22
+ limit?: number;
23
+ offset?: number;
24
+ since?: string;
25
+ }): {
26
+ entries: LogEntry[];
27
+ total: number;
28
+ };
29
+ getLatest(count?: number): LogEntry[];
30
+ }
31
+ export declare const logBuffer: LogRingBuffer;
32
+ /** Add a log entry */
33
+ export declare function addLog(level: LogEntry["level"], source: LogEntry["source"], message: string, metadata?: Record<string, unknown>): void;
34
+ /** Hono middleware to log API requests */
35
+ export declare function logMiddleware(): MiddlewareHandler<HonoEnv>;
36
+ declare const app: Hono<HonoEnv, import("hono/types").BlankSchema, "/">;
37
+ export default app;
@@ -17,6 +17,12 @@ export declare class RestApiGenerator {
17
17
  * Generate REST routes using existing DataDriver
18
18
  */
19
19
  generateRoutes(): Hono<HonoEnv>;
20
+ /**
21
+ * Check API key permissions for a collection operation.
22
+ * Throws 403 if the key doesn't have the required permission.
23
+ * No-ops if the request is not authenticated via an API key.
24
+ */
25
+ private enforceApiKeyPermission;
20
26
  /**
21
27
  * Get the typed RestFetchService from a driver if it exposes one (for include support).
22
28
  */
@@ -1,7 +1,8 @@
1
- import { EntityCollection } from "@rebasepro/types";
1
+ import { EntityCollection, VectorSearchParams } from "@rebasepro/types";
2
2
  import { AuthResult } from "../auth/middleware";
3
3
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
4
4
  import { DataDriver } from "@rebasepro/types";
5
+ import type { ApiKeyMasked } from "../auth/api-keys/api-key-types";
5
6
  /**
6
7
  * Hono Environment Variables
7
8
  * Passed to generic Hono<HonoEnv> to type `c.get()`
@@ -13,6 +14,8 @@ export type HonoEnv = {
13
14
  roles?: string[];
14
15
  };
15
16
  driver?: DataDriver;
17
+ /** Set when the request is authenticated via a Service API Key. */
18
+ apiKey?: ApiKeyMasked;
16
19
  };
17
20
  };
18
21
  /**
@@ -79,6 +82,8 @@ export interface QueryOptions {
79
82
  include?: string[];
80
83
  /** Columns to return in the response (field-level selection) */
81
84
  fields?: string[];
85
+ /** Vector similarity search configuration */
86
+ vectorSearch?: VectorSearchParams;
82
87
  }
83
88
  /**
84
89
  * Relation resolution configuration
@@ -6,9 +6,10 @@
6
6
  * an `AuthAdapter` to `initializeRebaseBackend()`.
7
7
  *
8
8
  * The middleware:
9
- * 1. Calls `adapter.verifyRequest(request)` to resolve the user
10
- * 2. Scopes the DataDriver via `withAuth()` for RLS
11
- * 3. Enforces auth (401) when `requireAuth` is true and no user is found
9
+ * 1. Checks for API key tokens (`rk_` prefix) first these are Rebase-level
10
+ * 2. Falls back to `adapter.verifyRequest(request)` to resolve the user
11
+ * 3. Scopes the DataDriver via `withAuth()` for RLS
12
+ * 4. Enforces auth (401) when `requireAuth` is true and no user is found
12
13
  *
13
14
  * The behavior is identical to `createAuthMiddleware()` — only the
14
15
  * token verification strategy is pluggable.
@@ -16,6 +17,7 @@
16
17
  import type { MiddlewareHandler } from "hono";
17
18
  import type { DataDriver, AuthAdapter } from "@rebasepro/types";
18
19
  import type { HonoEnv } from "../api/types";
20
+ import type { ApiKeyStore } from "./api-keys/api-key-store";
19
21
  export interface AdapterAuthMiddlewareOptions {
20
22
  /** The auth adapter to delegate verification to. */
21
23
  adapter: AuthAdapter;
@@ -26,6 +28,8 @@ export interface AdapterAuthMiddlewareOptions {
26
28
  * Defaults to `true` (secure by default).
27
29
  */
28
30
  requireAuth?: boolean;
31
+ /** Optional API key store — when provided, `rk_` bearer tokens are accepted. */
32
+ apiKeyStore?: ApiKeyStore;
29
33
  }
30
34
  /**
31
35
  * Create a Hono middleware that uses an `AuthAdapter` for request verification.
@@ -1,5 +1,5 @@
1
1
  import { Hono } from "hono";
2
- import type { AuthOverrides } from "./auth-overrides";
2
+ import type { AuthHooks } from "./auth-hooks";
3
3
  import { AuthModuleConfig } from "./routes";
4
4
  import type { BackendHooks } from "@rebasepro/types";
5
5
  interface AdminRouteOptions extends AuthModuleConfig {
@@ -14,10 +14,10 @@ interface AdminRouteOptions extends AuthModuleConfig {
14
14
  */
15
15
  hooks?: BackendHooks;
16
16
  /**
17
- * Auth overrides for customizing password hashing, credential
17
+ * Auth hooks for customizing password hashing, credential
18
18
  * verification, lifecycle hooks, etc.
19
19
  */
20
- overrides?: AuthOverrides;
20
+ authHooks?: AuthHooks;
21
21
  }
22
22
  import { HonoEnv } from "../api/types";
23
23
  /**
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Hono middleware for authenticating requests via Service API Keys.
3
+ *
4
+ * This middleware is integrated into `createAuthMiddleware()` and
5
+ * activates only when the bearer token starts with `rk_`. It:
6
+ *
7
+ * 1. Hashes the token with SHA-256
8
+ * 2. Looks up the hash in the `rebase.api_keys` table
9
+ * 3. Validates the key is not revoked and not expired
10
+ * 4. Sets `c.set("user", ...)` and `c.set("apiKey", ...)` for downstream use
11
+ * 5. Scopes the DataDriver via `withAuth()` using the API key's service identity
12
+ *
13
+ * @module
14
+ */
15
+ import type { Context } from "hono";
16
+ import type { DataDriver } from "@rebasepro/types";
17
+ import type { HonoEnv } from "../../api/types";
18
+ import type { ApiKeyStore } from "./api-key-store";
19
+ /**
20
+ * Check whether a token looks like a Rebase API key.
21
+ */
22
+ export declare function isApiKeyToken(token: string): boolean;
23
+ /**
24
+ * Options for the API key authentication handler.
25
+ */
26
+ export interface ApiKeyAuthOptions {
27
+ store: ApiKeyStore;
28
+ driver: DataDriver;
29
+ }
30
+ /**
31
+ * Validate an API key token and populate the Hono context.
32
+ *
33
+ * Returns `true` if the key is valid and context has been populated,
34
+ * or returns an error Response if the key is invalid.
35
+ *
36
+ * This is NOT a standalone middleware — it's called from within
37
+ * `createAuthMiddleware()` when a `rk_` prefixed token is detected.
38
+ */
39
+ export declare function validateApiKey(c: Context<HonoEnv>, token: string, options: ApiKeyAuthOptions): Promise<Response | true>;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Permission guard for Service API Keys.
3
+ *
4
+ * Checks whether an API key's permission set allows a specific operation
5
+ * on a specific collection. Used by the REST API generator middleware to
6
+ * enforce fine-grained access control.
7
+ *
8
+ * @module
9
+ */
10
+ import type { ApiKeyPermission } from "./api-key-types";
11
+ /** Operations that map to HTTP methods. */
12
+ export type ApiKeyOperation = "read" | "write" | "delete";
13
+ /**
14
+ * Map an HTTP method string to an `ApiKeyOperation`.
15
+ *
16
+ * - `GET`, `HEAD`, `OPTIONS` → `"read"`
17
+ * - `POST`, `PUT`, `PATCH` → `"write"`
18
+ * - `DELETE` → `"delete"`
19
+ */
20
+ export declare function httpMethodToOperation(method: string): ApiKeyOperation;
21
+ /**
22
+ * Check whether the given permissions array allows `operation` on `collection`.
23
+ *
24
+ * Supports the `"*"` wildcard for the collection field, which matches any
25
+ * collection. Returns `true` if at least one permission entry grants access.
26
+ *
27
+ * @param permissions - The API key's permission entries.
28
+ * @param collection - The target collection slug.
29
+ * @param operation - The requested operation.
30
+ * @returns `true` if the operation is permitted.
31
+ */
32
+ export declare function isOperationAllowed(permissions: ApiKeyPermission[], collection: string, operation: ApiKeyOperation): boolean;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Admin routes for managing Service API Keys.
3
+ *
4
+ * Mounted under `/api/admin/api-keys` with `requireAuth` + `requireAdmin`.
5
+ * All routes return masked keys (never the hash). The full plaintext key
6
+ * is returned exactly once in the POST response.
7
+ *
8
+ * @module
9
+ */
10
+ import { Hono } from "hono";
11
+ import type { HonoEnv } from "../../api/types";
12
+ import type { ApiKeyStore } from "./api-key-store";
13
+ export interface ApiKeyRouteOptions {
14
+ store: ApiKeyStore;
15
+ serviceKey?: string;
16
+ }
17
+ /**
18
+ * Create admin routes for API key management.
19
+ */
20
+ export declare function createApiKeyRoutes(options: ApiKeyRouteOptions): Hono<HonoEnv>;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Database operations for Service API Keys.
3
+ *
4
+ * Uses the DataDriver's `admin.executeSql` capability (same pattern as
5
+ * the cron-store and ensure-tables modules). All data lives in the
6
+ * `rebase.api_keys` table.
7
+ *
8
+ * @module
9
+ */
10
+ import type { DataDriver } from "@rebasepro/types";
11
+ import type { ApiKey, ApiKeyMasked, ApiKeyWithSecret, CreateApiKeyRequest, UpdateApiKeyRequest } from "./api-key-types";
12
+ export interface ApiKeyStore {
13
+ /** Ensure the `rebase.api_keys` table exists. Called once on startup. */
14
+ ensureTable(): Promise<void>;
15
+ /** Create a new API key. Returns the full plaintext key exactly once. */
16
+ createApiKey(request: CreateApiKeyRequest, createdBy: string): Promise<ApiKeyWithSecret>;
17
+ /** Look up an API key by its SHA-256 hash. Returns `null` if not found. */
18
+ findByKeyHash(hash: string): Promise<ApiKey | null>;
19
+ /** List all API keys (masked, never includes hash). */
20
+ listApiKeys(): Promise<ApiKeyMasked[]>;
21
+ /** Get a single API key by ID (masked). */
22
+ getApiKeyById(id: string): Promise<ApiKeyMasked | null>;
23
+ /** Update name, permissions, rate_limit, or expires_at. */
24
+ updateApiKey(id: string, updates: UpdateApiKeyRequest): Promise<ApiKeyMasked | null>;
25
+ /** Soft-delete: set `revoked_at` to now. */
26
+ revokeApiKey(id: string): Promise<boolean>;
27
+ /** Touch `last_used_at` to the current timestamp. */
28
+ updateLastUsed(id: string): Promise<void>;
29
+ }
30
+ /**
31
+ * Create an `ApiKeyStore` backed by the driver's SQL admin capability.
32
+ *
33
+ * Returns `undefined` if the driver does not support `executeSql`.
34
+ */
35
+ export declare function createApiKeyStore(driver: DataDriver): ApiKeyStore | undefined;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Type definitions for Service API Keys.
3
+ *
4
+ * API keys provide machine-to-machine authentication for scripts, cron jobs,
5
+ * and third-party integrations. Each key is scoped to specific collections
6
+ * and operations via the `ApiKeyPermission` model.
7
+ *
8
+ * @module
9
+ */
10
+ /**
11
+ * A single permission entry scoping an API key to a collection and set of operations.
12
+ *
13
+ * Use `"*"` as the collection value to grant access to all collections.
14
+ */
15
+ export interface ApiKeyPermission {
16
+ /** Collection slug, or `"*"` for all collections. */
17
+ collection: string;
18
+ /** Allowed operations on the collection. */
19
+ operations: ("read" | "write" | "delete")[];
20
+ }
21
+ /**
22
+ * Full database row for an API key.
23
+ * The `key_hash` is never exposed via the API — only stored for lookup.
24
+ */
25
+ export interface ApiKey {
26
+ id: string;
27
+ name: string;
28
+ /** First 12 characters of the plaintext key, for display only. */
29
+ key_prefix: string;
30
+ /** SHA-256 hash of the full plaintext key. */
31
+ key_hash: string;
32
+ permissions: ApiKeyPermission[];
33
+ /** Requests per 15-minute window. `null` means unlimited. */
34
+ rate_limit: number | null;
35
+ created_by: string;
36
+ created_at: string;
37
+ updated_at: string;
38
+ last_used_at: string | null;
39
+ expires_at: string | null;
40
+ revoked_at: string | null;
41
+ }
42
+ /**
43
+ * Masked version of an API key, safe for API responses.
44
+ * Omits `key_hash` and shows only the prefix.
45
+ */
46
+ export interface ApiKeyMasked {
47
+ id: string;
48
+ name: string;
49
+ key_prefix: string;
50
+ permissions: ApiKeyPermission[];
51
+ rate_limit: number | null;
52
+ created_by: string;
53
+ created_at: string;
54
+ updated_at: string;
55
+ last_used_at: string | null;
56
+ expires_at: string | null;
57
+ revoked_at: string | null;
58
+ }
59
+ /**
60
+ * Request body for creating a new API key.
61
+ */
62
+ export interface CreateApiKeyRequest {
63
+ name: string;
64
+ permissions: ApiKeyPermission[];
65
+ /** Requests per 15-minute window. Omit or `null` for unlimited. */
66
+ rate_limit?: number | null;
67
+ /** ISO-8601 expiration timestamp. Omit for no expiration. */
68
+ expires_at?: string | null;
69
+ }
70
+ /**
71
+ * Request body for updating an existing API key.
72
+ * All fields are optional — only provided fields are updated.
73
+ */
74
+ export interface UpdateApiKeyRequest {
75
+ name?: string;
76
+ permissions?: ApiKeyPermission[];
77
+ rate_limit?: number | null;
78
+ expires_at?: string | null;
79
+ }
80
+ /**
81
+ * Returned exactly once when a key is created.
82
+ * The `key` field contains the full plaintext key — it is never stored
83
+ * or returned again after creation.
84
+ */
85
+ export interface ApiKeyWithSecret extends ApiKeyMasked {
86
+ /** Full plaintext API key (e.g. `rk_live_abc123...`). */
87
+ key: string;
88
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Service API Keys module.
3
+ *
4
+ * Re-exports types, store, middleware, permission guard, and routes
5
+ * for the API key authentication system.
6
+ *
7
+ * @module
8
+ */
9
+ export type { ApiKey, ApiKeyMasked, ApiKeyPermission, ApiKeyWithSecret, CreateApiKeyRequest, UpdateApiKeyRequest, } from "./api-key-types";
10
+ export { createApiKeyStore } from "./api-key-store";
11
+ export type { ApiKeyStore } from "./api-key-store";
12
+ export { isApiKeyToken, validateApiKey } from "./api-key-middleware";
13
+ export type { ApiKeyAuthOptions } from "./api-key-middleware";
14
+ export { httpMethodToOperation, isOperationAllowed, } from "./api-key-permission-guard";
15
+ export type { ApiKeyOperation } from "./api-key-permission-guard";
16
+ export { createApiKeyRoutes } from "./api-key-routes";
17
+ export type { ApiKeyRouteOptions } from "./api-key-routes";
@@ -1,20 +1,20 @@
1
1
  /**
2
- * AuthOverrides
2
+ * AuthHooks
3
3
  *
4
- * Override specific behaviors of the built-in Rebase auth system.
4
+ * Customize specific behaviors of the built-in Rebase auth system.
5
5
  *
6
6
  * Each method replaces one piece of the default implementation.
7
7
  * Unset methods fall through to the built-in defaults (scrypt passwords,
8
8
  * standard validation rules, etc.).
9
9
  *
10
- * This interface is intentionally open for extension — new overrides
10
+ * This interface is intentionally open for extension — new hooks
11
11
  * can be added as optional methods without breaking existing configurations.
12
12
  *
13
13
  * @example bcrypt password support
14
14
  * ```ts
15
15
  * import bcrypt from "bcrypt";
16
16
  *
17
- * const overrides: AuthOverrides = {
17
+ * const hooks: AuthHooks = {
18
18
  * hashPassword: (pw) => bcrypt.hash(pw, 12),
19
19
  * verifyPassword: (pw, hash) => bcrypt.compare(pw, hash),
20
20
  * validatePasswordStrength: (pw) => ({
@@ -26,7 +26,7 @@
26
26
  *
27
27
  * @example Override the entire login credential check
28
28
  * ```ts
29
- * const overrides: AuthOverrides = {
29
+ * const hooks: AuthHooks = {
30
30
  * verifyCredentials: async (email, password, repo) => {
31
31
  * const user = await repo.getUserByEmail(email);
32
32
  * if (!user || !user.passwordHash) return null;
@@ -41,14 +41,14 @@ import type { AuthRepository, UserData, CreateUserData } from "./interfaces";
41
41
  /**
42
42
  * Authentication method identifier for lifecycle hooks.
43
43
  */
44
- export type AuthMethod = "login" | "register" | "oauth" | "refresh" | "password-reset";
44
+ export type AuthMethod = "login" | "register" | "oauth" | "refresh" | "password-reset" | "anonymous" | "magic-link" | "mfa";
45
45
  /**
46
- * Override specific parts of the built-in Rebase auth implementation.
46
+ * Hook specific parts of the built-in Rebase auth implementation.
47
47
  *
48
48
  * Every method is optional. The built-in defaults apply for any method
49
49
  * that is not provided.
50
50
  */
51
- export interface AuthOverrides {
51
+ export interface AuthHooks {
52
52
  /**
53
53
  * Hash a cleartext password for storage.
54
54
  *
@@ -118,10 +118,68 @@ export interface AuthOverrides {
118
118
  * This is fire-and-forget — errors are logged but do not fail the request.
119
119
  */
120
120
  afterUserCreate?(user: UserData): Promise<void>;
121
+ /**
122
+ * Pre-login validation. Called before credential verification.
123
+ *
124
+ * Throw an error to reject the login attempt (e.g. for account lockout,
125
+ * IP-based restrictions, etc.).
126
+ */
127
+ beforeLogin?(email: string, method: AuthMethod): Promise<void>;
128
+ /**
129
+ * Post-logout cleanup.
130
+ *
131
+ * Called after a user's session has been invalidated.
132
+ * Use for audit logging, cleanup of temporary resources, etc.
133
+ *
134
+ * This is fire-and-forget — errors are logged but do not fail the request.
135
+ */
136
+ afterLogout?(userId: string): Promise<void>;
137
+ /**
138
+ * Called after successful MFA verification.
139
+ *
140
+ * Use for audit logging, notifying external systems, etc.
141
+ *
142
+ * This is fire-and-forget — errors are logged but do not fail the request.
143
+ */
144
+ onMfaVerified?(userId: string, factorId: string): Promise<void>;
145
+ /**
146
+ * Customize JWT access token claims before signing.
147
+ *
148
+ * Return the modified claims object. The returned claims are merged
149
+ * into the JWT payload alongside standard claims (userId, roles).
150
+ *
151
+ * @param claims - The default claims that would be included.
152
+ * @param user - The authenticated user data.
153
+ * @returns Modified claims to include in the JWT.
154
+ */
155
+ customizeAccessToken?(claims: Record<string, unknown>, user: UserData): Promise<Record<string, unknown>>;
156
+ /**
157
+ * Called after a successful password reset.
158
+ *
159
+ * Use for audit logging, sending confirmation notifications, etc.
160
+ *
161
+ * This is fire-and-forget — errors are logged but do not fail the request.
162
+ */
163
+ onPasswordReset?(userId: string): Promise<void>;
164
+ /**
165
+ * Called before a user is deleted.
166
+ *
167
+ * Throw an error to prevent deletion (e.g. for users with active
168
+ * subscriptions, pending transactions, etc.).
169
+ */
170
+ beforeUserDelete?(userId: string): Promise<void>;
171
+ /**
172
+ * Called after a user is deleted.
173
+ *
174
+ * Use for cleaning up external resources, audit logging, etc.
175
+ *
176
+ * This is fire-and-forget — errors are logged but do not fail the request.
177
+ */
178
+ afterUserDelete?(userId: string): Promise<void>;
121
179
  }
122
180
  /**
123
181
  * Resolved auth operations — every method is guaranteed to exist.
124
- * Created by `resolveAuthOverrides()` which merges user overrides
182
+ * Created by `resolveAuthHooks()` which merges user hooks
125
183
  * with built-in defaults.
126
184
  */
127
185
  export interface ResolvedAuthOperations {
@@ -130,10 +188,10 @@ export interface ResolvedAuthOperations {
130
188
  validatePasswordStrength(password: string): PasswordValidationResult;
131
189
  }
132
190
  /**
133
- * Merge user-provided overrides with the built-in defaults to produce
191
+ * Merge user-provided hooks with the built-in defaults to produce
134
192
  * a complete set of resolved operations.
135
193
  *
136
194
  * This is the single point where defaults are applied — all consumers
137
195
  * call this once and use the resolved operations throughout.
138
196
  */
139
- export declare function resolveAuthOverrides(overrides?: AuthOverrides): ResolvedAuthOperations;
197
+ export declare function resolveAuthHooks(hooks?: AuthHooks): ResolvedAuthOperations;
@@ -11,7 +11,7 @@
11
11
  */
12
12
  import type { AuthAdapter, BackendHooks } from "@rebasepro/types";
13
13
  import type { AuthRepository, OAuthProvider } from "./interfaces";
14
- import type { AuthOverrides } from "./auth-overrides";
14
+ import type { AuthHooks } from "./auth-hooks";
15
15
  import type { EmailService, EmailConfig } from "../email";
16
16
  /**
17
17
  * Configuration for the built-in Rebase auth adapter.
@@ -36,8 +36,8 @@ export interface BuiltinAuthAdapterConfig {
36
36
  serviceKey?: string;
37
37
  /** Backend hooks for intercepting admin data. */
38
38
  hooks?: BackendHooks;
39
- /** Auth overrides for customizing password, credentials, lifecycle, etc. */
40
- overrides?: AuthOverrides;
39
+ /** Auth hooks for customizing password, credentials, lifecycle, etc. */
40
+ authHooks?: AuthHooks;
41
41
  }
42
42
  /**
43
43
  * Create the built-in Rebase auth adapter.
@@ -3,8 +3,8 @@ export { configureJwt, generateAccessToken, verifyAccessToken, generateRefreshTo
3
3
  export type { JwtConfig, AccessTokenPayload } from "./jwt";
4
4
  export { hashPassword, verifyPassword, validatePasswordStrength } from "./password";
5
5
  export type { PasswordValidationResult } from "./password";
6
- export type { AuthOverrides, AuthMethod, ResolvedAuthOperations } from "./auth-overrides";
7
- export { resolveAuthOverrides } from "./auth-overrides";
6
+ export type { AuthHooks, AuthMethod, ResolvedAuthOperations } from "./auth-hooks";
7
+ export { resolveAuthHooks } from "./auth-hooks";
8
8
  export { createGoogleProvider } from "./google-oauth";
9
9
  export type { GoogleProviderConfig } from "./google-oauth";
10
10
  export { createLinkedinProvider } from "./linkedin-oauth";
@@ -23,7 +23,9 @@ export type { AuthMiddlewareOptions, AuthResult } from "./middleware";
23
23
  export { createAuthRoutes } from "./routes";
24
24
  export type { AuthModuleConfig } from "./routes";
25
25
  export { createAdminRoutes } from "./admin-routes";
26
- export { createRateLimiter, defaultAuthLimiter, strictAuthLimiter } from "./rate-limiter";
26
+ export { createRateLimiter, defaultAuthLimiter, strictAuthLimiter, createApiKeyRateLimiter, apiKeyKeyGenerator } from "./rate-limiter";
27
+ export { createApiKeyStore, createApiKeyRoutes, isApiKeyToken, validateApiKey, httpMethodToOperation, isOperationAllowed } from "./api-keys";
28
+ export type { ApiKey, ApiKeyMasked, ApiKeyPermission, ApiKeyWithSecret, CreateApiKeyRequest, UpdateApiKeyRequest, ApiKeyStore, ApiKeyOperation } from "./api-keys";
27
29
  export { createBuiltinAuthAdapter } from "./builtin-auth-adapter";
28
30
  export type { BuiltinAuthAdapterConfig } from "./builtin-auth-adapter";
29
31
  export { createCustomAuthAdapter } from "./custom-auth-adapter";
@@ -18,6 +18,7 @@ export interface UserData {
18
18
  emailVerified: boolean;
19
19
  emailVerificationToken?: string | null;
20
20
  emailVerificationSentAt?: Date | null;
21
+ isAnonymous?: boolean;
21
22
  metadata?: Record<string, unknown>;
22
23
  createdAt: Date;
23
24
  updatedAt: Date;
@@ -31,6 +32,7 @@ export interface CreateUserData {
31
32
  displayName?: string;
32
33
  photoUrl?: string;
33
34
  emailVerified?: boolean;
35
+ isAnonymous?: boolean;
34
36
  metadata?: Record<string, unknown>;
35
37
  }
36
38
  /**
@@ -84,7 +86,6 @@ export interface RoleData {
84
86
  edit?: boolean;
85
87
  delete?: boolean;
86
88
  }> | null;
87
- config: Record<string, unknown> | null;
88
89
  }
89
90
  /**
90
91
  * Data for creating a new role
@@ -95,7 +96,6 @@ export interface CreateRoleData {
95
96
  isAdmin?: boolean;
96
97
  defaultPermissions?: RoleData["defaultPermissions"];
97
98
  collectionPermissions?: RoleData["collectionPermissions"];
98
- config?: RoleData["config"];
99
99
  }
100
100
  /**
101
101
  * Refresh token info
@@ -304,8 +304,98 @@ export interface TokenRepository {
304
304
  */
305
305
  deleteExpiredTokens(): Promise<void>;
306
306
  }
307
+ /**
308
+ * MFA factor data structure
309
+ */
310
+ export interface MfaFactor {
311
+ id: string;
312
+ userId: string;
313
+ factorType: "totp";
314
+ friendlyName?: string;
315
+ verified: boolean;
316
+ createdAt: Date;
317
+ updatedAt: Date;
318
+ }
319
+ /**
320
+ * MFA challenge information
321
+ */
322
+ export interface MfaChallengeInfo {
323
+ id: string;
324
+ factorId: string;
325
+ createdAt: Date;
326
+ verifiedAt?: Date;
327
+ ipAddress?: string;
328
+ }
329
+ /**
330
+ * Recovery code data structure
331
+ */
332
+ export interface RecoveryCode {
333
+ id: string;
334
+ userId: string;
335
+ usedAt?: Date;
336
+ }
337
+ /**
338
+ * Abstract MFA repository interface.
339
+ * Handles all MFA-related database operations.
340
+ */
341
+ export interface MfaRepository {
342
+ /**
343
+ * Create a new MFA factor for a user
344
+ */
345
+ createMfaFactor(userId: string, factorType: "totp", secretEncrypted: string, friendlyName?: string): Promise<MfaFactor>;
346
+ /**
347
+ * Get all MFA factors for a user
348
+ */
349
+ getMfaFactors(userId: string): Promise<MfaFactor[]>;
350
+ /**
351
+ * Get a specific MFA factor by ID
352
+ */
353
+ getMfaFactorById(factorId: string): Promise<(MfaFactor & {
354
+ secretEncrypted: string;
355
+ }) | null>;
356
+ /**
357
+ * Mark an MFA factor as verified
358
+ */
359
+ verifyMfaFactor(factorId: string): Promise<void>;
360
+ /**
361
+ * Delete an MFA factor
362
+ */
363
+ deleteMfaFactor(factorId: string, userId: string): Promise<void>;
364
+ /**
365
+ * Create an MFA challenge
366
+ */
367
+ createMfaChallenge(factorId: string, ipAddress?: string): Promise<MfaChallengeInfo>;
368
+ /**
369
+ * Get an MFA challenge by ID
370
+ */
371
+ getMfaChallengeById(challengeId: string): Promise<MfaChallengeInfo | null>;
372
+ /**
373
+ * Mark an MFA challenge as verified
374
+ */
375
+ verifyMfaChallenge(challengeId: string): Promise<void>;
376
+ /**
377
+ * Create recovery codes for a user
378
+ */
379
+ createRecoveryCodes(userId: string, codeHashes: string[]): Promise<void>;
380
+ /**
381
+ * Use a recovery code (mark as used)
382
+ */
383
+ useRecoveryCode(userId: string, codeHash: string): Promise<boolean>;
384
+ /**
385
+ * Get unused recovery code count for a user
386
+ */
387
+ getUnusedRecoveryCodeCount(userId: string): Promise<number>;
388
+ /**
389
+ * Delete all recovery codes for a user
390
+ */
391
+ deleteAllRecoveryCodes(userId: string): Promise<void>;
392
+ /**
393
+ * Check if a user has any verified MFA factors
394
+ */
395
+ hasVerifiedMfaFactors(userId: string): Promise<boolean>;
396
+ }
307
397
  /**
308
398
  * Combined auth repository interface for convenience
309
399
  */
310
- export interface AuthRepository extends UserRepository, RoleRepository, TokenRepository {
400
+ export interface AuthRepository extends UserRepository, RoleRepository, TokenRepository, MfaRepository {
311
401
  }