@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
package/dist/auth/jwt.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Context } from "hono";
|
|
2
|
+
import type { HonoEnv } from "../api/types.js";
|
|
1
3
|
import { type JwtSigningKeyConfig, type PublicJwk } from "./jwt-keys.js";
|
|
2
4
|
export interface JwtConfig {
|
|
3
5
|
secret: string;
|
|
@@ -61,6 +63,23 @@ export interface AccessTokenPayload {
|
|
|
61
63
|
* comparison and the watermark was read on exactly one path — refresh.
|
|
62
64
|
*/
|
|
63
65
|
iat?: number;
|
|
66
|
+
/**
|
|
67
|
+
* When the token expires, in seconds since the epoch — the standard `exp`
|
|
68
|
+
* claim. Carried through because a socket authenticated with this token
|
|
69
|
+
* has to stop honouring it at this instant, and has no request to refuse.
|
|
70
|
+
*/
|
|
71
|
+
exp?: number;
|
|
72
|
+
/**
|
|
73
|
+
* The sign-in this token was minted for: the session id its refresh
|
|
74
|
+
* tokens share, carried across every rotation.
|
|
75
|
+
*
|
|
76
|
+
* What lets one device be signed out — `POST /auth/logout`,
|
|
77
|
+
* `DELETE /auth/sessions/:id` — reach that device's access token rather
|
|
78
|
+
* than only its refresh token, and what lets `GET /auth/sessions` mark the
|
|
79
|
+
* caller's own session. Absent on tokens minted before it existed; those
|
|
80
|
+
* keep working until they expire, judged by the watermark alone.
|
|
81
|
+
*/
|
|
82
|
+
sid?: string;
|
|
64
83
|
/** Email claim from the JWT, if present */
|
|
65
84
|
email?: string;
|
|
66
85
|
/** Display name claim from the JWT, if present */
|
|
@@ -90,48 +109,284 @@ export interface AccessTokenPayload {
|
|
|
90
109
|
claims?: Record<string, unknown>;
|
|
91
110
|
}
|
|
92
111
|
/**
|
|
93
|
-
*
|
|
94
|
-
*
|
|
112
|
+
* The custom half of a verified token, or nothing when there is none.
|
|
113
|
+
*
|
|
114
|
+
* Exported for the one other place that builds a request identity's claims
|
|
115
|
+
* without a token to verify — `resolveUser`, which reads what a token minted
|
|
116
|
+
* now would carry — so both drop the same identity claims.
|
|
95
117
|
*/
|
|
96
|
-
export declare function
|
|
118
|
+
export declare function customClaimsOf(decoded: Record<string, unknown>): Record<string, unknown> | undefined;
|
|
97
119
|
/**
|
|
98
|
-
*
|
|
120
|
+
* How long a refresh token survives inactivity when no lifetime is configured.
|
|
121
|
+
* Sliding: every rotation re-ups it, so an active user stays signed in.
|
|
122
|
+
*/
|
|
123
|
+
export declare const DEFAULT_REFRESH_EXPIRES_IN = "30d";
|
|
124
|
+
/**
|
|
125
|
+
* One backend's tokens: what it signs with, what it verifies against, and how
|
|
126
|
+
* long what it mints lives.
|
|
99
127
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
128
|
+
* Every backend built by `createRebaseBackend` holds its own
|
|
129
|
+
* (`backend.services.jwt`), so two backends in one process sign and verify
|
|
130
|
+
* with their own secrets and keys and a token one minted means nothing to the
|
|
131
|
+
* other. {@link createJwtService} builds one by hand.
|
|
132
|
+
*
|
|
133
|
+
* The module-level functions of the same names — `generateAccessToken`,
|
|
134
|
+
* `verifyAccessToken`, … — answer for the process default instead: what
|
|
135
|
+
* {@link configureJwt} set, or the backend `initializeRebaseBackend` installed.
|
|
104
136
|
*/
|
|
105
|
-
export
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
|
|
137
|
+
export interface JwtService {
|
|
138
|
+
/** The access-token lifetime, in the grammar {@link parseDurationMs} reads. */
|
|
139
|
+
readonly accessExpiresIn: string;
|
|
140
|
+
/** The refresh-token lifetime, in the grammar {@link parseDurationMs} reads. */
|
|
141
|
+
readonly refreshExpiresIn: string;
|
|
142
|
+
/**
|
|
143
|
+
* Has this service been given a JWT secret?
|
|
144
|
+
*
|
|
145
|
+
* False on every backend that authenticates through an adapter — Firebase,
|
|
146
|
+
* Clerk, anything with its own tokens — because nothing gives it one there.
|
|
147
|
+
* Signing paths still throw when it is false, since asking for a token from
|
|
148
|
+
* a server that cannot mint one is a mistake worth hearing about. Paths
|
|
149
|
+
* whose contract is *"tolerate the absence of auth"* must ask first:
|
|
150
|
+
* `optionalAuth` crashing a request because this backend does not do JWT is
|
|
151
|
+
* a 500 on a route that had already decided anonymous was acceptable.
|
|
152
|
+
*/
|
|
153
|
+
isConfigured(): boolean;
|
|
154
|
+
/** Is this service signing access tokens asymmetrically? */
|
|
155
|
+
hasAsymmetricSigningKey(): boolean;
|
|
156
|
+
/**
|
|
157
|
+
* The public keys, in JWKS form, for `/.well-known/jwks.json`.
|
|
158
|
+
*
|
|
159
|
+
* An empty `keys` array on a backend with no asymmetric keys configured is
|
|
160
|
+
* the correct answer rather than a 404: it says "this issuer publishes
|
|
161
|
+
* none", which a verifier can act on, where a 404 is indistinguishable from
|
|
162
|
+
* a misconfigured URL.
|
|
163
|
+
*/
|
|
164
|
+
getJwks(): {
|
|
165
|
+
keys: PublicJwk[];
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* The symmetric secret this service signs purpose-scoped tokens with, or
|
|
169
|
+
* undefined when it has none — for deriving a key of the backend's own.
|
|
170
|
+
* MFA's encryption falls back to it when no `MFA_ENCRYPTION_KEY` is set.
|
|
171
|
+
* A method rather than a field, so serialising the service never prints it.
|
|
172
|
+
*/
|
|
173
|
+
hmacSecret(): string | undefined;
|
|
174
|
+
/**
|
|
175
|
+
* Generate an access token (short-lived, 1 hour by default).
|
|
176
|
+
*
|
|
177
|
+
* @param isAnonymous Whether this session is a guest — anonymous sign-in
|
|
178
|
+
* rather than an account. Written into the token so the RLS identity and
|
|
179
|
+
* the WebSocket path can tell the two apart without a database lookup.
|
|
180
|
+
* @param sessionId The sign-in this token belongs to — see
|
|
181
|
+
* {@link AccessTokenPayload.sid}. Every token the auth routes mint
|
|
182
|
+
* carries one.
|
|
183
|
+
*/
|
|
184
|
+
generateAccessToken(uid: string, roles: string[], aal?: "aal1" | "aal2", customClaims?: Record<string, unknown>, isAnonymous?: boolean, sessionId?: string): Promise<string>;
|
|
185
|
+
/**
|
|
186
|
+
* Verify and decode an access token.
|
|
187
|
+
*
|
|
188
|
+
* Every token this service issues is signed with the same secret, so what a
|
|
189
|
+
* token *is* comes from its claims, not from its signature. A download
|
|
190
|
+
* token ({@link JwtService.generateDownloadToken}) is therefore a
|
|
191
|
+
* validly-signed string that must never authenticate anybody: it is scoped
|
|
192
|
+
* to one file path and handed out in URLs, which is a far weaker thing to
|
|
193
|
+
* hold than a session.
|
|
194
|
+
*
|
|
195
|
+
* Today it is rejected below for want of an id — but only by luck, since
|
|
196
|
+
* nothing stops a future download token from carrying one. So the purpose
|
|
197
|
+
* is checked explicitly: a token minted for reading a file is not a token
|
|
198
|
+
* for being a user.
|
|
199
|
+
*/
|
|
200
|
+
verifyAccessToken(token: string): Promise<AccessTokenPayload | null>;
|
|
201
|
+
/** The access-token lifetime in milliseconds. */
|
|
202
|
+
getAccessTokenExpiryMs(): number;
|
|
203
|
+
/** When an access token minted now expires, in milliseconds since the epoch. */
|
|
204
|
+
getAccessTokenExpiry(): number;
|
|
205
|
+
/**
|
|
206
|
+
* How long a refresh token is valid for, in milliseconds.
|
|
207
|
+
*
|
|
208
|
+
* Every rotation issues a token with a fresh TTL, so this is a sliding
|
|
209
|
+
* window: a user who visits at all keeps their session indefinitely, and
|
|
210
|
+
* one who disappears loses it this long after their last visit. That is
|
|
211
|
+
* what both Firebase and Supabase do by default, and it is the behaviour
|
|
212
|
+
* people mean when they say they expect to still be signed in.
|
|
213
|
+
*/
|
|
214
|
+
getRefreshTokenTtlMs(): number;
|
|
215
|
+
/** When a refresh token minted now expires. */
|
|
216
|
+
getRefreshTokenExpiry(): Date;
|
|
217
|
+
/**
|
|
218
|
+
* Mint the short-lived credential handed back with an `MFA_REQUIRED`
|
|
219
|
+
* response.
|
|
220
|
+
*
|
|
221
|
+
* Short-lived on purpose: it is the window in which a caller who has
|
|
222
|
+
* proven the first factor may present the second, not a session to be
|
|
223
|
+
* carried around. Five minutes matches the challenge TTL.
|
|
224
|
+
*/
|
|
225
|
+
generateMfaPendingToken(uid: string, expiresInSeconds?: number): Promise<string>;
|
|
226
|
+
/**
|
|
227
|
+
* Verify a pre-auth token and return the user it was minted for.
|
|
228
|
+
*
|
|
229
|
+
* Returns `null` for anything else — including a perfectly valid *access*
|
|
230
|
+
* token, which must not be interchangeable with this one in either
|
|
231
|
+
* direction.
|
|
232
|
+
*
|
|
233
|
+
* `iat` comes back with the uid because this token is a first factor that
|
|
234
|
+
* was accepted at that instant, and a sign-out everywhere after it has to
|
|
235
|
+
* void it the way it voids an access token — see `isAccessTokenRevoked`.
|
|
236
|
+
*/
|
|
237
|
+
verifyMfaPendingToken(token: string): Promise<{
|
|
238
|
+
uid: string;
|
|
239
|
+
iat?: number;
|
|
240
|
+
} | null>;
|
|
241
|
+
/**
|
|
242
|
+
* Generate a short-lived download token scoped to a specific file path or
|
|
243
|
+
* prefix *within one storage source*.
|
|
244
|
+
*
|
|
245
|
+
* Both halves of that scope are load-bearing. A key is only unique inside
|
|
246
|
+
* its own bucket, and a project with more than one source routinely holds
|
|
247
|
+
* the same key in several of them — `avatars/u1.png` in `(default)` and in
|
|
248
|
+
* `media` are different objects, quite possibly with different owners. A
|
|
249
|
+
* token that names only the path is therefore a grant on every source at
|
|
250
|
+
* once: authorize a read on the source whose `storageAuthorize` hook says
|
|
251
|
+
* yes, then spend the token against `?storageId=` pointing somewhere else.
|
|
252
|
+
*
|
|
253
|
+
* `storageId` is optional here only because omitting it *is* the default
|
|
254
|
+
* source, which is what the overwhelming majority of deployments have. A
|
|
255
|
+
* mint site that forgets to pass a named source produces a default-scoped
|
|
256
|
+
* token, which fails closed at `/file/*` rather than over-granting.
|
|
257
|
+
*
|
|
258
|
+
* @param principal The user minting it, so the reads it buys are charged
|
|
259
|
+
* to them.
|
|
260
|
+
*/
|
|
261
|
+
generateDownloadToken(path: string, expiresInSeconds?: number, storageId?: string | null, principal?: string | null): Promise<string>;
|
|
262
|
+
/**
|
|
263
|
+
* Verify and decode a download token.
|
|
264
|
+
*
|
|
265
|
+
* A token minted before `storageId` existed carries no such claim. It is
|
|
266
|
+
* read as a grant on the **default source** rather than on all of them:
|
|
267
|
+
* that is the fail-closed reading, and it is what such a token almost
|
|
268
|
+
* always was, since a named source has to be asked for explicitly. The
|
|
269
|
+
* cost is bounded by the five-minute TTL — for at most that long after a
|
|
270
|
+
* deploy, an in-flight token for a *named* source is refused and the
|
|
271
|
+
* client re-fetches `/metadata`.
|
|
272
|
+
*/
|
|
273
|
+
verifyDownloadToken(token: string): Promise<DownloadTokenPayload | null>;
|
|
274
|
+
/**
|
|
275
|
+
* Sign a short-lived token that carries state across a redirect.
|
|
276
|
+
*
|
|
277
|
+
* The generalisation of the MFA-pending token: a signed blob the server
|
|
278
|
+
* hands out and expects back, where the point is that the holder cannot
|
|
279
|
+
* edit it. The OAuth authorize flow needs one to carry the authorization
|
|
280
|
+
* request across the sign-in hop — without it, the endpoint the consent
|
|
281
|
+
* page posts to would mint a code for whatever `client_id` and
|
|
282
|
+
* `redirect_uri` its caller typed, skipping every check the authorize
|
|
283
|
+
* endpoint performs.
|
|
284
|
+
*
|
|
285
|
+
* The `purpose` is not decoration. {@link JwtService.verifyAccessToken}
|
|
286
|
+
* refuses any token that has one, so nothing minted here can be replayed
|
|
287
|
+
* as a session, and {@link JwtService.verifyPurposeToken} refuses a token
|
|
288
|
+
* whose purpose is not the one the caller named — so a token minted for
|
|
289
|
+
* one hop cannot be spent on another.
|
|
290
|
+
*/
|
|
291
|
+
signPurposeToken(purpose: string, claims: Record<string, unknown>, expiresInSeconds: number): Promise<string>;
|
|
292
|
+
/** Verify a token minted by {@link JwtService.signPurposeToken}, for one named purpose. */
|
|
293
|
+
verifyPurposeToken<T>(purpose: string, token: string): Promise<T | null>;
|
|
294
|
+
/**
|
|
295
|
+
* Mint an access token for the MCP endpoint.
|
|
296
|
+
*
|
|
297
|
+
* Deliberately **not** an ordinary session token, and the `purpose` claim
|
|
298
|
+
* is what enforces that: {@link JwtService.verifyAccessToken} refuses any
|
|
299
|
+
* token carrying one, so a credential issued to a third-party MCP client —
|
|
300
|
+
* Claude, an IDE, anything that completed the OAuth flow — cannot be
|
|
301
|
+
* replayed against `/api/data`, `/api/admin` or the websocket. It buys
|
|
302
|
+
* exactly the one resource it names.
|
|
303
|
+
*
|
|
304
|
+
* That asymmetry matters more than it first looks. These tokens are handed
|
|
305
|
+
* to software the user authorized but the operator never vetted, they
|
|
306
|
+
* live in that software's storage, and they are the only credential in
|
|
307
|
+
* this system minted for a third party. Without the purpose separation,
|
|
308
|
+
* "connect Claude to your data" would silently mean "give Claude a full
|
|
309
|
+
* session".
|
|
310
|
+
*
|
|
311
|
+
* The audience is the canonical resource URI, and
|
|
312
|
+
* {@link JwtService.verifyMcpAccessToken} requires the caller to say which
|
|
313
|
+
* resource it is checking for. A token minted for another Rebase project —
|
|
314
|
+
* or another path on this one — fails there.
|
|
315
|
+
*
|
|
316
|
+
* HS256 with the shared secret, like the download token: the authorization
|
|
317
|
+
* server and the resource server are the same process, so nothing outside
|
|
318
|
+
* it ever needs to verify one, and an asymmetric key here would be a
|
|
319
|
+
* second key surface bought for nobody.
|
|
320
|
+
*/
|
|
321
|
+
generateMcpAccessToken(payload: Omit<McpAccessTokenPayload, "purpose">, expiresInSeconds: number): Promise<string>;
|
|
322
|
+
/**
|
|
323
|
+
* Verify an MCP access token **for a specific resource**.
|
|
324
|
+
*
|
|
325
|
+
* `expectedAudience` has no default. The MCP specification requires a
|
|
326
|
+
* resource server to validate that a token was issued for it
|
|
327
|
+
* specifically, and a default would be the thing that quietly turns that
|
|
328
|
+
* check off — the caller has to say what it is, every time, so a new call
|
|
329
|
+
* site cannot forget by omission.
|
|
330
|
+
*/
|
|
331
|
+
verifyMcpAccessToken(token: string, expectedAudience: string): Promise<McpAccessTokenPayload | null>;
|
|
332
|
+
}
|
|
110
333
|
/**
|
|
111
|
-
*
|
|
334
|
+
* Build a {@link JwtService} from a configuration, refusing one it would sign
|
|
335
|
+
* badly with: a short or well-known secret, an `activeKid` no key carries, a
|
|
336
|
+
* lifetime that is not one.
|
|
337
|
+
*
|
|
338
|
+
* Unset lifetimes take the defaults — an hour for access tokens,
|
|
339
|
+
* {@link DEFAULT_REFRESH_EXPIRES_IN} for refresh tokens.
|
|
112
340
|
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* JWT is a 500 on a route that had already decided anonymous was acceptable.
|
|
341
|
+
* @example
|
|
342
|
+
* ```ts
|
|
343
|
+
* const jwt = createJwtService({ secret: process.env.JWT_SECRET! });
|
|
344
|
+
* const token = await jwt.generateAccessToken("u1", ["editor"]);
|
|
345
|
+
* const payload = await jwt.verifyAccessToken(token); // { uid: "u1", roles: ["editor"], … }
|
|
346
|
+
* ```
|
|
120
347
|
*/
|
|
121
|
-
export declare function
|
|
348
|
+
export declare function createJwtService(config: JwtConfig): JwtService;
|
|
122
349
|
/**
|
|
123
|
-
*
|
|
350
|
+
* Configure the process default's JWT settings — what `verifyAccessToken`,
|
|
351
|
+
* `generateAccessToken` and the other module-level functions sign and verify
|
|
352
|
+
* with.
|
|
353
|
+
*
|
|
354
|
+
* Validates the secret strength to prevent deployment with default/weak
|
|
355
|
+
* secrets. A lifetime left unset keeps the one already configured; a
|
|
356
|
+
* configuration that fails validation leaves the previous one standing rather
|
|
357
|
+
* than half-applying a new one.
|
|
358
|
+
*/
|
|
359
|
+
export declare function configureJwt(config: JwtConfig): void;
|
|
360
|
+
/**
|
|
361
|
+
* @internal The process default itself, to install another or to restore it.
|
|
362
|
+
* `setDefaultBackend` and tests use these; an application calls
|
|
363
|
+
* {@link configureJwt} or `setDefaultBackend`.
|
|
124
364
|
*/
|
|
125
|
-
export declare function
|
|
365
|
+
export declare function defaultJwtService(): JwtService;
|
|
366
|
+
/** @internal See {@link defaultJwtService}. */
|
|
367
|
+
export declare function setDefaultJwtService(service: JwtService): void;
|
|
126
368
|
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
369
|
+
* The JWT service serving this request: the backend's whose route group is
|
|
370
|
+
* answering it, or the process default for a route mounted outside any
|
|
371
|
+
* backend.
|
|
130
372
|
*/
|
|
131
|
-
|
|
373
|
+
export declare function jwtOf(c: Context<HonoEnv>): JwtService;
|
|
374
|
+
/** See {@link JwtService.getJwks}. Answers for the process default. */
|
|
375
|
+
export declare function getJwks(): {
|
|
376
|
+
keys: PublicJwk[];
|
|
377
|
+
};
|
|
132
378
|
/**
|
|
133
|
-
*
|
|
379
|
+
* The secret {@link configureJwt} was given, or undefined before it is called.
|
|
380
|
+
* Answers for the process default — see {@link JwtService.hmacSecret}.
|
|
134
381
|
*/
|
|
382
|
+
export declare function configuredJwtSecret(): string | undefined;
|
|
383
|
+
/** See {@link JwtService.hasAsymmetricSigningKey}. Answers for the process default. */
|
|
384
|
+
export declare function hasAsymmetricSigningKey(): boolean;
|
|
385
|
+
/** See {@link JwtService.isConfigured}. Answers for the process default. */
|
|
386
|
+
export declare function isJwtConfigured(): boolean;
|
|
387
|
+
/** See {@link JwtService.generateAccessToken}. Answers for the process default. */
|
|
388
|
+
export declare function generateAccessToken(uid: string, roles: string[], aal?: "aal1" | "aal2", customClaims?: Record<string, unknown>, isAnonymous?: boolean, sessionId?: string): Promise<string>;
|
|
389
|
+
/** See {@link JwtService.getAccessTokenExpiryMs}. Answers for the process default. */
|
|
135
390
|
export declare function getAccessTokenExpiryMs(): number;
|
|
136
391
|
/**
|
|
137
392
|
* A lifetime setting in milliseconds, or null when it is not one.
|
|
@@ -145,27 +400,13 @@ export declare function getAccessTokenExpiryMs(): number;
|
|
|
145
400
|
* (ms reads "3600" as 3.6 seconds) and a lifetime of zero or less.
|
|
146
401
|
*/
|
|
147
402
|
export declare function parseDurationMs(value: string): number | null;
|
|
148
|
-
/**
|
|
149
|
-
* Get the expiration timestamp for an access token
|
|
150
|
-
*/
|
|
403
|
+
/** See {@link JwtService.getAccessTokenExpiry}. Answers for the process default. */
|
|
151
404
|
export declare function getAccessTokenExpiry(): number;
|
|
152
|
-
/**
|
|
153
|
-
* Verify and decode an access token.
|
|
154
|
-
*
|
|
155
|
-
* Every token this server issues is signed with the same secret, so what a
|
|
156
|
-
* token *is* comes from its claims, not from its signature. A download token
|
|
157
|
-
* ({@link generateDownloadToken}) is therefore a validly-signed string that
|
|
158
|
-
* must never authenticate anybody: it is scoped to one file path and handed out
|
|
159
|
-
* in URLs, which is a far weaker thing to hold than a session.
|
|
160
|
-
*
|
|
161
|
-
* Today it is rejected below for want of an id — but only by luck, since
|
|
162
|
-
* nothing stops a future download token from carrying one. So the purpose is
|
|
163
|
-
* checked explicitly: a token minted for reading a file is not a token for
|
|
164
|
-
* being a user.
|
|
165
|
-
*/
|
|
405
|
+
/** See {@link JwtService.verifyAccessToken}. Answers for the process default. */
|
|
166
406
|
export declare function verifyAccessToken(token: string): Promise<AccessTokenPayload | null>;
|
|
167
407
|
/**
|
|
168
|
-
* Generate a random refresh token
|
|
408
|
+
* Generate a random refresh token. Long-lived: 30 days by default, sliding —
|
|
409
|
+
* see {@link JwtService.getRefreshTokenTtlMs}.
|
|
169
410
|
*/
|
|
170
411
|
export declare function generateRefreshToken(): string;
|
|
171
412
|
/**
|
|
@@ -179,49 +420,24 @@ export declare function hashRefreshToken(token: string): Promise<string>;
|
|
|
179
420
|
* what is actually enforced, which is worse than knowing the ceiling.
|
|
180
421
|
*/
|
|
181
422
|
export declare const MAX_COOKIE_AGE_MS: number;
|
|
182
|
-
/**
|
|
183
|
-
* How long a refresh token is valid for, in milliseconds.
|
|
184
|
-
*
|
|
185
|
-
* Every rotation issues a token with a fresh TTL, so this is a sliding window:
|
|
186
|
-
* a user who visits at all keeps their session indefinitely, and one who
|
|
187
|
-
* disappears loses it this long after their last visit. That is what both
|
|
188
|
-
* Firebase and Supabase do by default, and it is the behaviour people mean
|
|
189
|
-
* when they say they expect to still be signed in.
|
|
190
|
-
*/
|
|
423
|
+
/** See {@link JwtService.getRefreshTokenTtlMs}. Answers for the process default. */
|
|
191
424
|
export declare function getRefreshTokenTtlMs(): number;
|
|
192
|
-
/**
|
|
193
|
-
* Calculate refresh token expiration date
|
|
194
|
-
*/
|
|
425
|
+
/** See {@link JwtService.getRefreshTokenExpiry}. Answers for the process default. */
|
|
195
426
|
export declare function getRefreshTokenExpiry(): Date;
|
|
196
427
|
/**
|
|
197
428
|
* The `purpose` claim carried by a credential that stands between "first factor
|
|
198
429
|
* accepted" and "session issued".
|
|
199
430
|
*
|
|
200
431
|
* A pre-auth token is NOT a session and must never be usable as one:
|
|
201
|
-
* {@link verifyAccessToken} refuses any token carrying a `purpose`,
|
|
202
|
-
* value cannot authenticate a request no matter which route it is
|
|
203
|
-
* The only thing that reads it is the MFA challenge pair, which
|
|
204
|
-
* plus a second factor — for a real session.
|
|
432
|
+
* {@link JwtService.verifyAccessToken} refuses any token carrying a `purpose`,
|
|
433
|
+
* so this value cannot authenticate a request no matter which route it is
|
|
434
|
+
* presented to. The only thing that reads it is the MFA challenge pair, which
|
|
435
|
+
* exchanges it — plus a second factor — for a real session.
|
|
205
436
|
*/
|
|
206
437
|
export declare const MFA_PENDING_PURPOSE = "mfa-pending";
|
|
207
|
-
/**
|
|
208
|
-
* Mint the short-lived credential handed back with an `MFA_REQUIRED` response.
|
|
209
|
-
*
|
|
210
|
-
* Short-lived on purpose: it is the window in which a caller who has proven the
|
|
211
|
-
* first factor may present the second, not a session to be carried around. Five
|
|
212
|
-
* minutes matches the challenge TTL.
|
|
213
|
-
*/
|
|
438
|
+
/** See {@link JwtService.generateMfaPendingToken}. Answers for the process default. */
|
|
214
439
|
export declare function generateMfaPendingToken(uid: string, expiresInSeconds?: number): Promise<string>;
|
|
215
|
-
/**
|
|
216
|
-
* Verify a pre-auth token and return the user it was minted for.
|
|
217
|
-
*
|
|
218
|
-
* Returns `null` for anything else — including a perfectly valid *access*
|
|
219
|
-
* token, which must not be interchangeable with this one in either direction.
|
|
220
|
-
*
|
|
221
|
-
* `iat` comes back with the uid because this token is a first factor that was
|
|
222
|
-
* accepted at that instant, and a sign-out everywhere after it has to void it
|
|
223
|
-
* the way it voids an access token — see `isAccessTokenRevoked`.
|
|
224
|
-
*/
|
|
440
|
+
/** See {@link JwtService.verifyMfaPendingToken}. Answers for the process default. */
|
|
225
441
|
export declare function verifyMfaPendingToken(token: string): Promise<{
|
|
226
442
|
uid: string;
|
|
227
443
|
iat?: number;
|
|
@@ -232,57 +448,27 @@ export interface DownloadTokenPayload {
|
|
|
232
448
|
/**
|
|
233
449
|
* The storage source the grant is good for, canonicalized by
|
|
234
450
|
* {@link canonicalStorageId}. Always present on a decoded payload; see
|
|
235
|
-
* {@link verifyDownloadToken} for how tokens minted before this
|
|
236
|
-
* existed are read.
|
|
451
|
+
* {@link JwtService.verifyDownloadToken} for how tokens minted before this
|
|
452
|
+
* claim existed are read.
|
|
237
453
|
*/
|
|
238
454
|
storageId: string;
|
|
455
|
+
/**
|
|
456
|
+
* An opaque mark of the user who minted the token, for rate limiting only:
|
|
457
|
+
* a read that spends the token is charged to that user's allowance rather
|
|
458
|
+
* than to the reader's address. A hash, because the token travels in a URL.
|
|
459
|
+
* Absent for a token minted for nobody.
|
|
460
|
+
*/
|
|
461
|
+
rl?: string;
|
|
239
462
|
}
|
|
240
|
-
/**
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
* own bucket, and a project with more than one source routinely holds the same
|
|
246
|
-
* key in several of them — `avatars/u1.png` in `(default)` and in `media` are
|
|
247
|
-
* different objects, quite possibly with different owners. A token that names
|
|
248
|
-
* only the path is therefore a grant on every source at once: authorize a read
|
|
249
|
-
* on the source whose `storageAuthorize` hook says yes, then spend the token
|
|
250
|
-
* against `?storageId=` pointing somewhere else.
|
|
251
|
-
*
|
|
252
|
-
* `storageId` is optional here only because omitting it *is* the default
|
|
253
|
-
* source, which is what the overwhelming majority of deployments have. A mint
|
|
254
|
-
* site that forgets to pass a named source produces a default-scoped token,
|
|
255
|
-
* which fails closed at `/file/*` rather than over-granting.
|
|
256
|
-
*/
|
|
257
|
-
export declare function generateDownloadToken(path: string, expiresInSeconds?: number, storageId?: string | null): Promise<string>;
|
|
258
|
-
/**
|
|
259
|
-
* Verify and decode a download token.
|
|
260
|
-
*
|
|
261
|
-
* A token minted before `storageId` existed carries no such claim. It is read
|
|
262
|
-
* as a grant on the **default source** rather than on all of them: that is the
|
|
263
|
-
* fail-closed reading, and it is what such a token almost always was, since a
|
|
264
|
-
* named source has to be asked for explicitly. The cost is bounded by the
|
|
265
|
-
* five-minute TTL — for at most that long after a deploy, an in-flight token
|
|
266
|
-
* for a *named* source is refused and the client re-fetches `/metadata`.
|
|
267
|
-
*/
|
|
463
|
+
/** See {@link JwtService.generateDownloadToken}. Answers for the process default. */
|
|
464
|
+
export declare function generateDownloadToken(path: string, expiresInSeconds?: number, storageId?: string | null,
|
|
465
|
+
/** The user minting it, so the reads it buys are charged to them. */
|
|
466
|
+
principal?: string | null): Promise<string>;
|
|
467
|
+
/** See {@link JwtService.verifyDownloadToken}. Answers for the process default. */
|
|
268
468
|
export declare function verifyDownloadToken(token: string): Promise<DownloadTokenPayload | null>;
|
|
269
|
-
/**
|
|
270
|
-
* Sign a short-lived token that carries state across a redirect.
|
|
271
|
-
*
|
|
272
|
-
* The generalisation of the MFA-pending token: a signed blob the server hands
|
|
273
|
-
* out and expects back, where the point is that the holder cannot edit it. The
|
|
274
|
-
* OAuth authorize flow needs one to carry the authorization request across the
|
|
275
|
-
* sign-in hop — without it, the endpoint the consent page posts to would mint a
|
|
276
|
-
* code for whatever `client_id` and `redirect_uri` its caller typed, skipping
|
|
277
|
-
* every check the authorize endpoint performs.
|
|
278
|
-
*
|
|
279
|
-
* The `purpose` is not decoration. {@link verifyAccessToken} refuses any token
|
|
280
|
-
* that has one, so nothing minted here can be replayed as a session, and
|
|
281
|
-
* {@link verifyPurposeToken} refuses a token whose purpose is not the one the
|
|
282
|
-
* caller named — so a token minted for one hop cannot be spent on another.
|
|
283
|
-
*/
|
|
469
|
+
/** See {@link JwtService.signPurposeToken}. Answers for the process default. */
|
|
284
470
|
export declare function signPurposeToken(purpose: string, claims: Record<string, unknown>, expiresInSeconds: number): Promise<string>;
|
|
285
|
-
/**
|
|
471
|
+
/** See {@link JwtService.verifyPurposeToken}. Answers for the process default. */
|
|
286
472
|
export declare function verifyPurposeToken<T>(purpose: string, token: string): Promise<T | null>;
|
|
287
473
|
/** The `purpose` that marks a token as belonging to the MCP resource. */
|
|
288
474
|
export declare const MCP_ACCESS_PURPOSE = "mcp-access";
|
|
@@ -299,37 +485,13 @@ export interface McpAccessTokenPayload {
|
|
|
299
485
|
aud: string;
|
|
300
486
|
iss: string;
|
|
301
487
|
}
|
|
302
|
-
/**
|
|
303
|
-
* Mint an access token for the MCP endpoint.
|
|
304
|
-
*
|
|
305
|
-
* Deliberately **not** an ordinary session token, and the `purpose` claim is
|
|
306
|
-
* what enforces that: {@link verifyAccessToken} refuses any token carrying one,
|
|
307
|
-
* so a credential issued to a third-party MCP client — Claude, an IDE, anything
|
|
308
|
-
* that completed the OAuth flow — cannot be replayed against `/api/data`,
|
|
309
|
-
* `/api/admin` or the websocket. It buys exactly the one resource it names.
|
|
310
|
-
*
|
|
311
|
-
* That asymmetry matters more than it first looks. These tokens are handed to
|
|
312
|
-
* software the user authorized but the operator never vetted, they live in that
|
|
313
|
-
* software's storage, and they are the only credential in this system minted
|
|
314
|
-
* for a third party. Without the purpose separation, "connect Claude to your
|
|
315
|
-
* data" would silently mean "give Claude a full session".
|
|
316
|
-
*
|
|
317
|
-
* The audience is the canonical resource URI, and {@link verifyMcpAccessToken}
|
|
318
|
-
* requires the caller to say which resource it is checking for. A token minted
|
|
319
|
-
* for another Rebase project — or another path on this one — fails there.
|
|
320
|
-
*
|
|
321
|
-
* HS256 with the shared secret, like the download token: the authorization
|
|
322
|
-
* server and the resource server are the same process, so nothing outside it
|
|
323
|
-
* ever needs to verify one, and an asymmetric key here would be a second key
|
|
324
|
-
* surface bought for nobody.
|
|
325
|
-
*/
|
|
488
|
+
/** See {@link JwtService.generateMcpAccessToken}. Answers for the process default. */
|
|
326
489
|
export declare function generateMcpAccessToken(payload: Omit<McpAccessTokenPayload, "purpose">, expiresInSeconds: number): Promise<string>;
|
|
490
|
+
/** See {@link JwtService.verifyMcpAccessToken}. Answers for the process default. */
|
|
491
|
+
export declare function verifyMcpAccessToken(token: string, expectedAudience: string): Promise<McpAccessTokenPayload | null>;
|
|
327
492
|
/**
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
* server to validate that a token was issued for it specifically, and a default
|
|
332
|
-
* would be the thing that quietly turns that check off — the caller has to say
|
|
333
|
-
* what it is, every time, so a new call site cannot forget by omission.
|
|
493
|
+
* The process default as a service object that follows it — every call reads
|
|
494
|
+
* whatever {@link configureJwt} or `setDefaultBackend` installed last. What a
|
|
495
|
+
* route built outside any backend falls back to.
|
|
334
496
|
*/
|
|
335
|
-
export declare
|
|
497
|
+
export declare const processJwtService: JwtService;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import type { MiddlewareHandler } from "hono";
|
|
3
|
-
import type { AuthModuleConfig } from "./routes.js";
|
|
3
|
+
import type { AuthModuleConfig, CreateSessionAndTokens } from "./routes.js";
|
|
4
4
|
import type { ResolvedAuthHooks } from "./auth-hooks.js";
|
|
5
5
|
import type { HonoEnv } from "../api/types.js";
|
|
6
|
+
import { type AuthLimiters } from "./rate-limiter.js";
|
|
6
7
|
import { z } from "zod";
|
|
7
8
|
import type { AuthResponsePayload, TransformAuthResponseContext } from "@rebasepro/types";
|
|
8
9
|
/**
|
|
@@ -13,6 +14,8 @@ import type { AuthResponsePayload, TransformAuthResponseContext } from "@rebasep
|
|
|
13
14
|
export declare function mountMagicLinkRoutes(deps: {
|
|
14
15
|
router: Hono<HonoEnv>;
|
|
15
16
|
config: AuthModuleConfig;
|
|
17
|
+
/** The backend's auth limiters. */
|
|
18
|
+
limiters: AuthLimiters;
|
|
16
19
|
ops: ResolvedAuthHooks;
|
|
17
20
|
parseBody: <T>(schema: z.ZodSchema<T>, body: unknown) => T;
|
|
18
21
|
buildAuthResponse: (user: {
|
|
@@ -24,11 +27,7 @@ export declare function mountMagicLinkRoutes(deps: {
|
|
|
24
27
|
isAnonymous?: boolean;
|
|
25
28
|
metadata?: Record<string, unknown> | null;
|
|
26
29
|
}, roleIds: string[], accessToken: string, refreshToken: string, providerId: string) => unknown;
|
|
27
|
-
createSessionAndTokens:
|
|
28
|
-
roleIds: string[];
|
|
29
|
-
accessToken: string;
|
|
30
|
-
refreshToken: string;
|
|
31
|
-
}>;
|
|
30
|
+
createSessionAndTokens: CreateSessionAndTokens;
|
|
32
31
|
applyTransformHook: (response: AuthResponsePayload, method: TransformAuthResponseContext["method"], request: Request, uid: string) => Promise<AuthResponsePayload>;
|
|
33
32
|
/**
|
|
34
33
|
* Built by the caller so a misconfiguration fails the boot once, rather
|
|
@@ -36,13 +36,16 @@
|
|
|
36
36
|
*
|
|
37
37
|
* @module
|
|
38
38
|
*/
|
|
39
|
+
import { type JwtService } from "./jwt.js";
|
|
39
40
|
/**
|
|
40
41
|
* Encrypt a plaintext TOTP secret.
|
|
41
42
|
*
|
|
42
43
|
* @param plaintext - The Base32 TOTP secret to encrypt.
|
|
44
|
+
* @param jwt - The backend whose JWT secret is the last key fallback. The
|
|
45
|
+
* process default when omitted.
|
|
43
46
|
* @returns A string in the format `v1.<keyId>:iv_hex:authTag_hex:ciphertext_hex`.
|
|
44
47
|
*/
|
|
45
|
-
export declare function encryptTotpSecret(plaintext: string): string;
|
|
48
|
+
export declare function encryptTotpSecret(plaintext: string, jwt?: JwtService): string;
|
|
46
49
|
/** What a factor's stored secret opened to, and whether it should be re-stored. */
|
|
47
50
|
export interface OpenedTotpSecret {
|
|
48
51
|
/** The Base32 TOTP secret. */
|
|
@@ -59,8 +62,10 @@ export interface OpenedTotpSecret {
|
|
|
59
62
|
*
|
|
60
63
|
* Accepts both the key-stamped format and the original unstamped
|
|
61
64
|
* `iv:authTag:ciphertext`, which is tried against each candidate in turn.
|
|
65
|
+
* `jwt` is the backend whose JWT secret is the last key fallback — the process
|
|
66
|
+
* default when omitted.
|
|
62
67
|
*/
|
|
63
|
-
export declare function openTotpSecret(ciphertext: string): OpenedTotpSecret;
|
|
68
|
+
export declare function openTotpSecret(ciphertext: string, jwt?: JwtService): OpenedTotpSecret;
|
|
64
69
|
/**
|
|
65
70
|
* Decrypt a previously encrypted TOTP secret.
|
|
66
71
|
*
|
|
@@ -69,6 +74,7 @@ export declare function openTotpSecret(ciphertext: string): OpenedTotpSecret;
|
|
|
69
74
|
* on whichever key it was written with.
|
|
70
75
|
*
|
|
71
76
|
* @param ciphertext - A stored TOTP secret, in either format.
|
|
77
|
+
* @param jwt - The backend whose JWT secret is the last key fallback.
|
|
72
78
|
* @returns The original Base32 TOTP secret.
|
|
73
79
|
*/
|
|
74
|
-
export declare function decryptTotpSecret(ciphertext: string): string;
|
|
80
|
+
export declare function decryptTotpSecret(ciphertext: string, jwt?: JwtService): string;
|
package/dist/auth/mfa-gate.d.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* @module
|
|
14
14
|
*/
|
|
15
|
+
import type { JwtService } from "./jwt.js";
|
|
15
16
|
import type { AuthRepository } from "./interfaces.js";
|
|
16
17
|
/** Error code a client watches for to switch a sign-in into its second step. */
|
|
17
18
|
export declare const MFA_REQUIRED_CODE = "MFA_REQUIRED";
|
|
@@ -37,6 +38,7 @@ export interface MfaRequiredDetails {
|
|
|
37
38
|
*
|
|
38
39
|
* A 401 rather than a 200 with a different body: a client that has not been
|
|
39
40
|
* taught about MFA must fail, not proceed. The credential in `details` is
|
|
40
|
-
* useless for anything except the challenge routes
|
|
41
|
+
* useless for anything except the challenge routes, and is signed by `jwt` —
|
|
42
|
+
* the backend's own key.
|
|
41
43
|
*/
|
|
42
|
-
export declare function assertMfaSatisfied(authRepo: AuthRepository, uid: string): Promise<void>;
|
|
44
|
+
export declare function assertMfaSatisfied(authRepo: AuthRepository, uid: string, jwt: JwtService): Promise<void>;
|