@rebasepro/server 0.22.0 → 0.24.0
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/README.md +1 -1
- package/bin/rebase-server.js +4 -2
- package/dist/{GCSStorageController-CLIJXwGS.js → GCSStorageController-BSiP1c-f.js} +57 -29
- package/dist/GCSStorageController-BSiP1c-f.js.map +1 -0
- package/dist/{S3StorageController-Dcuf8lMA.js → S3StorageController-CAwFRgjV.js} +19 -7
- package/dist/S3StorageController-CAwFRgjV.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +127 -1
- package/dist/api/errors.d.ts +9 -0
- package/dist/api/live-schema-routes.d.ts +52 -8
- package/dist/api/logs-routes.d.ts +39 -1
- package/dist/api/openapi-generator.d.ts +17 -0
- package/dist/api/rest/api-generator.d.ts +158 -42
- package/dist/api/rest/auth-collection-writes.d.ts +85 -0
- package/dist/api/rest/field-access-query.d.ts +6 -2
- package/dist/api/rest/idempotency.d.ts +7 -1
- package/dist/api/rest/nested-write-access.d.ts +46 -0
- package/dist/api/rest/write-validation.d.ts +34 -2
- package/dist/api/types.d.ts +17 -1
- package/dist/{ast-schema-editor-CslO8Oje.js → ast-schema-editor-CWqS_sLJ.js} +411 -13
- package/dist/ast-schema-editor-CWqS_sLJ.js.map +1 -0
- package/dist/auth/access.d.ts +105 -0
- package/dist/auth/adapter-middleware.d.ts +2 -1
- package/dist/auth/address-ownership.d.ts +68 -0
- package/dist/auth/admin-roles-route.d.ts +4 -2
- package/dist/auth/admin-roles.d.ts +17 -20
- package/dist/auth/admin-user-ops.d.ts +35 -2
- package/dist/auth/admin-users-route.d.ts +1 -0
- package/dist/auth/api-keys/api-key-middleware.d.ts +56 -55
- package/dist/auth/api-keys/api-key-routes.d.ts +41 -11
- package/dist/auth/api-keys/api-key-store.d.ts +31 -8
- package/dist/auth/api-keys/api-key-types.d.ts +14 -16
- package/dist/auth/api-keys/http-operation.d.ts +19 -0
- package/dist/auth/api-keys/index.d.ts +11 -11
- package/dist/auth/api-keys/key-grant.d.ts +41 -0
- package/dist/auth/api-keys/legacy-permissions.d.ts +33 -0
- package/dist/auth/auth-hooks.d.ts +50 -7
- package/dist/auth/builtin-auth-adapter.d.ts +8 -0
- package/dist/auth/captcha.d.ts +5 -0
- package/dist/auth/cookie-utils.d.ts +7 -0
- package/dist/auth/deliverable-address.d.ts +6 -0
- package/dist/auth/email-change-routes.d.ts +41 -0
- package/dist/auth/expired-token-sweep.d.ts +67 -0
- package/dist/auth/impersonation.d.ts +110 -0
- package/dist/auth/index.d.ts +4 -2
- package/dist/auth/interfaces.d.ts +146 -65
- package/dist/auth/jwt.d.ts +66 -3
- package/dist/auth/magic-link-routes.d.ts +2 -6
- package/dist/auth/mfa-routes.d.ts +2 -9
- package/dist/auth/middleware.d.ts +17 -5
- package/dist/auth/oauth-signin-policy.d.ts +25 -8
- package/dist/auth/otp-routes.d.ts +2 -6
- package/dist/auth/passwordless-signup.d.ts +27 -0
- package/dist/auth/platform-token.d.ts +122 -0
- package/dist/auth/rate-limiter.d.ts +72 -1
- package/dist/auth/routes.d.ts +45 -0
- package/dist/auth/scope-routes.d.ts +22 -0
- package/dist/auth/session-routes.d.ts +18 -6
- package/dist/auth/token-revocation.d.ts +53 -1
- package/dist/auth/verify-credential.d.ts +28 -0
- package/dist/{auth-CCDpk2rn.js → auth-DMLngxn_.js} +2712 -711
- package/dist/auth-DMLngxn_.js.map +1 -0
- package/dist/backend-DTAOsLQc.js +30 -0
- package/dist/backend-DTAOsLQc.js.map +1 -0
- package/dist/backup/backup-common.d.ts +29 -0
- package/dist/backup/backup-routes.d.ts +24 -4
- package/dist/backup/backup-schedule.d.ts +33 -0
- package/dist/backup/backup-storage.d.ts +14 -0
- package/dist/backup/index.d.ts +2 -0
- package/dist/backup-CN0s50D2.js +444 -0
- package/dist/backup-CN0s50D2.js.map +1 -0
- package/dist/boot/bundle.d.ts +19 -0
- package/dist/boot/driver.d.ts +10 -0
- package/dist/boot/env.d.ts +51 -6
- package/dist/boot/fetch-bundle.d.ts +18 -1
- package/dist/boot/rls-audit-option.d.ts +26 -0
- package/dist/boot/security-headers.d.ts +26 -0
- package/dist/boot/sources.d.ts +1 -0
- package/dist/boot/static-routing.d.ts +56 -0
- package/dist/collection_patch-BRu-BvDv.js +472 -0
- package/dist/collection_patch-BRu-BvDv.js.map +1 -0
- package/dist/{contract-routes-eLxV0le1.js → contract-routes-fz8i4pxs.js} +17 -4
- package/dist/contract-routes-fz8i4pxs.js.map +1 -0
- package/dist/cron/cron-routes.d.ts +7 -2
- package/dist/cron/cron-scheduler.d.ts +146 -21
- package/dist/cron/cron-store.d.ts +76 -8
- package/dist/cron/index.d.ts +1 -1
- package/dist/{cron-loader-CQjvjpEw.js → cron-loader-CwaANlOG.js} +4 -4
- package/dist/cron-loader-CwaANlOG.js.map +1 -0
- package/dist/cron-routes-Bc-SB0Se.js +96 -0
- package/dist/cron-routes-Bc-SB0Se.js.map +1 -0
- package/dist/{cron-scheduler-COPQxlEq.js → cron-scheduler-CYQgco86.js} +427 -83
- package/dist/cron-scheduler-CYQgco86.js.map +1 -0
- package/dist/{cron-store-BYGZFNWk.js → cron-store-D2Q9-Aco.js} +139 -23
- package/dist/cron-store-D2Q9-Aco.js.map +1 -0
- package/dist/{ddl-bootstrap-CfNvxMuK.js → ddl-bootstrap-BaqMSa4Y.js} +3 -26
- package/dist/ddl-bootstrap-BaqMSa4Y.js.map +1 -0
- package/dist/email/index.d.ts +2 -2
- package/dist/email/link-base.d.ts +5 -4
- package/dist/email/smtp-email-service.d.ts +13 -1
- package/dist/email/templates.d.ts +31 -0
- package/dist/email/types.d.ts +29 -2
- package/dist/env.d.ts +25 -7
- package/dist/{errors-DWsX4yTd.js → errors-D6_y86c5.js} +102 -8
- package/dist/errors-D6_y86c5.js.map +1 -0
- package/dist/{function-loader-xnbDAPfa.js → function-loader-D7o5Epjj.js} +2 -2
- package/dist/{function-loader-xnbDAPfa.js.map → function-loader-D7o5Epjj.js.map} +1 -1
- package/dist/{function-routes-Chet4-lB.js → function-routes-CaNG4waN.js} +24 -12
- package/dist/function-routes-CaNG4waN.js.map +1 -0
- package/dist/functions/context.d.ts +17 -6
- package/dist/functions/guards.d.ts +22 -5
- package/dist/functions/index.d.ts +2 -2
- package/dist/functions/index.js +90 -36
- package/dist/functions/index.js.map +1 -1
- package/dist/{history-recorder-BQmB0P_j.js → history-recorder-Nr8zLvoU.js} +9 -7
- package/dist/history-recorder-Nr8zLvoU.js.map +1 -0
- package/dist/{history-store-CetkrBBD.js → history-store-rcAm_xFR.js} +2 -2
- package/dist/{history-store-CetkrBBD.js.map → history-store-rcAm_xFR.js.map} +1 -1
- package/dist/index.d.ts +14 -4
- package/dist/index.es.js +5733 -1551
- package/dist/index.es.js.map +1 -1
- package/dist/init/docs.d.ts +5 -2
- package/dist/init/health.d.ts +17 -2
- package/dist/init/shutdown.d.ts +18 -3
- package/dist/init.d.ts +54 -0
- package/dist/jobs/index.d.ts +2 -2
- package/dist/jobs/job-queue.d.ts +23 -2
- package/dist/jobs/job-store.d.ts +37 -5
- package/dist/jobs/types.d.ts +8 -6
- package/dist/{jobs-Bjr8DZAi.js → jobs-DqYNfquG.js} +306 -167
- package/dist/jobs-DqYNfquG.js.map +1 -0
- package/dist/{jwt-C4OW-DNq.js → jwt-R6bSPMjk.js} +114 -38
- package/dist/{jwt-C4OW-DNq.js.map → jwt-R6bSPMjk.js.map} +1 -1
- package/dist/{keys-Qfc4XieN.js → keys-GAVZqbqx.js} +18 -17
- package/dist/{keys-Qfc4XieN.js.map → keys-GAVZqbqx.js.map} +1 -1
- package/dist/{logger-DO2PZc4i.js → logger-D-S-hO5e.js} +26 -3
- package/dist/logger-D-S-hO5e.js.map +1 -0
- package/dist/{logs-routes-3EEzPjhl.js → logs-routes-DAdv37GI.js} +54 -11
- package/dist/logs-routes-DAdv37GI.js.map +1 -0
- package/dist/mcp/consent-page.d.ts +1 -1
- package/dist/mcp/mcp-routes.d.ts +45 -2
- package/dist/mcp/mcp-tools.d.ts +22 -10
- package/dist/mcp/oauth-metadata.d.ts +21 -16
- package/dist/mcp/oauth-routes.d.ts +34 -1
- package/dist/mcp/oauth-store.d.ts +29 -13
- package/dist/metrics/history-recorder.d.ts +1 -1
- package/dist/{openapi-generator-D8uFz-LW.js → openapi-generator-DAq_XVDu.js} +135 -22
- package/dist/openapi-generator-DAq_XVDu.js.map +1 -0
- package/dist/{proxy-Czngl3p9.js → proxy-qRlqeUmO.js} +2 -2
- package/dist/{proxy-Czngl3p9.js.map → proxy-qRlqeUmO.js.map} +1 -1
- package/dist/{query-parser-BleZmY18.js → query-parser-BgiKJKvc.js} +41 -82
- package/dist/query-parser-BgiKJKvc.js.map +1 -0
- package/dist/{request-timeout-C_4C2BeR.js → request-timeout-DgH7j8qO.js} +3 -3
- package/dist/{request-timeout-C_4C2BeR.js.map → request-timeout-DgH7j8qO.js.map} +1 -1
- package/dist/rls-audit/index.d.ts +4 -0
- package/dist/schema-edit/apply-schema-change.d.ts +63 -3
- package/dist/schema-edit/project-root.d.ts +3 -2
- package/dist/schema-edit/remote-source.d.ts +9 -4
- package/dist/{schema-editor-routes-DdLihzp0.js → schema-editor-routes-oIyuWl3L.js} +12 -7
- package/dist/schema-editor-routes-oIyuWl3L.js.map +1 -0
- package/dist/serve-spa.d.ts +58 -0
- package/dist/services/routed-realtime-service.d.ts +11 -0
- package/dist/soft-delete-params-BWPilMPF.js +59 -0
- package/dist/soft-delete-params-BWPilMPF.js.map +1 -0
- package/dist/{src-Br6ARbs6.js → src-CatHFUym.js} +439 -45
- package/dist/src-CatHFUym.js.map +1 -0
- package/dist/{src-1vL-I1Po.js → src-I3aG1PcY.js} +371 -81
- package/dist/src-I3aG1PcY.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +13 -1
- package/dist/storage/LocalStorageController.d.ts +2 -0
- package/dist/storage/S3StorageController.d.ts +2 -0
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/keys.d.ts +12 -0
- package/dist/storage/property-limits.d.ts +41 -6
- package/dist/storage/rendition-cache.d.ts +11 -1
- package/dist/storage/request-keys.d.ts +82 -0
- package/dist/storage/requested-object.d.ts +74 -0
- package/dist/storage/routes.d.ts +36 -18
- package/dist/storage/tus-handler.d.ts +30 -5
- package/dist/storage/types.d.ts +36 -1
- package/dist/types-BfKcm9do.js.map +1 -1
- package/dist/utils/logger.d.ts +12 -0
- package/package.json +9 -9
- package/dist/GCSStorageController-CLIJXwGS.js.map +0 -1
- package/dist/S3StorageController-Dcuf8lMA.js.map +0 -1
- package/dist/admin-roles-vYdp_Pil.js +0 -36
- package/dist/admin-roles-vYdp_Pil.js.map +0 -1
- package/dist/admin_block-DxKLmdiv.js +0 -206
- package/dist/admin_block-DxKLmdiv.js.map +0 -1
- package/dist/ast-schema-editor-CslO8Oje.js.map +0 -1
- package/dist/auth/api-keys/api-key-permission-guard.d.ts +0 -65
- package/dist/auth-CCDpk2rn.js.map +0 -1
- package/dist/backup-DzI9jLwc.js +0 -192
- package/dist/backup-DzI9jLwc.js.map +0 -1
- package/dist/contract-routes-eLxV0le1.js.map +0 -1
- package/dist/cron-loader-CQjvjpEw.js.map +0 -1
- package/dist/cron-routes-B7CRGfiq.js +0 -72
- package/dist/cron-routes-B7CRGfiq.js.map +0 -1
- package/dist/cron-scheduler-COPQxlEq.js.map +0 -1
- package/dist/cron-store-BYGZFNWk.js.map +0 -1
- package/dist/ddl-bootstrap-CfNvxMuK.js.map +0 -1
- package/dist/errors-DWsX4yTd.js.map +0 -1
- package/dist/function-routes-Chet4-lB.js.map +0 -1
- package/dist/history-recorder-BQmB0P_j.js.map +0 -1
- package/dist/jobs-Bjr8DZAi.js.map +0 -1
- package/dist/logger-DO2PZc4i.js.map +0 -1
- package/dist/logs-routes-3EEzPjhl.js.map +0 -1
- package/dist/openapi-generator-D8uFz-LW.js.map +0 -1
- package/dist/query-parser-BleZmY18.js.map +0 -1
- package/dist/schema-editor-routes-DdLihzp0.js.map +0 -1
- package/dist/src-1vL-I1Po.js.map +0 -1
- package/dist/src-Br6ARbs6.js.map +0 -1
|
@@ -1,34 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Authenticating requests that present an API key (`rk_`).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* {@link resolveApiKey} turns a presented key into the identity it acts as and
|
|
5
|
+
* the scopes it holds; the HTTP middlewares, the realtime socket and `/mcp`
|
|
6
|
+
* all call it, so a key means the same thing on every surface.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* - A **service key** acts as `api-key:<id>`, with the RLS roles `service`
|
|
9
|
+
* plus whatever it was given, and holds exactly its scopes.
|
|
10
|
+
* - A **personal key** acts as its owner — their uid and their roles as they
|
|
11
|
+
* are *now*, read on every use — and holds its scopes narrowed to what the
|
|
12
|
+
* owner's roles still hold. Demote the owner and the key shrinks with them;
|
|
13
|
+
* delete the account and the key stops.
|
|
12
14
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* independent one
|
|
16
|
-
* `rebase_user` role like any other caller. An `admin` key passes RLS via the
|
|
17
|
-
* injected `default_admin` policies; a non-admin key (roles `["service"]`,
|
|
18
|
-
* uid `api-key:<id>`) only sees rows that a policy explicitly grants to the
|
|
19
|
-
* `service` role or to the public. Owner-style policies
|
|
20
|
-
* (`owner_id = rebase.uid()`) never match an API key's synthetic uid.
|
|
15
|
+
* A key never bypasses RLS: the request's driver is scoped to the identity
|
|
16
|
+
* like any other caller's, so the scopes are one ceiling and the database's
|
|
17
|
+
* policies another, independent one.
|
|
21
18
|
*
|
|
22
19
|
* @module
|
|
23
20
|
*/
|
|
24
21
|
import type { Context, MiddlewareHandler } from "hono";
|
|
25
|
-
import type
|
|
22
|
+
import { type DataDriver } from "@rebasepro/types";
|
|
26
23
|
import type { HonoEnv } from "../../api/types.js";
|
|
27
24
|
import type { ApiKeyStore } from "./api-key-store.js";
|
|
25
|
+
import type { ApiKeyMasked } from "./api-key-types.js";
|
|
28
26
|
/**
|
|
29
27
|
* Check whether a token looks like a Rebase API key.
|
|
30
28
|
*/
|
|
31
29
|
export declare function isApiKeyToken(token: string): boolean;
|
|
30
|
+
/** Who a verified key acts as, and what it may do. */
|
|
31
|
+
export interface ApiKeyIdentity {
|
|
32
|
+
uid: string;
|
|
33
|
+
roles: string[];
|
|
34
|
+
scopes: string[];
|
|
35
|
+
apiKey: ApiKeyMasked;
|
|
36
|
+
}
|
|
37
|
+
/** A presented key that does not authenticate, and why. */
|
|
38
|
+
export interface ApiKeyRefusal {
|
|
39
|
+
message: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Verify a presented key: it exists, is live, and — for a personal key —
|
|
43
|
+
* personal keys are on and its owner still exists. Records the use.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveApiKey(store: ApiKeyStore, token: string): Promise<ApiKeyIdentity | ApiKeyRefusal>;
|
|
32
46
|
/**
|
|
33
47
|
* Options for the API key authentication handler.
|
|
34
48
|
*/
|
|
@@ -37,59 +51,46 @@ export interface ApiKeyAuthOptions {
|
|
|
37
51
|
driver: DataDriver;
|
|
38
52
|
}
|
|
39
53
|
/**
|
|
40
|
-
* Validate an API key token and populate the Hono context
|
|
41
|
-
*
|
|
42
|
-
* Returns `true` if the key is valid and context has been populated,
|
|
43
|
-
* or returns an error Response if the key is invalid.
|
|
54
|
+
* Validate an API key token and populate the Hono context: `user`, `apiKey`,
|
|
55
|
+
* `scopes` and the RLS-scoped `driver`.
|
|
44
56
|
*
|
|
45
|
-
*
|
|
46
|
-
* `createAuthMiddleware()` when a `rk_` prefixed token is detected.
|
|
57
|
+
* Returns `true` when the context is populated, or the error Response.
|
|
47
58
|
*/
|
|
48
59
|
export declare function validateApiKey(c: Context<HonoEnv>, token: string, options: ApiKeyAuthOptions): Promise<Response | true>;
|
|
49
60
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* Storage previously did not accept API keys at all (`rk_` tokens were
|
|
53
|
-
* misparsed as JWTs and 401'd). Now that the pre-auth middleware
|
|
54
|
-
* authenticates them, this guard decides what they may do: the key needs a
|
|
55
|
-
* `"storage"` permission entry (or the global `"*"` wildcard) covering the
|
|
56
|
-
* operation derived from the HTTP method. Requests not authenticated via an
|
|
57
|
-
* API key pass through to the storage router's own auth gates.
|
|
58
|
-
*
|
|
59
|
-
* TUS resumable-upload routes (`/tus`, `/tus/:id`) are classified as `write`
|
|
60
|
-
* for EVERY method: the protocol's offset check is a GET and its cancel is a
|
|
61
|
-
* DELETE, but both are steps of an upload — a write-scoped key must be able
|
|
62
|
-
* to complete (and abort) its own resumable upload without also holding
|
|
63
|
-
* `read`/`delete` on stored objects.
|
|
61
|
+
* The 403 for a credential that lacks a data-plane scope outside the REST
|
|
62
|
+
* generator (storage and functions). Names the scope that would grant it.
|
|
64
63
|
*/
|
|
65
|
-
export declare function
|
|
64
|
+
export declare function forbidScope(c: Context<HonoEnv>, scope: string, target?: string): Response;
|
|
66
65
|
/**
|
|
67
|
-
*
|
|
66
|
+
* Scope guard for the resumable-upload routes (`/tus/:id`).
|
|
68
67
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
|
|
68
|
+
* Every step of an upload — the offset check (HEAD), the chunks (PATCH), the
|
|
69
|
+
* cancel (DELETE) — is part of writing it, so all of them need
|
|
70
|
+
* `storage:write`. Which source the upload writes to was checked when it was
|
|
71
|
+
* created; these steps only reach an upload the same caller owns.
|
|
72
|
+
*/
|
|
73
|
+
export declare function createTusScopeGuard(): MiddlewareHandler<HonoEnv>;
|
|
74
|
+
/**
|
|
75
|
+
* Scope guard for the custom-functions router: a narrowed credential needs
|
|
76
|
+
* `functions:invoke` on the function it calls. The functions index — the
|
|
77
|
+
* listing at the mount point itself — needs the unqualified scope.
|
|
74
78
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
79
|
+
* People pass: what a person may do inside a function is the function's own
|
|
80
|
+
* business, as it always was.
|
|
77
81
|
*
|
|
78
82
|
* @param mountPrefix - The path the functions router is mounted at
|
|
79
83
|
* (e.g. `/api/functions`), used to extract the function
|
|
80
84
|
* name from the request path.
|
|
81
85
|
*/
|
|
82
|
-
export declare function
|
|
86
|
+
export declare function createFunctionScopeGuard(mountPrefix: string): MiddlewareHandler<HonoEnv>;
|
|
83
87
|
/**
|
|
84
|
-
*
|
|
88
|
+
* Pre-auth middleware for `rk_` bearer tokens.
|
|
85
89
|
*
|
|
86
|
-
* Routers whose auth gate is JWT-based (`
|
|
87
|
-
* the
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* the downstream gates then see the already-resolved user and apply their
|
|
91
|
-
* role checks (`requireAdmin`) as usual — so an `admin: true` key passes and
|
|
92
|
-
* a non-admin key is rejected with 403.
|
|
90
|
+
* Routers whose auth gate is JWT-based (`createRequireAuth` — the admin
|
|
91
|
+
* surfaces, the key routes) don't know about API keys. Mounted in front of
|
|
92
|
+
* them, this authenticates `rk_` tokens and populates the request context; the
|
|
93
|
+
* downstream gates then see the already-resolved caller and check its scopes.
|
|
93
94
|
*
|
|
94
95
|
* Requests without an `rk_` bearer token pass through untouched. An invalid,
|
|
95
96
|
* revoked, or expired `rk_` token is rejected here (401) rather than falling
|
|
@@ -1,32 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Routes for managing API keys.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* - {@link createApiKeyRoutes} — the project's service keys, under
|
|
5
|
+
* `/api/admin/api-keys`, for holders of `keys:read` / `keys:write`.
|
|
6
|
+
* - {@link createPersonalKeyRoutes} — the caller's own personal keys, under
|
|
7
|
+
* `/api/auth/keys`, when the app enables `auth.personalKeys`.
|
|
8
|
+
*
|
|
9
|
+
* All routes return masked keys (never the hash). The full plaintext key is
|
|
10
|
+
* returned exactly once, in the response that creates it. No API key may call
|
|
11
|
+
* either router.
|
|
7
12
|
*
|
|
8
13
|
* @module
|
|
9
14
|
*/
|
|
10
15
|
import { Hono } from "hono";
|
|
16
|
+
import type { AccessModel } from "@rebasepro/types";
|
|
11
17
|
import type { HonoEnv } from "../../api/types.js";
|
|
12
18
|
import type { ApiKeyStore } from "./api-key-store.js";
|
|
19
|
+
import { type KeyTargets } from "./key-grant.js";
|
|
13
20
|
export interface ApiKeyRouteOptions {
|
|
14
21
|
store: ApiKeyStore;
|
|
15
22
|
serviceKey?: string;
|
|
16
23
|
/**
|
|
17
24
|
* Read the caller's roles from the database rather than from their token.
|
|
18
25
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* See `createRequireAuth`.
|
|
26
|
+
* A key minted here may outlive the session that minted it, so the roles
|
|
27
|
+
* that bound what it may hold must be the caller's roles now — not the
|
|
28
|
+
* ones a token issued before a demotion still claims. See
|
|
29
|
+
* `createRequireAuth`.
|
|
24
30
|
*/
|
|
25
31
|
resolveRoles?: (uid: string) => Promise<string[]>;
|
|
26
32
|
/** Repository for the token-revocation watermark. See `createRequireAuth`. */
|
|
27
|
-
revocationRepo?:
|
|
33
|
+
revocationRepo?: import("../token-revocation.js").AccessJudgeRepository;
|
|
34
|
+
/** What a scope's target may name. Unset, targets are not checked for existence. */
|
|
35
|
+
targets?: KeyTargets;
|
|
36
|
+
/** The access model to validate scopes against. Defaults to the configured one. */
|
|
37
|
+
accessModel?: () => AccessModel;
|
|
28
38
|
}
|
|
29
39
|
/**
|
|
30
|
-
*
|
|
40
|
+
* The project's service keys: `GET` needs `keys:read`, everything else
|
|
41
|
+
* `keys:write`.
|
|
31
42
|
*/
|
|
32
43
|
export declare function createApiKeyRoutes(options: ApiKeyRouteOptions): Hono<HonoEnv>;
|
|
44
|
+
export interface PersonalKeyRouteOptions {
|
|
45
|
+
store: ApiKeyStore;
|
|
46
|
+
/** Whether the app enabled `auth.personalKeys`. Off, every route explains how to turn it on. */
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
/** Recognised only to be refused with a reason: the service key has no account. */
|
|
49
|
+
serviceKey?: string;
|
|
50
|
+
resolveRoles?: (uid: string) => Promise<string[]>;
|
|
51
|
+
revocationRepo?: import("../token-revocation.js").AccessJudgeRepository;
|
|
52
|
+
targets?: KeyTargets;
|
|
53
|
+
accessModel?: () => AccessModel;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The caller's own keys. Each acts as the caller — their account, their roles
|
|
57
|
+
* as they are when the key is used — and holds no scope the caller does not.
|
|
58
|
+
*
|
|
59
|
+
* For a signed-in account only: not a key, not the service key (it has no
|
|
60
|
+
* account to act as) and not a guest, whose account is one sign-out from gone.
|
|
61
|
+
*/
|
|
62
|
+
export declare function createPersonalKeyRoutes(options: PersonalKeyRouteOptions): Hono<HonoEnv>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Database operations for
|
|
2
|
+
* Database operations for API keys.
|
|
3
3
|
*
|
|
4
4
|
* Uses the DataDriver's `admin.executeSql` capability (same pattern as
|
|
5
5
|
* the cron-store and ensure-tables modules). All data lives in the
|
|
@@ -8,22 +8,45 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
import type { DataDriver } from "@rebasepro/types";
|
|
11
|
-
import type { ApiKey, ApiKeyMasked, ApiKeyWithSecret,
|
|
11
|
+
import type { ApiKey, ApiKeyKind, ApiKeyMasked, ApiKeyWithSecret, UpdateApiKeyRequest } from "./api-key-types.js";
|
|
12
|
+
/** What a new key row is made of, already validated by the route that asked. */
|
|
13
|
+
export interface NewApiKey {
|
|
14
|
+
name: string;
|
|
15
|
+
kind: ApiKeyKind;
|
|
16
|
+
scopes: string[];
|
|
17
|
+
/** RLS roles beside `service`. Always empty for a personal key. */
|
|
18
|
+
roles: string[];
|
|
19
|
+
/** The account a personal key acts as. */
|
|
20
|
+
owner_uid: string | null;
|
|
21
|
+
rate_limit: number | null;
|
|
22
|
+
expires_at: string | null;
|
|
23
|
+
}
|
|
24
|
+
/** Which keys a listing returns. */
|
|
25
|
+
export type ApiKeyFilter = {
|
|
26
|
+
kind: "service";
|
|
27
|
+
} | {
|
|
28
|
+
kind: "personal";
|
|
29
|
+
owner_uid: string;
|
|
30
|
+
};
|
|
12
31
|
export interface ApiKeyStore {
|
|
13
32
|
/** Ensure the `rebase.api_keys` table exists. Called once on startup. */
|
|
14
33
|
ensureTable(): Promise<void>;
|
|
15
34
|
/** Create a new API key. Returns the full plaintext key exactly once. */
|
|
16
|
-
createApiKey(
|
|
35
|
+
createApiKey(key: NewApiKey, createdBy: string): Promise<ApiKeyWithSecret>;
|
|
17
36
|
/** Look up an API key by its SHA-256 hash. Returns `null` if not found. */
|
|
18
37
|
findByKeyHash(hash: string): Promise<ApiKey | null>;
|
|
19
|
-
/** List
|
|
20
|
-
listApiKeys(): Promise<ApiKeyMasked[]>;
|
|
38
|
+
/** List keys (masked, never includes hash), newest first. */
|
|
39
|
+
listApiKeys(filter: ApiKeyFilter): Promise<ApiKeyMasked[]>;
|
|
21
40
|
/** Get a single API key by ID (masked). */
|
|
22
41
|
getApiKeyById(id: string): Promise<ApiKeyMasked | null>;
|
|
23
|
-
/** Update name,
|
|
42
|
+
/** Update name, scopes, roles, rate_limit, or expires_at. */
|
|
24
43
|
updateApiKey(id: string, updates: UpdateApiKeyRequest): Promise<ApiKeyMasked | null>;
|
|
25
|
-
/**
|
|
26
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Soft-delete: set `revoked_at` to now. With `owner_uid`, only that
|
|
46
|
+
* account's personal key is revoked — the answer is false for anyone
|
|
47
|
+
* else's, so a route can 404 without saying whether the id exists.
|
|
48
|
+
*/
|
|
49
|
+
revokeApiKey(id: string, owner_uid?: string): Promise<boolean>;
|
|
27
50
|
/** Touch `last_used_at` to the current timestamp. */
|
|
28
51
|
updateLastUsed(id: string): Promise<void>;
|
|
29
52
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Type definitions for
|
|
2
|
+
* Type definitions for API keys.
|
|
3
3
|
*
|
|
4
|
-
* The wire contract —
|
|
5
|
-
* lives in `@rebasepro/types`, because the client SDK needs the same shapes
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The wire contract — scopes, the masked key, the create/update payloads —
|
|
5
|
+
* lives in `@rebasepro/types`, because the client SDK needs the same shapes.
|
|
6
|
+
* Only {@link ApiKey}, the database row carrying `key_hash`, is server-side
|
|
7
|
+
* and stays here.
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
|
-
import type {
|
|
12
|
-
export type {
|
|
11
|
+
import type { ApiKeyKind } from "@rebasepro/types";
|
|
12
|
+
export type { ApiKeyKind, ApiKeyMasked, ApiKeyWithSecret, CreateApiKeyRequest, CreatePersonalKeyRequest, UpdateApiKeyRequest } from "@rebasepro/types";
|
|
13
13
|
/**
|
|
14
14
|
* Full database row for an API key.
|
|
15
15
|
* The `key_hash` is never exposed via the API — only stored for lookup.
|
|
@@ -17,19 +17,17 @@ export type { ApiKeyPermission, ApiKeyMasked, ApiKeyWithSecret, CreateApiKeyRequ
|
|
|
17
17
|
export interface ApiKey {
|
|
18
18
|
id: string;
|
|
19
19
|
name: string;
|
|
20
|
+
kind: ApiKeyKind;
|
|
20
21
|
/** First 12 characters of the plaintext key, for display only. */
|
|
21
22
|
key_prefix: string;
|
|
22
23
|
/** SHA-256 hash of the full plaintext key. */
|
|
23
24
|
key_hash: string;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
* explicitly names that role.
|
|
31
|
-
*/
|
|
32
|
-
admin: boolean;
|
|
25
|
+
/** `resource:action[:target]` scope strings. */
|
|
26
|
+
scopes: string[];
|
|
27
|
+
/** RLS roles a service key runs as, beside `service`. Empty on a personal key. */
|
|
28
|
+
roles: string[];
|
|
29
|
+
/** The account a personal key acts as. Null on a service key. */
|
|
30
|
+
owner_uid: string | null;
|
|
33
31
|
/**
|
|
34
32
|
* Requests per 15-minute window. `null` means "no per-key override" —
|
|
35
33
|
* the data rate limiter then applies its default API-key limit
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which data-plane operation an HTTP request performs, for the scope it needs:
|
|
3
|
+
* `data:read`, `data:write` or `data:delete` (and the same for storage).
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
/** The operation part of a data-plane scope. */
|
|
8
|
+
export type DataOperation = "read" | "write" | "delete";
|
|
9
|
+
/**
|
|
10
|
+
* Map an HTTP method to the operation it performs.
|
|
11
|
+
*
|
|
12
|
+
* - `GET`, `HEAD`, `OPTIONS` → `"read"`
|
|
13
|
+
* - `POST`, `PUT`, `PATCH` → `"write"`
|
|
14
|
+
* - `DELETE` → `"delete"`
|
|
15
|
+
*
|
|
16
|
+
* Any other method is a `"write"`: a verb this does not know is not assumed
|
|
17
|
+
* to be harmless.
|
|
18
|
+
*/
|
|
19
|
+
export declare function httpMethodToOperation(method: string): DataOperation;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* API keys module.
|
|
3
3
|
*
|
|
4
|
-
* Re-exports types, store, middleware
|
|
5
|
-
* for the API key authentication system.
|
|
4
|
+
* Re-exports types, store, middleware and routes for API key authentication.
|
|
6
5
|
*
|
|
7
6
|
* @module
|
|
8
7
|
*/
|
|
9
|
-
export type { ApiKey,
|
|
8
|
+
export type { ApiKey, ApiKeyKind, ApiKeyMasked, ApiKeyWithSecret, CreateApiKeyRequest, CreatePersonalKeyRequest, UpdateApiKeyRequest } from "./api-key-types.js";
|
|
10
9
|
export { createApiKeyStore } from "./api-key-store.js";
|
|
11
|
-
export type { ApiKeyStore } from "./api-key-store.js";
|
|
12
|
-
export { isApiKeyToken, validateApiKey, createApiKeyPreAuth,
|
|
13
|
-
export type { ApiKeyAuthOptions } from "./api-key-middleware.js";
|
|
14
|
-
export { httpMethodToOperation
|
|
15
|
-
export type {
|
|
16
|
-
export { createApiKeyRoutes } from "./api-key-routes.js";
|
|
17
|
-
export type { ApiKeyRouteOptions } from "./api-key-routes.js";
|
|
10
|
+
export type { ApiKeyStore, ApiKeyFilter, NewApiKey } from "./api-key-store.js";
|
|
11
|
+
export { isApiKeyToken, resolveApiKey, validateApiKey, createApiKeyPreAuth, createFunctionScopeGuard, createTusScopeGuard } from "./api-key-middleware.js";
|
|
12
|
+
export type { ApiKeyAuthOptions, ApiKeyIdentity, ApiKeyRefusal } from "./api-key-middleware.js";
|
|
13
|
+
export { httpMethodToOperation } from "./http-operation.js";
|
|
14
|
+
export type { DataOperation } from "./http-operation.js";
|
|
15
|
+
export { createApiKeyRoutes, createPersonalKeyRoutes } from "./api-key-routes.js";
|
|
16
|
+
export type { ApiKeyRouteOptions, PersonalKeyRouteOptions } from "./api-key-routes.js";
|
|
17
|
+
export type { KeyTargets } from "./key-grant.js";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a new or changed key may hold, decided against whoever asked for it.
|
|
3
|
+
*
|
|
4
|
+
* One rule for every door that mints a credential: **nothing is minted with
|
|
5
|
+
* more than its minter holds.** A key's scopes must be within the creator's
|
|
6
|
+
* own; a service key's RLS roles must be roles the creator holds, unless the
|
|
7
|
+
* creator is an admin; and `keys:*` never goes on a key at all.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import { type AccessModel } from "@rebasepro/types";
|
|
12
|
+
/**
|
|
13
|
+
* The targets this backend serves, for refusing a key narrowed to something
|
|
14
|
+
* that does not exist — a typo there is a key that silently reaches nothing.
|
|
15
|
+
* Each list is read when a key is minted, so it reflects what is loaded then.
|
|
16
|
+
*/
|
|
17
|
+
export interface KeyTargets {
|
|
18
|
+
collections(): readonly string[];
|
|
19
|
+
functions(): readonly string[];
|
|
20
|
+
buckets(): readonly string[];
|
|
21
|
+
}
|
|
22
|
+
/** Read a `scopes` body field: a non-empty array of strings, deduplicated. */
|
|
23
|
+
export declare function readScopesField(value: unknown): string[];
|
|
24
|
+
/** Read a `roles` body field: an array of non-empty strings, deduplicated, without `service`. */
|
|
25
|
+
export declare function readRolesField(value: unknown): string[];
|
|
26
|
+
/**
|
|
27
|
+
* Refuse scopes that are malformed, unknown, aimed at a target that does not
|
|
28
|
+
* exist, for key management, or beyond what `minterScopes` covers.
|
|
29
|
+
*/
|
|
30
|
+
export declare function assertScopesGrantable(requested: readonly string[], minterScopes: readonly string[], model: AccessModel, targets?: KeyTargets): void;
|
|
31
|
+
/**
|
|
32
|
+
* Refuse RLS roles the minter does not hold. An admin may give any role — they
|
|
33
|
+
* already read every row — and anyone else only their own.
|
|
34
|
+
*/
|
|
35
|
+
export declare function assertRolesGrantable(requested: readonly string[], minterRoles: readonly string[]): void;
|
|
36
|
+
/** Read an optional `expires_at`: absent, null, or a future ISO-8601 instant. */
|
|
37
|
+
export declare function readExpiresAt(value: unknown, requireFuture: boolean): string | null | undefined;
|
|
38
|
+
/** Read an optional `rate_limit`: absent, null, or a positive integer. */
|
|
39
|
+
export declare function readRateLimit(value: unknown): number | null | undefined;
|
|
40
|
+
/** Read a key name: a non-empty string, trimmed. */
|
|
41
|
+
export declare function readName(value: unknown): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scopes a key stored before scopes existed is read as.
|
|
3
|
+
*
|
|
4
|
+
* Such a row carries `permissions` — `[{ collection, operations }]`, with
|
|
5
|
+
* `"*"`, `"storage"`, `"functions"` and `"functions/<name>"` overloading the
|
|
6
|
+
* collection field — and an `admin` flag. This turns it into the scopes and
|
|
7
|
+
* RLS roles it now holds, once, when the store backfills the row.
|
|
8
|
+
*
|
|
9
|
+
* The rule is that nothing widens. Where a stored grant has no exact
|
|
10
|
+
* equivalent, it narrows:
|
|
11
|
+
*
|
|
12
|
+
* - A function grant without `write` becomes nothing. Functions were checked
|
|
13
|
+
* by HTTP method, so a read-only grant let a key call GET functions; a
|
|
14
|
+
* function is code, and `functions:invoke` is not a read.
|
|
15
|
+
* - `admin: true` becomes the `admin` RLS role and the admin surfaces such a
|
|
16
|
+
* key could reach (users, schema, backups, cron, logs) — not `database:*`,
|
|
17
|
+
* which lived on the realtime socket where no key could authenticate, and
|
|
18
|
+
* not `keys:*`, which no key may hold.
|
|
19
|
+
*
|
|
20
|
+
* @module
|
|
21
|
+
*/
|
|
22
|
+
/** One stored permission entry, as the old column held it. */
|
|
23
|
+
export interface StoredPermission {
|
|
24
|
+
collection: string;
|
|
25
|
+
operations: string[];
|
|
26
|
+
}
|
|
27
|
+
/** Read a stored `permissions` value — JSON text or already-parsed — into entries. */
|
|
28
|
+
export declare function parseStoredPermissions(value: unknown): StoredPermission[];
|
|
29
|
+
/** The scopes and RLS roles a stored key now holds. Never more than it did. */
|
|
30
|
+
export declare function scopesFromStoredPermissions(permissions: readonly StoredPermission[], admin: boolean): {
|
|
31
|
+
scopes: string[];
|
|
32
|
+
roles: string[];
|
|
33
|
+
};
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
39
|
import type { PasswordValidationResult } from "./password.js";
|
|
40
|
+
import { ApiError } from "../api/errors.js";
|
|
40
41
|
import type { AuthRepository, UserData, CreateUserData } from "./interfaces.js";
|
|
41
42
|
import type { EmailService, EmailConfig } from "../email/index.js";
|
|
42
43
|
import type { AuthResponsePayload, TransformAuthResponseContext } from "@rebasepro/types";
|
|
@@ -93,8 +94,12 @@ export interface AuthHooks {
|
|
|
93
94
|
*/
|
|
94
95
|
verifyCredentials?(email: string, password: string, repo: AuthRepository): Promise<UserData | null>;
|
|
95
96
|
/**
|
|
96
|
-
* Called after any successful authentication event
|
|
97
|
-
*
|
|
97
|
+
* Called after any successful authentication event: password login
|
|
98
|
+
* (`login`), registration (`register`), an OAuth sign-in (`oauth`), a
|
|
99
|
+
* token refresh (`refresh`), a password reset (`password-reset`), a
|
|
100
|
+
* guest session (`anonymous`), a magic link (`magic-link`), an email code
|
|
101
|
+
* (`otp`) and a second factor (`mfa`). Every value of {@link AuthMethod}
|
|
102
|
+
* is passed by some route.
|
|
98
103
|
*
|
|
99
104
|
* Use for audit logging, syncing external state, updating
|
|
100
105
|
* last-login timestamps, etc.
|
|
@@ -103,7 +108,15 @@ export interface AuthHooks {
|
|
|
103
108
|
*/
|
|
104
109
|
onAuthenticated?(user: UserData, method: AuthMethod): Promise<void>;
|
|
105
110
|
/**
|
|
106
|
-
* Called before a new user is created
|
|
111
|
+
* Called before a new user is created: registration, an OAuth sign-in
|
|
112
|
+
* that creates the account, a guest, and admin creation.
|
|
113
|
+
*
|
|
114
|
+
* Throw to refuse: the caller gets 400 `HOOK_REJECTED` with your message,
|
|
115
|
+
* or the status your error carries (`ApiError`, or a 4xx `status`).
|
|
116
|
+
*
|
|
117
|
+
* Also called when a guest becomes an account through
|
|
118
|
+
* `POST /auth/anonymous/link`, with the email and password hash it is
|
|
119
|
+
* getting; the guest itself was created with `isAnonymous: true`.
|
|
107
120
|
*
|
|
108
121
|
* Return modified data to alter what gets stored, or throw an error
|
|
109
122
|
* to reject the creation entirely.
|
|
@@ -121,10 +134,14 @@ export interface AuthHooks {
|
|
|
121
134
|
*/
|
|
122
135
|
afterUserCreate?(user: UserData): Promise<void>;
|
|
123
136
|
/**
|
|
124
|
-
* Pre-login validation. Called before credential verification
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
137
|
+
* Pre-login validation. Called before credential verification on every
|
|
138
|
+
* sign-in: password (`login`), OAuth (`oauth`, with the provider's
|
|
139
|
+
* address), and the requests for a magic link (`magic-link`) or an email
|
|
140
|
+
* code (`otp`). Not on a token refresh, which is not a sign-in: to stop a
|
|
141
|
+
* signed-in account, disable it (`PUT /admin/users/:uid { disabled: true }`).
|
|
142
|
+
*
|
|
143
|
+
* Throw to refuse: 400 `HOOK_REJECTED` with your message, or the status
|
|
144
|
+
* your error carries.
|
|
128
145
|
*/
|
|
129
146
|
beforeLogin?(email: string, method: AuthMethod): Promise<void>;
|
|
130
147
|
/**
|
|
@@ -205,6 +222,19 @@ export interface AuthHooks {
|
|
|
205
222
|
* This is fire-and-forget — errors are logged but do not fail the request.
|
|
206
223
|
*/
|
|
207
224
|
afterUserDelete?(uid: string): Promise<void>;
|
|
225
|
+
/**
|
|
226
|
+
* Called when a signed-in user asks to move their account to `newEmail`
|
|
227
|
+
* (`POST /auth/change-email`), before anything is mailed. The address is
|
|
228
|
+
* normalized, free and deliverable by then.
|
|
229
|
+
*
|
|
230
|
+
* The address rule `beforeUserCreate` enforces at sign-up — only your own
|
|
231
|
+
* domain, say — belongs here too, or a member can sign up with an allowed
|
|
232
|
+
* address and then move to any other.
|
|
233
|
+
*
|
|
234
|
+
* Throw to refuse: 400 `HOOK_REJECTED` with your message, or the status
|
|
235
|
+
* your error carries.
|
|
236
|
+
*/
|
|
237
|
+
beforeEmailChange?(user: UserData, newEmail: string): Promise<void>;
|
|
208
238
|
/**
|
|
209
239
|
* Optional hook to customize or override the default user creation flow via the admin panel/REST API.
|
|
210
240
|
* When provided, this replaces the built-in password generation, hashing, and invitation email logic.
|
|
@@ -254,4 +284,17 @@ export type ResolvedAuthHooks = Required<Pick<AuthHooks, "hashPassword" | "verif
|
|
|
254
284
|
* This is the single point where defaults are applied — all consumers
|
|
255
285
|
* call this once and use the resolved hooks throughout.
|
|
256
286
|
*/
|
|
287
|
+
/**
|
|
288
|
+
* What a hook's thrown error answers.
|
|
289
|
+
*
|
|
290
|
+
* The hooks that refuse (`beforeUserCreate`, `beforeLogin`,
|
|
291
|
+
* `beforeUserDelete`, `beforeEmailChange`) are documented as "throw to reject", and a plain
|
|
292
|
+
* `Error` is what people throw. It reached the error handler as a 500
|
|
293
|
+
* "Internal Server Error", so a deployment that limits sign-ups to its own
|
|
294
|
+
* domain answered an outsider with a server fault. A refusal is the caller's
|
|
295
|
+
* answer: 400 `HOOK_REJECTED` with the hook's message — or the status an
|
|
296
|
+
* error carries, when the hook chose one (`ApiError`, or any error with a 4xx
|
|
297
|
+
* `status`/`statusCode`).
|
|
298
|
+
*/
|
|
299
|
+
export declare function hookRefusal(error: unknown, hook: string): ApiError;
|
|
257
300
|
export declare function resolveAuthHooks(hooks?: AuthHooks): ResolvedAuthHooks;
|
|
@@ -70,6 +70,14 @@ export interface BuiltinAuthAdapterConfig {
|
|
|
70
70
|
enableEmailOtp?: boolean;
|
|
71
71
|
/** Opt-in httpOnly cookie mode for refresh tokens. */
|
|
72
72
|
cookieAuth?: import("./routes.js").CookieAuthConfig;
|
|
73
|
+
/** Refuse password sign-in until the address is verified; confirm-first registration. */
|
|
74
|
+
requireEmailVerification?: boolean;
|
|
75
|
+
/** Seconds a rotated-away refresh token still mints a sibling. Default 10. */
|
|
76
|
+
refreshTokenReuseIntervalSeconds?: number;
|
|
77
|
+
/** What a refresh token replayed after that window does to its session. Default `"reject"`. */
|
|
78
|
+
refreshTokenReuse?: import("./routes.js").RefreshTokenReusePolicy;
|
|
79
|
+
/** A magic-link or email-code request for an unknown address creates its account, while registration is open. */
|
|
80
|
+
magicLinkCreatesUsers?: boolean;
|
|
73
81
|
}
|
|
74
82
|
/**
|
|
75
83
|
* Create the built-in Rebase auth adapter.
|
package/dist/auth/captcha.d.ts
CHANGED
|
@@ -39,6 +39,11 @@ export interface CaptchaConfig {
|
|
|
39
39
|
/** Milliseconds before a verification attempt is abandoned. Default 5000. */
|
|
40
40
|
timeoutMs?: number;
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* A protectable auth route. `register` guards both ways an account is made
|
|
44
|
+
* with a password: `POST /auth/register`, and `POST /auth/anonymous/link`,
|
|
45
|
+
* which turns a guest into one.
|
|
46
|
+
*/
|
|
42
47
|
export type CaptchaRoute = "register" | "login" | "forgotPassword" | "magicLink" | "emailOtp";
|
|
43
48
|
export declare const DEFAULT_CAPTCHA_ROUTES: CaptchaRoute[];
|
|
44
49
|
/**
|
|
@@ -22,6 +22,13 @@ export declare function setRefreshCookie(c: Context<HonoEnv>, refreshToken: stri
|
|
|
22
22
|
export declare function clearRefreshCookie(c: Context<HonoEnv>, config: CookieAuthConfig | undefined): void;
|
|
23
23
|
/**
|
|
24
24
|
* Read the refresh token from the request — cookie first, then body fallback.
|
|
25
|
+
*
|
|
26
|
+
* An empty string is no token, wherever it comes from. In cookie mode every
|
|
27
|
+
* response this server sends carries `refreshToken: ""` (see
|
|
28
|
+
* `redactRefreshToken`), and a client that echoes it back — `@rebasepro/client`
|
|
29
|
+
* did, on every refresh from a live tab — is presenting nothing. Taking the
|
|
30
|
+
* body's `""` as the token, or refusing it at the schema, kept the cookie
|
|
31
|
+
* beside it from ever being read.
|
|
25
32
|
*/
|
|
26
33
|
export declare function readRefreshToken(c: Context<HonoEnv>, body: {
|
|
27
34
|
refreshToken?: string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Addresses no mail can reach: the synthetic ones a guest and an X (Twitter)
|
|
3
|
+
* account are given, because `email` is NOT NULL. Nothing is mailed to them,
|
|
4
|
+
* and no account may move onto one.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isDeliverableAddress(email: string): boolean;
|