@tumbaland/backend-core 1.37.0 → 1.38.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apiKeys/ApiKey.d.ts +12 -3
- package/dist/apiKeys/ApiKey.d.ts.map +1 -1
- package/dist/apiKeys/ApiKey.js +5 -1
- package/dist/apiKeys/ApiKey.js.map +1 -1
- package/dist/apiKeys/index.d.ts +2 -2
- package/dist/apiKeys/index.d.ts.map +1 -1
- package/dist/apiKeys/index.js +5 -1
- package/dist/apiKeys/index.js.map +1 -1
- package/dist/apiKeys/middleware.d.ts +5 -3
- package/dist/apiKeys/middleware.d.ts.map +1 -1
- package/dist/apiKeys/middleware.js +56 -31
- package/dist/apiKeys/middleware.js.map +1 -1
- package/dist/apiKeys/service.d.ts +5 -3
- package/dist/apiKeys/service.d.ts.map +1 -1
- package/dist/apiKeys/service.js +10 -3
- package/dist/apiKeys/service.js.map +1 -1
- package/dist/apiKeys/types.d.ts +41 -8
- package/dist/apiKeys/types.d.ts.map +1 -1
- package/dist/apiKeys/types.js +35 -1
- package/dist/apiKeys/types.js.map +1 -1
- package/dist/oauth/models.d.ts +13 -2
- package/dist/oauth/models.d.ts.map +1 -1
- package/dist/oauth/models.js +9 -0
- package/dist/oauth/models.js.map +1 -1
- package/dist/oauth/service.d.ts +15 -8
- package/dist/oauth/service.d.ts.map +1 -1
- package/dist/oauth/service.js +10 -3
- package/dist/oauth/service.js.map +1 -1
- package/dist/oauth/tokens.d.ts +22 -5
- package/dist/oauth/tokens.d.ts.map +1 -1
- package/dist/oauth/tokens.js +5 -2
- package/dist/oauth/tokens.js.map +1 -1
- package/package.json +1 -1
- package/src/apiKeys/ApiKey.ts +16 -4
- package/src/apiKeys/index.ts +16 -2
- package/src/apiKeys/middleware.test.ts +103 -20
- package/src/apiKeys/middleware.ts +68 -35
- package/src/apiKeys/service.test.ts +56 -9
- package/src/apiKeys/service.ts +24 -6
- package/src/apiKeys/types.ts +61 -8
- package/src/middleware/authMiddleware.test.ts +4 -1
- package/src/oauth/models.ts +21 -2
- package/src/oauth/service.test.ts +58 -6
- package/src/oauth/service.ts +25 -7
- package/src/oauth/tokens.test.ts +20 -5
- package/src/oauth/tokens.ts +33 -7
package/dist/apiKeys/ApiKey.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import mongoose, { Document } from 'mongoose';
|
|
2
|
-
import type
|
|
2
|
+
import { type ApiKeyScope, type Tenant } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* One API key. Lives in backend-core rather than auth-service because both
|
|
5
5
|
* halves need it: auth-service issues and revokes keys, and every other service
|
|
@@ -30,8 +30,17 @@ export interface IApiKey extends Document {
|
|
|
30
30
|
sealedIv: string;
|
|
31
31
|
sealedTag: string;
|
|
32
32
|
scopes: ApiKeyScope[];
|
|
33
|
-
/**
|
|
34
|
-
|
|
33
|
+
/** every tenant this key may act in; see `Tenant` */
|
|
34
|
+
tenants: Tenant[];
|
|
35
|
+
/** the one it acts in when a request names none; always a member of `tenants` */
|
|
36
|
+
defaultTenant: Tenant;
|
|
37
|
+
/**
|
|
38
|
+
* The single tenant this key was pinned to, before tenants were a set.
|
|
39
|
+
*
|
|
40
|
+
* Kept so keys issued under the old model keep working; `readTenants` falls
|
|
41
|
+
* back to it. Nothing writes it any more.
|
|
42
|
+
*/
|
|
43
|
+
groupId?: string | null;
|
|
35
44
|
lastUsedAt?: Date;
|
|
36
45
|
expiresAt?: Date;
|
|
37
46
|
revokedAt?: Date;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiKey.d.ts","sourceRoot":"","sources":["../../src/apiKeys/ApiKey.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAE,QAAQ,EAAU,MAAM,UAAU,CAAC;AACtD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"ApiKey.d.ts","sourceRoot":"","sources":["../../src/apiKeys/ApiKey.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAE,QAAQ,EAAU,MAAM,UAAU,CAAC;AACtD,OAAO,EAAmB,KAAK,WAAW,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,OAAQ,SAAQ,QAAQ;IACvC,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;OAQG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,qDAAqD;IACrD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iFAAiF;IACjF,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,uFAAuF;IACvF,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,IAAI,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAgCD,eAAO,MAAM,MAAM;;;;;;gBAEgC,CAAC"}
|
package/dist/apiKeys/ApiKey.js
CHANGED
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.ApiKey = void 0;
|
|
37
37
|
const mongoose_1 = __importStar(require("mongoose"));
|
|
38
|
+
const types_1 = require("./types");
|
|
38
39
|
const ApiKeySchema = new mongoose_1.Schema({
|
|
39
40
|
userId: { type: String, required: true, index: true },
|
|
40
41
|
userEmail: { type: String, required: true },
|
|
@@ -46,8 +47,11 @@ const ApiKeySchema = new mongoose_1.Schema({
|
|
|
46
47
|
sealedIv: { type: String, required: true },
|
|
47
48
|
sealedTag: { type: String, required: true },
|
|
48
49
|
scopes: { type: [String], default: [] },
|
|
49
|
-
//
|
|
50
|
+
// Defaulted rather than optional: which data a key reaches is a decision the
|
|
50
51
|
// creator made, and it must not be indistinguishable from a field nobody set.
|
|
52
|
+
tenants: { type: [String], default: () => [types_1.PERSONAL_TENANT] },
|
|
53
|
+
defaultTenant: { type: String, default: types_1.PERSONAL_TENANT },
|
|
54
|
+
// Written by the single-tenant model this replaced; read-only now.
|
|
51
55
|
groupId: { type: String, default: null },
|
|
52
56
|
lastUsedAt: { type: Date },
|
|
53
57
|
expiresAt: { type: Date },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiKey.js","sourceRoot":"","sources":["../../src/apiKeys/ApiKey.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qDAAsD;
|
|
1
|
+
{"version":3,"file":"ApiKey.js","sourceRoot":"","sources":["../../src/apiKeys/ApiKey.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qDAAsD;AACtD,mCAAyE;AAqDzE,MAAM,YAAY,GAAG,IAAI,iBAAM,CAC7B;IACE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;IACrD,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC3C,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;IACvD,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE;IACjE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE;IAClE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;IACtC,gBAAgB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;IAClD,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC1C,SAAS,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;IAC3C,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE;IACvC,6EAA6E;IAC7E,8EAA8E;IAC9E,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,uBAAe,CAAC,EAAE;IAC7D,aAAa,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,uBAAe,EAAE;IACzD,mEAAmE;IACnE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;IACxC,UAAU,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;IAC1B,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;IACzB,SAAS,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;IACzB,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE;IACzC,cAAc,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;CAC/B,EACD,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,CAC7C,CAAC;AAEF,kDAAkD;AAClD,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;AAEpC,QAAA,MAAM,GAAG,kBAAQ,CAAC,MAAM,CAAC,MAAM;IAC1C,CAAC,CAAE,kBAAQ,CAAC,MAAM,CAAC,MAAkC;IACrD,CAAC,CAAC,kBAAQ,CAAC,KAAK,CAAU,QAAQ,EAAE,YAAY,CAAC,CAAC"}
|
package/dist/apiKeys/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { createApiKey, listApiKeys, revealApiKey, revokeApiKey, deleteApiKey, ve
|
|
|
4
4
|
export type { CreateApiKeyInput, CreatedApiKey } from './service';
|
|
5
5
|
export { authenticateAgent, requireScope, denyApiKeys } from './middleware';
|
|
6
6
|
export type { ApiKeyContext } from './middleware';
|
|
7
|
-
export { API_KEY_SCOPES, isApiKeyScope } from './types';
|
|
8
|
-
export type { ApiKeyScope, ApiKeySummary, ApiKeyVerification, ApiKeyRejection } from './types';
|
|
7
|
+
export { API_KEY_SCOPES, isApiKeyScope, PERSONAL_TENANT, groupIdOf, groupIdsOf, readTenants } from './types';
|
|
8
|
+
export type { ApiKeyScope, ApiKeySummary, ApiKeyVerification, ApiKeyRejection, ApiKeyTenant, Tenant } from './types';
|
|
9
9
|
export { looksLikeApiKey, displayPrefix, isEncryptionConfigured, resetEncryptionKeyCache } from './crypto';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/apiKeys/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EACL,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/apiKeys/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,YAAY,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EACL,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,YAAY,EACb,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC5E,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EACL,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,UAAU,EACV,WAAW,EACZ,MAAM,SAAS,CAAC;AACjB,YAAY,EACV,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,MAAM,EACP,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,eAAe,EACf,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACxB,MAAM,UAAU,CAAC"}
|
package/dist/apiKeys/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resetEncryptionKeyCache = exports.isEncryptionConfigured = exports.displayPrefix = exports.looksLikeApiKey = exports.isApiKeyScope = exports.API_KEY_SCOPES = exports.denyApiKeys = exports.requireScope = exports.authenticateAgent = exports.verifyApiKey = exports.deleteApiKey = exports.revokeApiKey = exports.revealApiKey = exports.listApiKeys = exports.createApiKey = exports.ApiKey = void 0;
|
|
3
|
+
exports.resetEncryptionKeyCache = exports.isEncryptionConfigured = exports.displayPrefix = exports.looksLikeApiKey = exports.readTenants = exports.groupIdsOf = exports.groupIdOf = exports.PERSONAL_TENANT = exports.isApiKeyScope = exports.API_KEY_SCOPES = exports.denyApiKeys = exports.requireScope = exports.authenticateAgent = exports.verifyApiKey = exports.deleteApiKey = exports.revokeApiKey = exports.revealApiKey = exports.listApiKeys = exports.createApiKey = exports.ApiKey = void 0;
|
|
4
4
|
var ApiKey_1 = require("./ApiKey");
|
|
5
5
|
Object.defineProperty(exports, "ApiKey", { enumerable: true, get: function () { return ApiKey_1.ApiKey; } });
|
|
6
6
|
var service_1 = require("./service");
|
|
@@ -17,6 +17,10 @@ Object.defineProperty(exports, "denyApiKeys", { enumerable: true, get: function
|
|
|
17
17
|
var types_1 = require("./types");
|
|
18
18
|
Object.defineProperty(exports, "API_KEY_SCOPES", { enumerable: true, get: function () { return types_1.API_KEY_SCOPES; } });
|
|
19
19
|
Object.defineProperty(exports, "isApiKeyScope", { enumerable: true, get: function () { return types_1.isApiKeyScope; } });
|
|
20
|
+
Object.defineProperty(exports, "PERSONAL_TENANT", { enumerable: true, get: function () { return types_1.PERSONAL_TENANT; } });
|
|
21
|
+
Object.defineProperty(exports, "groupIdOf", { enumerable: true, get: function () { return types_1.groupIdOf; } });
|
|
22
|
+
Object.defineProperty(exports, "groupIdsOf", { enumerable: true, get: function () { return types_1.groupIdsOf; } });
|
|
23
|
+
Object.defineProperty(exports, "readTenants", { enumerable: true, get: function () { return types_1.readTenants; } });
|
|
20
24
|
var crypto_1 = require("./crypto");
|
|
21
25
|
Object.defineProperty(exports, "looksLikeApiKey", { enumerable: true, get: function () { return crypto_1.looksLikeApiKey; } });
|
|
22
26
|
Object.defineProperty(exports, "displayPrefix", { enumerable: true, get: function () { return crypto_1.displayPrefix; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/apiKeys/index.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AAEf,qCAOmB;AANjB,uGAAA,YAAY,OAAA;AACZ,sGAAA,WAAW,OAAA;AACX,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AAGd,2CAA4E;AAAnE,+GAAA,iBAAiB,OAAA;AAAE,0GAAA,YAAY,OAAA;AAAE,yGAAA,WAAW,OAAA;AAErD,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/apiKeys/index.ts"],"names":[],"mappings":";;;AAAA,mCAAkC;AAAzB,gGAAA,MAAM,OAAA;AAEf,qCAOmB;AANjB,uGAAA,YAAY,OAAA;AACZ,sGAAA,WAAW,OAAA;AACX,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AACZ,uGAAA,YAAY,OAAA;AAGd,2CAA4E;AAAnE,+GAAA,iBAAiB,OAAA;AAAE,0GAAA,YAAY,OAAA;AAAE,yGAAA,WAAW,OAAA;AAErD,iCAOiB;AANf,uGAAA,cAAc,OAAA;AACd,sGAAA,aAAa,OAAA;AACb,wGAAA,eAAe,OAAA;AACf,kGAAA,SAAS,OAAA;AACT,mGAAA,UAAU,OAAA;AACV,oGAAA,WAAW,OAAA;AAUb,mCAKkB;AAJhB,yGAAA,eAAe,OAAA;AACf,uGAAA,aAAa,OAAA;AACb,gHAAA,sBAAsB,OAAA;AACtB,iHAAA,uBAAuB,OAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequestHandler } from 'express';
|
|
2
|
-
import { type ApiKeyScope } from './types';
|
|
2
|
+
import { type ApiKeyScope, type ApiKeyTenant, type Tenant } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Request-scoped facts about the key a request arrived on. Absent on ordinary
|
|
5
5
|
* session requests, which is itself the signal a handler needs: `req.apiKey`
|
|
@@ -8,8 +8,10 @@ import { type ApiKeyScope } from './types';
|
|
|
8
8
|
export interface ApiKeyContext {
|
|
9
9
|
keyId: string;
|
|
10
10
|
scopes: ApiKeyScope[];
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/** every tenant this credential may act in, and the one it acts in by default */
|
|
12
|
+
tenants: ApiKeyTenant;
|
|
13
|
+
/** the tenant this particular request resolved to */
|
|
14
|
+
actingAs: Tenant;
|
|
13
15
|
}
|
|
14
16
|
declare global {
|
|
15
17
|
namespace Express {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/apiKeys/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,cAAc,EAAY,MAAM,SAAS,CAAC;AAQ1E,OAAO,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/apiKeys/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,cAAc,EAAY,MAAM,SAAS,CAAC;AAQ1E,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,MAAM,EACZ,MAAM,SAAS,CAAC;AAEjB;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,iFAAiF;IACjF,OAAO,EAAE,YAAY,CAAC;IACtB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,OAAO,CAAC;QAChB,UAAU,OAAO;YACf,MAAM,CAAC,EAAE,aAAa,CAAC;SACxB;KACF;CACF;AAuID;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,GAAI,GAAG,gBAAgB,WAAW,EAAE,KAAG,cA+DlE,CAAC;AAEJ;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,YAAY,GAAI,GAAG,gBAAgB,WAAW,EAAE,KAAG,cAiB7D,CAAC;AAEJ;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,EAAE,cASzB,CAAC"}
|
|
@@ -20,10 +20,12 @@ const unauthorized = (res) => {
|
|
|
20
20
|
* The tenant a request is asking to act in, wherever it named one.
|
|
21
21
|
*
|
|
22
22
|
* Handlers read `groupId` from either the query string or the body depending on
|
|
23
|
-
* the verb, so both are checked — a
|
|
24
|
-
* no
|
|
23
|
+
* the verb, so both are checked — a rule that only covered one of them would be
|
|
24
|
+
* no rule at all. A caller names the personal tenant with the literal
|
|
25
|
+
* `personal`, since "no group" is what the resolved default gets written into
|
|
26
|
+
* and would otherwise be indistinguishable from not having asked.
|
|
25
27
|
*/
|
|
26
|
-
const
|
|
28
|
+
const requestedTenant = (req) => {
|
|
27
29
|
const fromQuery = req.query?.groupId;
|
|
28
30
|
if (typeof fromQuery === 'string' && fromQuery.length > 0)
|
|
29
31
|
return fromQuery;
|
|
@@ -32,33 +34,50 @@ const requestedGroupId = (req) => {
|
|
|
32
34
|
return fromBody;
|
|
33
35
|
return undefined;
|
|
34
36
|
};
|
|
37
|
+
/** Express 5 makes `req.query` a getter, so it is redefined rather than assigned. */
|
|
38
|
+
const setQueryGroupId = (req, groupId) => {
|
|
39
|
+
const query = { ...req.query };
|
|
40
|
+
if (groupId === undefined)
|
|
41
|
+
delete query.groupId;
|
|
42
|
+
else
|
|
43
|
+
query.groupId = groupId;
|
|
44
|
+
Object.defineProperty(req, 'query', {
|
|
45
|
+
value: query,
|
|
46
|
+
writable: true,
|
|
47
|
+
configurable: true,
|
|
48
|
+
enumerable: true
|
|
49
|
+
});
|
|
50
|
+
};
|
|
35
51
|
/**
|
|
36
|
-
* Hold a
|
|
52
|
+
* Hold a credential to the tenants it was granted, and settle which one this
|
|
53
|
+
* request is acting in.
|
|
54
|
+
*
|
|
55
|
+
* A request naming a tenant outside the grant is refused outright — that
|
|
56
|
+
* boundary is the whole reason a credential is safe to hand to an agent, and it
|
|
57
|
+
* is unchanged by the grant being a set rather than one. What the set adds is a
|
|
58
|
+
* choice *inside* it, which is what lets one connection reach a shared journal
|
|
59
|
+
* and a private photo library without reconnecting.
|
|
37
60
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* none has the pinned tenant written in for it, so the agent never has to know a
|
|
42
|
-
* group id exists, and a handler's `if (groupId) ... else personal` branch lands
|
|
43
|
-
* where the key's owner intended.
|
|
61
|
+
* A request that names nothing gets the default written in for it, so an agent
|
|
62
|
+
* granted a single tenant never has to know a group id exists and a handler's
|
|
63
|
+
* `if (groupId) … else personal` branch lands where the owner intended.
|
|
44
64
|
*/
|
|
45
|
-
const
|
|
46
|
-
const requested =
|
|
47
|
-
if (requested
|
|
48
|
-
return
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
const applyTenantGrant = (req, tenants) => {
|
|
66
|
+
const requested = requestedTenant(req) ?? tenants.default;
|
|
67
|
+
if (!tenants.allowed.includes(requested))
|
|
68
|
+
return null;
|
|
69
|
+
// Resolved either way, including when the caller asked for exactly what the
|
|
70
|
+
// default already was: handlers read the personal tenant as an absent
|
|
71
|
+
// `groupId`, so `personal` has to be erased rather than passed through.
|
|
72
|
+
const groupId = requested === types_1.PERSONAL_TENANT ? undefined : requested;
|
|
73
|
+
setQueryGroupId(req, groupId);
|
|
74
|
+
if (req.body && typeof req.body === 'object') {
|
|
75
|
+
if (groupId === undefined)
|
|
76
|
+
delete req.body.groupId;
|
|
77
|
+
else
|
|
58
78
|
req.body.groupId = groupId;
|
|
59
|
-
}
|
|
60
79
|
}
|
|
61
|
-
return
|
|
80
|
+
return requested;
|
|
62
81
|
};
|
|
63
82
|
/**
|
|
64
83
|
* Admit software acting for a user, on the terms its credential carries.
|
|
@@ -78,7 +97,8 @@ function admitAgent(req, res, next, requiredScopes, credential) {
|
|
|
78
97
|
});
|
|
79
98
|
return;
|
|
80
99
|
}
|
|
81
|
-
|
|
100
|
+
const actingAs = applyTenantGrant(req, credential.tenants);
|
|
101
|
+
if (actingAs === null) {
|
|
82
102
|
res.status(403).json({
|
|
83
103
|
success: false,
|
|
84
104
|
message: 'Credential is not permitted to act in the requested group'
|
|
@@ -86,11 +106,16 @@ function admitAgent(req, res, next, requiredScopes, credential) {
|
|
|
86
106
|
return;
|
|
87
107
|
}
|
|
88
108
|
req.user = { id: credential.userId, email: credential.email, name: credential.name };
|
|
89
|
-
// Only the
|
|
109
|
+
// Only the granted groups, never the owner's full membership: this is what
|
|
90
110
|
// stops a credential reaching a group it was not issued for through any
|
|
91
111
|
// handler that consults `userGroups` instead of the `groupId` parameter.
|
|
92
|
-
req.userGroups =
|
|
93
|
-
req.apiKey = {
|
|
112
|
+
req.userGroups = (0, types_1.groupIdsOf)(credential.tenants.allowed);
|
|
113
|
+
req.apiKey = {
|
|
114
|
+
keyId: credential.credentialId,
|
|
115
|
+
scopes: credential.scopes,
|
|
116
|
+
tenants: credential.tenants,
|
|
117
|
+
actingAs
|
|
118
|
+
};
|
|
94
119
|
next();
|
|
95
120
|
}
|
|
96
121
|
/**
|
|
@@ -143,7 +168,7 @@ const authenticateAgent = (...requiredScopes) => async function authenticateAgen
|
|
|
143
168
|
email: claims.email ?? '',
|
|
144
169
|
name: claims.name ?? '',
|
|
145
170
|
scopes: (claims.scope ?? '').split(' ').filter(types_1.isApiKeyScope),
|
|
146
|
-
|
|
171
|
+
tenants: (0, types_1.readTenants)(claims)
|
|
147
172
|
});
|
|
148
173
|
return;
|
|
149
174
|
}
|
|
@@ -161,7 +186,7 @@ const authenticateAgent = (...requiredScopes) => async function authenticateAgen
|
|
|
161
186
|
email: result.userEmail ?? '',
|
|
162
187
|
name: result.userName ?? '',
|
|
163
188
|
scopes: result.scopes ?? [],
|
|
164
|
-
|
|
189
|
+
tenants: result.tenants ?? (0, types_1.readTenants)({})
|
|
165
190
|
});
|
|
166
191
|
};
|
|
167
192
|
exports.authenticateAgent = authenticateAgent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/apiKeys/middleware.ts"],"names":[],"mappings":";;;;;;AACA,gEAA+B;AAC/B,uCAA2C;AAC3C,+DAAuC;AACvC,4CAA8E;AAE9E,qCAA2C;AAC3C,uCAAyC;AACzC,
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/apiKeys/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type ApiKeyScope, type ApiKeySummary, type ApiKeyVerification, type Tenant } from './types';
|
|
2
2
|
export interface CreateApiKeyInput {
|
|
3
3
|
userId: string;
|
|
4
4
|
userEmail: string;
|
|
5
5
|
userName: string;
|
|
6
6
|
name: string;
|
|
7
7
|
scopes: ApiKeyScope[];
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/** every tenant the key may act in; defaults to the user's own data alone */
|
|
9
|
+
tenants?: Tenant[];
|
|
10
|
+
/** which of them it acts in when a request names none */
|
|
11
|
+
defaultTenant?: Tenant;
|
|
10
12
|
expiresAt?: Date | null;
|
|
11
13
|
}
|
|
12
14
|
export interface CreatedApiKey {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/apiKeys/service.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/apiKeys/service.ts"],"names":[],"mappings":"AASA,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,MAAM,EACZ,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,aAAa,CAAC;IACvB,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;CACf;AAgBD,eAAO,MAAM,YAAY,GAAU,OAAO,iBAAiB,KAAG,OAAO,CAAC,aAAa,CA2BlF,CAAC;AAEF,eAAO,MAAM,WAAW,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,aAAa,EAAE,CAGzE,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,GAAU,QAAQ,MAAM,EAAE,IAAI,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAepF,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,YAAY,GAAU,QAAQ,MAAM,EAAE,IAAI,MAAM,KAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAU3F,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,QAAQ,MAAM,EAAE,IAAI,MAAM,KAAG,OAAO,CAAC,OAAO,CAG9E,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,GAAU,OAAO,OAAO,KAAG,OAAO,CAAC,kBAAkB,CA2B7E,CAAC"}
|
package/dist/apiKeys/service.js
CHANGED
|
@@ -3,18 +3,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.verifyApiKey = exports.deleteApiKey = exports.revokeApiKey = exports.revealApiKey = exports.listApiKeys = exports.createApiKey = void 0;
|
|
4
4
|
const ApiKey_1 = require("./ApiKey");
|
|
5
5
|
const crypto_1 = require("./crypto");
|
|
6
|
+
const types_1 = require("./types");
|
|
6
7
|
const toSummary = (key) => ({
|
|
7
8
|
id: String(key._id),
|
|
8
9
|
name: key.name,
|
|
9
10
|
prefix: (0, crypto_1.displayPrefix)(key.keyId),
|
|
10
11
|
scopes: key.scopes,
|
|
11
|
-
|
|
12
|
+
...(({ allowed, default: fallback }) => ({ tenants: allowed, defaultTenant: fallback }))((0, types_1.readTenants)(key)),
|
|
12
13
|
createdAt: key.createdAt.toISOString(),
|
|
13
14
|
lastUsedAt: key.lastUsedAt?.toISOString() ?? null,
|
|
14
15
|
expiresAt: key.expiresAt?.toISOString() ?? null,
|
|
15
16
|
revokedAt: key.revokedAt?.toISOString() ?? null
|
|
16
17
|
});
|
|
17
18
|
const createApiKey = async (input) => {
|
|
19
|
+
// A key that reaches nowhere would authenticate and then fail every call, so
|
|
20
|
+
// an empty grant is read as the safest non-empty one rather than stored.
|
|
21
|
+
const tenants = input.tenants?.length ? input.tenants : [types_1.PERSONAL_TENANT];
|
|
18
22
|
const { token, id, hash } = (0, crypto_1.generateKey)();
|
|
19
23
|
const sealed = (0, crypto_1.encryptSecret)(token);
|
|
20
24
|
const key = await ApiKey_1.ApiKey.create({
|
|
@@ -28,7 +32,10 @@ const createApiKey = async (input) => {
|
|
|
28
32
|
sealedIv: sealed.iv,
|
|
29
33
|
sealedTag: sealed.tag,
|
|
30
34
|
scopes: input.scopes,
|
|
31
|
-
|
|
35
|
+
tenants,
|
|
36
|
+
defaultTenant: tenants.includes(input.defaultTenant ?? '')
|
|
37
|
+
? input.defaultTenant
|
|
38
|
+
: tenants[0],
|
|
32
39
|
expiresAt: input.expiresAt ?? undefined
|
|
33
40
|
});
|
|
34
41
|
return { summary: toSummary(key), token };
|
|
@@ -115,7 +122,7 @@ const verifyApiKey = async (token) => {
|
|
|
115
122
|
userName: key.userName,
|
|
116
123
|
keyId: String(key._id),
|
|
117
124
|
scopes: key.scopes,
|
|
118
|
-
|
|
125
|
+
tenants: (0, types_1.readTenants)(key)
|
|
119
126
|
};
|
|
120
127
|
};
|
|
121
128
|
exports.verifyApiKey = verifyApiKey;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/apiKeys/service.ts"],"names":[],"mappings":";;;AAAA,qCAA2C;AAC3C,qCAOkB;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/apiKeys/service.ts"],"names":[],"mappings":"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"}
|
package/dist/apiKeys/types.d.ts
CHANGED
|
@@ -12,16 +12,48 @@ export declare const API_KEY_SCOPES: readonly ["relationship:read", "relationshi
|
|
|
12
12
|
export type ApiKeyScope = (typeof API_KEY_SCOPES)[number];
|
|
13
13
|
export declare const isApiKeyScope: (value: unknown) => value is ApiKeyScope;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* A tenant a credential may act in: a group id, or the user's own data.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* Personal is a named sentinel rather than `null` so that a tenant is always a
|
|
18
|
+
* plain string — storable in an array, sendable in a JWT claim, comparable
|
|
19
|
+
* without a special case at every layer. It cannot collide with a real tenant:
|
|
20
|
+
* group ids are 24-character hex.
|
|
21
|
+
*/
|
|
22
|
+
export declare const PERSONAL_TENANT = "personal";
|
|
23
|
+
export type Tenant = string;
|
|
24
|
+
/** The `groupId` a tenant corresponds to, as handlers have always read it. */
|
|
25
|
+
export declare const groupIdOf: (tenant: Tenant) => string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Which tenants a credential may act in, and which one it acts in by default.
|
|
28
|
+
*
|
|
29
|
+
* The web app picks a tenant per request from a selector; an agent has no such
|
|
30
|
+
* UI. So the *set* is chosen once, by a person, and the credential cannot escape
|
|
31
|
+
* it — but within that set the caller may name one per request, which is what
|
|
32
|
+
* lets a single connection reach both a shared journal and a private photo
|
|
33
|
+
* library without reconnecting.
|
|
34
|
+
*
|
|
35
|
+
* `allowed` is never empty and always contains `default`. A credential granted
|
|
36
|
+
* exactly one tenant behaves precisely as a pinned one did: nothing to name,
|
|
37
|
+
* nothing to get wrong.
|
|
21
38
|
*/
|
|
22
39
|
export interface ApiKeyTenant {
|
|
23
|
-
|
|
40
|
+
allowed: Tenant[];
|
|
41
|
+
default: Tenant;
|
|
24
42
|
}
|
|
43
|
+
/** Just the groups, for the `req.userGroups` every handler already reads. */
|
|
44
|
+
export declare const groupIdsOf: (tenants: Tenant[]) => string[];
|
|
45
|
+
/**
|
|
46
|
+
* Read a stored tenant grant, tolerating one written before tenants were a set.
|
|
47
|
+
*
|
|
48
|
+
* Keys and grants issued under the old single-tenant model carry `groupId`
|
|
49
|
+
* alone. Falling back to it here means they keep working across the deploy
|
|
50
|
+
* rather than every connected assistant breaking at once.
|
|
51
|
+
*/
|
|
52
|
+
export declare const readTenants: (stored: {
|
|
53
|
+
tenants?: Tenant[] | null;
|
|
54
|
+
defaultTenant?: Tenant | null;
|
|
55
|
+
groupId?: string | null;
|
|
56
|
+
}) => ApiKeyTenant;
|
|
25
57
|
/** A key as the API hands it back — never including the secret. */
|
|
26
58
|
export interface ApiKeySummary {
|
|
27
59
|
id: string;
|
|
@@ -29,7 +61,8 @@ export interface ApiKeySummary {
|
|
|
29
61
|
/** the public half, shown in listings so a key is identifiable at a glance */
|
|
30
62
|
prefix: string;
|
|
31
63
|
scopes: ApiKeyScope[];
|
|
32
|
-
|
|
64
|
+
tenants: Tenant[];
|
|
65
|
+
defaultTenant: Tenant;
|
|
33
66
|
createdAt: string;
|
|
34
67
|
lastUsedAt: string | null;
|
|
35
68
|
expiresAt: string | null;
|
|
@@ -45,6 +78,6 @@ export interface ApiKeyVerification {
|
|
|
45
78
|
userName?: string;
|
|
46
79
|
keyId?: string;
|
|
47
80
|
scopes?: ApiKeyScope[];
|
|
48
|
-
|
|
81
|
+
tenants?: ApiKeyTenant;
|
|
49
82
|
}
|
|
50
83
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/apiKeys/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,cAAc,oHAOjB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,WAC4B,CAAC;AAErF;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/apiKeys/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,+EAA+E;AAC/E,eAAO,MAAM,cAAc,oHAOjB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE1D,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,WAC4B,CAAC;AAErF;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,aAAa,CAAC;AAE1C,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B,8EAA8E;AAC9E,eAAO,MAAM,SAAS,GAAI,QAAQ,MAAM,KAAG,MAAM,GAAG,IACR,CAAC;AAE7C;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,6EAA6E;AAC7E,eAAO,MAAM,UAAU,GAAI,SAAS,MAAM,EAAE,KAAG,MAAM,EACG,CAAC;AAEzD;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GAAI,QAAQ;IAClC,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,KAAG,YAaH,CAAC;AAEF,mEAAmE;AACnE,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,2FAA2F;AAC3F,MAAM,MAAM,eAAe,GACvB,WAAW,GACX,SAAS,GACT,SAAS,GACT,SAAS,GACT,YAAY,CAAC;AAEjB,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB"}
|
package/dist/apiKeys/types.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* it writes to rather than choosing one per request the way the UI does.
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.isApiKeyScope = exports.API_KEY_SCOPES = void 0;
|
|
12
|
+
exports.readTenants = exports.groupIdsOf = exports.groupIdOf = exports.PERSONAL_TENANT = exports.isApiKeyScope = exports.API_KEY_SCOPES = void 0;
|
|
13
13
|
/** Everything a key may be granted. Read and write are separate on purpose. */
|
|
14
14
|
exports.API_KEY_SCOPES = [
|
|
15
15
|
'relationship:read',
|
|
@@ -21,4 +21,38 @@ exports.API_KEY_SCOPES = [
|
|
|
21
21
|
];
|
|
22
22
|
const isApiKeyScope = (value) => typeof value === 'string' && exports.API_KEY_SCOPES.includes(value);
|
|
23
23
|
exports.isApiKeyScope = isApiKeyScope;
|
|
24
|
+
/**
|
|
25
|
+
* A tenant a credential may act in: a group id, or the user's own data.
|
|
26
|
+
*
|
|
27
|
+
* Personal is a named sentinel rather than `null` so that a tenant is always a
|
|
28
|
+
* plain string — storable in an array, sendable in a JWT claim, comparable
|
|
29
|
+
* without a special case at every layer. It cannot collide with a real tenant:
|
|
30
|
+
* group ids are 24-character hex.
|
|
31
|
+
*/
|
|
32
|
+
exports.PERSONAL_TENANT = 'personal';
|
|
33
|
+
/** The `groupId` a tenant corresponds to, as handlers have always read it. */
|
|
34
|
+
const groupIdOf = (tenant) => tenant === exports.PERSONAL_TENANT ? null : tenant;
|
|
35
|
+
exports.groupIdOf = groupIdOf;
|
|
36
|
+
/** Just the groups, for the `req.userGroups` every handler already reads. */
|
|
37
|
+
const groupIdsOf = (tenants) => tenants.filter((tenant) => tenant !== exports.PERSONAL_TENANT);
|
|
38
|
+
exports.groupIdsOf = groupIdsOf;
|
|
39
|
+
/**
|
|
40
|
+
* Read a stored tenant grant, tolerating one written before tenants were a set.
|
|
41
|
+
*
|
|
42
|
+
* Keys and grants issued under the old single-tenant model carry `groupId`
|
|
43
|
+
* alone. Falling back to it here means they keep working across the deploy
|
|
44
|
+
* rather than every connected assistant breaking at once.
|
|
45
|
+
*/
|
|
46
|
+
const readTenants = (stored) => {
|
|
47
|
+
const allowed = stored.tenants && stored.tenants.length > 0
|
|
48
|
+
? stored.tenants
|
|
49
|
+
: [stored.groupId ?? exports.PERSONAL_TENANT];
|
|
50
|
+
const fallbackDefault = allowed[0];
|
|
51
|
+
const preferred = stored.defaultTenant ?? fallbackDefault;
|
|
52
|
+
return {
|
|
53
|
+
allowed,
|
|
54
|
+
default: allowed.includes(preferred) ? preferred : fallbackDefault
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
exports.readTenants = readTenants;
|
|
24
58
|
//# sourceMappingURL=types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/apiKeys/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEH,+EAA+E;AAClE,QAAA,cAAc,GAAG;IAC5B,mBAAmB;IACnB,oBAAoB;IACpB,YAAY;IACZ,aAAa;IACb,cAAc;IACd,eAAe;CACP,CAAC;AAIJ,MAAM,aAAa,GAAG,CAAC,KAAc,EAAwB,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAK,sBAAoC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AADxE,QAAA,aAAa,iBAC2D"}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/apiKeys/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEH,+EAA+E;AAClE,QAAA,cAAc,GAAG;IAC5B,mBAAmB;IACnB,oBAAoB;IACpB,YAAY;IACZ,aAAa;IACb,cAAc;IACd,eAAe;CACP,CAAC;AAIJ,MAAM,aAAa,GAAG,CAAC,KAAc,EAAwB,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAK,sBAAoC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AADxE,QAAA,aAAa,iBAC2D;AAErF;;;;;;;GAOG;AACU,QAAA,eAAe,GAAG,UAAU,CAAC;AAI1C,8EAA8E;AACvE,MAAM,SAAS,GAAG,CAAC,MAAc,EAAiB,EAAE,CACzD,MAAM,KAAK,uBAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AADhC,QAAA,SAAS,aACuB;AAoB7C,6EAA6E;AACtE,MAAM,UAAU,GAAG,CAAC,OAAiB,EAAY,EAAE,CACxD,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,uBAAe,CAAC,CAAC;AAD5C,QAAA,UAAU,cACkC;AAEzD;;;;;;GAMG;AACI,MAAM,WAAW,GAAG,CAAC,MAI3B,EAAgB,EAAE;IACjB,MAAM,OAAO,GACX,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;QACzC,CAAC,CAAC,MAAM,CAAC,OAAO;QAChB,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,IAAI,uBAAe,CAAC,CAAC;IAE1C,MAAM,eAAe,GAAG,OAAO,CAAC,CAAC,CAAW,CAAC;IAC7C,MAAM,SAAS,GAAG,MAAM,CAAC,aAAa,IAAI,eAAe,CAAC;IAE1D,OAAO;QACL,OAAO;QACP,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,eAAe;KACnE,CAAC;AACJ,CAAC,CAAC;AAjBW,QAAA,WAAW,eAiBtB"}
|
package/dist/oauth/models.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import mongoose, { Document } from 'mongoose';
|
|
2
|
+
import { type Tenant } from '../apiKeys/types';
|
|
2
3
|
/**
|
|
3
4
|
* A client registered through RFC 7591 Dynamic Client Registration.
|
|
4
5
|
*
|
|
@@ -38,7 +39,13 @@ export interface IAuthorizationCode extends Document {
|
|
|
38
39
|
userName: string;
|
|
39
40
|
redirectUri: string;
|
|
40
41
|
scopes: string[];
|
|
41
|
-
|
|
42
|
+
/** every tenant the user approved, and the one chosen as the default */
|
|
43
|
+
tenants: Tenant[];
|
|
44
|
+
defaultTenant: Tenant;
|
|
45
|
+
/** what each approved tenant is called, snapshotted at consent */
|
|
46
|
+
tenantNames: Record<Tenant, string>;
|
|
47
|
+
/** the single tenant an older grant was pinned to; read by `readTenants` */
|
|
48
|
+
groupId?: string | null;
|
|
42
49
|
/** RFC 8707: the MCP server this will be minted for */
|
|
43
50
|
resource: string;
|
|
44
51
|
/** PKCE, always S256 — the spec does not allow `plain` */
|
|
@@ -68,7 +75,11 @@ export interface IRefreshToken extends Document {
|
|
|
68
75
|
clientId: string;
|
|
69
76
|
userId: string;
|
|
70
77
|
scopes: string[];
|
|
71
|
-
|
|
78
|
+
tenants: Tenant[];
|
|
79
|
+
defaultTenant: Tenant;
|
|
80
|
+
tenantNames: Record<Tenant, string>;
|
|
81
|
+
/** the single tenant an older grant was pinned to; read by `readTenants` */
|
|
82
|
+
groupId?: string | null;
|
|
72
83
|
resource: string;
|
|
73
84
|
/**
|
|
74
85
|
* When the user actually connected this assistant.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/oauth/models.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAE,QAAQ,EAAU,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/oauth/models.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,EAAE,EAAE,QAAQ,EAAU,MAAM,UAAU,CAAC;AACtD,OAAO,EAAmB,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEhE;;;;;;;GAOG;AACH,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAWD,eAAO,MAAM,WAAW;;;;;;qBAE0C,CAAC;AAEnE;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAmB,SAAQ,QAAQ;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,wEAAwE;IACxE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,0DAA0D;IAC1D,aAAa,EAAE,MAAM,CAAC;IACtB,mFAAmF;IACnF,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AA4BD,eAAO,MAAM,iBAAiB;;;;;;2BAEsD,CAAC;AAErF;;;;;;;GAOG;AACH,MAAM,WAAW,aAAc,SAAQ,QAAQ;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,UAAU,CAAC,EAAE,IAAI,CAAC;IAClB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAuBD,eAAO,MAAM,YAAY;;;;;;sBAE4C,CAAC"}
|