@mostajs/auth 2.3.2 → 2.3.3
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/lib/check-request.d.ts +10 -0
- package/dist/lib/check-request.js +47 -2
- package/package.json +1 -1
|
@@ -24,6 +24,16 @@ export interface CheckRequestParams {
|
|
|
24
24
|
}>;
|
|
25
25
|
/** Allow request through without apikey (legacy / public-mode). */
|
|
26
26
|
openMode?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* When no apikey is provided, fall back to a labeled apikey stored in
|
|
29
|
+
* the api_keys table (typically `public-default`). The fallback's
|
|
30
|
+
* permissions still apply — so a public-default key with read-only
|
|
31
|
+
* scope keeps writes blocked even when no key is sent.
|
|
32
|
+
*
|
|
33
|
+
* Use case : MCP endpoint that wants to preserve compat with existing
|
|
34
|
+
* mcp.so / Claude Desktop users who haven't published the apikey yet.
|
|
35
|
+
*/
|
|
36
|
+
fallbackPublicLabel?: string;
|
|
27
37
|
}
|
|
28
38
|
export interface CheckRequestResult {
|
|
29
39
|
ok: boolean;
|
|
@@ -36,7 +36,7 @@ function pickQuery(q, name) {
|
|
|
36
36
|
* (e.g. `if (!result.ok) reply.code(result.status).send(result.body)`).
|
|
37
37
|
*/
|
|
38
38
|
export async function checkRequest(params) {
|
|
39
|
-
const { dialect, headers, query, ip, transport, checks = [], openMode = false } = params;
|
|
39
|
+
const { dialect, headers, query, ip, transport, checks = [], openMode = false, fallbackPublicLabel } = params;
|
|
40
40
|
// Extract auth identifiers from the request
|
|
41
41
|
const apiKey = pickHeader(headers, 'x-api-key') ??
|
|
42
42
|
(pickHeader(headers, 'authorization')?.replace(/^Bearer\s+/i, '').trim() || undefined) ??
|
|
@@ -53,8 +53,53 @@ export async function checkRequest(params) {
|
|
|
53
53
|
projectName,
|
|
54
54
|
meta: { ip: resolvedIp },
|
|
55
55
|
};
|
|
56
|
-
// No apikey → either openMode
|
|
56
|
+
// No apikey → either fallback to a public labeled key, or openMode pass-through, or 401
|
|
57
57
|
if (!apiKey) {
|
|
58
|
+
// Fallback to a labeled public apikey (e.g. 'public-default') if configured.
|
|
59
|
+
// Looks up the row by label, applies its scope checks. Useful for MCP /
|
|
60
|
+
// demo endpoints that want to preserve compat with users who haven't yet
|
|
61
|
+
// published the public apikey in their config.
|
|
62
|
+
if (fallbackPublicLabel && dialect) {
|
|
63
|
+
try {
|
|
64
|
+
const { getApiKeyRepo, isScopeAuthorized } = await import('@mostajs/api-keys/server');
|
|
65
|
+
const repo = getApiKeyRepo(dialect);
|
|
66
|
+
const fallbackKey = await repo.findOne({ label: fallbackPublicLabel, enabled: true });
|
|
67
|
+
if (fallbackKey) {
|
|
68
|
+
// Normalize legacy permission shape (move projects/operations/transports into scopes)
|
|
69
|
+
const perms = fallbackKey.permissions || {};
|
|
70
|
+
const scopes = { ...(perms.scopes ?? {}) };
|
|
71
|
+
for (const k of ['projects', 'operations', 'transports']) {
|
|
72
|
+
if (perms[k] !== undefined && scopes[k] === undefined)
|
|
73
|
+
scopes[k] = perms[k];
|
|
74
|
+
}
|
|
75
|
+
const normPerms = { ...perms, scopes };
|
|
76
|
+
// Run the same scope checks against the public key's perms
|
|
77
|
+
for (const c of checks) {
|
|
78
|
+
if (!isScopeAuthorized(normPerms, c.scope, c.value)) {
|
|
79
|
+
return {
|
|
80
|
+
ok: false, status: 403,
|
|
81
|
+
body: { status: 'error', error: { code: 'FORBIDDEN',
|
|
82
|
+
message: `Public access not authorized for ${c.scope}="${c.value}"`, } },
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
ok: true, status: 200,
|
|
88
|
+
apikey: fallbackKey,
|
|
89
|
+
ctx: {
|
|
90
|
+
...baseCtx,
|
|
91
|
+
subscription: fallbackKey.label || fallbackKey.id,
|
|
92
|
+
permissions: normPerms.scopes ?? {},
|
|
93
|
+
accountId: fallbackKey.account || fallbackKey.accountId,
|
|
94
|
+
apikeyId: fallbackKey.id,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Fallback failed — fall through to standard 401 / openMode
|
|
101
|
+
}
|
|
102
|
+
}
|
|
58
103
|
if (openMode)
|
|
59
104
|
return { ok: true, status: 200, ctx: baseCtx };
|
|
60
105
|
return {
|