@roarkanalytics/sdk-mcp 4.12.1 → 4.12.2
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/CHANGELOG.md +7 -0
- package/http.d.mts.map +1 -1
- package/http.d.ts.map +1 -1
- package/http.js +14 -1
- package/http.js.map +1 -1
- package/http.mjs +15 -2
- package/http.mjs.map +1 -1
- package/oauth.d.mts +57 -1
- package/oauth.d.mts.map +1 -1
- package/oauth.d.ts +57 -1
- package/oauth.d.ts.map +1 -1
- package/oauth.js +133 -1
- package/oauth.js.map +1 -1
- package/oauth.mjs +128 -0
- package/oauth.mjs.map +1 -1
- package/package.json +1 -1
- package/server.js +1 -1
- package/server.mjs +1 -1
- package/src/http.ts +16 -1
- package/src/oauth.ts +171 -2
- package/src/server.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.12.2](https://github.com/roarkhq/mcp-roark-analytics/compare/v4.12.1...v4.12.2) (2026-09-29)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **mcp:** the resource server accepted any bearer, valid or not ([#132](https://github.com/roarkhq/mcp-roark-analytics/issues/132)) ([eef8fbd](https://github.com/roarkhq/mcp-roark-analytics/commit/eef8fbdc837817a60dd45328eb72fa055a7ef99b))
|
|
9
|
+
|
|
3
10
|
## [4.12.1](https://github.com/roarkhq/mcp-roark-analytics/compare/v4.12.0...v4.12.1) (2026-09-29)
|
|
4
11
|
|
|
5
12
|
|
package/http.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.mts","sourceRoot":"","sources":["src/http.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,OAAO,MAAM,SAAS,CAAC;AAK9B,OAAO,EAAE,UAAU,EAAE,sBAAkB;
|
|
1
|
+
{"version":3,"file":"http.d.mts","sourceRoot":"","sources":["src/http.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,OAAO,MAAM,SAAS,CAAC;AAK9B,OAAO,EAAE,UAAU,EAAE,sBAAkB;AA6LvC,eAAO,MAAM,iBAAiB,GAAI,gCAG/B;IACD,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,EAAE,UAAU,CAAC;CACxB,KAAG,OAAO,CAAC,OAyHX,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAU,uBAG9C;IACD,UAAU,EAAE,UAAU,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC,kBAcA,CAAC"}
|
package/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["src/http.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,OAAO,MAAM,SAAS,CAAC;AAK9B,OAAO,EAAE,UAAU,EAAE,qBAAkB;
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["src/http.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,OAAO,MAAM,SAAS,CAAC;AAK9B,OAAO,EAAE,UAAU,EAAE,qBAAkB;AA6LvC,eAAO,MAAM,iBAAiB,GAAI,gCAG/B;IACD,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,UAAU,EAAE,UAAU,CAAC;CACxB,KAAG,OAAO,CAAC,OAyHX,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAU,uBAG9C;IACD,UAAU,EAAE,UAAU,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;CACnC,kBAcA,CAAC"}
|
package/http.js
CHANGED
|
@@ -37,7 +37,20 @@ const newServer = async ({ clientOptions, mcpOptions, req, res, }) => {
|
|
|
37
37
|
// not be able to make us do that.
|
|
38
38
|
let authOptions;
|
|
39
39
|
if (mcpOptions.oauth) {
|
|
40
|
-
|
|
40
|
+
const connector = connectorFrom(req);
|
|
41
|
+
const authorized = (0, oauth_1.requireBearer)(req, mcpOptions.oauth, connector);
|
|
42
|
+
// Presence is not validity. Ask customer-api whether the token is still live, so a revoked or
|
|
43
|
+
// expired one is answered with 401 + WWW-Authenticate here - the signal a client needs to
|
|
44
|
+
// re-run the OAuth flow - rather than completing the handshake and failing later inside a
|
|
45
|
+
// tool result, where nothing is listening for it.
|
|
46
|
+
//
|
|
47
|
+
// The base URL is resolved the way the SDK resolves it, NOT defaulted to prod: this server is
|
|
48
|
+
// launched without `clientOptions`, so on any stage but prod a prod default would check the
|
|
49
|
+
// token against a server that never issued it and refuse every request.
|
|
50
|
+
await (0, oauth_1.requireLiveBearer)(authorized.bearerToken, mcpOptions.oauth, connector, {
|
|
51
|
+
baseURL: (0, oauth_1.resolveApiBaseUrl)(clientOptions),
|
|
52
|
+
});
|
|
53
|
+
authOptions = authorized;
|
|
41
54
|
}
|
|
42
55
|
else {
|
|
43
56
|
authOptions = (0, auth_1.parseClientAuthHeaders)(req, false);
|
package/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["src/http.ts"],"names":[],"mappings":";AAAA,sFAAsF;;;;;;AAGtF,0FAAmG;AAEnG,gDAAwB;AACxB,sDAA8B;AAC9B,gDAAwB;AACxB,0DAAiC;AACjC,oCAAoE;AACpE,wCAAqC;AAErC,
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["src/http.ts"],"names":[],"mappings":"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"}
|
package/http.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import pino from 'pino';
|
|
|
6
6
|
import pinoHttp from 'pino-http';
|
|
7
7
|
import { getStainlessApiKey, parseClientAuthHeaders } from "./auth.mjs";
|
|
8
8
|
import { getLogger } from "./logger.mjs";
|
|
9
|
-
import { ORIGIN_CONNECTOR, protectedResourceMetadata, requireBearer, UnauthorizedError, } from "./oauth.mjs";
|
|
9
|
+
import { ORIGIN_CONNECTOR, protectedResourceMetadata, requireBearer, requireLiveBearer, resolveApiBaseUrl, UnauthorizedError, } from "./oauth.mjs";
|
|
10
10
|
import { initMcpServer, newMcpServer } from "./server.mjs";
|
|
11
11
|
/**
|
|
12
12
|
* Which connector URL this request arrived on.
|
|
@@ -31,7 +31,20 @@ const newServer = async ({ clientOptions, mcpOptions, req, res, }) => {
|
|
|
31
31
|
// not be able to make us do that.
|
|
32
32
|
let authOptions;
|
|
33
33
|
if (mcpOptions.oauth) {
|
|
34
|
-
|
|
34
|
+
const connector = connectorFrom(req);
|
|
35
|
+
const authorized = requireBearer(req, mcpOptions.oauth, connector);
|
|
36
|
+
// Presence is not validity. Ask customer-api whether the token is still live, so a revoked or
|
|
37
|
+
// expired one is answered with 401 + WWW-Authenticate here - the signal a client needs to
|
|
38
|
+
// re-run the OAuth flow - rather than completing the handshake and failing later inside a
|
|
39
|
+
// tool result, where nothing is listening for it.
|
|
40
|
+
//
|
|
41
|
+
// The base URL is resolved the way the SDK resolves it, NOT defaulted to prod: this server is
|
|
42
|
+
// launched without `clientOptions`, so on any stage but prod a prod default would check the
|
|
43
|
+
// token against a server that never issued it and refuse every request.
|
|
44
|
+
await requireLiveBearer(authorized.bearerToken, mcpOptions.oauth, connector, {
|
|
45
|
+
baseURL: resolveApiBaseUrl(clientOptions),
|
|
46
|
+
});
|
|
47
|
+
authOptions = authorized;
|
|
35
48
|
}
|
|
36
49
|
else {
|
|
37
50
|
authOptions = parseClientAuthHeaders(req, false);
|
package/http.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.mjs","sourceRoot":"","sources":["src/http.ts"],"names":[],"mappings":"AAAA,sFAAsF;AAGtF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAEnG,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,mBAAe;AACpE,OAAO,EAAE,SAAS,EAAE,qBAAiB;AAErC,OAAO,EAEL,gBAAgB,EAChB,yBAAyB,EACzB,aAAa,EACb,iBAAiB,GAClB,oBAAgB;AACjB,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,qBAAiB;AAEvD;;;;;GAKG;AACH,MAAM,aAAa,GAAG,CAAC,GAAoB,EAAa,EAAE;IACxD,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,WAAW,CAAC,CAAC;IAC5C,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;IACjG,OAAO,GAAG,CAAC,IAAI,KAAK,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,gBAAgB,CAAC;AAClG,CAAC,CAAC;AAEF,MAAM,SAAS,GAAG,KAAK,EAAE,EACvB,aAAa,EACb,UAAU,EACV,GAAG,EACH,GAAG,GAMJ,EAA6B,EAAE;IAC9B,8EAA8E;IAC9E,+EAA+E;IAC/E,gFAAgF;IAChF,uEAAuE;IACvE,EAAE;IACF,8EAA8E;IAC9E,+EAA+E;IAC/E,kCAAkC;IAClC,IAAI,WAAmC,CAAC;IACxC,IAAI,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,
|
|
1
|
+
{"version":3,"file":"http.mjs","sourceRoot":"","sources":["src/http.ts"],"names":[],"mappings":"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"}
|
package/oauth.d.mts
CHANGED
|
@@ -66,5 +66,61 @@ export declare const protectedResourceMetadata: (config: OAuthConfig, connector?
|
|
|
66
66
|
* default header would silently override. On the unpinned `/mcp` connector no project is set at
|
|
67
67
|
* all, and choosing one is the caller's job.
|
|
68
68
|
*/
|
|
69
|
-
export declare const requireBearer: (req: IncomingMessage, config: OAuthConfig, connector?: Connector) => Partial<ClientOptions
|
|
69
|
+
export declare const requireBearer: (req: IncomingMessage, config: OAuthConfig, connector?: Connector) => Partial<ClientOptions> & {
|
|
70
|
+
bearerToken: string;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* What customer-api says about a bearer: `unknown` means it could not be asked, not that the
|
|
74
|
+
* token is fine.
|
|
75
|
+
*/
|
|
76
|
+
export type BearerVerdict = 'valid' | 'invalid' | 'unknown';
|
|
77
|
+
/** The SDK's own default, for when neither an option nor the environment names one. */
|
|
78
|
+
export declare const DEFAULT_API_BASE_URL = "https://api.roark.ai";
|
|
79
|
+
/**
|
|
80
|
+
* The base URL the SDK will actually call, resolved exactly the way the SDK resolves it:
|
|
81
|
+
* explicit option, then `ROARK_BASE_URL`, then the default.
|
|
82
|
+
*
|
|
83
|
+
* This has to agree with the SDK or the liveness check asks the wrong host. `launchStreamableHTTPServer`
|
|
84
|
+
* passes no `clientOptions` at all, so for the deployed server the answer always comes from the
|
|
85
|
+
* environment: a hardcoded prod default would send every check to prod while data calls went to
|
|
86
|
+
* whatever `ROARK_BASE_URL` names. On any stage but prod that turns a valid token into a 401 from
|
|
87
|
+
* a server it was never issued for, and this file would then refuse every request - a dead
|
|
88
|
+
* connector, which is strictly worse than the bug it is here to fix.
|
|
89
|
+
*/
|
|
90
|
+
export declare const resolveApiBaseUrl: (clientOptions?: Pick<ClientOptions, "baseURL">) => string;
|
|
91
|
+
/** Test seam. */
|
|
92
|
+
export declare const clearBearerVerdictCache: () => void;
|
|
93
|
+
export interface VerifyBearerDeps {
|
|
94
|
+
/** customer-api base URL. Use {@link resolveApiBaseUrl} rather than passing a literal. */
|
|
95
|
+
baseURL: string;
|
|
96
|
+
fetchImpl?: typeof fetch;
|
|
97
|
+
now?: () => number;
|
|
98
|
+
ttlMs?: number;
|
|
99
|
+
timeoutMs?: number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Asks customer-api whether a bearer is still live, via the `/v1/me` endpoint it already exposes
|
|
103
|
+
* for exactly this ("who is this credential"), and caches the answer briefly.
|
|
104
|
+
*
|
|
105
|
+
* Only a 401 counts as invalid. A 403 means the token authenticated and then failed a permission
|
|
106
|
+
* check, which is a live token; treating it as invalid would tell a client to re-authorize when
|
|
107
|
+
* re-authorizing cannot help. Anything else - a 5xx, a timeout, a DNS failure - is `unknown`.
|
|
108
|
+
*/
|
|
109
|
+
export declare const verifyBearer: (token: string, deps: VerifyBearerDeps) => Promise<BearerVerdict>;
|
|
110
|
+
/**
|
|
111
|
+
* Rejects a bearer customer-api has already disowned, with the 401 + WWW-Authenticate that tells
|
|
112
|
+
* an MCP client to re-run the OAuth flow.
|
|
113
|
+
*
|
|
114
|
+
* Without this the resource server only checked that a token was PRESENT. A revoked, expired or
|
|
115
|
+
* entirely invented token completed `initialize` and `tools/list`, and the caller learned nothing
|
|
116
|
+
* until a tool call failed with an opaque 401 in its result text. No data was reachable - every
|
|
117
|
+
* data path revalidates at customer-api - but a client that never sees a 401 from the resource
|
|
118
|
+
* server has no reason to refresh, which for a 24h token means a connector that looks connected
|
|
119
|
+
* and silently cannot work.
|
|
120
|
+
*
|
|
121
|
+
* `unknown` is allowed through deliberately. Failing closed would turn a customer-api blip into a
|
|
122
|
+
* dead connector for everyone, and it would buy no confidentiality: the token still has to pass
|
|
123
|
+
* customer-api on the very next call that touches data.
|
|
124
|
+
*/
|
|
125
|
+
export declare const requireLiveBearer: (token: string, config: OAuthConfig, connector: Connector, deps: VerifyBearerDeps) => Promise<void>;
|
|
70
126
|
//# sourceMappingURL=oauth.d.mts.map
|
package/oauth.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.d.mts","sourceRoot":"","sources":["src/oauth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"oauth.d.mts","sourceRoot":"","sources":["src/oauth.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,MAAM,MAAM,WAAW,GAAG;IACxB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,yHAAyH;IACzH,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,gGAAgG;AAChG,qBAAa,iBAAkB,SAAQ,KAAK;IAGxC,QAAQ,CAAC,eAAe,EAAE,MAAM;IAChC,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG;gBAF1B,OAAO,EAAE,MAAM,EACN,eAAe,EAAE,MAAM,EACvB,MAAM,GAAE,GAAG,GAAG,GAAS;CAKnC;AAID;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtG,eAAO,MAAM,gBAAgB,EAAE,SAA8B,CAAC;AAQ9D;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,WAAW,EAAE,YAAW,SAA4B,KAAG,MAC9B,CAAC;AAErE;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,GACvC,QAAQ,WAAW,EACnB,YAAW,SAA4B,KACtC,MACsG,CAAC;AAE1G,qDAAqD;AACrD,eAAO,MAAM,yBAAyB,GAAI,QAAQ,WAAW,EAAE,YAAW,SAA4B;;;;;;CAMpG,CAAC;AAuBH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GACxB,KAAK,eAAe,EACpB,QAAQ,WAAW,EACnB,YAAW,SAA4B,KACtC,OAAO,CAAC,aAAa,CAAC,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAahD,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAE5D,uFAAuF;AACvF,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAAI,gBAAe,IAAI,CAAC,aAAa,EAAE,SAAS,CAAM,KAAG,MACX,CAAC;AAmC7E,iBAAiB;AACjB,eAAO,MAAM,uBAAuB,QAAO,IAG1C,CAAC;AAgBF,MAAM,WAAW,gBAAgB;IAC/B,0FAA0F;IAC1F,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAU,OAAO,MAAM,EAAE,MAAM,gBAAgB,KAAG,OAAO,CAAC,aAAa,CA2C/F,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,GAC5B,OAAO,MAAM,EACb,QAAQ,WAAW,EACnB,WAAW,SAAS,EACpB,MAAM,gBAAgB,KACrB,OAAO,CAAC,IAAI,CAOd,CAAC"}
|
package/oauth.d.ts
CHANGED
|
@@ -66,5 +66,61 @@ export declare const protectedResourceMetadata: (config: OAuthConfig, connector?
|
|
|
66
66
|
* default header would silently override. On the unpinned `/mcp` connector no project is set at
|
|
67
67
|
* all, and choosing one is the caller's job.
|
|
68
68
|
*/
|
|
69
|
-
export declare const requireBearer: (req: IncomingMessage, config: OAuthConfig, connector?: Connector) => Partial<ClientOptions
|
|
69
|
+
export declare const requireBearer: (req: IncomingMessage, config: OAuthConfig, connector?: Connector) => Partial<ClientOptions> & {
|
|
70
|
+
bearerToken: string;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* What customer-api says about a bearer: `unknown` means it could not be asked, not that the
|
|
74
|
+
* token is fine.
|
|
75
|
+
*/
|
|
76
|
+
export type BearerVerdict = 'valid' | 'invalid' | 'unknown';
|
|
77
|
+
/** The SDK's own default, for when neither an option nor the environment names one. */
|
|
78
|
+
export declare const DEFAULT_API_BASE_URL = "https://api.roark.ai";
|
|
79
|
+
/**
|
|
80
|
+
* The base URL the SDK will actually call, resolved exactly the way the SDK resolves it:
|
|
81
|
+
* explicit option, then `ROARK_BASE_URL`, then the default.
|
|
82
|
+
*
|
|
83
|
+
* This has to agree with the SDK or the liveness check asks the wrong host. `launchStreamableHTTPServer`
|
|
84
|
+
* passes no `clientOptions` at all, so for the deployed server the answer always comes from the
|
|
85
|
+
* environment: a hardcoded prod default would send every check to prod while data calls went to
|
|
86
|
+
* whatever `ROARK_BASE_URL` names. On any stage but prod that turns a valid token into a 401 from
|
|
87
|
+
* a server it was never issued for, and this file would then refuse every request - a dead
|
|
88
|
+
* connector, which is strictly worse than the bug it is here to fix.
|
|
89
|
+
*/
|
|
90
|
+
export declare const resolveApiBaseUrl: (clientOptions?: Pick<ClientOptions, "baseURL">) => string;
|
|
91
|
+
/** Test seam. */
|
|
92
|
+
export declare const clearBearerVerdictCache: () => void;
|
|
93
|
+
export interface VerifyBearerDeps {
|
|
94
|
+
/** customer-api base URL. Use {@link resolveApiBaseUrl} rather than passing a literal. */
|
|
95
|
+
baseURL: string;
|
|
96
|
+
fetchImpl?: typeof fetch;
|
|
97
|
+
now?: () => number;
|
|
98
|
+
ttlMs?: number;
|
|
99
|
+
timeoutMs?: number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Asks customer-api whether a bearer is still live, via the `/v1/me` endpoint it already exposes
|
|
103
|
+
* for exactly this ("who is this credential"), and caches the answer briefly.
|
|
104
|
+
*
|
|
105
|
+
* Only a 401 counts as invalid. A 403 means the token authenticated and then failed a permission
|
|
106
|
+
* check, which is a live token; treating it as invalid would tell a client to re-authorize when
|
|
107
|
+
* re-authorizing cannot help. Anything else - a 5xx, a timeout, a DNS failure - is `unknown`.
|
|
108
|
+
*/
|
|
109
|
+
export declare const verifyBearer: (token: string, deps: VerifyBearerDeps) => Promise<BearerVerdict>;
|
|
110
|
+
/**
|
|
111
|
+
* Rejects a bearer customer-api has already disowned, with the 401 + WWW-Authenticate that tells
|
|
112
|
+
* an MCP client to re-run the OAuth flow.
|
|
113
|
+
*
|
|
114
|
+
* Without this the resource server only checked that a token was PRESENT. A revoked, expired or
|
|
115
|
+
* entirely invented token completed `initialize` and `tools/list`, and the caller learned nothing
|
|
116
|
+
* until a tool call failed with an opaque 401 in its result text. No data was reachable - every
|
|
117
|
+
* data path revalidates at customer-api - but a client that never sees a 401 from the resource
|
|
118
|
+
* server has no reason to refresh, which for a 24h token means a connector that looks connected
|
|
119
|
+
* and silently cannot work.
|
|
120
|
+
*
|
|
121
|
+
* `unknown` is allowed through deliberately. Failing closed would turn a customer-api blip into a
|
|
122
|
+
* dead connector for everyone, and it would buy no confidentiality: the token still has to pass
|
|
123
|
+
* customer-api on the very next call that touches data.
|
|
124
|
+
*/
|
|
125
|
+
export declare const requireLiveBearer: (token: string, config: OAuthConfig, connector: Connector, deps: VerifyBearerDeps) => Promise<void>;
|
|
70
126
|
//# sourceMappingURL=oauth.d.ts.map
|
package/oauth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["src/oauth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["src/oauth.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,MAAM,MAAM,WAAW,GAAG;IACxB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAC;IACf,yHAAyH;IACzH,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,gGAAgG;AAChG,qBAAa,iBAAkB,SAAQ,KAAK;IAGxC,QAAQ,CAAC,eAAe,EAAE,MAAM;IAChC,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG;gBAF1B,OAAO,EAAE,MAAM,EACN,eAAe,EAAE,MAAM,EACvB,MAAM,GAAE,GAAG,GAAG,GAAS;CAKnC;AAID;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtG,eAAO,MAAM,gBAAgB,EAAE,SAA8B,CAAC;AAQ9D;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAQ,WAAW,EAAE,YAAW,SAA4B,KAAG,MAC9B,CAAC;AAErE;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,GACvC,QAAQ,WAAW,EACnB,YAAW,SAA4B,KACtC,MACsG,CAAC;AAE1G,qDAAqD;AACrD,eAAO,MAAM,yBAAyB,GAAI,QAAQ,WAAW,EAAE,YAAW,SAA4B;;;;;;CAMpG,CAAC;AAuBH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GACxB,KAAK,eAAe,EACpB,QAAQ,WAAW,EACnB,YAAW,SAA4B,KACtC,OAAO,CAAC,aAAa,CAAC,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAahD,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAE5D,uFAAuF;AACvF,eAAO,MAAM,oBAAoB,yBAAyB,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,GAAI,gBAAe,IAAI,CAAC,aAAa,EAAE,SAAS,CAAM,KAAG,MACX,CAAC;AAmC7E,iBAAiB;AACjB,eAAO,MAAM,uBAAuB,QAAO,IAG1C,CAAC;AAgBF,MAAM,WAAW,gBAAgB;IAC/B,0FAA0F;IAC1F,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAU,OAAO,MAAM,EAAE,MAAM,gBAAgB,KAAG,OAAO,CAAC,aAAa,CA2C/F,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,iBAAiB,GAC5B,OAAO,MAAM,EACb,QAAQ,WAAW,EACnB,WAAW,SAAS,EACpB,MAAM,gBAAgB,KACrB,OAAO,CAAC,IAAI,CAOd,CAAC"}
|
package/oauth.js
CHANGED
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
// rotated by the client's refresh token), so it is forwarded to customer-api as
|
|
11
11
|
// the SDK bearer unchanged: customer-api validates it on every call.
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.requireBearer = exports.protectedResourceMetadata = exports.protectedResourceMetadataUrl = exports.resourceIdentifier = exports.ORIGIN_CONNECTOR = exports.UnauthorizedError = void 0;
|
|
13
|
+
exports.requireLiveBearer = exports.verifyBearer = exports.clearBearerVerdictCache = exports.resolveApiBaseUrl = exports.DEFAULT_API_BASE_URL = exports.requireBearer = exports.protectedResourceMetadata = exports.protectedResourceMetadataUrl = exports.resourceIdentifier = exports.ORIGIN_CONNECTOR = exports.UnauthorizedError = void 0;
|
|
14
|
+
const node_crypto_1 = require("node:crypto");
|
|
15
|
+
const util_1 = require("./util.js");
|
|
14
16
|
/** Thrown when a request cannot be authorized; carries the WWW-Authenticate value to return. */
|
|
15
17
|
class UnauthorizedError extends Error {
|
|
16
18
|
wwwAuthenticate;
|
|
@@ -98,4 +100,134 @@ const requireBearer = (req, config, connector = exports.ORIGIN_CONNECTOR) => {
|
|
|
98
100
|
return options;
|
|
99
101
|
};
|
|
100
102
|
exports.requireBearer = requireBearer;
|
|
103
|
+
/** The SDK's own default, for when neither an option nor the environment names one. */
|
|
104
|
+
exports.DEFAULT_API_BASE_URL = 'https://api.roark.ai';
|
|
105
|
+
/**
|
|
106
|
+
* The base URL the SDK will actually call, resolved exactly the way the SDK resolves it:
|
|
107
|
+
* explicit option, then `ROARK_BASE_URL`, then the default.
|
|
108
|
+
*
|
|
109
|
+
* This has to agree with the SDK or the liveness check asks the wrong host. `launchStreamableHTTPServer`
|
|
110
|
+
* passes no `clientOptions` at all, so for the deployed server the answer always comes from the
|
|
111
|
+
* environment: a hardcoded prod default would send every check to prod while data calls went to
|
|
112
|
+
* whatever `ROARK_BASE_URL` names. On any stage but prod that turns a valid token into a 401 from
|
|
113
|
+
* a server it was never issued for, and this file would then refuse every request - a dead
|
|
114
|
+
* connector, which is strictly worse than the bug it is here to fix.
|
|
115
|
+
*/
|
|
116
|
+
const resolveApiBaseUrl = (clientOptions = {}) => clientOptions.baseURL ?? (0, util_1.readEnv)('ROARK_BASE_URL') ?? exports.DEFAULT_API_BASE_URL;
|
|
117
|
+
exports.resolveApiBaseUrl = resolveApiBaseUrl;
|
|
118
|
+
/**
|
|
119
|
+
* How long a verdict is reused. `newServer` runs per POST, not per session, so an uncached check
|
|
120
|
+
* would add a round trip to every single MCP request. The window is what a revoked token buys:
|
|
121
|
+
* it can still complete a handshake and read the tool list for up to this long. It cannot read
|
|
122
|
+
* anything, because every data path carries the same token to customer-api and is refused there
|
|
123
|
+
* immediately.
|
|
124
|
+
*/
|
|
125
|
+
const BEARER_CACHE_TTL_MS = 60_000;
|
|
126
|
+
/**
|
|
127
|
+
* An unreachable customer-api is handled by the catch below, but a customer-api that accepts the
|
|
128
|
+
* connection and then never answers is not: without a deadline every MCP request would hang on
|
|
129
|
+
* it, which is worse than the failure this check exists to report. A hang becomes `unknown` like
|
|
130
|
+
* any other transport failure.
|
|
131
|
+
*/
|
|
132
|
+
const BEARER_CHECK_TIMEOUT_MS = 3_000;
|
|
133
|
+
/**
|
|
134
|
+
* Bounded so that spraying invented tokens cannot grow the process's memory. Keys are hashes,
|
|
135
|
+
* not the tokens themselves: these are live credentials and there is no reason to keep a second
|
|
136
|
+
* copy of them in a long-lived map.
|
|
137
|
+
*/
|
|
138
|
+
const BEARER_CACHE_MAX = 1_000;
|
|
139
|
+
const bearerVerdicts = new Map();
|
|
140
|
+
/**
|
|
141
|
+
* Checks already in flight, so concurrent requests bearing the same uncached token make one call
|
|
142
|
+
* rather than one each. That is the common shape right after a verdict expires, and whenever a
|
|
143
|
+
* client opens several requests at once.
|
|
144
|
+
*/
|
|
145
|
+
const inFlightChecks = new Map();
|
|
146
|
+
/** Test seam. */
|
|
147
|
+
const clearBearerVerdictCache = () => {
|
|
148
|
+
bearerVerdicts.clear();
|
|
149
|
+
inFlightChecks.clear();
|
|
150
|
+
};
|
|
151
|
+
exports.clearBearerVerdictCache = clearBearerVerdictCache;
|
|
152
|
+
const hashToken = (token) => (0, node_crypto_1.createHash)('sha256').update(token).digest('hex');
|
|
153
|
+
const rememberVerdict = (key, verdict, expiresAt) => {
|
|
154
|
+
// Evict the oldest entry rather than clearing the map. A `clear()` would let anyone spraying
|
|
155
|
+
// invented tokens flush every valid verdict too, making legitimate callers pay a round trip
|
|
156
|
+
// each and handing customer-api the extra work. Map iterates in insertion order, so the first
|
|
157
|
+
// key is the oldest.
|
|
158
|
+
if (bearerVerdicts.size >= BEARER_CACHE_MAX) {
|
|
159
|
+
const oldest = bearerVerdicts.keys().next();
|
|
160
|
+
if (!oldest.done)
|
|
161
|
+
bearerVerdicts.delete(oldest.value);
|
|
162
|
+
}
|
|
163
|
+
bearerVerdicts.set(key, { verdict, expiresAt });
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Asks customer-api whether a bearer is still live, via the `/v1/me` endpoint it already exposes
|
|
167
|
+
* for exactly this ("who is this credential"), and caches the answer briefly.
|
|
168
|
+
*
|
|
169
|
+
* Only a 401 counts as invalid. A 403 means the token authenticated and then failed a permission
|
|
170
|
+
* check, which is a live token; treating it as invalid would tell a client to re-authorize when
|
|
171
|
+
* re-authorizing cannot help. Anything else - a 5xx, a timeout, a DNS failure - is `unknown`.
|
|
172
|
+
*/
|
|
173
|
+
const verifyBearer = async (token, deps) => {
|
|
174
|
+
const { baseURL, fetchImpl = fetch, now = Date.now, ttlMs = BEARER_CACHE_TTL_MS, timeoutMs = BEARER_CHECK_TIMEOUT_MS, } = deps;
|
|
175
|
+
const key = hashToken(token);
|
|
176
|
+
const cached = bearerVerdicts.get(key);
|
|
177
|
+
if (cached && cached.expiresAt > now())
|
|
178
|
+
return cached.verdict;
|
|
179
|
+
const alreadyRunning = inFlightChecks.get(key);
|
|
180
|
+
if (alreadyRunning)
|
|
181
|
+
return alreadyRunning;
|
|
182
|
+
const check = (async () => {
|
|
183
|
+
let response;
|
|
184
|
+
try {
|
|
185
|
+
response = await fetchImpl(`${stripSlash(baseURL)}/v1/me`, {
|
|
186
|
+
method: 'GET',
|
|
187
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
188
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return 'unknown';
|
|
193
|
+
}
|
|
194
|
+
const verdict = response.status === 401 ? 'invalid'
|
|
195
|
+
: response.ok ? 'valid'
|
|
196
|
+
: 'unknown';
|
|
197
|
+
// An `unknown` is never cached: a blip would otherwise pin the answer for the whole TTL.
|
|
198
|
+
if (verdict !== 'unknown')
|
|
199
|
+
rememberVerdict(key, verdict, now() + ttlMs);
|
|
200
|
+
return verdict;
|
|
201
|
+
})();
|
|
202
|
+
inFlightChecks.set(key, check);
|
|
203
|
+
try {
|
|
204
|
+
return await check;
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
inFlightChecks.delete(key);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
exports.verifyBearer = verifyBearer;
|
|
211
|
+
/**
|
|
212
|
+
* Rejects a bearer customer-api has already disowned, with the 401 + WWW-Authenticate that tells
|
|
213
|
+
* an MCP client to re-run the OAuth flow.
|
|
214
|
+
*
|
|
215
|
+
* Without this the resource server only checked that a token was PRESENT. A revoked, expired or
|
|
216
|
+
* entirely invented token completed `initialize` and `tools/list`, and the caller learned nothing
|
|
217
|
+
* until a tool call failed with an opaque 401 in its result text. No data was reachable - every
|
|
218
|
+
* data path revalidates at customer-api - but a client that never sees a 401 from the resource
|
|
219
|
+
* server has no reason to refresh, which for a 24h token means a connector that looks connected
|
|
220
|
+
* and silently cannot work.
|
|
221
|
+
*
|
|
222
|
+
* `unknown` is allowed through deliberately. Failing closed would turn a customer-api blip into a
|
|
223
|
+
* dead connector for everyone, and it would buy no confidentiality: the token still has to pass
|
|
224
|
+
* customer-api on the very next call that touches data.
|
|
225
|
+
*/
|
|
226
|
+
const requireLiveBearer = async (token, config, connector, deps) => {
|
|
227
|
+
const verdict = await (0, exports.verifyBearer)(token, deps);
|
|
228
|
+
if (verdict !== 'invalid')
|
|
229
|
+
return;
|
|
230
|
+
throw new UnauthorizedError('The access token is expired or has been revoked', bearerChallenge(config, connector, 'invalid_token', 'The access token is expired or has been revoked'));
|
|
231
|
+
};
|
|
232
|
+
exports.requireLiveBearer = requireLiveBearer;
|
|
101
233
|
//# sourceMappingURL=oauth.js.map
|
package/oauth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["src/oauth.ts"],"names":[],"mappings":";AAAA,wCAAwC;AACxC,EAAE;AACF,iFAAiF;AACjF,8EAA8E;AAC9E,2EAA2E;AAC3E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,gFAAgF;AAChF,qEAAqE;;;
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["src/oauth.ts"],"names":[],"mappings":"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"}
|
package/oauth.mjs
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
// key (minted at consent with the permissions the user picked, expiring and
|
|
9
9
|
// rotated by the client's refresh token), so it is forwarded to customer-api as
|
|
10
10
|
// the SDK bearer unchanged: customer-api validates it on every call.
|
|
11
|
+
import { createHash } from 'node:crypto';
|
|
12
|
+
import { readEnv } from "./util.mjs";
|
|
11
13
|
/** Thrown when a request cannot be authorized; carries the WWW-Authenticate value to return. */
|
|
12
14
|
export class UnauthorizedError extends Error {
|
|
13
15
|
wwwAuthenticate;
|
|
@@ -90,4 +92,130 @@ export const requireBearer = (req, config, connector = ORIGIN_CONNECTOR) => {
|
|
|
90
92
|
}
|
|
91
93
|
return options;
|
|
92
94
|
};
|
|
95
|
+
/** The SDK's own default, for when neither an option nor the environment names one. */
|
|
96
|
+
export const DEFAULT_API_BASE_URL = 'https://api.roark.ai';
|
|
97
|
+
/**
|
|
98
|
+
* The base URL the SDK will actually call, resolved exactly the way the SDK resolves it:
|
|
99
|
+
* explicit option, then `ROARK_BASE_URL`, then the default.
|
|
100
|
+
*
|
|
101
|
+
* This has to agree with the SDK or the liveness check asks the wrong host. `launchStreamableHTTPServer`
|
|
102
|
+
* passes no `clientOptions` at all, so for the deployed server the answer always comes from the
|
|
103
|
+
* environment: a hardcoded prod default would send every check to prod while data calls went to
|
|
104
|
+
* whatever `ROARK_BASE_URL` names. On any stage but prod that turns a valid token into a 401 from
|
|
105
|
+
* a server it was never issued for, and this file would then refuse every request - a dead
|
|
106
|
+
* connector, which is strictly worse than the bug it is here to fix.
|
|
107
|
+
*/
|
|
108
|
+
export const resolveApiBaseUrl = (clientOptions = {}) => clientOptions.baseURL ?? readEnv('ROARK_BASE_URL') ?? DEFAULT_API_BASE_URL;
|
|
109
|
+
/**
|
|
110
|
+
* How long a verdict is reused. `newServer` runs per POST, not per session, so an uncached check
|
|
111
|
+
* would add a round trip to every single MCP request. The window is what a revoked token buys:
|
|
112
|
+
* it can still complete a handshake and read the tool list for up to this long. It cannot read
|
|
113
|
+
* anything, because every data path carries the same token to customer-api and is refused there
|
|
114
|
+
* immediately.
|
|
115
|
+
*/
|
|
116
|
+
const BEARER_CACHE_TTL_MS = 60_000;
|
|
117
|
+
/**
|
|
118
|
+
* An unreachable customer-api is handled by the catch below, but a customer-api that accepts the
|
|
119
|
+
* connection and then never answers is not: without a deadline every MCP request would hang on
|
|
120
|
+
* it, which is worse than the failure this check exists to report. A hang becomes `unknown` like
|
|
121
|
+
* any other transport failure.
|
|
122
|
+
*/
|
|
123
|
+
const BEARER_CHECK_TIMEOUT_MS = 3_000;
|
|
124
|
+
/**
|
|
125
|
+
* Bounded so that spraying invented tokens cannot grow the process's memory. Keys are hashes,
|
|
126
|
+
* not the tokens themselves: these are live credentials and there is no reason to keep a second
|
|
127
|
+
* copy of them in a long-lived map.
|
|
128
|
+
*/
|
|
129
|
+
const BEARER_CACHE_MAX = 1_000;
|
|
130
|
+
const bearerVerdicts = new Map();
|
|
131
|
+
/**
|
|
132
|
+
* Checks already in flight, so concurrent requests bearing the same uncached token make one call
|
|
133
|
+
* rather than one each. That is the common shape right after a verdict expires, and whenever a
|
|
134
|
+
* client opens several requests at once.
|
|
135
|
+
*/
|
|
136
|
+
const inFlightChecks = new Map();
|
|
137
|
+
/** Test seam. */
|
|
138
|
+
export const clearBearerVerdictCache = () => {
|
|
139
|
+
bearerVerdicts.clear();
|
|
140
|
+
inFlightChecks.clear();
|
|
141
|
+
};
|
|
142
|
+
const hashToken = (token) => createHash('sha256').update(token).digest('hex');
|
|
143
|
+
const rememberVerdict = (key, verdict, expiresAt) => {
|
|
144
|
+
// Evict the oldest entry rather than clearing the map. A `clear()` would let anyone spraying
|
|
145
|
+
// invented tokens flush every valid verdict too, making legitimate callers pay a round trip
|
|
146
|
+
// each and handing customer-api the extra work. Map iterates in insertion order, so the first
|
|
147
|
+
// key is the oldest.
|
|
148
|
+
if (bearerVerdicts.size >= BEARER_CACHE_MAX) {
|
|
149
|
+
const oldest = bearerVerdicts.keys().next();
|
|
150
|
+
if (!oldest.done)
|
|
151
|
+
bearerVerdicts.delete(oldest.value);
|
|
152
|
+
}
|
|
153
|
+
bearerVerdicts.set(key, { verdict, expiresAt });
|
|
154
|
+
};
|
|
155
|
+
/**
|
|
156
|
+
* Asks customer-api whether a bearer is still live, via the `/v1/me` endpoint it already exposes
|
|
157
|
+
* for exactly this ("who is this credential"), and caches the answer briefly.
|
|
158
|
+
*
|
|
159
|
+
* Only a 401 counts as invalid. A 403 means the token authenticated and then failed a permission
|
|
160
|
+
* check, which is a live token; treating it as invalid would tell a client to re-authorize when
|
|
161
|
+
* re-authorizing cannot help. Anything else - a 5xx, a timeout, a DNS failure - is `unknown`.
|
|
162
|
+
*/
|
|
163
|
+
export const verifyBearer = async (token, deps) => {
|
|
164
|
+
const { baseURL, fetchImpl = fetch, now = Date.now, ttlMs = BEARER_CACHE_TTL_MS, timeoutMs = BEARER_CHECK_TIMEOUT_MS, } = deps;
|
|
165
|
+
const key = hashToken(token);
|
|
166
|
+
const cached = bearerVerdicts.get(key);
|
|
167
|
+
if (cached && cached.expiresAt > now())
|
|
168
|
+
return cached.verdict;
|
|
169
|
+
const alreadyRunning = inFlightChecks.get(key);
|
|
170
|
+
if (alreadyRunning)
|
|
171
|
+
return alreadyRunning;
|
|
172
|
+
const check = (async () => {
|
|
173
|
+
let response;
|
|
174
|
+
try {
|
|
175
|
+
response = await fetchImpl(`${stripSlash(baseURL)}/v1/me`, {
|
|
176
|
+
method: 'GET',
|
|
177
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
178
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return 'unknown';
|
|
183
|
+
}
|
|
184
|
+
const verdict = response.status === 401 ? 'invalid'
|
|
185
|
+
: response.ok ? 'valid'
|
|
186
|
+
: 'unknown';
|
|
187
|
+
// An `unknown` is never cached: a blip would otherwise pin the answer for the whole TTL.
|
|
188
|
+
if (verdict !== 'unknown')
|
|
189
|
+
rememberVerdict(key, verdict, now() + ttlMs);
|
|
190
|
+
return verdict;
|
|
191
|
+
})();
|
|
192
|
+
inFlightChecks.set(key, check);
|
|
193
|
+
try {
|
|
194
|
+
return await check;
|
|
195
|
+
}
|
|
196
|
+
finally {
|
|
197
|
+
inFlightChecks.delete(key);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Rejects a bearer customer-api has already disowned, with the 401 + WWW-Authenticate that tells
|
|
202
|
+
* an MCP client to re-run the OAuth flow.
|
|
203
|
+
*
|
|
204
|
+
* Without this the resource server only checked that a token was PRESENT. A revoked, expired or
|
|
205
|
+
* entirely invented token completed `initialize` and `tools/list`, and the caller learned nothing
|
|
206
|
+
* until a tool call failed with an opaque 401 in its result text. No data was reachable - every
|
|
207
|
+
* data path revalidates at customer-api - but a client that never sees a 401 from the resource
|
|
208
|
+
* server has no reason to refresh, which for a 24h token means a connector that looks connected
|
|
209
|
+
* and silently cannot work.
|
|
210
|
+
*
|
|
211
|
+
* `unknown` is allowed through deliberately. Failing closed would turn a customer-api blip into a
|
|
212
|
+
* dead connector for everyone, and it would buy no confidentiality: the token still has to pass
|
|
213
|
+
* customer-api on the very next call that touches data.
|
|
214
|
+
*/
|
|
215
|
+
export const requireLiveBearer = async (token, config, connector, deps) => {
|
|
216
|
+
const verdict = await verifyBearer(token, deps);
|
|
217
|
+
if (verdict !== 'invalid')
|
|
218
|
+
return;
|
|
219
|
+
throw new UnauthorizedError('The access token is expired or has been revoked', bearerChallenge(config, connector, 'invalid_token', 'The access token is expired or has been revoked'));
|
|
220
|
+
};
|
|
93
221
|
//# sourceMappingURL=oauth.mjs.map
|
package/oauth.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.mjs","sourceRoot":"","sources":["src/oauth.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,EAAE;AACF,iFAAiF;AACjF,8EAA8E;AAC9E,2EAA2E;AAC3E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,gFAAgF;AAChF,qEAAqE;
|
|
1
|
+
{"version":3,"file":"oauth.mjs","sourceRoot":"","sources":["src/oauth.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,EAAE;AACF,iFAAiF;AACjF,8EAA8E;AAC9E,2EAA2E;AAC3E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,gFAAgF;AAChF,qEAAqE;AAErE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EAAE,OAAO,EAAE,mBAAe;AASjC,gGAAgG;AAChG,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAG/B;IACA;IAHX,YACE,OAAe,EACN,eAAuB,EACvB,SAAoB,GAAG;QAEhC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHN,oBAAe,GAAf,eAAe,CAAQ;QACvB,WAAM,GAAN,MAAM,CAAiB;QAGhC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,MAAM,UAAU,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAgBpE,MAAM,CAAC,MAAM,gBAAgB,GAAc,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAE9D,iEAAiE;AACjE,MAAM,aAAa,GAAG,CAAC,SAAoB,EAAU,EAAE,CACrD,SAAS,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE;IAChC,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM;QACnC,CAAC,CAAC,QAAQ,SAAS,CAAC,SAAS,EAAE,CAAC;AAElC;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAAmB,EAAE,YAAuB,gBAAgB,EAAU,EAAE,CACzG,GAAG,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,GAAG,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;AAErE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAC1C,MAAmB,EACnB,YAAuB,gBAAgB,EAC/B,EAAE,CACV,GAAG,UAAU,CAAC,MAAM,CAAC,eAAe,CAAC,wCAAwC,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;AAE1G,qDAAqD;AACrD,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,MAAmB,EAAE,YAAuB,gBAAgB,EAAE,EAAE,CAAC,CAAC;IAC1G,QAAQ,EAAE,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC;IAC/C,qBAAqB,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClD,wBAAwB,EAAE,CAAC,QAAQ,CAAC;IACpC,gBAAgB,EAAE,CAAC,KAAK,CAAC;IACzB,sBAAsB,EAAE,uBAAuB;CAChD,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,CACtB,MAAmB,EACnB,SAAoB,EACpB,KAAc,EACd,WAAoB,EACZ,EAAE;IACV,MAAM,KAAK,GAAG,CAAC,6BAA6B,4BAA4B,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAChG,IAAI,KAAK;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,KAAK,GAAG,CAAC,CAAC;IAC1C,IAAI,WAAW;QAAE,KAAK,CAAC,IAAI,CAAC,sBAAsB,WAAW,GAAG,CAAC,CAAC;IAClE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,GAAoB,EAAsB,EAAE;IACjE,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IACzC,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,GAAoB,EACpB,MAAmB,EACnB,YAAuB,gBAAgB,EACW,EAAE;IACpD,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,iBAAiB,CAAC,sBAAsB,EAAE,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAC1F,CAAC;IACD,8FAA8F;IAC9F,0FAA0F;IAC1F,qEAAqE;IACrE,MAAM,OAAO,GAAqD,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;IACzF,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,SAAS,CAAC;IACxC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAQF,uFAAuF;AACvF,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,gBAAgD,EAAE,EAAU,EAAE,CAC9F,aAAa,CAAC,OAAO,IAAI,OAAO,CAAC,gBAAgB,CAAC,IAAI,oBAAoB,CAAC;AAE7E;;;;;;GAMG;AACH,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC;;;;;GAKG;AACH,MAAM,uBAAuB,GAAG,KAAK,CAAC;AAEtC;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,MAAM,cAAc,GAAG,IAAI,GAAG,EAA+D,CAAC;AAE9F;;;;GAIG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkC,CAAC;AAEjE,iBAAiB;AACjB,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAS,EAAE;IAChD,cAAc,CAAC,KAAK,EAAE,CAAC;IACvB,cAAc,CAAC,KAAK,EAAE,CAAC;AACzB,CAAC,CAAC;AAEF,MAAM,SAAS,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAE9F,MAAM,eAAe,GAAG,CAAC,GAAW,EAAE,OAA4B,EAAE,SAAiB,EAAQ,EAAE;IAC7F,6FAA6F;IAC7F,4FAA4F;IAC5F,8FAA8F;IAC9F,qBAAqB;IACrB,IAAI,cAAc,CAAC,IAAI,IAAI,gBAAgB,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,IAAI;YAAE,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACxD,CAAC;IACD,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;AAClD,CAAC,CAAC;AAWF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAAE,KAAa,EAAE,IAAsB,EAA0B,EAAE;IAClG,MAAM,EACJ,OAAO,EACP,SAAS,GAAG,KAAK,EACjB,GAAG,GAAG,IAAI,CAAC,GAAG,EACd,KAAK,GAAG,mBAAmB,EAC3B,SAAS,GAAG,uBAAuB,GACpC,GAAG,IAAI,CAAC;IACT,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAE7B,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,GAAG,GAAG,EAAE;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC;IAE9D,MAAM,cAAc,GAAG,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,cAAc;QAAE,OAAO,cAAc,CAAC;IAE1C,MAAM,KAAK,GAAG,CAAC,KAAK,IAA4B,EAAE;QAChD,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE;gBACzD,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;gBAC7C,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;aACvC,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,OAAO,GACX,QAAQ,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,SAAS;YACnC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO;gBACvB,CAAC,CAAC,SAAS,CAAC;QACd,yFAAyF;QACzF,IAAI,OAAO,KAAK,SAAS;YAAE,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC;QACxE,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC,EAAE,CAAC;IAEL,cAAc,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,CAAC;IACrB,CAAC;YAAS,CAAC;QACT,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,EACpC,KAAa,EACb,MAAmB,EACnB,SAAoB,EACpB,IAAsB,EACP,EAAE;IACjB,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAChD,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO;IAClC,MAAM,IAAI,iBAAiB,CACzB,iDAAiD,EACjD,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,iDAAiD,CAAC,CACvG,CAAC;AACJ,CAAC,CAAC"}
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -19,7 +19,7 @@ const instructions_1 = require("./instructions.js");
|
|
|
19
19
|
const methods_1 = require("./methods.js");
|
|
20
20
|
const newMcpServer = async ({ stainlessApiKey, customInstructionsPath, }) => new mcp_js_1.McpServer({
|
|
21
21
|
name: 'roarkanalytics_sdk_api',
|
|
22
|
-
version: '4.12.
|
|
22
|
+
version: '4.12.2', // x-release-please-version
|
|
23
23
|
}, {
|
|
24
24
|
instructions: await (0, instructions_1.getInstructions)({ stainlessApiKey, customInstructionsPath }),
|
|
25
25
|
capabilities: { tools: {}, logging: {} },
|
package/server.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { getInstructions } from "./instructions.mjs";
|
|
|
10
10
|
import { blockedMethodsForCodeTool } from "./methods.mjs";
|
|
11
11
|
export const newMcpServer = async ({ stainlessApiKey, customInstructionsPath, }) => new McpServer({
|
|
12
12
|
name: 'roarkanalytics_sdk_api',
|
|
13
|
-
version: '4.12.
|
|
13
|
+
version: '4.12.2', // x-release-please-version
|
|
14
14
|
}, {
|
|
15
15
|
instructions: await getInstructions({ stainlessApiKey, customInstructionsPath }),
|
|
16
16
|
capabilities: { tools: {}, logging: {} },
|
package/src/http.ts
CHANGED
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
ORIGIN_CONNECTOR,
|
|
16
16
|
protectedResourceMetadata,
|
|
17
17
|
requireBearer,
|
|
18
|
+
requireLiveBearer,
|
|
19
|
+
resolveApiBaseUrl,
|
|
18
20
|
UnauthorizedError,
|
|
19
21
|
} from './oauth';
|
|
20
22
|
import { initMcpServer, newMcpServer } from './server';
|
|
@@ -52,7 +54,20 @@ const newServer = async ({
|
|
|
52
54
|
// not be able to make us do that.
|
|
53
55
|
let authOptions: Partial<ClientOptions>;
|
|
54
56
|
if (mcpOptions.oauth) {
|
|
55
|
-
|
|
57
|
+
const connector = connectorFrom(req);
|
|
58
|
+
const authorized = requireBearer(req, mcpOptions.oauth, connector);
|
|
59
|
+
// Presence is not validity. Ask customer-api whether the token is still live, so a revoked or
|
|
60
|
+
// expired one is answered with 401 + WWW-Authenticate here - the signal a client needs to
|
|
61
|
+
// re-run the OAuth flow - rather than completing the handshake and failing later inside a
|
|
62
|
+
// tool result, where nothing is listening for it.
|
|
63
|
+
//
|
|
64
|
+
// The base URL is resolved the way the SDK resolves it, NOT defaulted to prod: this server is
|
|
65
|
+
// launched without `clientOptions`, so on any stage but prod a prod default would check the
|
|
66
|
+
// token against a server that never issued it and refuse every request.
|
|
67
|
+
await requireLiveBearer(authorized.bearerToken, mcpOptions.oauth, connector, {
|
|
68
|
+
baseURL: resolveApiBaseUrl(clientOptions),
|
|
69
|
+
});
|
|
70
|
+
authOptions = authorized;
|
|
56
71
|
} else {
|
|
57
72
|
authOptions = parseClientAuthHeaders(req, false);
|
|
58
73
|
}
|
package/src/oauth.ts
CHANGED
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
// rotated by the client's refresh token), so it is forwarded to customer-api as
|
|
10
10
|
// the SDK bearer unchanged: customer-api validates it on every call.
|
|
11
11
|
|
|
12
|
+
import { createHash } from 'node:crypto';
|
|
12
13
|
import { IncomingMessage } from 'node:http';
|
|
13
14
|
import { ClientOptions } from '@roarkanalytics/sdk';
|
|
15
|
+
import { readEnv } from './util';
|
|
14
16
|
|
|
15
17
|
export type OAuthConfig = {
|
|
16
18
|
/** Authorization server issuer, e.g. https://mcp-oauth.api.roark.ai */
|
|
@@ -121,7 +123,7 @@ export const requireBearer = (
|
|
|
121
123
|
req: IncomingMessage,
|
|
122
124
|
config: OAuthConfig,
|
|
123
125
|
connector: Connector = ORIGIN_CONNECTOR,
|
|
124
|
-
): Partial<ClientOptions> => {
|
|
126
|
+
): Partial<ClientOptions> & { bearerToken: string } => {
|
|
125
127
|
const token = extractBearer(req);
|
|
126
128
|
if (!token) {
|
|
127
129
|
throw new UnauthorizedError('Missing bearer token', bearerChallenge(config, connector));
|
|
@@ -129,9 +131,176 @@ export const requireBearer = (
|
|
|
129
131
|
// Assigned rather than conditionally spread: a spread of `cond && { project }` is not checked
|
|
130
132
|
// against `ClientOptions`, so on an SDK without the option the pin would compile and then
|
|
131
133
|
// silently vanish. This way the dependency is enforced by the build.
|
|
132
|
-
const options: Partial<ClientOptions> = { bearerToken: token };
|
|
134
|
+
const options: Partial<ClientOptions> & { bearerToken: string } = { bearerToken: token };
|
|
133
135
|
if (connector.kind === 'project') {
|
|
134
136
|
options.project = connector.projectId;
|
|
135
137
|
}
|
|
136
138
|
return options;
|
|
137
139
|
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* What customer-api says about a bearer: `unknown` means it could not be asked, not that the
|
|
143
|
+
* token is fine.
|
|
144
|
+
*/
|
|
145
|
+
export type BearerVerdict = 'valid' | 'invalid' | 'unknown';
|
|
146
|
+
|
|
147
|
+
/** The SDK's own default, for when neither an option nor the environment names one. */
|
|
148
|
+
export const DEFAULT_API_BASE_URL = 'https://api.roark.ai';
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The base URL the SDK will actually call, resolved exactly the way the SDK resolves it:
|
|
152
|
+
* explicit option, then `ROARK_BASE_URL`, then the default.
|
|
153
|
+
*
|
|
154
|
+
* This has to agree with the SDK or the liveness check asks the wrong host. `launchStreamableHTTPServer`
|
|
155
|
+
* passes no `clientOptions` at all, so for the deployed server the answer always comes from the
|
|
156
|
+
* environment: a hardcoded prod default would send every check to prod while data calls went to
|
|
157
|
+
* whatever `ROARK_BASE_URL` names. On any stage but prod that turns a valid token into a 401 from
|
|
158
|
+
* a server it was never issued for, and this file would then refuse every request - a dead
|
|
159
|
+
* connector, which is strictly worse than the bug it is here to fix.
|
|
160
|
+
*/
|
|
161
|
+
export const resolveApiBaseUrl = (clientOptions: Pick<ClientOptions, 'baseURL'> = {}): string =>
|
|
162
|
+
clientOptions.baseURL ?? readEnv('ROARK_BASE_URL') ?? DEFAULT_API_BASE_URL;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* How long a verdict is reused. `newServer` runs per POST, not per session, so an uncached check
|
|
166
|
+
* would add a round trip to every single MCP request. The window is what a revoked token buys:
|
|
167
|
+
* it can still complete a handshake and read the tool list for up to this long. It cannot read
|
|
168
|
+
* anything, because every data path carries the same token to customer-api and is refused there
|
|
169
|
+
* immediately.
|
|
170
|
+
*/
|
|
171
|
+
const BEARER_CACHE_TTL_MS = 60_000;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* An unreachable customer-api is handled by the catch below, but a customer-api that accepts the
|
|
175
|
+
* connection and then never answers is not: without a deadline every MCP request would hang on
|
|
176
|
+
* it, which is worse than the failure this check exists to report. A hang becomes `unknown` like
|
|
177
|
+
* any other transport failure.
|
|
178
|
+
*/
|
|
179
|
+
const BEARER_CHECK_TIMEOUT_MS = 3_000;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Bounded so that spraying invented tokens cannot grow the process's memory. Keys are hashes,
|
|
183
|
+
* not the tokens themselves: these are live credentials and there is no reason to keep a second
|
|
184
|
+
* copy of them in a long-lived map.
|
|
185
|
+
*/
|
|
186
|
+
const BEARER_CACHE_MAX = 1_000;
|
|
187
|
+
|
|
188
|
+
const bearerVerdicts = new Map<string, { verdict: 'valid' | 'invalid'; expiresAt: number }>();
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Checks already in flight, so concurrent requests bearing the same uncached token make one call
|
|
192
|
+
* rather than one each. That is the common shape right after a verdict expires, and whenever a
|
|
193
|
+
* client opens several requests at once.
|
|
194
|
+
*/
|
|
195
|
+
const inFlightChecks = new Map<string, Promise<BearerVerdict>>();
|
|
196
|
+
|
|
197
|
+
/** Test seam. */
|
|
198
|
+
export const clearBearerVerdictCache = (): void => {
|
|
199
|
+
bearerVerdicts.clear();
|
|
200
|
+
inFlightChecks.clear();
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const hashToken = (token: string): string => createHash('sha256').update(token).digest('hex');
|
|
204
|
+
|
|
205
|
+
const rememberVerdict = (key: string, verdict: 'valid' | 'invalid', expiresAt: number): void => {
|
|
206
|
+
// Evict the oldest entry rather than clearing the map. A `clear()` would let anyone spraying
|
|
207
|
+
// invented tokens flush every valid verdict too, making legitimate callers pay a round trip
|
|
208
|
+
// each and handing customer-api the extra work. Map iterates in insertion order, so the first
|
|
209
|
+
// key is the oldest.
|
|
210
|
+
if (bearerVerdicts.size >= BEARER_CACHE_MAX) {
|
|
211
|
+
const oldest = bearerVerdicts.keys().next();
|
|
212
|
+
if (!oldest.done) bearerVerdicts.delete(oldest.value);
|
|
213
|
+
}
|
|
214
|
+
bearerVerdicts.set(key, { verdict, expiresAt });
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export interface VerifyBearerDeps {
|
|
218
|
+
/** customer-api base URL. Use {@link resolveApiBaseUrl} rather than passing a literal. */
|
|
219
|
+
baseURL: string;
|
|
220
|
+
fetchImpl?: typeof fetch;
|
|
221
|
+
now?: () => number;
|
|
222
|
+
ttlMs?: number;
|
|
223
|
+
timeoutMs?: number;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Asks customer-api whether a bearer is still live, via the `/v1/me` endpoint it already exposes
|
|
228
|
+
* for exactly this ("who is this credential"), and caches the answer briefly.
|
|
229
|
+
*
|
|
230
|
+
* Only a 401 counts as invalid. A 403 means the token authenticated and then failed a permission
|
|
231
|
+
* check, which is a live token; treating it as invalid would tell a client to re-authorize when
|
|
232
|
+
* re-authorizing cannot help. Anything else - a 5xx, a timeout, a DNS failure - is `unknown`.
|
|
233
|
+
*/
|
|
234
|
+
export const verifyBearer = async (token: string, deps: VerifyBearerDeps): Promise<BearerVerdict> => {
|
|
235
|
+
const {
|
|
236
|
+
baseURL,
|
|
237
|
+
fetchImpl = fetch,
|
|
238
|
+
now = Date.now,
|
|
239
|
+
ttlMs = BEARER_CACHE_TTL_MS,
|
|
240
|
+
timeoutMs = BEARER_CHECK_TIMEOUT_MS,
|
|
241
|
+
} = deps;
|
|
242
|
+
const key = hashToken(token);
|
|
243
|
+
|
|
244
|
+
const cached = bearerVerdicts.get(key);
|
|
245
|
+
if (cached && cached.expiresAt > now()) return cached.verdict;
|
|
246
|
+
|
|
247
|
+
const alreadyRunning = inFlightChecks.get(key);
|
|
248
|
+
if (alreadyRunning) return alreadyRunning;
|
|
249
|
+
|
|
250
|
+
const check = (async (): Promise<BearerVerdict> => {
|
|
251
|
+
let response: Response;
|
|
252
|
+
try {
|
|
253
|
+
response = await fetchImpl(`${stripSlash(baseURL)}/v1/me`, {
|
|
254
|
+
method: 'GET',
|
|
255
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
256
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
257
|
+
});
|
|
258
|
+
} catch {
|
|
259
|
+
return 'unknown';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const verdict: BearerVerdict =
|
|
263
|
+
response.status === 401 ? 'invalid'
|
|
264
|
+
: response.ok ? 'valid'
|
|
265
|
+
: 'unknown';
|
|
266
|
+
// An `unknown` is never cached: a blip would otherwise pin the answer for the whole TTL.
|
|
267
|
+
if (verdict !== 'unknown') rememberVerdict(key, verdict, now() + ttlMs);
|
|
268
|
+
return verdict;
|
|
269
|
+
})();
|
|
270
|
+
|
|
271
|
+
inFlightChecks.set(key, check);
|
|
272
|
+
try {
|
|
273
|
+
return await check;
|
|
274
|
+
} finally {
|
|
275
|
+
inFlightChecks.delete(key);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Rejects a bearer customer-api has already disowned, with the 401 + WWW-Authenticate that tells
|
|
281
|
+
* an MCP client to re-run the OAuth flow.
|
|
282
|
+
*
|
|
283
|
+
* Without this the resource server only checked that a token was PRESENT. A revoked, expired or
|
|
284
|
+
* entirely invented token completed `initialize` and `tools/list`, and the caller learned nothing
|
|
285
|
+
* until a tool call failed with an opaque 401 in its result text. No data was reachable - every
|
|
286
|
+
* data path revalidates at customer-api - but a client that never sees a 401 from the resource
|
|
287
|
+
* server has no reason to refresh, which for a 24h token means a connector that looks connected
|
|
288
|
+
* and silently cannot work.
|
|
289
|
+
*
|
|
290
|
+
* `unknown` is allowed through deliberately. Failing closed would turn a customer-api blip into a
|
|
291
|
+
* dead connector for everyone, and it would buy no confidentiality: the token still has to pass
|
|
292
|
+
* customer-api on the very next call that touches data.
|
|
293
|
+
*/
|
|
294
|
+
export const requireLiveBearer = async (
|
|
295
|
+
token: string,
|
|
296
|
+
config: OAuthConfig,
|
|
297
|
+
connector: Connector,
|
|
298
|
+
deps: VerifyBearerDeps,
|
|
299
|
+
): Promise<void> => {
|
|
300
|
+
const verdict = await verifyBearer(token, deps);
|
|
301
|
+
if (verdict !== 'invalid') return;
|
|
302
|
+
throw new UnauthorizedError(
|
|
303
|
+
'The access token is expired or has been revoked',
|
|
304
|
+
bearerChallenge(config, connector, 'invalid_token', 'The access token is expired or has been revoked'),
|
|
305
|
+
);
|
|
306
|
+
};
|
package/src/server.ts
CHANGED
|
@@ -28,7 +28,7 @@ export const newMcpServer = async ({
|
|
|
28
28
|
new McpServer(
|
|
29
29
|
{
|
|
30
30
|
name: 'roarkanalytics_sdk_api',
|
|
31
|
-
version: '4.12.
|
|
31
|
+
version: '4.12.2', // x-release-please-version
|
|
32
32
|
},
|
|
33
33
|
{
|
|
34
34
|
instructions: await getInstructions({ stainlessApiKey, customInstructionsPath }),
|