@rebasepro/server 0.23.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-CjrA4PMo.js → GCSStorageController-BSiP1c-f.js} +22 -8
- package/dist/GCSStorageController-BSiP1c-f.js.map +1 -0
- package/dist/{S3StorageController-B6pKDNVj.js → S3StorageController-CAwFRgjV.js} +19 -7
- package/dist/S3StorageController-CAwFRgjV.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +92 -1
- package/dist/api/errors.d.ts +9 -0
- package/dist/api/live-schema-routes.d.ts +38 -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 +44 -10
- package/dist/api/rest/write-validation.d.ts +2 -2
- package/dist/api/types.d.ts +17 -1
- package/dist/{ast-schema-editor-Mvr50v_S.js → ast-schema-editor-CWqS_sLJ.js} +309 -11
- 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 +16 -1
- package/dist/auth/admin-roles-route.d.ts +4 -2
- package/dist/auth/admin-roles.d.ts +17 -20
- 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 +46 -7
- package/dist/auth/builtin-auth-adapter.d.ts +8 -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 +110 -59
- package/dist/auth/jwt.d.ts +49 -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/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 +41 -0
- package/dist/auth/routes.d.ts +45 -0
- package/dist/auth/scope-routes.d.ts +22 -0
- package/dist/auth/session-routes.d.ts +11 -6
- package/dist/auth/token-revocation.d.ts +50 -1
- package/dist/auth/verify-credential.d.ts +28 -0
- package/dist/{auth-B-GIMpDG.js → auth-DMLngxn_.js} +2159 -569
- package/dist/auth-DMLngxn_.js.map +1 -0
- package/dist/backend-DTAOsLQc.js.map +1 -1
- package/dist/backup/backup-common.d.ts +10 -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/env.d.ts +49 -4
- package/dist/boot/security-headers.d.ts +26 -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-CbFjuBwa.js → contract-routes-fz8i4pxs.js} +17 -4
- package/dist/contract-routes-fz8i4pxs.js.map +1 -0
- package/dist/cron/cron-scheduler.d.ts +25 -20
- package/dist/cron/cron-store.d.ts +6 -2
- package/dist/{cron-loader-DfTj2Hbi.js → cron-loader-CwaANlOG.js} +4 -4
- package/dist/cron-loader-CwaANlOG.js.map +1 -0
- package/dist/{cron-routes-eE8nif_b.js → cron-routes-Bc-SB0Se.js} +10 -7
- package/dist/cron-routes-Bc-SB0Se.js.map +1 -0
- package/dist/{cron-scheduler-B0pLfAix.js → cron-scheduler-CYQgco86.js} +52 -34
- package/dist/cron-scheduler-CYQgco86.js.map +1 -0
- package/dist/{cron-store-TcoGz-xS.js → cron-store-D2Q9-Aco.js} +10 -15
- package/dist/cron-store-D2Q9-Aco.js.map +1 -0
- package/dist/{ddl-bootstrap-C6mo0Kmz.js → ddl-bootstrap-BaqMSa4Y.js} +2 -2
- package/dist/{ddl-bootstrap-C6mo0Kmz.js.map → ddl-bootstrap-BaqMSa4Y.js.map} +1 -1
- package/dist/email/index.d.ts +2 -2
- package/dist/email/templates.d.ts +22 -0
- package/dist/email/types.d.ts +26 -0
- package/dist/env.d.ts +1 -2
- 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-B4MpJfJK.js → history-recorder-Nr8zLvoU.js} +4 -4
- package/dist/{history-recorder-B4MpJfJK.js.map → history-recorder-Nr8zLvoU.js.map} +1 -1
- package/dist/{history-store-BhxWOuz9.js → history-store-rcAm_xFR.js} +2 -2
- package/dist/{history-store-BhxWOuz9.js.map → history-store-rcAm_xFR.js.map} +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.es.js +3084 -753
- package/dist/index.es.js.map +1 -1
- package/dist/init/health.d.ts +17 -2
- package/dist/init/shutdown.d.ts +10 -0
- package/dist/init.d.ts +54 -0
- package/dist/{jobs-CazMYhyy.js → jobs-DqYNfquG.js} +5 -5
- package/dist/{jobs-CazMYhyy.js.map → jobs-DqYNfquG.js.map} +1 -1
- package/dist/{jwt-DnQHNFCl.js → jwt-R6bSPMjk.js} +39 -15
- package/dist/{jwt-DnQHNFCl.js.map → jwt-R6bSPMjk.js.map} +1 -1
- package/dist/{keys-CogCQpxG.js → keys-GAVZqbqx.js} +3 -17
- package/dist/{keys-CogCQpxG.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-Bj4TYYUl.js → logs-routes-DAdv37GI.js} +48 -8
- 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 +7 -0
- package/dist/mcp/mcp-tools.d.ts +15 -9
- package/dist/mcp/oauth-metadata.d.ts +21 -16
- package/dist/mcp/oauth-routes.d.ts +7 -1
- package/dist/{openapi-generator-O_O24MAT.js → openapi-generator-DAq_XVDu.js} +104 -13
- 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-DGRVFNM3.js → query-parser-BgiKJKvc.js} +6 -56
- 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/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-C5-lh_jO.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-vkcwKXbT.js → src-CatHFUym.js} +439 -20
- package/dist/src-CatHFUym.js.map +1 -0
- package/dist/{src-pmvW7BFx.js → src-I3aG1PcY.js} +252 -70
- package/dist/src-I3aG1PcY.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +2 -0
- 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/property-limits.d.ts +41 -6
- package/dist/storage/request-keys.d.ts +15 -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 +19 -0
- package/dist/types-BfKcm9do.js.map +1 -1
- package/dist/utils/logger.d.ts +12 -0
- package/package.json +5 -5
- package/dist/GCSStorageController-CjrA4PMo.js.map +0 -1
- package/dist/S3StorageController-B6pKDNVj.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-Mvr50v_S.js.map +0 -1
- package/dist/auth/api-keys/api-key-permission-guard.d.ts +0 -65
- package/dist/auth-B-GIMpDG.js.map +0 -1
- package/dist/backup-D7YR94N3.js +0 -253
- package/dist/backup-D7YR94N3.js.map +0 -1
- package/dist/contract-routes-CbFjuBwa.js.map +0 -1
- package/dist/cron-loader-DfTj2Hbi.js.map +0 -1
- package/dist/cron-routes-eE8nif_b.js.map +0 -1
- package/dist/cron-scheduler-B0pLfAix.js.map +0 -1
- package/dist/cron-store-TcoGz-xS.js.map +0 -1
- package/dist/errors-DWsX4yTd.js.map +0 -1
- package/dist/function-routes-Chet4-lB.js.map +0 -1
- package/dist/logger-DO2PZc4i.js.map +0 -1
- package/dist/logs-routes-Bj4TYYUl.js.map +0 -1
- package/dist/openapi-generator-O_O24MAT.js.map +0 -1
- package/dist/query-parser-DGRVFNM3.js.map +0 -1
- package/dist/schema-editor-routes-C5-lh_jO.js.map +0 -1
- package/dist/src-pmvW7BFx.js.map +0 -1
- package/dist/src-vkcwKXbT.js.map +0 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform tokens: a short-lived credential the platform hosting this server
|
|
3
|
+
* mints for one person, for one project, for a few read-only scopes.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this exists
|
|
6
|
+
*
|
|
7
|
+
* On Rebase Cloud the owner of a project is signed in to the *control plane*,
|
|
8
|
+
* not to their own app. Their app's admin surfaces — the cron job list and its
|
|
9
|
+
* run history first — accept an admin user of the app, an `rk_` key, or the
|
|
10
|
+
* service key, and an owner may hold none of them: a fresh deploy has no admin
|
|
11
|
+
* account, and the service key is admin on the whole API, which is the wrong
|
|
12
|
+
* thing to hand someone who wants to read why last night's job failed.
|
|
13
|
+
*
|
|
14
|
+
* So the control plane signs a token instead. It checks the caller's project
|
|
15
|
+
* membership, then signs `{ aud: <project>, sub: <who>, scope: "cron:read" }`
|
|
16
|
+
* with a key only it holds, valid for minutes. This server verifies it against
|
|
17
|
+
* the public half — `REBASE_PLATFORM_TOKEN_KEY` — which the platform sets at
|
|
18
|
+
* deploy. Nothing secret lives in the tenant: the env holds a public key, and
|
|
19
|
+
* leaking it lets nobody mint anything.
|
|
20
|
+
*
|
|
21
|
+
* ## What a platform token can never do
|
|
22
|
+
*
|
|
23
|
+
* - **Exceed {@link PLATFORM_TOKEN_SCOPES}.** The token names scopes; this
|
|
24
|
+
* server grants only the ones on that list, whatever the platform signed. The
|
|
25
|
+
* ceiling lives here, in the tenant's code, so a compromised or buggy control
|
|
26
|
+
* plane cannot widen it.
|
|
27
|
+
* - **Outlive {@link PLATFORM_TOKEN_MAX_LIFETIME_SECONDS}.** A token whose
|
|
28
|
+
* `exp - iat` is longer is refused, not clamped: a long-lived platform token
|
|
29
|
+
* is a static secret by another name.
|
|
30
|
+
* - **Reach another project.** One platform key signs for every tenant, so the
|
|
31
|
+
* audience is what binds a token to this one: `aud` must equal
|
|
32
|
+
* `REBASE_PLATFORM_TOKEN_AUDIENCE`.
|
|
33
|
+
* - **Act as a person of this app.** The caller is `platform:<sub>` with no
|
|
34
|
+
* roles: row-level security sees nobody it knows, and every surface that
|
|
35
|
+
* checks a scope other than the granted ones refuses it.
|
|
36
|
+
*
|
|
37
|
+
* ## Wire format
|
|
38
|
+
*
|
|
39
|
+
* `rpt_` followed by a compact ES256 JWS. The prefix keeps the credential
|
|
40
|
+
* classes apart the way `rk_` does: a platform token is never tried as a user
|
|
41
|
+
* session, and a user session is never tried as a platform token.
|
|
42
|
+
*
|
|
43
|
+
* @module
|
|
44
|
+
*/
|
|
45
|
+
import type { MiddlewareHandler } from "hono";
|
|
46
|
+
import type { AdminScope } from "@rebasepro/types";
|
|
47
|
+
import type { HonoEnv } from "../api/types.js";
|
|
48
|
+
/** Marks a bearer token as a platform token. */
|
|
49
|
+
export declare const PLATFORM_TOKEN_PREFIX = "rpt_";
|
|
50
|
+
/** The `iss` every platform token carries. */
|
|
51
|
+
export declare const PLATFORM_TOKEN_ISSUER = "rebase-cloud";
|
|
52
|
+
/**
|
|
53
|
+
* Everything a platform token can be granted on this server.
|
|
54
|
+
*
|
|
55
|
+
* Read-only on purpose. Adding a scope here is a decision about what the
|
|
56
|
+
* platform may do inside a customer's app on a person's behalf, and belongs in
|
|
57
|
+
* its own change.
|
|
58
|
+
*/
|
|
59
|
+
export declare const PLATFORM_TOKEN_SCOPES: readonly AdminScope[];
|
|
60
|
+
/** The longest `exp - iat` a platform token may declare. */
|
|
61
|
+
export declare const PLATFORM_TOKEN_MAX_LIFETIME_SECONDS = 600;
|
|
62
|
+
/** The environment variables a platform sets to turn platform tokens on. */
|
|
63
|
+
export declare const PLATFORM_TOKEN_KEY_ENV = "REBASE_PLATFORM_TOKEN_KEY";
|
|
64
|
+
export declare const PLATFORM_TOKEN_AUDIENCE_ENV = "REBASE_PLATFORM_TOKEN_AUDIENCE";
|
|
65
|
+
/** What this server verifies platform tokens against. */
|
|
66
|
+
export interface PlatformTokenConfig {
|
|
67
|
+
/**
|
|
68
|
+
* PEM-encoded SPKI public keys, EC P-256. More than one during a key
|
|
69
|
+
* rotation: a token verifies against any of them.
|
|
70
|
+
*/
|
|
71
|
+
publicKeys: string[];
|
|
72
|
+
/** This project, as the platform names it in `aud`. */
|
|
73
|
+
audience: string;
|
|
74
|
+
}
|
|
75
|
+
/** A verified platform token, as the request will act. */
|
|
76
|
+
export interface PlatformCaller {
|
|
77
|
+
/** Who the platform minted it for — a control-plane account. */
|
|
78
|
+
subject: string;
|
|
79
|
+
/** The granted scopes: what the token asked for, within the ceiling. */
|
|
80
|
+
scopes: string[];
|
|
81
|
+
/** `jti`, when the platform set one — what an audit line quotes. */
|
|
82
|
+
tokenId?: string;
|
|
83
|
+
}
|
|
84
|
+
/** Why a presented platform token does not authenticate. */
|
|
85
|
+
export interface PlatformTokenRefusal {
|
|
86
|
+
refusal: string;
|
|
87
|
+
}
|
|
88
|
+
export declare function isPlatformToken(token: string): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* The PEM blocks in an env value: real newlines, `\n`-escaped ones (what a
|
|
91
|
+
* one-line `.env` holds), or the whole thing base64-encoded.
|
|
92
|
+
*/
|
|
93
|
+
export declare function parsePublicKeys(value: string): string[];
|
|
94
|
+
/**
|
|
95
|
+
* Read the platform-token configuration from the environment.
|
|
96
|
+
*
|
|
97
|
+
* `undefined` when platform tokens are off — neither variable set, which is
|
|
98
|
+
* every self-hosted server — or misconfigured. A misconfiguration is logged and
|
|
99
|
+
* leaves them off rather than failing the boot: the platform set these values,
|
|
100
|
+
* the app's owner cannot fix them, and the app serving traffic matters more
|
|
101
|
+
* than its cron history being readable from the CLI.
|
|
102
|
+
*/
|
|
103
|
+
export declare function platformTokensFromEnv(env: Record<string, string | undefined>): Promise<PlatformTokenConfig | undefined>;
|
|
104
|
+
/**
|
|
105
|
+
* Verify a presented platform token: signature, issuer, audience, lifetime, and
|
|
106
|
+
* the scopes it may be granted here.
|
|
107
|
+
*/
|
|
108
|
+
export declare function verifyPlatformToken(token: string, config: PlatformTokenConfig, nowSeconds?: number): Promise<PlatformCaller | PlatformTokenRefusal>;
|
|
109
|
+
/**
|
|
110
|
+
* Authenticate an `rpt_` bearer token ahead of an admin gate.
|
|
111
|
+
*
|
|
112
|
+
* Mounted where the `rk_` pre-auth is, and shaped like it: a request it does not
|
|
113
|
+
* recognise passes through untouched, a recognised one either becomes a caller
|
|
114
|
+
* with narrowed `scopes` or is refused here. The gate's scope check then decides
|
|
115
|
+
* — so a token holding `cron:read` reads cron, and is a 403 on every other
|
|
116
|
+
* surface.
|
|
117
|
+
*
|
|
118
|
+
* With `config` undefined, platform tokens are off on this server; an `rpt_`
|
|
119
|
+
* token is still recognised, so its holder learns that rather than the generic
|
|
120
|
+
* "invalid token" a JWT parser would answer.
|
|
121
|
+
*/
|
|
122
|
+
export declare function createPlatformTokenPreAuth(config: PlatformTokenConfig | undefined): MiddlewareHandler<HonoEnv>;
|
|
@@ -83,6 +83,41 @@ export declare function setSharedRateLimitStore(store: RateLimitStore | undefine
|
|
|
83
83
|
* Uses a sliding window: only hits within the last `windowMs` are counted.
|
|
84
84
|
*/
|
|
85
85
|
export declare function createRateLimiter(options?: RateLimiterOptions): MiddlewareHandler<HonoEnv>;
|
|
86
|
+
/**
|
|
87
|
+
* Default key generator: the client's address, from the most trustworthy source
|
|
88
|
+
* this deployment has.
|
|
89
|
+
*
|
|
90
|
+
* `X-Forwarded-For` is a client-writable header; only the entries appended by
|
|
91
|
+
* trusted reverse proxies can be believed. With `trustedProxyHops` proxies in
|
|
92
|
+
* front, each appends the address it saw, so the real client IP is the
|
|
93
|
+
* `trustedProxyHops`-th entry from the right — everything further left is
|
|
94
|
+
* client-supplied and must be ignored. This is what prevents a caller from
|
|
95
|
+
* spoofing `X-Forwarded-For` to spread its requests across many rate-limit keys.
|
|
96
|
+
*
|
|
97
|
+
* `X-Real-IP` is the *same* kind of header and needs the same rule, which it
|
|
98
|
+
* did not have: it was read unconditionally, including under
|
|
99
|
+
* `trustedProxyHops === 0` — the mode whose entire meaning is "no proxy is in
|
|
100
|
+
* front of me". With no proxy there, nothing writes `X-Real-IP` except the
|
|
101
|
+
* caller, so the key was theirs to choose: one header per request bought an
|
|
102
|
+
* unlimited number of buckets, and the limiters on login, registration and
|
|
103
|
+
* password reset counted to one. The reasoning had been done carefully for one
|
|
104
|
+
* spelling of a proxy header and not carried to its twin.
|
|
105
|
+
*
|
|
106
|
+
* So `X-Real-IP` is now believed only where a proxy is declared to exist. With
|
|
107
|
+
* none, the connection's own address is used — unforgeable, and available
|
|
108
|
+
* because the server runs on `@hono/node-server`. `"unknown"` is the last
|
|
109
|
+
* resort only, and it is a single shared bucket by design: better that
|
|
110
|
+
* anonymous callers throttle each other than that any of them throttles nobody.
|
|
111
|
+
*/
|
|
112
|
+
/**
|
|
113
|
+
* Where a request comes from, as the rate limiters judge it: the socket's own
|
|
114
|
+
* address, or — only behind as many proxies as `TRUSTED_PROXY_HOPS` declares —
|
|
115
|
+
* the address they report. What a session row records as its IP, so the
|
|
116
|
+
* sessions list shows the same address the limiter counted, rather than the
|
|
117
|
+
* raw `X-Forwarded-For` (whose leftmost entry the caller chooses) or
|
|
118
|
+
* `"unknown"` for every direct connection.
|
|
119
|
+
*/
|
|
120
|
+
export declare function requestClientAddress(c: Parameters<MiddlewareHandler<HonoEnv>>[0]): string;
|
|
86
121
|
/**
|
|
87
122
|
* Pre-configured rate limiter for general auth endpoints (login, register).
|
|
88
123
|
* 200 requests per 15 minutes per IP.
|
|
@@ -209,6 +244,12 @@ export interface DataRateLimitConfig {
|
|
|
209
244
|
anonymousFunctions?: number | null;
|
|
210
245
|
/** Share counts across replicas. Defaults to this process's memory. */
|
|
211
246
|
store?: RateLimitStore;
|
|
247
|
+
/**
|
|
248
|
+
* The deployment's service key, recognised as a Bearer before any auth
|
|
249
|
+
* middleware has run — the storage router's limiter runs ahead of its
|
|
250
|
+
* routes' auth — and never limited. See {@link dataRateLimitBuckets}.
|
|
251
|
+
*/
|
|
252
|
+
serviceKey?: string;
|
|
212
253
|
}
|
|
213
254
|
/** @see DataRateLimitConfig.anonymousFunctions */
|
|
214
255
|
export declare const DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT = 3000;
|
package/dist/auth/routes.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { AuthHooks } from "./auth-hooks.js";
|
|
|
4
4
|
import { EmailService, EmailConfig } from "../email/index.js";
|
|
5
5
|
import { HonoEnv } from "../api/types.js";
|
|
6
6
|
import { type CaptchaConfig } from "./captcha.js";
|
|
7
|
+
import { isDeliverableAddress } from "./deliverable-address.js";
|
|
8
|
+
export { isDeliverableAddress };
|
|
7
9
|
/**
|
|
8
10
|
* Shared configuration for auth and admin route factories.
|
|
9
11
|
*/
|
|
@@ -86,7 +88,30 @@ export interface AuthModuleConfig {
|
|
|
86
88
|
* how long a captured token stays useful to someone who copied it.
|
|
87
89
|
*/
|
|
88
90
|
refreshTokenReuseIntervalSeconds?: number;
|
|
91
|
+
/**
|
|
92
|
+
* Let a magic-link or email-code request for an address with no account
|
|
93
|
+
* create one (no password, unverified until the link or code is used),
|
|
94
|
+
* while registration is open. Off by default. See `passwordless-signup.ts`.
|
|
95
|
+
*/
|
|
96
|
+
magicLinkCreatesUsers?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* What a refresh token presented after its reuse window does to its
|
|
99
|
+
* session. See `RebaseAuthConfig.refreshTokenReuse`. Default `"reject"`.
|
|
100
|
+
*/
|
|
101
|
+
refreshTokenReuse?: RefreshTokenReusePolicy;
|
|
102
|
+
/**
|
|
103
|
+
* Refuse password sign-in until the account's address is verified, and
|
|
104
|
+
* register confirm-first: `POST /auth/register` answers the same "check
|
|
105
|
+
* your inbox" whether or not the address already has an account, and
|
|
106
|
+
* signs nobody in. Off by default. Needs email; the boot refuses it
|
|
107
|
+
* without. See `RebaseAuthConfig.requireEmailVerification`.
|
|
108
|
+
*/
|
|
109
|
+
requireEmailVerification?: boolean;
|
|
89
110
|
}
|
|
111
|
+
/** What a refresh token replayed after its reuse window does to its session. */
|
|
112
|
+
export type RefreshTokenReusePolicy = "reject" | "revoke-session";
|
|
113
|
+
/** How long an email-verification link stays usable. */
|
|
114
|
+
export declare const EMAIL_VERIFICATION_TTL_MS: number;
|
|
90
115
|
/**
|
|
91
116
|
* Configuration for httpOnly refresh-token cookies.
|
|
92
117
|
*/
|
|
@@ -109,4 +134,24 @@ export interface CookieAuthConfig {
|
|
|
109
134
|
*/
|
|
110
135
|
secure?: boolean;
|
|
111
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* What {@link CreateSessionAndTokens} is told about the sign-in it opens.
|
|
139
|
+
*
|
|
140
|
+
* `method` is what `providerId` says for the session from now on — stored on
|
|
141
|
+
* its refresh token and carried across every rotation — so each door names
|
|
142
|
+
* its own rather than leaving it to a default.
|
|
143
|
+
*/
|
|
144
|
+
export interface SessionOptions {
|
|
145
|
+
/** `"password"`, `"anonymous"`, `"magic-link"`, `"otp"`, `"mfa"` or a provider id. */
|
|
146
|
+
method: string;
|
|
147
|
+
/** Only for the route that has just seen the second factor. */
|
|
148
|
+
skipMfaGate?: boolean;
|
|
149
|
+
aal?: "aal1" | "aal2";
|
|
150
|
+
}
|
|
151
|
+
/** Mint a session, as every sign-in door does. See `createSessionAndTokens`. */
|
|
152
|
+
export type CreateSessionAndTokens = (uid: string, userAgent: string, ipAddress: string, options: SessionOptions) => Promise<{
|
|
153
|
+
roleIds: string[];
|
|
154
|
+
accessToken: string;
|
|
155
|
+
refreshToken: string;
|
|
156
|
+
}>;
|
|
112
157
|
export declare function createAuthRoutes(config: AuthModuleConfig): Hono<HonoEnv>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `GET /auth/scopes` — every scope this backend knows, described, and the
|
|
3
|
+
* ones the caller holds.
|
|
4
|
+
*
|
|
5
|
+
* What a screen needs to offer scopes for a key or a role: the built-in ones,
|
|
6
|
+
* the app's own `auth.scopes`, their wording and what their targets name. Any
|
|
7
|
+
* authenticated caller may read it; it describes the backend's vocabulary,
|
|
8
|
+
* not anybody's data.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { Hono, type MiddlewareHandler } from "hono";
|
|
13
|
+
import type { AccessJudgeRepository } from "./token-revocation.js";
|
|
14
|
+
import type { HonoEnv } from "../api/types.js";
|
|
15
|
+
export interface ScopeRouteOptions {
|
|
16
|
+
serviceKey?: string;
|
|
17
|
+
resolveRoles?: (uid: string) => Promise<string[]>;
|
|
18
|
+
revocationRepo?: AccessJudgeRepository;
|
|
19
|
+
/** Authenticates `rk_` keys first, so a key can read what it holds. */
|
|
20
|
+
apiKeyPreAuth?: MiddlewareHandler<HonoEnv>;
|
|
21
|
+
}
|
|
22
|
+
export declare function createScopeRoutes(options: ScopeRouteOptions): Hono<HonoEnv>;
|
|
@@ -2,7 +2,7 @@ import { Hono } from "hono";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { HonoEnv } from "../api/types.js";
|
|
4
4
|
import type { MiddlewareHandler } from "hono";
|
|
5
|
-
import type { AuthModuleConfig } from "./routes.js";
|
|
5
|
+
import type { AuthModuleConfig, CreateSessionAndTokens } from "./routes.js";
|
|
6
6
|
import type { AuthResponsePayload, TransformAuthResponseContext } from "@rebasepro/types";
|
|
7
7
|
import type { resolveAuthHooks } from "./auth-hooks.js";
|
|
8
8
|
interface SessionRoutesConfig {
|
|
@@ -28,11 +28,7 @@ interface SessionRoutesConfig {
|
|
|
28
28
|
isAnonymous?: boolean;
|
|
29
29
|
metadata?: Record<string, unknown> | null;
|
|
30
30
|
}, roleIds: string[], accessToken: string, refreshToken: string, providerId: string) => unknown;
|
|
31
|
-
createSessionAndTokens:
|
|
32
|
-
roleIds: string[];
|
|
33
|
-
accessToken: string;
|
|
34
|
-
refreshToken: string;
|
|
35
|
-
}>;
|
|
31
|
+
createSessionAndTokens: CreateSessionAndTokens;
|
|
36
32
|
applyTransformHook: (response: AuthResponsePayload, method: TransformAuthResponseContext["method"], request: Request, uid: string) => Promise<AuthResponsePayload>;
|
|
37
33
|
/**
|
|
38
34
|
* The `register` captcha, built by the caller so a misconfiguration fails
|
|
@@ -41,6 +37,15 @@ interface SessionRoutesConfig {
|
|
|
41
37
|
* `/register`. Absent when captcha is off or `register` is not protected.
|
|
42
38
|
*/
|
|
43
39
|
registerCaptcha?: MiddlewareHandler<HonoEnv>;
|
|
40
|
+
/**
|
|
41
|
+
* Mail the account a verification link, in the background. Upgrading a
|
|
42
|
+
* guest is registration, and registration starts the address proof.
|
|
43
|
+
*/
|
|
44
|
+
sendVerificationMail?: (user: {
|
|
45
|
+
id: string;
|
|
46
|
+
email: string;
|
|
47
|
+
displayName?: string | null;
|
|
48
|
+
}) => void;
|
|
44
49
|
}
|
|
45
50
|
export declare function mountSessionRoutes(opts: SessionRoutesConfig): void;
|
|
46
51
|
export {};
|
|
@@ -37,6 +37,55 @@ import type { AccessTokenPayload } from "./jwt.js";
|
|
|
37
37
|
* failure is logged at warn so it is visible rather than silent.
|
|
38
38
|
*/
|
|
39
39
|
export declare function isAccessTokenRevoked(authRepo: Pick<AuthRepository, "getTokensValidAfter">, payload: Pick<AccessTokenPayload, "uid" | "iat">): Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* The repository reads {@link judgeAccessToken} may make. All optional: a
|
|
42
|
+
* repository answers what it can, and the judge says what it could not ask.
|
|
43
|
+
*/
|
|
44
|
+
export type AccessJudgeRepository = Partial<Pick<AuthRepository, "getAccountAccessState" | "getUserWithRoles" | "getTokensValidAfter">>;
|
|
45
|
+
/**
|
|
46
|
+
* Why an access token that verifies is not honoured.
|
|
47
|
+
*
|
|
48
|
+
* - `revoked`: issued before the account's revocation watermark — a sign-out
|
|
49
|
+
* everywhere, a password change or reset.
|
|
50
|
+
* - `session-revoked`: its own sign-in was ended — `POST /auth/logout`, or
|
|
51
|
+
* `DELETE /auth/sessions/:id` from another device. Needs the token's `sid`.
|
|
52
|
+
* - `account-deleted`: the account it names no longer exists.
|
|
53
|
+
* - `account-disabled`: an administrator switched the account off.
|
|
54
|
+
*/
|
|
55
|
+
export type AccessTokenRefusal = "revoked" | "session-revoked" | "account-deleted" | "account-disabled";
|
|
56
|
+
export type AccessTokenVerdict = {
|
|
57
|
+
live: true;
|
|
58
|
+
/**
|
|
59
|
+
* The account's roles now, or `undefined` when the repository could
|
|
60
|
+
* not say (it reads neither the account nor its roles).
|
|
61
|
+
*/
|
|
62
|
+
roles?: string[];
|
|
63
|
+
} | {
|
|
64
|
+
live: false;
|
|
65
|
+
refusal: AccessTokenRefusal;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Is the account behind this verified access token still the one that may use
|
|
69
|
+
* it — and with which roles?
|
|
70
|
+
*
|
|
71
|
+
* A verified signature says who the token was minted for, an hour ago at
|
|
72
|
+
* most. This asks the database what has happened to that account since. Every
|
|
73
|
+
* door that honours an access token — the data plane, the admin gates, the
|
|
74
|
+
* realtime socket — asks it here, so they cannot disagree about it.
|
|
75
|
+
*
|
|
76
|
+
* They did. A deleted account read as "not revoked": the watermark lives on
|
|
77
|
+
* the user row, so once the row was gone there was no watermark, and the
|
|
78
|
+
* roles lookup answered `[]` rather than "nobody". A token its owner had
|
|
79
|
+
* revoked came back to life when an administrator deleted the account, as an
|
|
80
|
+
* authenticated principal with that uid, for the rest of its lifetime. MCP
|
|
81
|
+
* refresh and personal API keys already treated a missing account as revoked;
|
|
82
|
+
* the main door did not.
|
|
83
|
+
*
|
|
84
|
+
* Throws when the repository does. Unlike the watermark read on its own, this
|
|
85
|
+
* one decides the roles a request runs with, so a failure is a refusal (the
|
|
86
|
+
* callers answer 503) rather than a guess.
|
|
87
|
+
*/
|
|
88
|
+
export declare function judgeAccessToken(authRepo: AccessJudgeRepository, payload: Pick<AccessTokenPayload, "uid" | "iat" | "sid">): Promise<AccessTokenVerdict>;
|
|
40
89
|
/**
|
|
41
90
|
* End every session this user holds, on every device.
|
|
42
91
|
*
|
|
@@ -72,4 +121,4 @@ export declare function revokeAllSessions(authRepo: Pick<AuthRepository, "delete
|
|
|
72
121
|
* `null` removes the password instead, for the same reason and with the same
|
|
73
122
|
* revocation: see `confirmAddressOwnership`.
|
|
74
123
|
*/
|
|
75
|
-
export declare function replaceUserPassword(authRepo: Pick<AuthRepository, "updatePassword" | "deleteAllRefreshTokensForUser" | "setTokensValidAfter"
|
|
124
|
+
export declare function replaceUserPassword(authRepo: Pick<AuthRepository, "updatePassword" | "deleteAllRefreshTokensForUser" | "setTokensValidAfter"> & Partial<Pick<AuthRepository, "deleteAllPasswordResetTokensForUser">>, uid: string, passwordHash: string | null): Promise<void>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verify a bearer credential outside the HTTP middlewares — for a custom
|
|
3
|
+
* socket, a tunnel, anything an app authenticates from a frame or a header it
|
|
4
|
+
* read itself.
|
|
5
|
+
*
|
|
6
|
+
* One answer for the two kinds of credential a person can hold: a session
|
|
7
|
+
* token, and an API key (`rk_`). Both come back as who the caller acts as and
|
|
8
|
+
* what it may do, so the endpoint checks a scope the same way either way.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import type { ApiKeyStore } from "./api-keys/api-key-store.js";
|
|
13
|
+
/** Who a verified credential acts as, and what it may do. */
|
|
14
|
+
export interface VerifiedCredential {
|
|
15
|
+
uid: string;
|
|
16
|
+
roles: string[];
|
|
17
|
+
scopes: string[];
|
|
18
|
+
/** `"api-key"` when an `rk_` key was presented, otherwise `"session"`. */
|
|
19
|
+
kind: "session" | "api-key";
|
|
20
|
+
}
|
|
21
|
+
/** Install the key store `verifyCredential` checks `rk_` keys against. Called once at boot. */
|
|
22
|
+
export declare function configureCredentialStore(store: ApiKeyStore | undefined): void;
|
|
23
|
+
/**
|
|
24
|
+
* The identity and scopes behind a bearer credential, or null when it does
|
|
25
|
+
* not verify — expired, revoked, unknown, or a key on a backend with no key
|
|
26
|
+
* store.
|
|
27
|
+
*/
|
|
28
|
+
export declare function verifyCredential(token: string): Promise<VerifiedCredential | null>;
|