@rebasepro/server 0.14.0 → 0.14.1
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/dist/api/rest/query-parser.d.ts +37 -1
- package/dist/api/rest/write-validation.d.ts +26 -0
- package/dist/auth/index.d.ts +3 -1
- package/dist/auth/interfaces.d.ts +14 -1
- package/dist/auth/jwks-routes.d.ts +17 -0
- package/dist/auth/jwt-keys.d.ts +108 -0
- package/dist/auth/jwt.d.ts +62 -2
- package/dist/{auth-CYoPVf-E.js → auth-BobZVd0j.js} +142 -167
- package/dist/auth-BobZVd0j.js.map +1 -0
- package/dist/boot/boot.d.ts +36 -50
- package/dist/boot/ddl-bootstrap.d.ts +15 -0
- package/dist/boot/env.d.ts +20 -0
- package/dist/boot/provision.d.ts +182 -0
- package/dist/boot/role.d.ts +88 -0
- package/dist/{cron-store-Dvr4Y1sZ.js → cron-store-CB1x-Ken.js} +3 -3
- package/dist/{cron-store-Dvr4Y1sZ.js.map → cron-store-CB1x-Ken.js.map} +1 -1
- package/dist/{ddl-bootstrap-BhXbTnBl.js → ddl-bootstrap-Cywoj8Ta.js} +40 -2
- package/dist/ddl-bootstrap-Cywoj8Ta.js.map +1 -0
- package/dist/env.d.ts +2 -0
- package/dist/functions/proxy.d.ts +41 -0
- package/dist/functions/selection.d.ts +45 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.es.js +1113 -661
- package/dist/index.es.js.map +1 -1
- package/dist/init/shutdown.d.ts +4 -0
- package/dist/init/surfaces.d.ts +79 -0
- package/dist/init.d.ts +121 -1
- package/dist/jobs/index.d.ts +5 -0
- package/dist/jobs/job-queue.d.ts +14 -0
- package/dist/jobs/job-store.d.ts +22 -0
- package/dist/jobs/types.d.ts +125 -0
- package/dist/jobs-DR4SjGrD.js +326 -0
- package/dist/jobs-DR4SjGrD.js.map +1 -0
- package/dist/{jwt-_IFqfTOg.js → jwt-VJyXTdQQ.js} +447 -11
- package/dist/jwt-VJyXTdQQ.js.map +1 -0
- package/dist/{openapi-generator-DPKtUC9X.js → openapi-generator-DQeQ_q2f.js} +68 -3
- package/dist/openapi-generator-DQeQ_q2f.js.map +1 -0
- package/dist/proxy-Bj5DVllb.js +139 -0
- package/dist/proxy-Bj5DVllb.js.map +1 -0
- package/dist/{request-timeout-RivJsME0.js → request-timeout-BuFoEKwT.js} +6 -3
- package/dist/request-timeout-BuFoEKwT.js.map +1 -0
- package/dist/selection-_z6TM1DB.js +64 -0
- package/dist/selection-_z6TM1DB.js.map +1 -0
- package/dist/services/webhook-service.d.ts +43 -5
- package/dist/{src-C7rkDGxA.js → src-8XDWyDfR.js} +84 -13
- package/dist/src-8XDWyDfR.js.map +1 -0
- package/dist/src-Cz9nMgUR.js.map +1 -1
- package/dist/storage/keys.d.ts +17 -0
- package/dist/storage/routes.d.ts +1 -1
- package/dist/storage/storage-registry.d.ts +46 -4
- package/dist/storage/tus-handler.d.ts +1 -1
- package/package.json +5 -5
- package/dist/auth-CYoPVf-E.js.map +0 -1
- package/dist/ddl-bootstrap-BhXbTnBl.js.map +0 -1
- package/dist/jwt-_IFqfTOg.js.map +0 -1
- package/dist/openapi-generator-DPKtUC9X.js.map +0 -1
- package/dist/request-timeout-RivJsME0.js.map +0 -1
- package/dist/src-C7rkDGxA.js.map +0 -1
|
@@ -2,17 +2,16 @@ import { createRequire as __createRequire } from "module";
|
|
|
2
2
|
import process from "process";
|
|
3
3
|
__createRequire(import.meta.url);
|
|
4
4
|
import { i as __toESM, n as __exportAll } from "./rolldown-runtime-DSJWtz9O.js";
|
|
5
|
-
import { C as
|
|
5
|
+
import { C as ListLimitError, D as isAnonymousUid, E as ANONYMOUS_USER_ID, T as resolveClientListLimit, a as deserializeLogicalCondition, i as deserializeFilter, r as UnknownFilterOperatorError } from "./src-8XDWyDfR.js";
|
|
6
6
|
import "./src-Cz9nMgUR.js";
|
|
7
|
-
import {
|
|
7
|
+
import { n as createDdlBootstrapper, o as revokeInternalTableSql, s as isSQLAdmin } from "./ddl-bootstrap-Cywoj8Ta.js";
|
|
8
|
+
import { S as canonicalStorageId, T as require_jsonwebtoken, _ as verifyMfaPendingToken, a as generateMfaPendingToken, c as getJwks, d as hasAsymmetricSigningKey, f as hashRefreshToken, g as verifyDownloadToken, h as verifyAccessToken, i as generateDownloadToken, l as getRefreshTokenExpiry, n as configureJwt, o as generateRefreshToken, p as isJwtConfigured, r as generateAccessToken, s as getAccessTokenExpiry, t as MAX_COOKIE_AGE_MS, u as getRefreshTokenTtlMs, w as tryCanonicalStorageKey } from "./jwt-VJyXTdQQ.js";
|
|
8
9
|
import { t as logger } from "./logger-DfvF_8r-.js";
|
|
9
10
|
import { n as errorHandler, t as ApiError } from "./errors-EBYiaJ2E.js";
|
|
10
|
-
import { a as generateMfaPendingToken, c as getRefreshTokenExpiry, d as isJwtConfigured, g as require_jsonwebtoken, h as verifyMfaPendingToken, i as generateDownloadToken, l as getRefreshTokenTtlMs, m as verifyDownloadToken, n as configureJwt, o as generateRefreshToken, p as verifyAccessToken, r as generateAccessToken, s as getAccessTokenExpiry, t as MAX_COOKIE_AGE_MS, u as hashRefreshToken } from "./jwt-_IFqfTOg.js";
|
|
11
11
|
import { createHash, randomBytes, randomInt } from "node:crypto";
|
|
12
12
|
import { Hono } from "hono";
|
|
13
13
|
import { promisify } from "util";
|
|
14
14
|
import { createCipheriv, createDecipheriv, createHash as createHash$1, createHmac, createPublicKey, randomBytes as randomBytes$1, randomUUID as randomUUID$1, scrypt, timingSafeEqual } from "crypto";
|
|
15
|
-
import path from "node:path";
|
|
16
15
|
import { getConnInfo } from "@hono/node-server/conninfo";
|
|
17
16
|
//#region ../types/src/controllers/storage.ts
|
|
18
17
|
/**
|
|
@@ -172,9 +171,68 @@ function parseWhereParam(raw) {
|
|
|
172
171
|
const filter = decodeFilter(parsed);
|
|
173
172
|
return Object.keys(filter).length > 0 ? filter : void 0;
|
|
174
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* The parsed entries as the driver contract spells them: `[field, direction]`
|
|
176
|
+
* tuples in order of significance.
|
|
177
|
+
*
|
|
178
|
+
* The REST layer used to hand the driver `orderBy[0].field` and drop the rest,
|
|
179
|
+
* so `?orderBy=[{"field":"roles"},{"field":"created_at","direction":"desc"}]`
|
|
180
|
+
* — a shape this parser has always accepted and validated in full — sorted by
|
|
181
|
+
* `roles` alone and returned the ties in whatever order Postgres pleased.
|
|
182
|
+
*/
|
|
183
|
+
function orderByEntriesToTuples(entries) {
|
|
184
|
+
if (!entries || entries.length === 0) return void 0;
|
|
185
|
+
return entries.map(({ field, direction }) => [field, direction]);
|
|
186
|
+
}
|
|
175
187
|
function invalidOrderBy(detail) {
|
|
176
188
|
throw invalidParam(`Invalid \`orderBy\` parameter: ${detail}. Expected \`field\`, \`field:desc\`, or a JSON array like [{"field":"created_at","direction":"desc"}]`, "INVALID_ORDER_BY");
|
|
177
189
|
}
|
|
190
|
+
/** The aggregate functions `?select=` accepts. */
|
|
191
|
+
var AGGREGATE_FUNCTIONS = /* @__PURE__ */ new Set([
|
|
192
|
+
"count",
|
|
193
|
+
"sum",
|
|
194
|
+
"avg",
|
|
195
|
+
"min",
|
|
196
|
+
"max"
|
|
197
|
+
]);
|
|
198
|
+
/**
|
|
199
|
+
* Parse `?select=count(),sum(total),avg(total)`.
|
|
200
|
+
*
|
|
201
|
+
* The spelling is SQL's, because whoever writes it is thinking in SQL and
|
|
202
|
+
* because any other spelling has to be learned first. `count()` with no field
|
|
203
|
+
* counts rows; every other function names a column.
|
|
204
|
+
*
|
|
205
|
+
* Aliases are derived rather than accepted: `sum(total)` returns as
|
|
206
|
+
* `sum_total`, `count()` as `count`. Letting a caller choose would mean
|
|
207
|
+
* checking their alias is not also a `groupBy` field — a rule nobody would
|
|
208
|
+
* guess, and a silently overwritten value if it went unchecked.
|
|
209
|
+
*/
|
|
210
|
+
function parseAggregateSelect(raw) {
|
|
211
|
+
const value = getLastValue(raw);
|
|
212
|
+
if (!value) return void 0;
|
|
213
|
+
const entries = String(value).split(",").map((s) => s.trim()).filter(Boolean);
|
|
214
|
+
if (entries.length === 0) return void 0;
|
|
215
|
+
return entries.map((entry) => {
|
|
216
|
+
const match = /^([a-z]+)\(\s*([A-Za-z0-9_]*)\s*\)$/i.exec(entry);
|
|
217
|
+
if (!match) throw invalidParam(`Invalid \`select\` entry "${entry}". Expected \`fn(field)\`, e.g. \`sum(total)\` or \`count()\`.`, "INVALID_AGGREGATE_SELECT");
|
|
218
|
+
const fn = match[1].toLowerCase();
|
|
219
|
+
const field = match[2] || void 0;
|
|
220
|
+
if (!AGGREGATE_FUNCTIONS.has(fn)) throw invalidParam(`Unknown aggregate function "${fn}". Expected: ${[...AGGREGATE_FUNCTIONS].join(", ")}.`, "INVALID_AGGREGATE_FUNCTION");
|
|
221
|
+
if (fn !== "count" && !field) throw invalidParam(`\`${fn}()\` needs a field, e.g. \`${fn}(total)\`. Only \`count()\` may be empty.`, "INVALID_AGGREGATE_SELECT");
|
|
222
|
+
return {
|
|
223
|
+
fn,
|
|
224
|
+
field,
|
|
225
|
+
alias: field ? `${fn}_${field}` : fn
|
|
226
|
+
};
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/** Parse `?groupBy=status,country`. */
|
|
230
|
+
function parseGroupBy(raw) {
|
|
231
|
+
const value = getLastValue(raw);
|
|
232
|
+
if (!value) return void 0;
|
|
233
|
+
const fields = String(value).split(",").map((s) => s.trim()).filter(Boolean);
|
|
234
|
+
return fields.length > 0 ? fields : void 0;
|
|
235
|
+
}
|
|
178
236
|
/** `asc`/`desc`, in any case. Anything else is a request to sort in a way that does not exist. */
|
|
179
237
|
function toDirection(raw, context) {
|
|
180
238
|
if (raw === void 0 || raw === null) return "asc";
|
|
@@ -299,7 +357,9 @@ function parseQueryOptions(query, limits = {}) {
|
|
|
299
357
|
"vector_threshold",
|
|
300
358
|
"or",
|
|
301
359
|
"and",
|
|
302
|
-
"where"
|
|
360
|
+
"where",
|
|
361
|
+
"select",
|
|
362
|
+
"groupBy"
|
|
303
363
|
];
|
|
304
364
|
const filterDict = {};
|
|
305
365
|
for (const [key, rawValue] of Object.entries(query)) {
|
|
@@ -832,137 +892,6 @@ function createApiKeyPreAuth(options) {
|
|
|
832
892
|
};
|
|
833
893
|
}
|
|
834
894
|
//#endregion
|
|
835
|
-
//#region src/storage/keys.ts
|
|
836
|
-
/**
|
|
837
|
-
* Canonical storage keys and bucket names.
|
|
838
|
-
*
|
|
839
|
-
* Storage is not under RLS, so a `storageAuthorize` hook is the whole access
|
|
840
|
-
* control model — and a hook can only be correct if the key it is shown is the
|
|
841
|
-
* key that is written. That is the invariant this module exists to hold: one
|
|
842
|
-
* canonical string, computed once per request, handed to the hook, to the
|
|
843
|
-
* controller, and to the download token alike.
|
|
844
|
-
*
|
|
845
|
-
* ## Why rejecting beats stripping
|
|
846
|
-
*
|
|
847
|
-
* The previous `sanitizeStorageKey` *stripped* `../` in a single pass. Two
|
|
848
|
-
* things were wrong with that, and only one of them was the obvious one.
|
|
849
|
-
*
|
|
850
|
-
* The obvious one: a single pass is not a fixed point. `....//` contains `../`
|
|
851
|
-
* at offset 2, so removing it leaves `../` behind — the sanitizer manufactured
|
|
852
|
-
* the traversal it was there to remove. `users/alice/....//bob/x` came out as
|
|
853
|
-
* `users/alice/../bob/x`, which a prefix hook reads as alice's (it starts with
|
|
854
|
-
* `users/alice/`) and the filesystem reads as bob's. The hook approved one
|
|
855
|
-
* object and the controller wrote another.
|
|
856
|
-
*
|
|
857
|
-
* The subtler one, and the reason this is a rewrite rather than a loop: even a
|
|
858
|
-
* correct strip is a silent rewrite. A caller who asks to store at `a/../b` and
|
|
859
|
-
* gets an object at `a/b` was not protected, they were misled — and every later
|
|
860
|
-
* read, ownership row and audit line now refers to a path nobody chose. So a key
|
|
861
|
-
* that means something other than what it says is refused (400), not repaired.
|
|
862
|
-
*
|
|
863
|
-
* Note what is NOT traversal under this rule: `....` is an ordinary directory
|
|
864
|
-
* name, and `users/alice/....//bob/x` canonicalizes to
|
|
865
|
-
* `users/alice/..../bob/x` — still comfortably inside alice's prefix, which is
|
|
866
|
-
* exactly right. Only a real `..` segment is refused.
|
|
867
|
-
*/
|
|
868
|
-
/** Longest key accepted, in UTF-16 code units. Matches the previous cap. */
|
|
869
|
-
var MAX_STORAGE_KEY_LENGTH = 1024;
|
|
870
|
-
/**
|
|
871
|
-
* A key that cannot be canonicalized. Carries no path back to the caller
|
|
872
|
-
* beyond what they sent, so it is safe to surface as a 400 message.
|
|
873
|
-
*/
|
|
874
|
-
var InvalidStorageKeyError = class extends Error {
|
|
875
|
-
constructor(message) {
|
|
876
|
-
super(message);
|
|
877
|
-
this.name = "InvalidStorageKeyError";
|
|
878
|
-
}
|
|
879
|
-
};
|
|
880
|
-
/**
|
|
881
|
-
* Canonicalize a caller-supplied storage key, or throw
|
|
882
|
-
* {@link InvalidStorageKeyError}.
|
|
883
|
-
*
|
|
884
|
-
* Normalizations applied (safe, idempotent, and meaning-preserving):
|
|
885
|
-
* - leading slashes removed — `/a/b` and `a/b` name the same object
|
|
886
|
-
* - `.` segments and repeated slashes collapsed
|
|
887
|
-
*
|
|
888
|
-
* Refusals (the key means something other than what it says):
|
|
889
|
-
* - any `..` segment, on either separator, at any depth
|
|
890
|
-
* - null bytes
|
|
891
|
-
* - keys longer than {@link MAX_STORAGE_KEY_LENGTH}
|
|
892
|
-
*
|
|
893
|
-
* A trailing slash is preserved: it is how the folder route marks a prefix.
|
|
894
|
-
*/
|
|
895
|
-
function canonicalStorageKey(rawKey) {
|
|
896
|
-
if (rawKey.includes("\0")) throw new InvalidStorageKeyError("Storage key contains a null byte.");
|
|
897
|
-
if (rawKey.length > 1024) throw new InvalidStorageKeyError(`Storage key exceeds the maximum length of ${MAX_STORAGE_KEY_LENGTH} characters.`);
|
|
898
|
-
if (rawKey.split(/[\\/]/).some((segment) => segment === "..")) throw new InvalidStorageKeyError("Storage key contains a '..' path segment. Keys must name an object directly.");
|
|
899
|
-
const withoutLeadingSlashes = rawKey.replace(/^\/+/, "");
|
|
900
|
-
if (withoutLeadingSlashes === "") return "";
|
|
901
|
-
const denotesDirectory = /(?:^|\/)\.?$/.test(withoutLeadingSlashes);
|
|
902
|
-
const normalized = path.posix.normalize(withoutLeadingSlashes);
|
|
903
|
-
if (normalized === "." || normalized === "./") return "";
|
|
904
|
-
const key = normalized.replace(/^\.\//, "").replace(/^\/+/, "");
|
|
905
|
-
if (key === "") return "";
|
|
906
|
-
return denotesDirectory && !key.endsWith("/") ? `${key}/` : key;
|
|
907
|
-
}
|
|
908
|
-
/**
|
|
909
|
-
* Canonicalize, or return `null` when the key is not canonicalizable.
|
|
910
|
-
*
|
|
911
|
-
* For callers that must fail closed without an exception — the download-token
|
|
912
|
-
* middleware compares a request path against a granted path, and a key it
|
|
913
|
-
* cannot canonicalize simply matches nothing.
|
|
914
|
-
*/
|
|
915
|
-
function tryCanonicalStorageKey(rawKey) {
|
|
916
|
-
try {
|
|
917
|
-
return canonicalStorageKey(rawKey);
|
|
918
|
-
} catch {
|
|
919
|
-
return null;
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
/** A bucket name that does not name a bucket. See {@link canonicalStorageBucket}. */
|
|
923
|
-
var InvalidStorageBucketError = class extends Error {
|
|
924
|
-
constructor(message) {
|
|
925
|
-
super(message);
|
|
926
|
-
this.name = "InvalidStorageBucketError";
|
|
927
|
-
}
|
|
928
|
-
};
|
|
929
|
-
/**
|
|
930
|
-
* One path segment: letters, digits, `.`, `_`, `-`, first character
|
|
931
|
-
* alphanumeric. Deliberately narrow — it is the intersection of what S3, GCS
|
|
932
|
-
* and a filesystem directory all accept, and it makes `..`, `.tus-uploads`,
|
|
933
|
-
* absolute paths and anything containing a separator unrepresentable.
|
|
934
|
-
*/
|
|
935
|
-
var STORAGE_BUCKET_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
936
|
-
/**
|
|
937
|
-
* Canonicalize a caller-supplied bucket name, or throw
|
|
938
|
-
* {@link InvalidStorageBucketError}.
|
|
939
|
-
*
|
|
940
|
-
* The bucket is the *other* caller-controlled routing value in an upload
|
|
941
|
-
* request, and it was the one nobody validated. `LocalStorageController`
|
|
942
|
-
* builds `join(basePath, bucket)` and then checks containment against that
|
|
943
|
-
* result, so a bucket of `../../etc` moved the boundary rather than crossing
|
|
944
|
-
* it — the guard passed because the guard's reference point was the attacker's.
|
|
945
|
-
* The containment check now resolves against the storage root, and this is the
|
|
946
|
-
* check at the route boundary that stops the value before it gets there.
|
|
947
|
-
*
|
|
948
|
-
* A bucket is configuration, not user data: there is no legitimate caller that
|
|
949
|
-
* needs a separator, a leading dot, or a `..` in one. So this refuses rather
|
|
950
|
-
* than repairs, exactly as {@link canonicalStorageKey} does — a rewritten
|
|
951
|
-
* bucket would silently store the object somewhere the caller did not ask for.
|
|
952
|
-
*
|
|
953
|
-
* Returns `undefined` when the caller named no bucket (absent, or an empty
|
|
954
|
-
* form field), which is how every controller spells "use my default". An empty
|
|
955
|
-
* string used to reach `getFullPath` and resolve to the storage *root* rather
|
|
956
|
-
* than the `default` bucket, which is a third place a bare key did not
|
|
957
|
-
* round-trip.
|
|
958
|
-
*/
|
|
959
|
-
function canonicalStorageBucket(rawBucket) {
|
|
960
|
-
if (rawBucket === void 0 || rawBucket === null || rawBucket === "") return void 0;
|
|
961
|
-
if (rawBucket.length > 63) throw new InvalidStorageBucketError(`Storage bucket exceeds the maximum length of 63 characters.`);
|
|
962
|
-
if (!STORAGE_BUCKET_PATTERN.test(rawBucket)) throw new InvalidStorageBucketError("Storage bucket must be a single name of letters, digits, '.', '_' or '-', starting with a letter or digit.");
|
|
963
|
-
return rawBucket;
|
|
964
|
-
}
|
|
965
|
-
//#endregion
|
|
966
895
|
//#region src/auth/middleware.ts
|
|
967
896
|
/**
|
|
968
897
|
* Hono middleware that requires a valid JWT token via Authorization header.
|
|
@@ -1326,25 +1255,49 @@ var fileTokenAuth = async (c, next) => {
|
|
|
1326
1255
|
resolvedPath: tryCanonicalStorageKey(filePath)
|
|
1327
1256
|
};
|
|
1328
1257
|
};
|
|
1258
|
+
/**
|
|
1259
|
+
* Decide what a valid download token entitles this request to.
|
|
1260
|
+
*
|
|
1261
|
+
* One function rather than a copy per token location: a Bearer token and a
|
|
1262
|
+
* `?token=` are the same grant presented two ways, and the whole failure
|
|
1263
|
+
* this guards against is two places disagreeing about what was granted.
|
|
1264
|
+
*
|
|
1265
|
+
* `"no-path"` is distinct from the two denials because the caller treats it
|
|
1266
|
+
* differently — a request with no object path at all is not a denied read,
|
|
1267
|
+
* it is a request this middleware has nothing to say about, and on
|
|
1268
|
+
* `/metadata/*` it must still reach the downstream auth gate.
|
|
1269
|
+
*/
|
|
1270
|
+
const evaluateGrant = (payload) => {
|
|
1271
|
+
if (canonicalStorageId(c.req.query("storageId")) !== payload.storageId) return "deny-storage";
|
|
1272
|
+
const rawPath = extractWildcard(c);
|
|
1273
|
+
if (!rawPath) return "no-path";
|
|
1274
|
+
const { bucket, resolvedPath } = parseBucketPath(decodeURIComponent(rawPath));
|
|
1275
|
+
if (resolvedPath === null) return "deny-path";
|
|
1276
|
+
return isPathMatch(`${bucket}/${resolvedPath}`, payload.path) ? "grant" : "deny-path";
|
|
1277
|
+
};
|
|
1278
|
+
/**
|
|
1279
|
+
* Which half of the scope failed is named in the message. The holder of the
|
|
1280
|
+
* token already knows both values, so this reveals nothing to an attacker —
|
|
1281
|
+
* and without it, a client that forgets to forward `?storageId=` on the file
|
|
1282
|
+
* URL reports a "path mismatch" for a path that matches perfectly.
|
|
1283
|
+
*/
|
|
1284
|
+
const denyMismatch = (outcome) => c.json({ error: {
|
|
1285
|
+
message: outcome === "deny-storage" ? "Forbidden: Scoped token storage mismatch" : "Forbidden: Scoped token path mismatch",
|
|
1286
|
+
code: "FORBIDDEN"
|
|
1287
|
+
} }, 403);
|
|
1329
1288
|
const bearerToken = extractBearerToken(authHeader);
|
|
1330
1289
|
if (bearerToken !== void 0) {
|
|
1331
1290
|
const payload = verifyDownloadToken(bearerToken);
|
|
1332
1291
|
if (payload) {
|
|
1333
|
-
const
|
|
1334
|
-
if (
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
roles: ["reader"]
|
|
1341
|
-
});
|
|
1342
|
-
return next();
|
|
1343
|
-
} else return c.json({ error: {
|
|
1344
|
-
message: "Forbidden: Scoped token path mismatch",
|
|
1345
|
-
code: "FORBIDDEN"
|
|
1346
|
-
} }, 403);
|
|
1292
|
+
const outcome = evaluateGrant(payload);
|
|
1293
|
+
if (outcome === "grant") {
|
|
1294
|
+
c.set("user", {
|
|
1295
|
+
uid: "download-token",
|
|
1296
|
+
roles: ["reader"]
|
|
1297
|
+
});
|
|
1298
|
+
return next();
|
|
1347
1299
|
}
|
|
1300
|
+
if (outcome !== "no-path") return denyMismatch(outcome);
|
|
1348
1301
|
}
|
|
1349
1302
|
if (isFileRoute) return c.json({ error: {
|
|
1350
1303
|
message: "Unauthorized: Access JWT not allowed on file routes",
|
|
@@ -1355,21 +1308,15 @@ var fileTokenAuth = async (c, next) => {
|
|
|
1355
1308
|
if (queryToken) {
|
|
1356
1309
|
const payload = verifyDownloadToken(queryToken);
|
|
1357
1310
|
if (payload) {
|
|
1358
|
-
const
|
|
1359
|
-
if (
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
roles: ["reader"]
|
|
1366
|
-
});
|
|
1367
|
-
return next();
|
|
1368
|
-
} else return c.json({ error: {
|
|
1369
|
-
message: "Forbidden: Scoped token path mismatch",
|
|
1370
|
-
code: "FORBIDDEN"
|
|
1371
|
-
} }, 403);
|
|
1311
|
+
const outcome = evaluateGrant(payload);
|
|
1312
|
+
if (outcome === "grant") {
|
|
1313
|
+
c.set("user", {
|
|
1314
|
+
uid: "download-token",
|
|
1315
|
+
roles: ["reader"]
|
|
1316
|
+
});
|
|
1317
|
+
return next();
|
|
1372
1318
|
}
|
|
1319
|
+
if (outcome !== "no-path") return denyMismatch(outcome);
|
|
1373
1320
|
}
|
|
1374
1321
|
return c.json({ error: {
|
|
1375
1322
|
message: "Unauthorized: Invalid or unauthorized token",
|
|
@@ -7419,7 +7366,7 @@ function mountSessionRoutes(opts) {
|
|
|
7419
7366
|
clearRefreshCookie(c, config.cookieAuth);
|
|
7420
7367
|
const accessToken = extractBearerToken(c.req.header("authorization"));
|
|
7421
7368
|
if (ops.afterLogout && accessToken !== void 0) {
|
|
7422
|
-
const { verifyAccessToken } = await import("./jwt-
|
|
7369
|
+
const { verifyAccessToken } = await import("./jwt-VJyXTdQQ.js").then((n) => n.m);
|
|
7423
7370
|
const payload = verifyAccessToken(accessToken);
|
|
7424
7371
|
if (payload) ops.afterLogout(payload.uid).catch((err) => {
|
|
7425
7372
|
logger.error("[AuthHooks] afterLogout error", { error: err instanceof Error ? err.message : err });
|
|
@@ -9049,6 +8996,31 @@ function toAuthUserData(user) {
|
|
|
9049
8996
|
};
|
|
9050
8997
|
}
|
|
9051
8998
|
//#endregion
|
|
8999
|
+
//#region src/auth/jwks-routes.ts
|
|
9000
|
+
/**
|
|
9001
|
+
* `GET /.well-known/jwks.json` — the public keys that verify this issuer's
|
|
9002
|
+
* access tokens.
|
|
9003
|
+
*
|
|
9004
|
+
* Deliberately unauthenticated and world-readable: the whole point is that a
|
|
9005
|
+
* gateway, an edge function or a neighbouring service can check a Rebase token
|
|
9006
|
+
* without being trusted with anything. Public keys are not a secret, and
|
|
9007
|
+
* `jwt-keys.ts` derives what is served here from the public half of each pair.
|
|
9008
|
+
*
|
|
9009
|
+
* Mounted at the root rather than under `basePath`, because `/.well-known/` is
|
|
9010
|
+
* where every verifier looks — an issuer of `https://api.example.com` implies
|
|
9011
|
+
* `https://api.example.com/.well-known/jwks.json`, whatever the API happens to
|
|
9012
|
+
* be prefixed with.
|
|
9013
|
+
*/
|
|
9014
|
+
function createJwksRoutes() {
|
|
9015
|
+
const router = new Hono();
|
|
9016
|
+
router.get("/jwks.json", (c) => {
|
|
9017
|
+
const jwks = getJwks();
|
|
9018
|
+
c.header("Cache-Control", "public, max-age=300");
|
|
9019
|
+
return c.json(jwks);
|
|
9020
|
+
});
|
|
9021
|
+
return router;
|
|
9022
|
+
}
|
|
9023
|
+
//#endregion
|
|
9052
9024
|
//#region src/auth/oauth-code-flow.ts
|
|
9053
9025
|
/**
|
|
9054
9026
|
* Build the request schema for an authorization-code provider.
|
|
@@ -10478,6 +10450,7 @@ var auth_exports = /* @__PURE__ */ __exportAll({
|
|
|
10478
10450
|
createGitHubProvider: () => createGitHubProvider,
|
|
10479
10451
|
createGitLabProvider: () => createGitLabProvider,
|
|
10480
10452
|
createGoogleProvider: () => createGoogleProvider,
|
|
10453
|
+
createJwksRoutes: () => createJwksRoutes,
|
|
10481
10454
|
createLinkedinProvider: () => createLinkedinProvider,
|
|
10482
10455
|
createMicrosoftProvider: () => createMicrosoftProvider,
|
|
10483
10456
|
createRateLimiter: () => createRateLimiter,
|
|
@@ -10496,7 +10469,9 @@ var auth_exports = /* @__PURE__ */ __exportAll({
|
|
|
10496
10469
|
generateSecurePassword: () => generateSecurePassword,
|
|
10497
10470
|
generateSecureToken: () => generateSecureToken,
|
|
10498
10471
|
getAccessTokenExpiry: () => getAccessTokenExpiry,
|
|
10472
|
+
getJwks: () => getJwks,
|
|
10499
10473
|
getRefreshTokenExpiry: () => getRefreshTokenExpiry,
|
|
10474
|
+
hasAsymmetricSigningKey: () => hasAsymmetricSigningKey,
|
|
10500
10475
|
hashPassword: () => hashPassword,
|
|
10501
10476
|
hashRefreshToken: () => hashRefreshToken,
|
|
10502
10477
|
hashToken: () => hashToken,
|
|
@@ -10526,6 +10501,6 @@ var auth_exports = /* @__PURE__ */ __exportAll({
|
|
|
10526
10501
|
verifyPassword: () => verifyPassword
|
|
10527
10502
|
});
|
|
10528
10503
|
//#endregion
|
|
10529
|
-
export {
|
|
10504
|
+
export { extractUserFromToken as $, createDataRateLimiter as A, generateSecurePassword as B, createBuiltinAuthAdapter as C, isPublicStoragePath as Ct, string as D, object as E, resolveEmailLinkBase as F, getWelcomeEmailTemplate as G, getMagicLinkTemplate as H, resolveAuthHooks as I, html as J, RawHtml as K, hashPassword as L, SMTPEmailService as M, createEmailService as N, _coercedNumber as O, assertEmailLinkBases as P, createRequireAuth as Q, validatePasswordStrength as R, createJwksRoutes as S, PUBLIC_STORAGE_PREFIX as St, _enum as T, getPasswordResetTemplate as U, getEmailVerificationTemplate as V, getUserInvitationTemplate as W, createAdapterAuthMiddleware as X, raw as Y, createAuthMiddleware as Z, createLinkedinProvider as _, orderByEntriesToTuples as _t, createSpotifyProvider as a, requireAuth as at, pkceTokenParams as b, parseQueryOptions as bt, createGitLabProvider as c, createStorageApiKeyGuard as ct, createFacebookProvider as d, extractBearerToken as dt, fileTokenAuth as et, createAppleProvider as f, safeCompare as ft, createGitHubProvider as g, isOperationAllowed as gt, verifyOidcIdToken as h, httpMethodToOperation as ht, createApiKeyStore as i, requireAdmin as it, MemoryRateLimitStore as j, DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT as k, createDiscordProvider as l, isApiKeyToken as lt, tryVerifyOidcIdToken as m, scopeDataDriver as mt, createCustomAuthAdapter as n, publicObjectAuth as nt, createSlackProvider as o, createApiKeyPreAuth as ot, createMicrosoftProvider as p, SERVICE_IDENTITY as pt, escapeHtml as q, createApiKeyRoutes as r, queryTokenAuth as rt, createBitbucketProvider as s, createFunctionApiKeyGuard as st, auth_exports as t, optionalAuth as tt, createTwitterProvider as u, validateApiKey as ut, createGoogleProvider as v, parseAggregateSelect as vt, ZodNumber as w, providerVerifiedEmail as x, resolveListLimitParam as xt, oauthCodeFlowSchema as y, parseGroupBy as yt, verifyPassword as z };
|
|
10530
10505
|
|
|
10531
|
-
//# sourceMappingURL=auth-
|
|
10506
|
+
//# sourceMappingURL=auth-BobZVd0j.js.map
|