@rebasepro/server 0.23.0 → 0.24.1-canary.g19596ef
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 +28 -3
- package/bin/rebase-server.js +4 -2
- package/dist/GCSStorageController-DEga1ULx.js +402 -0
- package/dist/GCSStorageController-DEga1ULx.js.map +1 -0
- package/dist/S3StorageController-Btx-6bJ5.js +480 -0
- package/dist/S3StorageController-Btx-6bJ5.js.map +1 -0
- package/dist/api/ast-schema-editor.d.ts +92 -1
- package/dist/api/errors.d.ts +41 -0
- package/dist/api/live-schema-routes.d.ts +64 -10
- package/dist/api/logs-routes.d.ts +39 -1
- package/dist/api/mount-path.d.ts +30 -0
- package/dist/api/mount.d.ts +6 -13
- package/dist/api/openapi-generator.d.ts +17 -0
- package/dist/api/rest/api-generator.d.ts +97 -11
- package/dist/api/rest/auth-collection-writes.d.ts +28 -21
- package/dist/api/rest/field-access-query.d.ts +28 -8
- package/dist/api/rest/field-ops.d.ts +19 -7
- package/dist/api/rest/query-body.d.ts +10 -0
- package/dist/api/rest/write-validation.d.ts +9 -7
- package/dist/api/schema-editor-routes.d.ts +10 -1
- package/dist/api/sql-export-routes.d.ts +8 -0
- package/dist/api/types.d.ts +52 -1
- package/dist/{ast-schema-editor-Mvr50v_S.js → ast-schema-editor-mCeSC03x.js} +310 -12
- package/dist/ast-schema-editor-mCeSC03x.js.map +1 -0
- package/dist/auth/access.d.ts +156 -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-user-ops.d.ts +24 -2
- package/dist/auth/admin-users-route.d.ts +2 -0
- package/dist/auth/api-keys/api-key-middleware.d.ts +64 -56
- package/dist/auth/api-keys/api-key-routes.d.ts +44 -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 +21 -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 +44 -0
- package/dist/auth/expired-token-sweep.d.ts +71 -0
- package/dist/auth/impersonation.d.ts +110 -0
- package/dist/auth/index.d.ts +4 -2
- package/dist/auth/interfaces.d.ts +120 -59
- package/dist/auth/jwks-routes.d.ts +5 -1
- package/dist/auth/jwt-crypto.d.ts +7 -0
- package/dist/auth/jwt-keys.d.ts +4 -0
- package/dist/auth/jwt.d.ts +319 -157
- package/dist/auth/magic-link-routes.d.ts +5 -6
- package/dist/auth/mfa-crypto.d.ts +9 -3
- package/dist/auth/mfa-gate.d.ts +4 -2
- package/dist/auth/mfa-routes.d.ts +8 -9
- package/dist/auth/middleware.d.ts +26 -7
- package/dist/auth/otp-routes.d.ts +5 -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 +99 -31
- package/dist/auth/registration-policy.d.ts +17 -0
- package/dist/auth/require-auth.d.ts +1 -1
- package/dist/auth/routes.d.ts +61 -0
- package/dist/auth/scope-routes.d.ts +22 -0
- package/dist/auth/session-routes.d.ts +17 -6
- package/dist/auth/token-revocation.d.ts +56 -1
- package/dist/auth/user-picking.d.ts +37 -0
- package/dist/auth/verify-credential.d.ts +49 -0
- package/dist/{auth-B-GIMpDG.js → auth-CSBky1mt.js} +4564 -1929
- package/dist/auth-CSBky1mt.js.map +1 -0
- package/dist/auth_adapter-CIItCTc8.js +21 -0
- package/dist/auth_adapter-CIItCTc8.js.map +1 -0
- package/dist/backend/create-backend.d.ts +281 -0
- package/dist/backend/default-backend.d.ts +35 -0
- package/dist/backend/no-collections.d.ts +19 -0
- package/dist/backend/options.d.ts +912 -0
- package/dist/backend/scope.d.ts +23 -0
- package/dist/backend/services.d.ts +54 -0
- package/dist/backend-DTAOsLQc.js.map +1 -1
- package/dist/backup/backup-common.d.ts +24 -3
- package/dist/backup/backup-routes.d.ts +46 -5
- package/dist/backup/backup-schedule.d.ts +33 -0
- package/dist/backup/backup-storage.d.ts +16 -0
- package/dist/backup/index.d.ts +5 -1
- package/dist/backup-DBGRUVTS.js +585 -0
- package/dist/backup-DBGRUVTS.js.map +1 -0
- package/dist/boot/boot.d.ts +2 -2
- package/dist/boot/bundle.d.ts +19 -0
- package/dist/boot/driver.d.ts +3 -0
- package/dist/boot/env.d.ts +58 -6
- package/dist/boot/http-server.d.ts +22 -0
- package/dist/boot/options.d.ts +16 -1
- package/dist/boot/provision.d.ts +4 -2
- package/dist/boot/resource-adapters.d.ts +11 -14
- package/dist/boot/runtime-cors.d.ts +17 -0
- package/dist/boot/security-headers.d.ts +26 -0
- package/dist/boot/sources.d.ts +9 -0
- package/dist/boot/static-routing.d.ts +65 -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-DiFwiLg2.js} +5 -8
- package/dist/contract-routes-DiFwiLg2.js.map +1 -0
- package/dist/cron/cron-loader.d.ts +77 -18
- package/dist/cron/cron-routes.d.ts +1 -1
- package/dist/cron/cron-scheduler.d.ts +94 -23
- package/dist/cron/cron-store.d.ts +37 -5
- package/dist/cron/define-cron.d.ts +6 -2
- package/dist/cron/index.d.ts +3 -3
- package/dist/cron-loader-NHqRfKF0.js +202 -0
- package/dist/cron-loader-NHqRfKF0.js.map +1 -0
- package/dist/{cron-routes-eE8nif_b.js → cron-routes-DsH8Jwn5.js} +34 -8
- package/dist/cron-routes-DsH8Jwn5.js.map +1 -0
- package/dist/{cron-scheduler-B0pLfAix.js → cron-scheduler-D1DT0nw5.js} +349 -65
- package/dist/cron-scheduler-D1DT0nw5.js.map +1 -0
- package/dist/cron-store-DEpVstUU.js +577 -0
- package/dist/cron-store-DEpVstUU.js.map +1 -0
- package/dist/{ddl-bootstrap-C6mo0Kmz.js → ddl-bootstrap-BNqi2NZe.js} +2 -2
- package/dist/{ddl-bootstrap-C6mo0Kmz.js.map → ddl-bootstrap-BNqi2NZe.js.map} +1 -1
- package/dist/email/index.d.ts +2 -2
- package/dist/email/smtp-email-service.d.ts +23 -0
- package/dist/email/templates.d.ts +22 -0
- package/dist/email/types.d.ts +44 -0
- package/dist/env.d.ts +1 -39
- package/dist/{errors-DWsX4yTd.js → errors-Cqp86KnA.js} +263 -10
- package/dist/errors-Cqp86KnA.js.map +1 -0
- package/dist/filter-dialect-O1HK6Nvr.js +1628 -0
- package/dist/filter-dialect-O1HK6Nvr.js.map +1 -0
- package/dist/{function-loader-xnbDAPfa.js → function-loader-DbCSoNJj.js} +69 -39
- package/dist/function-loader-DbCSoNJj.js.map +1 -0
- package/dist/{function-routes-Chet4-lB.js → function-routes-BSTClQNa.js} +28 -15
- package/dist/function-routes-BSTClQNa.js.map +1 -0
- package/dist/functions/context.d.ts +26 -6
- package/dist/functions/define-function.d.ts +10 -6
- package/dist/functions/function-loader.d.ts +75 -18
- package/dist/functions/function-routes.d.ts +2 -1
- package/dist/functions/guards.d.ts +22 -5
- package/dist/functions/index.d.ts +6 -5
- package/dist/functions/index.js +148 -50
- package/dist/functions/index.js.map +1 -1
- package/dist/functions/internal.d.ts +2 -2
- package/dist/functions/proxy.d.ts +6 -2
- package/dist/functions/request-timeout.d.ts +7 -5
- package/dist/history/history-routes.d.ts +9 -0
- package/dist/history/retention-sweep.d.ts +81 -0
- package/dist/{history-recorder-B4MpJfJK.js → history-recorder-RZ1Ow4YY.js} +4 -4
- package/dist/{history-recorder-B4MpJfJK.js.map → history-recorder-RZ1Ow4YY.js.map} +1 -1
- package/dist/{history-store-BhxWOuz9.js → history-store-DPofGVDp.js} +2 -2
- package/dist/{history-store-BhxWOuz9.js.map → history-store-DPofGVDp.js.map} +1 -1
- package/dist/index.d.ts +32 -13
- package/dist/index.es.js +25329 -18393
- package/dist/index.es.js.map +1 -1
- package/dist/init/docs.d.ts +35 -10
- package/dist/init/health.d.ts +17 -2
- package/dist/init/process-safety.d.ts +7 -2
- package/dist/init/shutdown.d.ts +26 -1
- package/dist/init.d.ts +49 -811
- package/dist/internal-tables-L2t39RdT.js +136 -0
- package/dist/internal-tables-L2t39RdT.js.map +1 -0
- package/dist/jobs/job-queue.d.ts +8 -0
- package/dist/jobs/job-store.d.ts +24 -0
- package/dist/jobs/types.d.ts +25 -4
- package/dist/{jobs-CazMYhyy.js → jobs-8G-_agld.js} +235 -25
- package/dist/jobs-8G-_agld.js.map +1 -0
- package/dist/{jwt-DnQHNFCl.js → jwt-CBaFc34Y.js} +546 -447
- package/dist/jwt-CBaFc34Y.js.map +1 -0
- package/dist/{keys-CogCQpxG.js → keys-COzQ2aE8.js} +3 -17
- package/dist/{keys-CogCQpxG.js.map → keys-COzQ2aE8.js.map} +1 -1
- package/dist/load-failure-L5hvfDoG.js +80 -0
- package/dist/load-failure-L5hvfDoG.js.map +1 -0
- package/dist/{logger-DO2PZc4i.js → logger-Bt32sHFw.js} +41 -5
- package/dist/logger-Bt32sHFw.js.map +1 -0
- package/dist/{logs-routes-Bj4TYYUl.js → logs-routes-DQJhrf-b.js} +48 -8
- package/dist/logs-routes-DQJhrf-b.js.map +1 -0
- package/dist/mcp/consent-page.d.ts +1 -1
- package/dist/mcp/mcp-routes.d.ts +13 -0
- package/dist/mcp/mcp-tools.d.ts +23 -9
- package/dist/mcp/oauth-metadata.d.ts +21 -16
- package/dist/mcp/oauth-routes.d.ts +14 -1
- package/dist/metrics/index.d.ts +18 -0
- package/dist/metrics/pool-wait.d.ts +50 -0
- package/dist/offline-QNQgsPOq-B2yGeXkn.js +3305 -0
- package/dist/offline-QNQgsPOq-B2yGeXkn.js.map +1 -0
- package/dist/offline-store-Cm5sAX81-CALiO4Gn.js +1915 -0
- package/dist/offline-store-Cm5sAX81-CALiO4Gn.js.map +1 -0
- package/dist/{openapi-generator-O_O24MAT.js → openapi-generator-CeGW2dLC.js} +455 -80
- package/dist/openapi-generator-CeGW2dLC.js.map +1 -0
- package/dist/policy-PdzX-Opd.js +121 -0
- package/dist/policy-PdzX-Opd.js.map +1 -0
- package/dist/project_manifest-R6y_KHms.js +59 -0
- package/dist/project_manifest-R6y_KHms.js.map +1 -0
- package/dist/{proxy-Czngl3p9.js → proxy-BEKbsil6.js} +5 -4
- package/dist/proxy-BEKbsil6.js.map +1 -0
- package/dist/{query-parser-DGRVFNM3.js → query-parser-ECxP0nDw.js} +497 -52
- package/dist/query-parser-ECxP0nDw.js.map +1 -0
- package/dist/{request-timeout-C_4C2BeR.js → request-timeout-BiRelY-U.js} +17 -10
- package/dist/request-timeout-BiRelY-U.js.map +1 -0
- package/dist/resumable-upload-threshold-CKv-Ry0l.js +24 -0
- package/dist/resumable-upload-threshold-CKv-Ry0l.js.map +1 -0
- package/dist/schema-edit/apply-schema-change.d.ts +73 -3
- package/dist/schema-edit/local-source.d.ts +29 -0
- package/dist/schema-edit/project-root.d.ts +3 -2
- package/dist/schema-edit/remote-source.d.ts +20 -5
- package/dist/schema-edit/source-editor.d.ts +28 -0
- package/dist/schema-editor-routes-DRXBAbZy.js +136 -0
- package/dist/schema-editor-routes-DRXBAbZy.js.map +1 -0
- package/dist/serve-spa.d.ts +58 -0
- package/dist/services/outbound-post.d.ts +50 -0
- package/dist/services/outbound-url-guard.d.ts +34 -5
- package/dist/services/routed-realtime-service.d.ts +16 -0
- package/dist/services/webhook-service.d.ts +66 -14
- package/dist/singleton.d.ts +9 -5
- package/dist/soft-delete-params-CBryZXMq.js +668 -0
- package/dist/soft-delete-params-CBryZXMq.js.map +1 -0
- package/dist/sql-export-routes-D8ZaRvoR.js +240 -0
- package/dist/sql-export-routes-D8ZaRvoR.js.map +1 -0
- package/dist/{src-vkcwKXbT.js → src-D5Y3N-S4.js} +440 -20
- package/dist/src-D5Y3N-S4.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +48 -2
- package/dist/storage/LocalStorageController.d.ts +69 -2
- package/dist/storage/S3StorageController.d.ts +39 -3
- package/dist/storage/image-transform.d.ts +15 -0
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/maintenance-routes.d.ts +23 -0
- package/dist/storage/maintenance.d.ts +126 -0
- package/dist/storage/property-limits.d.ts +41 -6
- package/dist/storage/rendition-cache.d.ts +9 -11
- package/dist/storage/request-keys.d.ts +15 -0
- package/dist/storage/request-principals.d.ts +30 -0
- package/dist/storage/requested-object.d.ts +74 -0
- package/dist/storage/routes.d.ts +52 -14
- package/dist/storage/source-variables.d.ts +10 -0
- package/dist/storage/streams.d.ts +42 -0
- package/dist/storage/tus-handler.d.ts +76 -7
- package/dist/storage/types.d.ts +101 -7
- package/dist/storage/upload-validation.d.ts +13 -0
- package/dist/storage-resumable-VO-cMpse-C_YX71pO.js +125 -0
- package/dist/storage-resumable-VO-cMpse-C_YX71pO.js.map +1 -0
- package/dist/strings.es-BeZWAodn.js +256 -0
- package/dist/strings.es-BeZWAodn.js.map +1 -0
- package/dist/upload-validation-CVobI5xO.js +172 -0
- package/dist/upload-validation-CVobI5xO.js.map +1 -0
- package/dist/utils/byte-size.d.ts +10 -0
- package/dist/utils/compression.d.ts +6 -0
- package/dist/utils/load-failure.d.ts +14 -0
- package/dist/utils/logger.d.ts +21 -0
- package/package.json +5 -5
- package/dist/GCSStorageController-CjrA4PMo.js +0 -242
- package/dist/GCSStorageController-CjrA4PMo.js.map +0 -1
- package/dist/S3StorageController-B6pKDNVj.js +0 -264
- 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 +0 -118
- 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 +0 -305
- package/dist/cron-store-TcoGz-xS.js.map +0 -1
- package/dist/dynamic-import-X40pTZUQ.js +0 -23
- package/dist/dynamic-import-X40pTZUQ.js.map +0 -1
- package/dist/errors-DWsX4yTd.js.map +0 -1
- package/dist/function-loader-xnbDAPfa.js.map +0 -1
- package/dist/function-routes-Chet4-lB.js.map +0 -1
- package/dist/jobs-CazMYhyy.js.map +0 -1
- package/dist/jwt-DnQHNFCl.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/proxy-Czngl3p9.js.map +0 -1
- package/dist/query-parser-DGRVFNM3.js.map +0 -1
- package/dist/request-timeout-C_4C2BeR.js.map +0 -1
- package/dist/schema-editor-routes-C5-lh_jO.js +0 -87
- package/dist/schema-editor-routes-C5-lh_jO.js.map +0 -1
- package/dist/src-pmvW7BFx.js +0 -6688
- package/dist/src-pmvW7BFx.js.map +0 -1
- package/dist/src-vkcwKXbT.js.map +0 -1
- package/dist/types-BfKcm9do.js +0 -39
- package/dist/types-BfKcm9do.js.map +0 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A signed-in user changes their own email address.
|
|
3
|
+
*
|
|
4
|
+
* `POST /auth/change-email { newEmail }` records the change and mails a link to
|
|
5
|
+
* the new address, and a notice without a link to the old one.
|
|
6
|
+
* `POST /auth/confirm-email-change { token }` — the link's landing screen calls
|
|
7
|
+
* it — moves the account onto the new address.
|
|
8
|
+
*
|
|
9
|
+
* The address moves only when its inbox answers, so the account is never
|
|
10
|
+
* pointed at an address nobody proved, and nothing is reserved meanwhile: a
|
|
11
|
+
* pending change that held the address would let any account keep a stranger
|
|
12
|
+
* from signing up with their own address. Whoever has the address when the link
|
|
13
|
+
* is followed keeps it, and the link answers 409.
|
|
14
|
+
*
|
|
15
|
+
* On confirmation the address is the account's, verified. The identities whose
|
|
16
|
+
* provider vouched for the old address are detached: the old address may be
|
|
17
|
+
* the reason for the change — a job left, an inbox lost — and whoever controls
|
|
18
|
+
* it now would otherwise sign in through that provider. The sessions stay: the
|
|
19
|
+
* change was asked for from one, and the link proves the new inbox, not
|
|
20
|
+
* anything about the others.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
import { Hono } from "hono";
|
|
25
|
+
import { z } from "zod";
|
|
26
|
+
import type { HonoEnv } from "../api/types.js";
|
|
27
|
+
import type { MiddlewareHandler } from "hono";
|
|
28
|
+
import type { resolveAuthHooks } from "./auth-hooks.js";
|
|
29
|
+
import type { AuthLimiters } from "./rate-limiter.js";
|
|
30
|
+
import type { AuthModuleConfig } from "./routes.js";
|
|
31
|
+
/** How long an address-change link stays usable. */
|
|
32
|
+
export declare const EMAIL_CHANGE_TTL_MS: number;
|
|
33
|
+
interface EmailChangeRoutesConfig {
|
|
34
|
+
router: Hono<HonoEnv>;
|
|
35
|
+
config: AuthModuleConfig;
|
|
36
|
+
ops: ReturnType<typeof resolveAuthHooks>;
|
|
37
|
+
parseBody: <T>(schema: z.ZodSchema<T>, body: unknown) => T;
|
|
38
|
+
/** A signed-in user, with the revocation watermark consulted. */
|
|
39
|
+
requireLiveSession: MiddlewareHandler<HonoEnv>;
|
|
40
|
+
/** The backend's auth limiters. */
|
|
41
|
+
limiters: AuthLimiters;
|
|
42
|
+
}
|
|
43
|
+
export declare function mountEmailChangeRoutes(opts: EmailChangeRoutesConfig): void;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deleting the auth tokens nobody can use any more, on a schedule.
|
|
3
|
+
*
|
|
4
|
+
* `TokenRepository.deleteExpiredTokens` was declared, implemented and never
|
|
5
|
+
* called. Every expired reset link, magic link, email code, abandoned refresh
|
|
6
|
+
* token and stale MFA challenge stayed in the database for good: the tables
|
|
7
|
+
* grew by a row per sign-in attempt and nothing took one away.
|
|
8
|
+
*
|
|
9
|
+
* Runs on the process that owns the timers (`ownership.cronScheduler`), once
|
|
10
|
+
* an hour, and across instances once per hour in all: each tick claims its
|
|
11
|
+
* hour's slot in `rebase.cron_claims` — the claim the cron scheduler takes for
|
|
12
|
+
* a job's run — and only the instance that wins it sweeps. A store that cannot
|
|
13
|
+
* answer the claim means no sweep this hour rather than every instance at
|
|
14
|
+
* once; the tokens are already refused when presented, so a late sweep costs
|
|
15
|
+
* space, not safety.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import type { DataDriver } from "@rebasepro/types";
|
|
20
|
+
import type { TokenRepository } from "./interfaces.js";
|
|
21
|
+
/** The slot key in `rebase.cron_claims`. Namespaced so no job file can take it. */
|
|
22
|
+
export declare const EXPIRED_TOKEN_SWEEP_JOB_ID = "rebase:auth:expired-tokens";
|
|
23
|
+
/** One sweep an hour, fleet-wide. */
|
|
24
|
+
export declare const EXPIRED_TOKEN_SWEEP_INTERVAL_MS: number;
|
|
25
|
+
export interface ExpiredTokenSweepOptions {
|
|
26
|
+
authRepo: Pick<TokenRepository, "deleteExpiredTokens">;
|
|
27
|
+
/**
|
|
28
|
+
* Claim `slot` for `jobId` across every instance: `true` for the one that
|
|
29
|
+
* wins it. Throws when the store cannot tell. Without it — a driver with
|
|
30
|
+
* no SQL, cron persistence switched off — every instance sweeps, which is
|
|
31
|
+
* wasted work but harmless: deleting what has expired is idempotent.
|
|
32
|
+
*/
|
|
33
|
+
claimSlot?: (jobId: string, slot: string) => Promise<boolean>;
|
|
34
|
+
intervalMs?: number;
|
|
35
|
+
/** Clock, for tests. */
|
|
36
|
+
now?: () => number;
|
|
37
|
+
}
|
|
38
|
+
/** What one tick did. */
|
|
39
|
+
export type ExpiredTokenSweepOutcome = "swept" | "claimed-elsewhere" | "unclaimed" | "failed";
|
|
40
|
+
export interface ExpiredTokenSweep {
|
|
41
|
+
/** Sweep now, then once per interval. */
|
|
42
|
+
start(): void;
|
|
43
|
+
/**
|
|
44
|
+
* No more ticks. Resolves once a tick already running has finished, so a
|
|
45
|
+
* caller about to close the pool can wait for the tick still using it.
|
|
46
|
+
*/
|
|
47
|
+
stop(): Promise<void>;
|
|
48
|
+
/** One tick: claim this interval's slot and, if it is ours, sweep. */
|
|
49
|
+
runSlot(): Promise<ExpiredTokenSweepOutcome>;
|
|
50
|
+
}
|
|
51
|
+
export declare function createExpiredTokenSweep(options: ExpiredTokenSweepOptions): ExpiredTokenSweep;
|
|
52
|
+
/** What {@link startExpiredTokenSweep} is given by the boot. */
|
|
53
|
+
export interface ExpiredTokenSweepWiring {
|
|
54
|
+
/** The auth repository, when the backend has one. */
|
|
55
|
+
authRepo?: Partial<Pick<TokenRepository, "deleteExpiredTokens">>;
|
|
56
|
+
/** The default data driver: its SQL admin backs the slot claim. */
|
|
57
|
+
driver: DataDriver;
|
|
58
|
+
/** Whether this process owns the timers (`ownership.cronScheduler`). */
|
|
59
|
+
ownsTimers: boolean;
|
|
60
|
+
/** `cronPersistence: false` keeps the claims table out of the database, here too. */
|
|
61
|
+
persistClaims?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Start the sweep where it belongs, or answer `undefined` where it does not:
|
|
65
|
+
* a process that does not own the timers, or a backend whose auth repository
|
|
66
|
+
* cannot delete expired tokens.
|
|
67
|
+
*
|
|
68
|
+
* The claim is the cron store's, made on the first tick rather than at boot so
|
|
69
|
+
* the boot does not wait on its tables.
|
|
70
|
+
*/
|
|
71
|
+
export declare function startExpiredTokenSweep(wiring: ExpiredTokenSweepWiring): ExpiredTokenSweep | undefined;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running a request as another user: the `x-rebase-impersonate` header, and
|
|
3
|
+
* the `impersonate` field of the realtime socket's `AUTHENTICATE`.
|
|
4
|
+
*
|
|
5
|
+
* It exists for an administrator checking what one user can see and do — "can
|
|
6
|
+
* B read A's rows, can B write them?" — against the real policies, from
|
|
7
|
+
* Studio's API explorer and JS editor. A granted request runs as one of B's
|
|
8
|
+
* own would: B's uid, B's roles as they are now, B's guest flag and the claims
|
|
9
|
+
* a token minted for B now would carry reach `withAuth`, so the statement runs
|
|
10
|
+
* as `rebase_user` with B's identity and every policy is evaluated for B.
|
|
11
|
+
*
|
|
12
|
+
* Who may: a signed-in administrator, on their own session, judged on their
|
|
13
|
+
* roles as the database has them now rather than as their token claims. Never
|
|
14
|
+
* an API key — not a personal key whose owner is an administrator either — and
|
|
15
|
+
* never the service key. Impersonation is a person checking on another person,
|
|
16
|
+
* and the audit line has to name one.
|
|
17
|
+
*
|
|
18
|
+
* Where: the data API, custom functions and the realtime socket. Every other
|
|
19
|
+
* route refuses the header ({@link refuseUnhonouredImpersonation}).
|
|
20
|
+
*
|
|
21
|
+
* Fail closed: a request asking to act as someone either runs as the user it
|
|
22
|
+
* names or is refused. It never runs as its caller, because that is the
|
|
23
|
+
* failure this exists to end — a response showing the administrator's rows
|
|
24
|
+
* under a label saying they were B's.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import type { Context, MiddlewareHandler } from "hono";
|
|
29
|
+
import { type AuthenticatedUser, type DataDriver } from "@rebasepro/types";
|
|
30
|
+
import type { HonoEnv } from "../api/types.js";
|
|
31
|
+
import { ApiError } from "../api/errors.js";
|
|
32
|
+
/** How the caller authenticated, as far as impersonation is concerned. */
|
|
33
|
+
export type ImpersonationCredential = "session" | "api-key" | "service-key" | "none";
|
|
34
|
+
/** How the auth in use turns a uid into a request identity — `AuthAdapter.resolveUser`. */
|
|
35
|
+
export type ImpersonationUserResolver = (uid: string) => Promise<AuthenticatedUser | null>;
|
|
36
|
+
export interface ImpersonationRequest {
|
|
37
|
+
/** The uid the request asked to act as, as sent. */
|
|
38
|
+
requestedUid: string;
|
|
39
|
+
/** What the caller presented. Only a `session` may impersonate. */
|
|
40
|
+
credential: ImpersonationCredential;
|
|
41
|
+
/** The caller, as their credential verified. */
|
|
42
|
+
caller?: {
|
|
43
|
+
uid: string;
|
|
44
|
+
roles: readonly string[];
|
|
45
|
+
};
|
|
46
|
+
/** Absent when the auth in use has no `resolveUser`: then nobody may. */
|
|
47
|
+
resolveUser?: ImpersonationUserResolver;
|
|
48
|
+
/**
|
|
49
|
+
* Where the request came in, for the security audit line — or absent to
|
|
50
|
+
* write none, for a decision that only re-confirms one already logged
|
|
51
|
+
* (the socket re-asks before every frame).
|
|
52
|
+
*/
|
|
53
|
+
audit?: Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
/** The answer to a request to act as another user. */
|
|
56
|
+
export type ImpersonationDecision = {
|
|
57
|
+
granted: AuthenticatedUser;
|
|
58
|
+
impersonator: string;
|
|
59
|
+
} | {
|
|
60
|
+
refused: ApiError;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Decide whether this caller may act as the user they named, and who that
|
|
64
|
+
* user is now.
|
|
65
|
+
*
|
|
66
|
+
* The one decision both doors take — the HTTP data plane through
|
|
67
|
+
* {@link applyImpersonation}, the realtime socket at `AUTHENTICATE` and before
|
|
68
|
+
* every frame after it. The caller is judged before the named user is looked
|
|
69
|
+
* up, so the answer to a caller who may not impersonate says nothing about
|
|
70
|
+
* whether the uid exists.
|
|
71
|
+
*
|
|
72
|
+
* Throws when a lookup fails: that is not an answer about anyone, and each
|
|
73
|
+
* door refuses it its own way.
|
|
74
|
+
*/
|
|
75
|
+
export declare function decideImpersonation(request: ImpersonationRequest): Promise<ImpersonationDecision>;
|
|
76
|
+
export interface ImpersonationOptions {
|
|
77
|
+
/** What the caller presented. Only a `session` may impersonate. */
|
|
78
|
+
credential: ImpersonationCredential;
|
|
79
|
+
/** The unscoped delegate the request's driver was scoped from. */
|
|
80
|
+
driver: DataDriver;
|
|
81
|
+
/** The auth adapter's `resolveUser`. Absent: the header is refused for everyone. */
|
|
82
|
+
resolveUser?: ImpersonationUserResolver;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Honour or refuse this request's {@link IMPERSONATE_HEADER}.
|
|
86
|
+
*
|
|
87
|
+
* Called by the data-plane auth middlewares once the caller is on the context
|
|
88
|
+
* and its driver is scoped. When the header names a user the caller may act
|
|
89
|
+
* as, the context's `user` and `driver` are replaced with that user's and
|
|
90
|
+
* `impersonator` names the caller.
|
|
91
|
+
*
|
|
92
|
+
* @returns The refusal to send, or `undefined` to carry on — as the caller
|
|
93
|
+
* when the header is absent, as the named user when it was granted.
|
|
94
|
+
*/
|
|
95
|
+
export declare function applyImpersonation(c: Context<HonoEnv>, { credential, driver, resolveUser }: ImpersonationOptions): Promise<Response | undefined>;
|
|
96
|
+
/**
|
|
97
|
+
* Refuse {@link IMPERSONATE_HEADER} on every route that does not honour it.
|
|
98
|
+
*
|
|
99
|
+
* Only the data API and custom functions run a request as another user; the
|
|
100
|
+
* rest — storage, auth, the admin surfaces — authenticate the caller and
|
|
101
|
+
* ignore the header. Ignored, a request asking to act as B would be answered
|
|
102
|
+
* as the administrator who sent it, which is the one outcome impersonation
|
|
103
|
+
* must never have. Mounted ahead of every route, so a surface added later is
|
|
104
|
+
* refused until it is listed here as honouring the header.
|
|
105
|
+
*
|
|
106
|
+
* @param honouredMounts - The mount points whose auth middleware applies the
|
|
107
|
+
* header (`/api/data`, `/api/functions`). A path is under one when it
|
|
108
|
+
* is the mount itself or continues it with a `/`.
|
|
109
|
+
*/
|
|
110
|
+
export declare function refuseUnhonouredImpersonation(honouredMounts: readonly string[]): MiddlewareHandler<HonoEnv>;
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -49,8 +49,10 @@ export { createSqlRateLimitStore } from "./sql-rate-limit-store.js";
|
|
|
49
49
|
export type { SqlRateLimitStoreOptions } from "./sql-rate-limit-store.js";
|
|
50
50
|
export { resolveRateLimitStoreKind, RateLimitStoreConfigurationError } from "./resolve-rate-limit-store.js";
|
|
51
51
|
export type { RateLimitStoreKind, RateLimitStoreEnv } from "./resolve-rate-limit-store.js";
|
|
52
|
-
export { createApiKeyStore, createApiKeyRoutes, isApiKeyToken, validateApiKey, httpMethodToOperation
|
|
53
|
-
export type { ApiKey,
|
|
52
|
+
export { createApiKeyStore, createApiKeyRoutes, createPersonalKeyRoutes, isApiKeyToken, resolveApiKey, validateApiKey, httpMethodToOperation } from "./api-keys/index.js";
|
|
53
|
+
export type { ApiKey, ApiKeyKind, ApiKeyMasked, ApiKeyWithSecret, CreateApiKeyRequest, CreatePersonalKeyRequest, UpdateApiKeyRequest, ApiKeyStore, DataOperation, KeyTargets } from "./api-keys/index.js";
|
|
54
|
+
export { requireScope, requireScopeByMethod, hasScope, callerScopes, getAccessModel, configureAccess, accessModelFromCollections, AccessModelError } from "./access.js";
|
|
55
|
+
export type { KeyOwnerResolver } from "./access.js";
|
|
54
56
|
export { createBuiltinAuthAdapter } from "./builtin-auth-adapter.js";
|
|
55
57
|
export type { BuiltinAuthAdapterConfig } from "./builtin-auth-adapter.js";
|
|
56
58
|
export { createCustomAuthAdapter } from "./custom-auth-adapter.js";
|
|
@@ -19,6 +19,11 @@ export interface UserData {
|
|
|
19
19
|
emailVerificationToken?: string | null;
|
|
20
20
|
emailVerificationSentAt?: Date | null;
|
|
21
21
|
isAnonymous?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* An administrator switched the account off: no sign-in, no refresh, and
|
|
24
|
+
* no token it already holds is honoured. See `setUserDisabled`.
|
|
25
|
+
*/
|
|
26
|
+
disabled?: boolean;
|
|
22
27
|
metadata?: Record<string, unknown>;
|
|
23
28
|
createdAt: Date;
|
|
24
29
|
updatedAt: Date;
|
|
@@ -35,6 +40,16 @@ export interface CreateUserData {
|
|
|
35
40
|
isAnonymous?: boolean;
|
|
36
41
|
metadata?: Record<string, unknown>;
|
|
37
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* An address change waiting for its confirmation link. See
|
|
45
|
+
* {@link UserRepository.setPendingEmailChange}.
|
|
46
|
+
*/
|
|
47
|
+
export interface PendingEmailChange {
|
|
48
|
+
/** The address the account is moving to, normalized. */
|
|
49
|
+
email: string;
|
|
50
|
+
/** When the link was mailed; it is refused 24 hours after this. */
|
|
51
|
+
sentAt: Date;
|
|
52
|
+
}
|
|
38
53
|
/**
|
|
39
54
|
* User Identity Data (OAuth accounts linked to user)
|
|
40
55
|
*/
|
|
@@ -92,36 +107,6 @@ export interface OAuthProvider<T = unknown> {
|
|
|
92
107
|
*/
|
|
93
108
|
verify(payload: T): Promise<OAuthProviderProfile | null>;
|
|
94
109
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Role data structure
|
|
97
|
-
*/
|
|
98
|
-
export interface RoleData {
|
|
99
|
-
id: string;
|
|
100
|
-
name: string;
|
|
101
|
-
isAdmin: boolean;
|
|
102
|
-
defaultPermissions: {
|
|
103
|
-
read?: boolean;
|
|
104
|
-
create?: boolean;
|
|
105
|
-
edit?: boolean;
|
|
106
|
-
delete?: boolean;
|
|
107
|
-
} | null;
|
|
108
|
-
collectionPermissions: Record<string, {
|
|
109
|
-
read?: boolean;
|
|
110
|
-
create?: boolean;
|
|
111
|
-
edit?: boolean;
|
|
112
|
-
delete?: boolean;
|
|
113
|
-
}> | null;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Data for creating a new role
|
|
117
|
-
*/
|
|
118
|
-
export interface CreateRoleData {
|
|
119
|
-
id: string;
|
|
120
|
-
name: string;
|
|
121
|
-
isAdmin?: boolean;
|
|
122
|
-
defaultPermissions?: RoleData["defaultPermissions"];
|
|
123
|
-
collectionPermissions?: RoleData["collectionPermissions"];
|
|
124
|
-
}
|
|
125
110
|
/**
|
|
126
111
|
* Refresh token info
|
|
127
112
|
*/
|
|
@@ -161,6 +146,31 @@ export interface RefreshTokenInfo {
|
|
|
161
146
|
* that do not store it; both read as `aal1`, the restrictive value.
|
|
162
147
|
*/
|
|
163
148
|
aal?: "aal1" | "aal2";
|
|
149
|
+
/**
|
|
150
|
+
* How the session was signed in — `"password"`, `"anonymous"`,
|
|
151
|
+
* `"magic-link"`, `"otp"`, `"mfa"` or a provider id such as `"google"`.
|
|
152
|
+
* See {@link RefreshTokenSession.method}. Absent on rows written before
|
|
153
|
+
* the column existed, which read as `"password"`.
|
|
154
|
+
*/
|
|
155
|
+
method?: string;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* What {@link TokenRepository.getAccountAccessState} reads about an account
|
|
159
|
+
* that exists.
|
|
160
|
+
*/
|
|
161
|
+
export interface AccountAccessState {
|
|
162
|
+
/** The account's roles as the database has them now. */
|
|
163
|
+
roles: string[];
|
|
164
|
+
/** The revocation watermark: sessions that began before it are void. */
|
|
165
|
+
tokensValidAfter: Date | null;
|
|
166
|
+
/**
|
|
167
|
+
* Whether the session asked about has a refresh token that is not
|
|
168
|
+
* revoked. `undefined` when no session was asked about, or the store
|
|
169
|
+
* cannot tell (a refresh-token table without session grouping).
|
|
170
|
+
*/
|
|
171
|
+
sessionActive?: boolean;
|
|
172
|
+
/** The account is switched off (`UserData.disabled`). */
|
|
173
|
+
disabled?: boolean;
|
|
164
174
|
}
|
|
165
175
|
/**
|
|
166
176
|
* Identity of the sign-in a refresh token belongs to, threaded through
|
|
@@ -179,6 +189,14 @@ export interface RefreshTokenSession {
|
|
|
179
189
|
* assurance level is a property of the *sign-in*, not of the account.
|
|
180
190
|
*/
|
|
181
191
|
aal?: "aal1" | "aal2";
|
|
192
|
+
/**
|
|
193
|
+
* How the session was signed in: what `providerId` says in every auth
|
|
194
|
+
* response for it. Written at sign-in and carried across rotations like
|
|
195
|
+
* {@link aal}, because a refresh is not a sign-in and has nothing else to
|
|
196
|
+
* read it from — answering `"password"` there turned a Google session into
|
|
197
|
+
* a password one an access-token lifetime after it began.
|
|
198
|
+
*/
|
|
199
|
+
method?: string;
|
|
182
200
|
/**
|
|
183
201
|
* The hash of the token this one replaces, when it is minted by rotating
|
|
184
202
|
* one. A repository that honours it writes the new token only while that
|
|
@@ -216,7 +234,11 @@ export interface ListUsersOptions {
|
|
|
216
234
|
limit?: number;
|
|
217
235
|
/** Number of results to skip (default 0) */
|
|
218
236
|
offset?: number;
|
|
219
|
-
/**
|
|
237
|
+
/**
|
|
238
|
+
* Search term — a case-insensitive substring of the email, the display
|
|
239
|
+
* name, any role the user holds, or the uid. (The Postgres store matches
|
|
240
|
+
* all four; the Mongo store, email and display name.)
|
|
241
|
+
*/
|
|
220
242
|
search?: string;
|
|
221
243
|
/** Field to sort by (default "createdAt") */
|
|
222
244
|
orderBy?: string;
|
|
@@ -302,6 +324,16 @@ export interface UserRepository {
|
|
|
302
324
|
* List users with server-side pagination, search, and sorting.
|
|
303
325
|
*/
|
|
304
326
|
listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult>;
|
|
327
|
+
/**
|
|
328
|
+
* The ids of at most `limit` users, in no particular order. Answers "is
|
|
329
|
+
* there any user?" (`limit` 1) and "is this the only one?" (`limit` 2).
|
|
330
|
+
*
|
|
331
|
+
* `listUsersPaginated` answered those by counting the whole table and
|
|
332
|
+
* sorting it, on every anonymous `GET /auth/config` and every sign-up.
|
|
333
|
+
* Optional: a repository without it is asked through `listUsersPaginated`
|
|
334
|
+
* instead (`isUserTableEmpty` / `isOnlyUser` in `registration-policy.ts`).
|
|
335
|
+
*/
|
|
336
|
+
firstUserIds?(limit: number): Promise<string[]>;
|
|
305
337
|
/**
|
|
306
338
|
* Update user's password hash. `null` removes the password: the account
|
|
307
339
|
* then signs in only by link, code or provider.
|
|
@@ -319,10 +351,6 @@ export interface UserRepository {
|
|
|
319
351
|
* Find user by email verification token
|
|
320
352
|
*/
|
|
321
353
|
getUserByVerificationToken(token: string): Promise<UserData | null>;
|
|
322
|
-
/**
|
|
323
|
-
* Get roles for a user
|
|
324
|
-
*/
|
|
325
|
-
getUserRoles(uid: string): Promise<RoleData[]>;
|
|
326
354
|
/**
|
|
327
355
|
* Get role IDs for a user
|
|
328
356
|
*/
|
|
@@ -340,34 +368,45 @@ export interface UserRepository {
|
|
|
340
368
|
*/
|
|
341
369
|
getUserWithRoles(uid: string): Promise<{
|
|
342
370
|
user: UserData;
|
|
343
|
-
roles:
|
|
371
|
+
roles: string[];
|
|
344
372
|
} | null>;
|
|
345
|
-
}
|
|
346
|
-
/**
|
|
347
|
-
* Abstract role repository interface.
|
|
348
|
-
* Handles all role-related database operations.
|
|
349
|
-
*/
|
|
350
|
-
export interface RoleRepository {
|
|
351
|
-
/**
|
|
352
|
-
* Get a role by ID
|
|
353
|
-
*/
|
|
354
|
-
getRoleById(id: string): Promise<RoleData | null>;
|
|
355
373
|
/**
|
|
356
|
-
*
|
|
374
|
+
* Switch an account off, or back on. Off, it cannot sign in or refresh,
|
|
375
|
+
* and the tokens it holds are refused (`judgeAccessToken`). Optional: a
|
|
376
|
+
* repository without it cannot disable accounts, and the admin route says
|
|
377
|
+
* so rather than pretending.
|
|
357
378
|
*/
|
|
358
|
-
|
|
379
|
+
setUserDisabled?(uid: string, disabled: boolean): Promise<void>;
|
|
359
380
|
/**
|
|
360
|
-
*
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
*
|
|
365
|
-
|
|
366
|
-
|
|
381
|
+
* Record the address an account is moving to and the hash of the token
|
|
382
|
+
* mailed there, stamped now — or with `null`, drop it. One per account: a
|
|
383
|
+
* new request replaces the last, and its link stops working.
|
|
384
|
+
*
|
|
385
|
+
* Nothing is reserved by it. The address stays free for anyone to
|
|
386
|
+
* register until the link is followed, because a pending change that held
|
|
387
|
+
* it would let any account lock a stranger out of signing up with their
|
|
388
|
+
* own address.
|
|
389
|
+
*/
|
|
390
|
+
setPendingEmailChange?(uid: string, change: {
|
|
391
|
+
email: string;
|
|
392
|
+
tokenHash: string;
|
|
393
|
+
} | null): Promise<void>;
|
|
394
|
+
/** The account's pending change, if it has one. */
|
|
395
|
+
getPendingEmailChange?(uid: string): Promise<PendingEmailChange | null>;
|
|
396
|
+
/** The account whose pending change `tokenHash` confirms, with the change. */
|
|
397
|
+
findPendingEmailChange?(tokenHash: string): Promise<{
|
|
398
|
+
user: UserData;
|
|
399
|
+
change: PendingEmailChange;
|
|
400
|
+
} | null>;
|
|
367
401
|
/**
|
|
368
|
-
*
|
|
402
|
+
* Move the account onto its pending address, in one write that holds only
|
|
403
|
+
* while `tokenHash` is still that change's token: the address becomes the
|
|
404
|
+
* account's, verified, and the pending change and any outstanding
|
|
405
|
+
* verification token are cleared. `null` when the token no longer names
|
|
406
|
+
* the change (confirmed already, replaced, cancelled). An address another
|
|
407
|
+
* account holds by then is a 409 `EMAIL_EXISTS`, as `updateUser` answers.
|
|
369
408
|
*/
|
|
370
|
-
|
|
409
|
+
applyPendingEmailChange?(uid: string, tokenHash: string): Promise<UserData | null>;
|
|
371
410
|
}
|
|
372
411
|
/**
|
|
373
412
|
* Abstract token repository interface.
|
|
@@ -418,6 +457,24 @@ export interface TokenRepository {
|
|
|
418
457
|
* user's tokens so a rotation racing the delete cannot survive it.
|
|
419
458
|
*/
|
|
420
459
|
setTokensValidAfter?(uid: string, at: Date): Promise<void>;
|
|
460
|
+
/**
|
|
461
|
+
* Everything an access token is judged against, in one read — or `null`
|
|
462
|
+
* when there is no such account.
|
|
463
|
+
*
|
|
464
|
+
* An access token is a bearer credential minted up to an hour ago, and the
|
|
465
|
+
* account it names may since have been deleted, revoked or demoted. Every
|
|
466
|
+
* door that honours one asks this before it does: the data plane, the admin
|
|
467
|
+
* gates, the realtime socket on every frame. See `judgeAccessToken`.
|
|
468
|
+
*
|
|
469
|
+
* `sessionId` is the token's `sid`: when given, the state also says
|
|
470
|
+
* whether that sign-in is still live, which is how signing one device out
|
|
471
|
+
* reaches the access token that device holds.
|
|
472
|
+
*
|
|
473
|
+
* Optional. Without it the judge composes the answer from
|
|
474
|
+
* `getUserWithRoles` and `getTokensValidAfter`, two reads instead of one,
|
|
475
|
+
* and cannot see one revoked session — only every session at once.
|
|
476
|
+
*/
|
|
477
|
+
getAccountAccessState?(uid: string, sessionId?: string): Promise<AccountAccessState | null>;
|
|
421
478
|
/**
|
|
422
479
|
* Find a refresh token by hash
|
|
423
480
|
*/
|
|
@@ -455,7 +512,11 @@ export interface TokenRepository {
|
|
|
455
512
|
*/
|
|
456
513
|
deleteAllPasswordResetTokensForUser(uid: string): Promise<void>;
|
|
457
514
|
/**
|
|
458
|
-
*
|
|
515
|
+
* Delete every token past its expiry: reset links, magic links and email
|
|
516
|
+
* codes, refresh tokens, and whatever else the repository keeps that
|
|
517
|
+
* expires. A token is refused when presented whether or not this has run,
|
|
518
|
+
* so it is housekeeping, not revocation. The server calls it once an hour
|
|
519
|
+
* on one instance of the fleet (`startExpiredTokenSweep`).
|
|
459
520
|
*/
|
|
460
521
|
deleteExpiredTokens(): Promise<void>;
|
|
461
522
|
/**
|
|
@@ -613,5 +674,5 @@ export interface MfaRepository {
|
|
|
613
674
|
/**
|
|
614
675
|
* Combined auth repository interface for convenience
|
|
615
676
|
*/
|
|
616
|
-
export interface AuthRepository extends UserRepository,
|
|
677
|
+
export interface AuthRepository extends UserRepository, TokenRepository, MfaRepository {
|
|
617
678
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import type { HonoEnv } from "../api/types.js";
|
|
3
|
+
import { type JwtService } from "./jwt.js";
|
|
3
4
|
/**
|
|
4
5
|
* `GET /.well-known/jwks.json` — the public keys that verify this issuer's
|
|
5
6
|
* access tokens.
|
|
@@ -13,5 +14,8 @@ import type { HonoEnv } from "../api/types.js";
|
|
|
13
14
|
* where every verifier looks — an issuer of `https://api.example.com` implies
|
|
14
15
|
* `https://api.example.com/.well-known/jwks.json`, whatever the API happens to
|
|
15
16
|
* be prefixed with.
|
|
17
|
+
*
|
|
18
|
+
* `jwt` is the issuer whose keys these are — a backend's own
|
|
19
|
+
* (`backend.services.jwt`), or the process default when omitted.
|
|
16
20
|
*/
|
|
17
|
-
export declare function createJwksRoutes(): Hono<HonoEnv>;
|
|
21
|
+
export declare function createJwksRoutes(jwt?: JwtService): Hono<HonoEnv>;
|
|
@@ -74,6 +74,13 @@ export declare function signJwt(payload: JwtClaims, key: JwtSigningKey, options:
|
|
|
74
74
|
export declare function verifyJwt(token: string, key: JwtVerificationKey, options: {
|
|
75
75
|
algorithms: string[];
|
|
76
76
|
}): Promise<JwtClaims>;
|
|
77
|
+
/**
|
|
78
|
+
* Whether a {@link verifyJwt} rejection says only that the token's `exp` has
|
|
79
|
+
* passed — the routine refusal that sends a client to refresh — rather than
|
|
80
|
+
* that the token is not one we signed. Here because the library decides how
|
|
81
|
+
* that is spelled.
|
|
82
|
+
*/
|
|
83
|
+
export declare function isExpiredJwtError(error: unknown): boolean;
|
|
77
84
|
/**
|
|
78
85
|
* The token's unverified header — `kid` and `alg` — or an empty object.
|
|
79
86
|
*
|
package/dist/auth/jwt-keys.d.ts
CHANGED
|
@@ -106,3 +106,7 @@ export declare function normalizePemFromEnv(value: string): string;
|
|
|
106
106
|
export declare function toJwks(keys: ResolvedJwtKey[]): {
|
|
107
107
|
keys: PublicJwk[];
|
|
108
108
|
};
|
|
109
|
+
/** A verification key, as `jsonwebtoken.verify` would import it, once per string. */
|
|
110
|
+
export declare function importedVerificationKey<K>(key: K): K | KeyObject;
|
|
111
|
+
/** A signing key, as `jsonwebtoken.sign` would import it, once per string. */
|
|
112
|
+
export declare function importedSigningKey<K>(key: K): K | KeyObject;
|