mcp-google-multi 6.0.0-alpha.59 → 6.0.0-alpha.60
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/mcp-token.d.ts +7 -2
- package/dist/mcp-token.js +14 -2
- package/dist/oauth-as.d.ts +6 -0
- package/dist/oauth-as.js +44 -1
- package/dist/usage-metrics.js +1 -1
- package/package.json +1 -1
package/dist/mcp-token.d.ts
CHANGED
|
@@ -99,8 +99,13 @@ export declare class RefreshStore {
|
|
|
99
99
|
/** Rotate a presented refresh token; null if unknown OR if the presented
|
|
100
100
|
* token was already rotated away (reuse => the family is revoked). The
|
|
101
101
|
* record's sub is copied forward and returned so the caller can mint the
|
|
102
|
-
* matching access token without trusting anything client-supplied.
|
|
103
|
-
|
|
102
|
+
* matching access token without trusting anything client-supplied.
|
|
103
|
+
*
|
|
104
|
+
* `accept` (optional) is called inside the store lock, before any mutation,
|
|
105
|
+
* with the record's sub. false: every active record of that sub is dropped
|
|
106
|
+
* and null returned. A throw: nothing is mutated and the error propagates,
|
|
107
|
+
* so the presented token stays valid. Absent: unchanged. */
|
|
108
|
+
rotate(oldToken: string, nowMs: number, accept?: (sub: string) => boolean): {
|
|
104
109
|
token: string;
|
|
105
110
|
sub: string;
|
|
106
111
|
} | null;
|
package/dist/mcp-token.js
CHANGED
|
@@ -175,8 +175,13 @@ export class RefreshStore {
|
|
|
175
175
|
/** Rotate a presented refresh token; null if unknown OR if the presented
|
|
176
176
|
* token was already rotated away (reuse => the family is revoked). The
|
|
177
177
|
* record's sub is copied forward and returned so the caller can mint the
|
|
178
|
-
* matching access token without trusting anything client-supplied.
|
|
179
|
-
|
|
178
|
+
* matching access token without trusting anything client-supplied.
|
|
179
|
+
*
|
|
180
|
+
* `accept` (optional) is called inside the store lock, before any mutation,
|
|
181
|
+
* with the record's sub. false: every active record of that sub is dropped
|
|
182
|
+
* and null returned. A throw: nothing is mutated and the error propagates,
|
|
183
|
+
* so the presented token stays valid. Absent: unchanged. */
|
|
184
|
+
rotate(oldToken, nowMs, accept) {
|
|
180
185
|
return withFileLock(this.path, () => {
|
|
181
186
|
const data = this.load();
|
|
182
187
|
const rec = data.active[oldToken];
|
|
@@ -191,6 +196,13 @@ export class RefreshStore {
|
|
|
191
196
|
}
|
|
192
197
|
return null;
|
|
193
198
|
}
|
|
199
|
+
if (accept && !accept(rec.sub)) {
|
|
200
|
+
for (const [t, r] of Object.entries(data.active))
|
|
201
|
+
if (r.sub === rec.sub)
|
|
202
|
+
delete data.active[t];
|
|
203
|
+
this.save(data);
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
194
206
|
delete data.active[oldToken];
|
|
195
207
|
data.spent[oldToken] = rec.family;
|
|
196
208
|
const next = randomBytes(32).toString('base64url');
|
package/dist/oauth-as.d.ts
CHANGED
|
@@ -103,6 +103,12 @@ export interface AuthServerDeps {
|
|
|
103
103
|
* A returned refusal answers 403 with its slug; a throw or rejection answers
|
|
104
104
|
* 500 E_ALIAS_ADD_FAILED. */
|
|
105
105
|
bindTenantAlias?: (bind: TenantAliasBind) => void | AliasBindRefusal | Promise<void | AliasBindRefusal>;
|
|
106
|
+
/** False for a subject this host no longer serves (a multi-tenant host that
|
|
107
|
+
* removed or suspended it): /token then issues and rotates nothing for it
|
|
108
|
+
* (400 invalid_grant; a rotated sub's refresh tokens are dropped). A throw
|
|
109
|
+
* means the answer is unknown right now: 503 temporarily_unavailable, and
|
|
110
|
+
* the presented refresh token is not spent. Absent: every subject is active. */
|
|
111
|
+
subjectActive?: (sub: string) => boolean;
|
|
106
112
|
}
|
|
107
113
|
export interface AuthServer {
|
|
108
114
|
routes: Record<string, RouteHandler>;
|
package/dist/oauth-as.js
CHANGED
|
@@ -56,6 +56,11 @@ function errorPage(res, status, slug, message) {
|
|
|
56
56
|
res.end(`${slug}: ${message}`);
|
|
57
57
|
return true;
|
|
58
58
|
}
|
|
59
|
+
class SubjectCheckUnavailable extends Error {
|
|
60
|
+
}
|
|
61
|
+
function subjectUnavailable(res) {
|
|
62
|
+
return json(res, 503, { error: 'temporarily_unavailable', message: 'the sign-in could not be checked right now; retry shortly' }, { 'Retry-After': '5' });
|
|
63
|
+
}
|
|
59
64
|
function redirect(res, location) {
|
|
60
65
|
res.writeHead(302, { Location: location });
|
|
61
66
|
res.end();
|
|
@@ -508,12 +513,50 @@ export function buildAuthServer(config, deps = {}) {
|
|
|
508
513
|
return json(res, 400, { error: 'invalid_grant', message: 'PKCE verification failed' });
|
|
509
514
|
}
|
|
510
515
|
const access = await signAccessToken({ base, secret, iat: nowSec(), ttlSec: accessTtl, sub: code.sub });
|
|
516
|
+
if (deps.subjectActive) {
|
|
517
|
+
let active;
|
|
518
|
+
try {
|
|
519
|
+
active = deps.subjectActive(code.sub);
|
|
520
|
+
}
|
|
521
|
+
catch (e) {
|
|
522
|
+
log(`token deferred grant=authorization_code: subject check failed: ${e instanceof Error ? e.message : 'non-Error throw'}`);
|
|
523
|
+
return subjectUnavailable(res);
|
|
524
|
+
}
|
|
525
|
+
if (!active) {
|
|
526
|
+
log('token refused grant=authorization_code: subject inactive');
|
|
527
|
+
return json(res, 400, { error: 'invalid_grant', message: 'the subject is no longer provisioned' });
|
|
528
|
+
}
|
|
529
|
+
}
|
|
511
530
|
const refreshToken = refresh.issue(now(), code.sub);
|
|
512
531
|
log('token issued grant=authorization_code');
|
|
513
532
|
return json(res, 200, { access_token: access, token_type: 'Bearer', expires_in: accessTtl, refresh_token: refreshToken, scope: 'mcp:use' });
|
|
514
533
|
}
|
|
515
534
|
if (grant === 'refresh_token') {
|
|
516
|
-
const
|
|
535
|
+
const subjectActive = deps.subjectActive;
|
|
536
|
+
const accept = subjectActive
|
|
537
|
+
? (sub) => {
|
|
538
|
+
let active;
|
|
539
|
+
try {
|
|
540
|
+
active = subjectActive(sub);
|
|
541
|
+
}
|
|
542
|
+
catch (e) {
|
|
543
|
+
throw new SubjectCheckUnavailable('subject check failed', { cause: e });
|
|
544
|
+
}
|
|
545
|
+
if (!active)
|
|
546
|
+
log('token refused grant=refresh_token: subject inactive');
|
|
547
|
+
return active;
|
|
548
|
+
}
|
|
549
|
+
: undefined;
|
|
550
|
+
let next;
|
|
551
|
+
try {
|
|
552
|
+
next = refresh.rotate(form.refresh_token ?? '', now(), accept);
|
|
553
|
+
}
|
|
554
|
+
catch (e) {
|
|
555
|
+
if (!(e instanceof SubjectCheckUnavailable))
|
|
556
|
+
throw e;
|
|
557
|
+
log(`token deferred grant=refresh_token: subject check failed: ${e.cause instanceof Error ? e.cause.message : 'non-Error throw'}`);
|
|
558
|
+
return subjectUnavailable(res);
|
|
559
|
+
}
|
|
517
560
|
if (!next)
|
|
518
561
|
return json(res, 400, { error: 'invalid_grant', message: 'unknown or rotated refresh token' });
|
|
519
562
|
const access = await signAccessToken({ base, secret, iat: nowSec(), ttlSec: accessTtl, sub: next.sub });
|
package/dist/usage-metrics.js
CHANGED
|
@@ -44,7 +44,7 @@ export const KNOWN_ERROR_SLUGS = new Set([
|
|
|
44
44
|
'insufficient_scope', 'internal', 'invalid_client', 'invalid_client_metadata',
|
|
45
45
|
'invalid_grant', 'invalid_params', 'invalid_query', 'invalid_request',
|
|
46
46
|
'invalid_scope', 'network_error', 'not_found', 'rate_limited',
|
|
47
|
-
'reauth_required', 'recipient_not_allowed', 'tenant_not_found', 'too_large', 'toolset_disabled', 'unknown_api',
|
|
47
|
+
'reauth_required', 'recipient_not_allowed', 'temporarily_unavailable', 'tenant_not_found', 'too_large', 'toolset_disabled', 'unknown_api',
|
|
48
48
|
'unknown_argument', 'unknown_method', 'unsupported_grant_type', 'unsupported_type',
|
|
49
49
|
'untrusted_host', 'upstream_error', 'validation_error', 'write_disabled',
|
|
50
50
|
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-google-multi",
|
|
3
|
-
"version": "6.0.0-alpha.
|
|
3
|
+
"version": "6.0.0-alpha.60",
|
|
4
4
|
"mcpName": "io.github.bakissation/mcp-google-multi",
|
|
5
5
|
"description": "Local MCP server for Google Workspace (Gmail, Drive, Calendar, Sheets, Docs, Contacts, Tasks, Meet, Search Console, +Forms/Chat/Admin) across multiple accounts — OAuth-only, encrypted token storage, deny-by-default writes.",
|
|
6
6
|
"type": "module",
|