arc-1 0.7.2 → 0.8.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/README.md +27 -4
- package/dist/adt/btp.d.ts +14 -3
- package/dist/adt/btp.d.ts.map +1 -1
- package/dist/adt/btp.js +18 -3
- package/dist/adt/btp.js.map +1 -1
- package/dist/adt/diagnostics.d.ts +5 -1
- package/dist/adt/diagnostics.d.ts.map +1 -1
- package/dist/adt/diagnostics.js +20 -3
- package/dist/adt/diagnostics.js.map +1 -1
- package/dist/adt/errors.d.ts +1 -1
- package/dist/adt/errors.d.ts.map +1 -1
- package/dist/adt/errors.js +46 -2
- package/dist/adt/errors.js.map +1 -1
- package/dist/handlers/intent.js +2 -2
- package/dist/handlers/intent.js.map +1 -1
- package/dist/server/audit.d.ts +41 -1
- package/dist/server/audit.d.ts.map +1 -1
- package/dist/server/audit.js.map +1 -1
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js +32 -0
- package/dist/server/config.js.map +1 -1
- package/dist/server/http.d.ts +25 -0
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +149 -7
- package/dist/server/http.js.map +1 -1
- package/dist/server/server.d.ts +1 -1
- package/dist/server/server.js +1 -1
- package/dist/server/stateless-client-store.d.ts +97 -0
- package/dist/server/stateless-client-store.d.ts.map +1 -0
- package/dist/server/stateless-client-store.js +334 -0
- package/dist/server/stateless-client-store.js.map +1 -0
- package/dist/server/types.d.ts +12 -0
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js +2 -0
- package/dist/server/types.js.map +1 -1
- package/dist/server/xsuaa.d.ts +11 -43
- package/dist/server/xsuaa.d.ts.map +1 -1
- package/dist/server/xsuaa.js +12 -177
- package/dist/server/xsuaa.js.map +1 -1
- package/package.json +8 -2
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stateless OAuth Dynamic Client Registration store.
|
|
3
|
+
*
|
|
4
|
+
* MCP clients (Claude Desktop, Cursor, Copilot CLI…) register dynamically
|
|
5
|
+
* via RFC 7591 and cache the returned `client_id` locally. With an
|
|
6
|
+
* in-memory or local-disk store, every CF push / restart wipes the
|
|
7
|
+
* server-side registry — the cached `client_id` then fails with
|
|
8
|
+
* `invalid_client` and the user has to clear their MCP client's OAuth
|
|
9
|
+
* cache to recover.
|
|
10
|
+
*
|
|
11
|
+
* This store eliminates the storage problem entirely. Each `client_id`
|
|
12
|
+
* is a self-validating token: it carries the registration payload
|
|
13
|
+
* (redirect_uris, grant_types, …) plus an HMAC-SHA256 signature derived
|
|
14
|
+
* from a server-held key. `getClient` re-derives the payload by
|
|
15
|
+
* verifying the signature; no persistence is needed. Any process with
|
|
16
|
+
* the same signing key can validate any client_id ever issued.
|
|
17
|
+
*
|
|
18
|
+
* Tradeoffs vs the persisted in-memory store:
|
|
19
|
+
* + Survives `cf push`, `cf restart`, cell moves, multi-instance scale-out
|
|
20
|
+
* + No external dependency, no service binding, no native module
|
|
21
|
+
* - Per-client revocation is impossible (only TTL or full key rotation)
|
|
22
|
+
* - Rotating the signing key invalidates every outstanding registration
|
|
23
|
+
*
|
|
24
|
+
* The signing key is derived (via HKDF-style HMAC) from the XSUAA
|
|
25
|
+
* `clientsecret`, so it's already as stable as the service binding —
|
|
26
|
+
* service rebinding rotates both at once, which is the right boundary.
|
|
27
|
+
*/
|
|
28
|
+
import type { OAuthRegisteredClientsStore } from '@modelcontextprotocol/sdk/server/auth/clients.js';
|
|
29
|
+
import type { OAuthClientInformationFull } from '@modelcontextprotocol/sdk/shared/auth.js';
|
|
30
|
+
export interface StatelessDcrClientStoreOptions {
|
|
31
|
+
/**
|
|
32
|
+
* How long an issued client_id remains valid, in seconds. After this
|
|
33
|
+
* window `getClient()` returns undefined and clients re-register via
|
|
34
|
+
* `/register`. Default: 30 days. Lower values bound the blast radius if
|
|
35
|
+
* the signing key leaks; higher values reduce re-auth churn.
|
|
36
|
+
*/
|
|
37
|
+
ttlSeconds?: number;
|
|
38
|
+
/** Clock injection point for tests. Default: `Date.now`. */
|
|
39
|
+
now?: () => number;
|
|
40
|
+
}
|
|
41
|
+
export declare class StatelessDcrClientStore implements OAuthRegisteredClientsStore {
|
|
42
|
+
private readonly xsuaaClient;
|
|
43
|
+
private readonly hmacKey;
|
|
44
|
+
private readonly ttlSeconds;
|
|
45
|
+
private readonly now;
|
|
46
|
+
constructor(xsuaaClientId: string, xsuaaClientSecret: string, signingSecret: string, options?: StatelessDcrClientStoreOptions);
|
|
47
|
+
getClient(clientId: string): Promise<OAuthClientInformationFull | undefined>;
|
|
48
|
+
registerClient(client: Omit<OAuthClientInformationFull, 'client_id' | 'client_id_issued_at'>): Promise<OAuthClientInformationFull>;
|
|
49
|
+
/**
|
|
50
|
+
* Called by the MCP SDK before redirect_uri validation on `/authorize`.
|
|
51
|
+
*
|
|
52
|
+
* For the pre-registered XSUAA client we mutate the in-memory list (XSUAA
|
|
53
|
+
* itself is the authoritative validator via `xs-security.json` wildcards,
|
|
54
|
+
* so the SDK's local list is decorative). The mutation is replayed on
|
|
55
|
+
* every `/authorize`, so it doesn't need to persist.
|
|
56
|
+
*
|
|
57
|
+
* For DCR (`arc1-…`) clients we are stateless by design: there's nothing
|
|
58
|
+
* to mutate. The previous in-memory store implemented a percent-encoding
|
|
59
|
+
* loose-match (BAS/Theia registers `?x=1` then authorizes with `%3Fx=1`).
|
|
60
|
+
* Reproducing that statelessly would require either bundling every
|
|
61
|
+
* encoding variant in the signed payload or keeping a per-process scratch
|
|
62
|
+
* map, both of which undermine the "no state" goal. We accept the
|
|
63
|
+
* regression: affected clients re-register on encoding-variant mismatch,
|
|
64
|
+
* which is exactly what they did under the old store after every restart.
|
|
65
|
+
*/
|
|
66
|
+
ensureRedirectUri(clientId: string, uri: string): void;
|
|
67
|
+
private payloadToClientInfo;
|
|
68
|
+
private encode;
|
|
69
|
+
/**
|
|
70
|
+
* Decode and verify a `client_id`. Returns either the parsed payload or a
|
|
71
|
+
* structured failure reason — the caller emits the failure as an audit
|
|
72
|
+
* event with the right reason code (so probing attempts are observable).
|
|
73
|
+
*/
|
|
74
|
+
private decodeAndVerify;
|
|
75
|
+
private verifySignature;
|
|
76
|
+
private sign;
|
|
77
|
+
/**
|
|
78
|
+
* The client_secret is derived deterministically from the client_id, so
|
|
79
|
+
* any instance with the same signing key can validate it. This is the
|
|
80
|
+
* core reason DCR survives container restarts and scales out horizontally
|
|
81
|
+
* with no shared state.
|
|
82
|
+
*/
|
|
83
|
+
private deriveSecret;
|
|
84
|
+
private emitLookupFailed;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Validate a redirect URI against the allowed scheme/host policy.
|
|
88
|
+
*
|
|
89
|
+
* Allowed: `https://*`, `http://` to localhost / 127.0.0.1 / [::1], and known
|
|
90
|
+
* MCP-client custom schemes (`claude:`, `cursor:`, `vscode:`,
|
|
91
|
+
* `vscode-insiders:`).
|
|
92
|
+
*
|
|
93
|
+
* Rejected: `javascript:`, `data:`, `file:`, `ftp:`, and any `http://` to
|
|
94
|
+
* non-loopback hosts.
|
|
95
|
+
*/
|
|
96
|
+
export declare function validateRedirectUri(uri: string): void;
|
|
97
|
+
//# sourceMappingURL=stateless-client-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stateless-client-store.d.ts","sourceRoot":"","sources":["../../src/server/stateless-client-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,kDAAkD,CAAC;AACpG,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AAyE3F,MAAM,WAAW,8BAA8B;IAC7C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,4DAA4D;IAC5D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAuBD,qBAAa,uBAAwB,YAAW,2BAA2B;IACzE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;gBAGjC,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,MAAM,EACzB,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,8BAAmC;IAgBxC,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,0BAA0B,GAAG,SAAS,CAAC;IA0B5E,cAAc,CAClB,MAAM,EAAE,IAAI,CAAC,0BAA0B,EAAE,WAAW,GAAG,qBAAqB,CAAC,GAC5E,OAAO,CAAC,0BAA0B,CAAC;IA8CtC;;;;;;;;;;;;;;;;OAgBG;IACH,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAiBtD,OAAO,CAAC,mBAAmB;IAa3B,OAAO,CAAC,MAAM;IAMd;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAsBvB,OAAO,CAAC,eAAe;IAOvB,OAAO,CAAC,IAAI;IAMZ;;;;;OAKG;IACH,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,gBAAgB;CAczB;AAoBD;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CA6BrD"}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stateless OAuth Dynamic Client Registration store.
|
|
3
|
+
*
|
|
4
|
+
* MCP clients (Claude Desktop, Cursor, Copilot CLI…) register dynamically
|
|
5
|
+
* via RFC 7591 and cache the returned `client_id` locally. With an
|
|
6
|
+
* in-memory or local-disk store, every CF push / restart wipes the
|
|
7
|
+
* server-side registry — the cached `client_id` then fails with
|
|
8
|
+
* `invalid_client` and the user has to clear their MCP client's OAuth
|
|
9
|
+
* cache to recover.
|
|
10
|
+
*
|
|
11
|
+
* This store eliminates the storage problem entirely. Each `client_id`
|
|
12
|
+
* is a self-validating token: it carries the registration payload
|
|
13
|
+
* (redirect_uris, grant_types, …) plus an HMAC-SHA256 signature derived
|
|
14
|
+
* from a server-held key. `getClient` re-derives the payload by
|
|
15
|
+
* verifying the signature; no persistence is needed. Any process with
|
|
16
|
+
* the same signing key can validate any client_id ever issued.
|
|
17
|
+
*
|
|
18
|
+
* Tradeoffs vs the persisted in-memory store:
|
|
19
|
+
* + Survives `cf push`, `cf restart`, cell moves, multi-instance scale-out
|
|
20
|
+
* + No external dependency, no service binding, no native module
|
|
21
|
+
* - Per-client revocation is impossible (only TTL or full key rotation)
|
|
22
|
+
* - Rotating the signing key invalidates every outstanding registration
|
|
23
|
+
*
|
|
24
|
+
* The signing key is derived (via HKDF-style HMAC) from the XSUAA
|
|
25
|
+
* `clientsecret`, so it's already as stable as the service binding —
|
|
26
|
+
* service rebinding rotates both at once, which is the right boundary.
|
|
27
|
+
*/
|
|
28
|
+
import crypto from 'node:crypto';
|
|
29
|
+
import { logger } from './logger.js';
|
|
30
|
+
// ─── Constants ────────────────────────────────────────────────────────
|
|
31
|
+
/** All DCR-issued client_ids start with this prefix. */
|
|
32
|
+
const ID_PREFIX = 'arc1-';
|
|
33
|
+
/**
|
|
34
|
+
* Domain-separation label bound into the HMAC key derivation. Bumping the
|
|
35
|
+
* suffix ("v1" → "v2") invalidates every previously-issued client_id without
|
|
36
|
+
* requiring a service-binding rotation, which is a useful escape hatch.
|
|
37
|
+
*/
|
|
38
|
+
const KDF_LABEL = 'arc1-dcr/v1';
|
|
39
|
+
/** Schema version of the JSON payload embedded in the signed client_id. */
|
|
40
|
+
const PAYLOAD_VERSION = 1;
|
|
41
|
+
/**
|
|
42
|
+
* Truncated HMAC-SHA256 length in bytes. 16 bytes = 128 bits, which is well
|
|
43
|
+
* above the practical forgery threshold for opaque IDs (NIST SP 800-107
|
|
44
|
+
* acceptable for non-replayable identifiers).
|
|
45
|
+
*/
|
|
46
|
+
const SIG_BYTES = 16;
|
|
47
|
+
/**
|
|
48
|
+
* Default lifetime of a DCR registration. Tunable via `ttlSeconds` so deployments
|
|
49
|
+
* with stricter compromise-window requirements can shorten it. 30 days matches
|
|
50
|
+
* typical OAuth refresh-token lifetimes — long enough that users don't see
|
|
51
|
+
* spurious "re-authenticate" prompts during normal use.
|
|
52
|
+
*/
|
|
53
|
+
const DEFAULT_TTL_SECONDS = 30 * 24 * 60 * 60;
|
|
54
|
+
// Defaults applied when a registration omits these fields.
|
|
55
|
+
const DEFAULT_GRANT_TYPES = ['authorization_code', 'refresh_token'];
|
|
56
|
+
const DEFAULT_RESPONSE_TYPES = ['code'];
|
|
57
|
+
const DEFAULT_TOKEN_AUTH_METHOD = 'client_secret_post';
|
|
58
|
+
/**
|
|
59
|
+
* Built-in redirect_uris for the pre-registered XSUAA client. These cover the
|
|
60
|
+
* common MCP clients out of the box; additional URIs can be added at
|
|
61
|
+
* `/authorize` time via `ensureRedirectUri()`. The list MUST also be registered
|
|
62
|
+
* in `xs-security.json` — XSUAA is the authoritative validator for this client.
|
|
63
|
+
*/
|
|
64
|
+
const XSUAA_DEFAULT_REDIRECT_URIS = [
|
|
65
|
+
'http://localhost:6274/oauth/callback', // MCP Inspector
|
|
66
|
+
'http://localhost:3000/oauth/callback', // Local dev
|
|
67
|
+
'https://claude.ai/api/mcp/auth_callback', // Claude Desktop
|
|
68
|
+
'cursor://anysphere.cursor-retrieval/oauth/callback', // Cursor
|
|
69
|
+
'vscode://vscode.microsoft-authentication/callback', // VS Code
|
|
70
|
+
];
|
|
71
|
+
// ─── Default XSUAA client ─────────────────────────────────────────────
|
|
72
|
+
/**
|
|
73
|
+
* Pre-registered XSUAA client config. MCP clients that hit the XSUAA
|
|
74
|
+
* `clientid` directly (Manual mode in Copilot Studio, etc.) resolve through
|
|
75
|
+
* this entry instead of going through DCR.
|
|
76
|
+
*/
|
|
77
|
+
function buildXsuaaDefaultClient(clientId, clientSecret) {
|
|
78
|
+
return {
|
|
79
|
+
client_id: clientId,
|
|
80
|
+
client_secret: clientSecret,
|
|
81
|
+
redirect_uris: [...XSUAA_DEFAULT_REDIRECT_URIS],
|
|
82
|
+
grant_types: [...DEFAULT_GRANT_TYPES],
|
|
83
|
+
response_types: [...DEFAULT_RESPONSE_TYPES],
|
|
84
|
+
token_endpoint_auth_method: DEFAULT_TOKEN_AUTH_METHOD,
|
|
85
|
+
client_name: 'ARC-1 XSUAA Default Client',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
// ─── Store ────────────────────────────────────────────────────────────
|
|
89
|
+
export class StatelessDcrClientStore {
|
|
90
|
+
xsuaaClient;
|
|
91
|
+
hmacKey;
|
|
92
|
+
ttlSeconds;
|
|
93
|
+
now;
|
|
94
|
+
constructor(xsuaaClientId, xsuaaClientSecret, signingSecret, options = {}) {
|
|
95
|
+
if (!signingSecret) {
|
|
96
|
+
throw new Error('StatelessDcrClientStore requires a non-empty signingSecret');
|
|
97
|
+
}
|
|
98
|
+
// Derive a dedicated HMAC key so the raw service-binding secret is never
|
|
99
|
+
// used directly to sign client_ids. The KDF_LABEL doubles as a domain
|
|
100
|
+
// separator (see comment on the constant).
|
|
101
|
+
this.hmacKey = crypto.createHmac('sha256', signingSecret).update(KDF_LABEL).digest();
|
|
102
|
+
this.xsuaaClient = buildXsuaaDefaultClient(xsuaaClientId, xsuaaClientSecret);
|
|
103
|
+
this.ttlSeconds = options.ttlSeconds ?? DEFAULT_TTL_SECONDS;
|
|
104
|
+
this.now = options.now ?? (() => Date.now());
|
|
105
|
+
}
|
|
106
|
+
// ── OAuthRegisteredClientsStore implementation ──
|
|
107
|
+
async getClient(clientId) {
|
|
108
|
+
if (clientId === this.xsuaaClient.client_id) {
|
|
109
|
+
return this.xsuaaClient;
|
|
110
|
+
}
|
|
111
|
+
if (!clientId.startsWith(ID_PREFIX)) {
|
|
112
|
+
this.emitLookupFailed(clientId, 'unknown_prefix');
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const decoded = this.decodeAndVerify(clientId);
|
|
116
|
+
if (decoded.kind === 'error') {
|
|
117
|
+
this.emitLookupFailed(clientId, decoded.reason);
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
const ageSec = Math.floor(this.now() / 1000) - decoded.payload.iat;
|
|
121
|
+
if (ageSec > this.ttlSeconds) {
|
|
122
|
+
this.emitLookupFailed(clientId, 'expired');
|
|
123
|
+
logger.debug('OAuth client expired (TTL)', { clientId, ageSec, ttlSeconds: this.ttlSeconds });
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
return this.payloadToClientInfo(clientId, decoded.payload);
|
|
127
|
+
}
|
|
128
|
+
async registerClient(client) {
|
|
129
|
+
if (client.redirect_uris) {
|
|
130
|
+
for (const uri of client.redirect_uris) {
|
|
131
|
+
validateRedirectUri(uri);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const issuedAt = Math.floor(this.now() / 1000);
|
|
135
|
+
const payload = {
|
|
136
|
+
v: PAYLOAD_VERSION,
|
|
137
|
+
iat: issuedAt,
|
|
138
|
+
ru: client.redirect_uris ?? [],
|
|
139
|
+
};
|
|
140
|
+
if (client.grant_types)
|
|
141
|
+
payload.gt = client.grant_types;
|
|
142
|
+
if (client.response_types)
|
|
143
|
+
payload.rt = client.response_types;
|
|
144
|
+
if (client.token_endpoint_auth_method)
|
|
145
|
+
payload.am = client.token_endpoint_auth_method;
|
|
146
|
+
if (client.client_name)
|
|
147
|
+
payload.cn = client.client_name;
|
|
148
|
+
const clientId = this.encode(payload);
|
|
149
|
+
const clientSecret = this.deriveSecret(clientId);
|
|
150
|
+
logger.debug('OAuth client registered (stateless)', {
|
|
151
|
+
clientId,
|
|
152
|
+
clientName: client.client_name,
|
|
153
|
+
idBytes: clientId.length,
|
|
154
|
+
});
|
|
155
|
+
logger.emitAudit({
|
|
156
|
+
timestamp: new Date().toISOString(),
|
|
157
|
+
level: 'info',
|
|
158
|
+
event: 'oauth_client_registered',
|
|
159
|
+
registeredClientId: clientId,
|
|
160
|
+
clientName: client.client_name,
|
|
161
|
+
redirectUriCount: payload.ru.length,
|
|
162
|
+
idBytes: clientId.length,
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
...client,
|
|
166
|
+
client_id: clientId,
|
|
167
|
+
client_secret: clientSecret,
|
|
168
|
+
client_id_issued_at: issuedAt,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
// ── SDK redirect_uri hook ──
|
|
172
|
+
/**
|
|
173
|
+
* Called by the MCP SDK before redirect_uri validation on `/authorize`.
|
|
174
|
+
*
|
|
175
|
+
* For the pre-registered XSUAA client we mutate the in-memory list (XSUAA
|
|
176
|
+
* itself is the authoritative validator via `xs-security.json` wildcards,
|
|
177
|
+
* so the SDK's local list is decorative). The mutation is replayed on
|
|
178
|
+
* every `/authorize`, so it doesn't need to persist.
|
|
179
|
+
*
|
|
180
|
+
* For DCR (`arc1-…`) clients we are stateless by design: there's nothing
|
|
181
|
+
* to mutate. The previous in-memory store implemented a percent-encoding
|
|
182
|
+
* loose-match (BAS/Theia registers `?x=1` then authorizes with `%3Fx=1`).
|
|
183
|
+
* Reproducing that statelessly would require either bundling every
|
|
184
|
+
* encoding variant in the signed payload or keeping a per-process scratch
|
|
185
|
+
* map, both of which undermine the "no state" goal. We accept the
|
|
186
|
+
* regression: affected clients re-register on encoding-variant mismatch,
|
|
187
|
+
* which is exactly what they did under the old store after every restart.
|
|
188
|
+
*/
|
|
189
|
+
ensureRedirectUri(clientId, uri) {
|
|
190
|
+
if (clientId !== this.xsuaaClient.client_id)
|
|
191
|
+
return;
|
|
192
|
+
if (this.xsuaaClient.redirect_uris.includes(uri))
|
|
193
|
+
return;
|
|
194
|
+
this.xsuaaClient.redirect_uris.push(uri);
|
|
195
|
+
logger.debug('Dynamic redirect_uri registered for XSUAA client', { clientId, uri });
|
|
196
|
+
logger.emitAudit({
|
|
197
|
+
timestamp: new Date().toISOString(),
|
|
198
|
+
level: 'info',
|
|
199
|
+
event: 'oauth_redirect_uri_registered',
|
|
200
|
+
registeredClientId: clientId,
|
|
201
|
+
redirectUri: uri,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
// ── Internals: encode / decode / sign / verify ──
|
|
205
|
+
payloadToClientInfo(clientId, payload) {
|
|
206
|
+
return {
|
|
207
|
+
client_id: clientId,
|
|
208
|
+
client_secret: this.deriveSecret(clientId),
|
|
209
|
+
client_id_issued_at: payload.iat,
|
|
210
|
+
redirect_uris: payload.ru,
|
|
211
|
+
grant_types: payload.gt ?? [...DEFAULT_GRANT_TYPES],
|
|
212
|
+
response_types: payload.rt ?? [...DEFAULT_RESPONSE_TYPES],
|
|
213
|
+
token_endpoint_auth_method: payload.am ?? DEFAULT_TOKEN_AUTH_METHOD,
|
|
214
|
+
client_name: payload.cn,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
encode(payload) {
|
|
218
|
+
const payloadB64 = Buffer.from(JSON.stringify(payload), 'utf8').toString('base64url');
|
|
219
|
+
const sig = this.sign(payloadB64);
|
|
220
|
+
return `${ID_PREFIX}${payloadB64}.${sig}`;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Decode and verify a `client_id`. Returns either the parsed payload or a
|
|
224
|
+
* structured failure reason — the caller emits the failure as an audit
|
|
225
|
+
* event with the right reason code (so probing attempts are observable).
|
|
226
|
+
*/
|
|
227
|
+
decodeAndVerify(clientId) {
|
|
228
|
+
const stripped = clientId.slice(ID_PREFIX.length);
|
|
229
|
+
const dot = stripped.lastIndexOf('.');
|
|
230
|
+
if (dot < 0)
|
|
231
|
+
return { kind: 'error', reason: 'malformed' };
|
|
232
|
+
const payloadB64 = stripped.slice(0, dot);
|
|
233
|
+
const sigB64 = stripped.slice(dot + 1);
|
|
234
|
+
if (!this.verifySignature(payloadB64, sigB64)) {
|
|
235
|
+
return { kind: 'error', reason: 'bad_signature' };
|
|
236
|
+
}
|
|
237
|
+
const payload = parsePayload(payloadB64);
|
|
238
|
+
if (!payload)
|
|
239
|
+
return { kind: 'error', reason: 'invalid_payload' };
|
|
240
|
+
return { kind: 'ok', payload };
|
|
241
|
+
}
|
|
242
|
+
verifySignature(payloadB64, sigB64) {
|
|
243
|
+
const expected = Buffer.from(this.sign(payloadB64), 'base64url');
|
|
244
|
+
const actual = Buffer.from(sigB64, 'base64url');
|
|
245
|
+
if (actual.length !== expected.length || actual.length !== SIG_BYTES)
|
|
246
|
+
return false;
|
|
247
|
+
return crypto.timingSafeEqual(actual, expected);
|
|
248
|
+
}
|
|
249
|
+
sign(payloadB64) {
|
|
250
|
+
const fullDigest = crypto.createHmac('sha256', this.hmacKey).update(payloadB64).digest();
|
|
251
|
+
// Truncate to SIG_BYTES — see the comment on the constant for rationale.
|
|
252
|
+
return fullDigest.subarray(0, SIG_BYTES).toString('base64url');
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* The client_secret is derived deterministically from the client_id, so
|
|
256
|
+
* any instance with the same signing key can validate it. This is the
|
|
257
|
+
* core reason DCR survives container restarts and scales out horizontally
|
|
258
|
+
* with no shared state.
|
|
259
|
+
*/
|
|
260
|
+
deriveSecret(clientId) {
|
|
261
|
+
return crypto.createHmac('sha256', this.hmacKey).update(`secret:${clientId}`).digest('base64url');
|
|
262
|
+
}
|
|
263
|
+
emitLookupFailed(clientId, reason) {
|
|
264
|
+
logger.debug('OAuth client lookup failed', { clientId, reason });
|
|
265
|
+
logger.emitAudit({
|
|
266
|
+
timestamp: new Date().toISOString(),
|
|
267
|
+
// 'expired' is normal-ish (TTL eviction); the rest are probing/forgery signals.
|
|
268
|
+
level: reason === 'expired' ? 'info' : 'warn',
|
|
269
|
+
event: 'oauth_client_lookup_failed',
|
|
270
|
+
registeredClientId: clientId,
|
|
271
|
+
reason,
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// ─── Module-level helpers ─────────────────────────────────────────────
|
|
276
|
+
/**
|
|
277
|
+
* Parse a base64url-encoded payload back into a typed `SignedPayload`. Returns
|
|
278
|
+
* `undefined` on any failure (decode error, JSON parse error, schema mismatch).
|
|
279
|
+
*/
|
|
280
|
+
function parsePayload(payloadB64) {
|
|
281
|
+
try {
|
|
282
|
+
const json = Buffer.from(payloadB64, 'base64url').toString('utf8');
|
|
283
|
+
const parsed = JSON.parse(json);
|
|
284
|
+
if (parsed.v !== PAYLOAD_VERSION)
|
|
285
|
+
return undefined;
|
|
286
|
+
if (typeof parsed.iat !== 'number' || !Array.isArray(parsed.ru))
|
|
287
|
+
return undefined;
|
|
288
|
+
return parsed;
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Validate a redirect URI against the allowed scheme/host policy.
|
|
296
|
+
*
|
|
297
|
+
* Allowed: `https://*`, `http://` to localhost / 127.0.0.1 / [::1], and known
|
|
298
|
+
* MCP-client custom schemes (`claude:`, `cursor:`, `vscode:`,
|
|
299
|
+
* `vscode-insiders:`).
|
|
300
|
+
*
|
|
301
|
+
* Rejected: `javascript:`, `data:`, `file:`, `ftp:`, and any `http://` to
|
|
302
|
+
* non-loopback hosts.
|
|
303
|
+
*/
|
|
304
|
+
export function validateRedirectUri(uri) {
|
|
305
|
+
const ALLOWED_CUSTOM_SCHEMES = ['claude:', 'cursor:', 'vscode:', 'vscode-insiders:'];
|
|
306
|
+
const BLOCKED_SCHEMES = ['javascript:', 'data:', 'file:', 'ftp:'];
|
|
307
|
+
for (const scheme of BLOCKED_SCHEMES) {
|
|
308
|
+
if (uri.toLowerCase().startsWith(scheme)) {
|
|
309
|
+
throw new Error(`Redirect URI rejected: '${scheme}' scheme is not allowed. Use https:// or a registered custom scheme.`);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
for (const scheme of ALLOWED_CUSTOM_SCHEMES) {
|
|
313
|
+
if (uri.toLowerCase().startsWith(scheme))
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
const parsed = new URL(uri);
|
|
318
|
+
if (parsed.protocol === 'https:')
|
|
319
|
+
return;
|
|
320
|
+
if (parsed.protocol === 'http:') {
|
|
321
|
+
const host = parsed.hostname.toLowerCase();
|
|
322
|
+
if (host === 'localhost' || host === '127.0.0.1' || host === '[::1]' || host === '::1')
|
|
323
|
+
return;
|
|
324
|
+
throw new Error(`Redirect URI rejected: http:// is only allowed for localhost/127.0.0.1. Got: '${uri}'`);
|
|
325
|
+
}
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
catch (err) {
|
|
329
|
+
if (err instanceof Error && err.message.startsWith('Redirect URI rejected'))
|
|
330
|
+
throw err;
|
|
331
|
+
// URL parsing failed for some other reason (unknown protocol etc.) — allow.
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
//# sourceMappingURL=stateless-client-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stateless-client-store.js","sourceRoot":"","sources":["../../src/server/stateless-client-store.ts"],"names":[],"mappings":"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"}
|
package/dist/server/types.d.ts
CHANGED
|
@@ -61,6 +61,13 @@ export interface ServerConfig {
|
|
|
61
61
|
/** Clock tolerance in seconds for JWT exp/nbf validation (default: 0 — no tolerance) */
|
|
62
62
|
oidcClockTolerance?: number;
|
|
63
63
|
xsuaaAuth: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Lifetime of an OAuth DCR registration (`client_id`) in seconds. Default:
|
|
66
|
+
* 30 days. Lower values bound the blast radius if the signing key leaks;
|
|
67
|
+
* higher values reduce re-auth churn. Hard-capped at 90 days — longer-lived
|
|
68
|
+
* credentials should use the pre-registered XSUAA client instead of DCR.
|
|
69
|
+
* Only consulted when XSUAA OAuth proxy mode is active. */
|
|
70
|
+
oauthDcrTtlSeconds: number;
|
|
64
71
|
btpServiceKey?: string;
|
|
65
72
|
btpServiceKeyFile?: string;
|
|
66
73
|
btpOAuthCallbackPort: number;
|
|
@@ -99,6 +106,11 @@ export interface ServerConfig {
|
|
|
99
106
|
cacheWarmupPackages: string;
|
|
100
107
|
/** Maximum concurrent SAP HTTP requests (default: 10). Prevents work process exhaustion. */
|
|
101
108
|
maxConcurrent: number;
|
|
109
|
+
/** Exact-match CORS allowlist. Empty array (the default) disables CORS entirely so that
|
|
110
|
+
* browser-originated cross-origin requests are blocked. Native MCP clients
|
|
111
|
+
* (Claude Desktop / Cursor / VS Code Copilot / Copilot Studio) do not need this — they
|
|
112
|
+
* use native HTTP, not the browser fetch API, and never trigger CORS. */
|
|
113
|
+
allowedOrigins: string[];
|
|
102
114
|
verbose: boolean;
|
|
103
115
|
}
|
|
104
116
|
/** Default configuration values — restrictive by default. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,yBAAyB;AACzB,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,iBAAiB,CAAC;AAExD,kEAAkE;AAClE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;AAElD,qFAAqF;AACrF,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAE3B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAGlB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,SAAS,EAAE,aAAa,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IAGjB,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;IACtB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,EAAE,OAAO,CAAC;IACxB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,2FAA2F;IAC3F,WAAW,EAAE,MAAM,EAAE,CAAC;IAGtB,cAAc,EAAE,aAAa,CAAC;IAC9B,WAAW,EAAE,aAAa,CAAC;IAC3B,UAAU,EAAE,aAAa,CAAC;IAC1B,WAAW,EAAE,aAAa,CAAC;IAC3B,UAAU,EAAE,aAAa,CAAC;IAC1B,gBAAgB,EAAE,aAAa,CAAC;IAChC,WAAW,EAAE,aAAa,CAAC;IAC3B,cAAc,EAAE,aAAa,CAAC;IAC9B,UAAU,EAAE,aAAa,CAAC;IAG1B,uEAAuE;IACvE,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAGtC,sHAAsH;IACtH,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,yBAAyB;AACzB,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,iBAAiB,CAAC;AAExD,kEAAkE;AAClE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC;AAElD,qFAAqF;AACrF,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAE3B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAGlB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,SAAS,EAAE,aAAa,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IAGjB,WAAW,EAAE,OAAO,CAAC;IACrB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;IACtB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,cAAc,EAAE,OAAO,CAAC;IACxB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,2FAA2F;IAC3F,WAAW,EAAE,MAAM,EAAE,CAAC;IAGtB,cAAc,EAAE,aAAa,CAAC;IAC9B,WAAW,EAAE,aAAa,CAAC;IAC3B,UAAU,EAAE,aAAa,CAAC;IAC1B,WAAW,EAAE,aAAa,CAAC;IAC3B,UAAU,EAAE,aAAa,CAAC;IAC1B,gBAAgB,EAAE,aAAa,CAAC;IAChC,WAAW,EAAE,aAAa,CAAC;IAC3B,cAAc,EAAE,aAAa,CAAC;IAC9B,UAAU,EAAE,aAAa,CAAC;IAG1B,uEAAuE;IACvE,UAAU,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAGtC,sHAAsH;IACtH,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IAEnB;;;;;+DAK2D;IAC3D,kBAAkB,EAAE,MAAM,CAAC;IAG3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;IAG7B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,+EAA+E;IAC/E,oBAAoB,EAAE,OAAO,CAAC;IAG9B,oFAAoF;IACpF,YAAY,EAAE,OAAO,CAAC;IAGtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAC9C,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAG3B,0HAA0H;IAC1H,QAAQ,EAAE,UAAU,GAAG,cAAc,CAAC;IAGtC,+DAA+D;IAC/D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uDAAuD;IACvD,eAAe,EAAE,OAAO,CAAC;IACzB;;;;;;;;iDAQ6C;IAC7C,gBAAgB,EAAE,OAAO,CAAC;IAG1B,oGAAoG;IACpG,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjD,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,WAAW,EAAE,OAAO,CAAC;IACrB,8EAA8E;IAC9E,mBAAmB,EAAE,MAAM,CAAC;IAG5B,4FAA4F;IAC5F,aAAa,EAAE,MAAM,CAAC;IAGtB;;;8EAG0E;IAC1E,cAAc,EAAE,MAAM,EAAE,CAAC;IAGzB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,6DAA6D;AAC7D,eAAO,MAAM,cAAc,EAAE,YA8C5B,CAAC"}
|
package/dist/server/types.js
CHANGED
|
@@ -36,6 +36,7 @@ export const DEFAULT_CONFIG = {
|
|
|
36
36
|
featureFlp: 'auto',
|
|
37
37
|
systemType: 'auto',
|
|
38
38
|
xsuaaAuth: false,
|
|
39
|
+
oauthDcrTtlSeconds: 30 * 24 * 60 * 60, // 30 days
|
|
39
40
|
btpOAuthCallbackPort: 0,
|
|
40
41
|
ppEnabled: false,
|
|
41
42
|
ppStrict: false,
|
|
@@ -49,6 +50,7 @@ export const DEFAULT_CONFIG = {
|
|
|
49
50
|
cacheWarmup: false,
|
|
50
51
|
cacheWarmupPackages: '',
|
|
51
52
|
maxConcurrent: 10,
|
|
53
|
+
allowedOrigins: [],
|
|
52
54
|
logLevel: 'info',
|
|
53
55
|
logFormat: 'text',
|
|
54
56
|
verbose: false,
|
package/dist/server/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/server/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAyIH,6DAA6D;AAC7D,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C,GAAG,EAAE,EAAE;IACP,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;IACZ,MAAM,EAAE,KAAK;IACb,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,KAAK;IACf,SAAS,EAAE,OAAO;IAClB,QAAQ,EAAE,cAAc;IACxB,WAAW,EAAE,KAAK;IAClB,gBAAgB,EAAE,KAAK;IACvB,YAAY,EAAE,KAAK;IACnB,oBAAoB,EAAE,KAAK;IAC3B,cAAc,EAAE,KAAK;IACrB,eAAe,EAAE,CAAC,MAAM,CAAC;IACzB,iBAAiB,EAAE,EAAE;IACrB,WAAW,EAAE,EAAE;IACf,cAAc,EAAE,MAAM;IACtB,WAAW,EAAE,MAAM;IACnB,UAAU,EAAE,MAAM;IAClB,WAAW,EAAE,MAAM;IACnB,UAAU,EAAE,MAAM;IAClB,gBAAgB,EAAE,MAAM;IACxB,WAAW,EAAE,MAAM;IACnB,cAAc,EAAE,MAAM;IACtB,UAAU,EAAE,MAAM;IAClB,UAAU,EAAE,MAAM;IAClB,SAAS,EAAE,KAAK;IAChB,kBAAkB,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE,UAAU;IACjD,oBAAoB,EAAE,CAAC;IACvB,SAAS,EAAE,KAAK;IAChB,QAAQ,EAAE,KAAK;IACf,oBAAoB,EAAE,KAAK;IAC3B,YAAY,EAAE,KAAK;IACnB,QAAQ,EAAE,UAAU;IACpB,eAAe,EAAE,IAAI;IACrB,gBAAgB,EAAE,KAAK;IACvB,SAAS,EAAE,MAAM;IACjB,SAAS,EAAE,gBAAgB;IAC3B,WAAW,EAAE,KAAK;IAClB,mBAAmB,EAAE,EAAE;IACvB,aAAa,EAAE,EAAE;IACjB,cAAc,EAAE,EAAE;IAClB,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,MAAM;IACjB,OAAO,EAAE,KAAK;CACf,CAAC"}
|
package/dist/server/xsuaa.d.ts
CHANGED
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
* - Offline validation with automatic JWKS caching
|
|
16
16
|
* - checkLocalScope() for scope enforcement
|
|
17
17
|
*
|
|
18
|
-
* 2.
|
|
18
|
+
* 2. Stateless DCR client store (StatelessDcrClientStore):
|
|
19
19
|
* - MCP clients (Claude Desktop, Cursor) register dynamically via RFC 7591
|
|
20
|
-
* -
|
|
20
|
+
* - client_ids are HMAC-signed by the XSUAA clientsecret, so they
|
|
21
|
+
* survive restarts / pushes / cell moves without any backing store
|
|
21
22
|
* - XSUAA clientId is pre-registered as the default client
|
|
22
23
|
*
|
|
23
24
|
* 3. Chained token verifier:
|
|
24
25
|
* - Tries XSUAA → Entra ID OIDC → API key in order
|
|
25
26
|
* - All three auth modes coexist on the same /mcp endpoint
|
|
26
27
|
*/
|
|
27
|
-
import type { OAuthRegisteredClientsStore } from '@modelcontextprotocol/sdk/server/auth/clients.js';
|
|
28
28
|
import { ProxyOAuthServerProvider } from '@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js';
|
|
29
29
|
import type { AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
|
|
30
|
-
import
|
|
30
|
+
import { StatelessDcrClientStore } from './stateless-client-store.js';
|
|
31
31
|
/** XSUAA credentials from VCAP_SERVICES */
|
|
32
32
|
export interface XsuaaCredentials {
|
|
33
33
|
url: string;
|
|
@@ -37,43 +37,6 @@ export interface XsuaaCredentials {
|
|
|
37
37
|
uaadomain: string;
|
|
38
38
|
verificationkey?: string;
|
|
39
39
|
}
|
|
40
|
-
/**
|
|
41
|
-
* In-memory store for OAuth client registrations.
|
|
42
|
-
*
|
|
43
|
-
* MCP clients dynamically register via RFC 7591. The XSUAA service binding
|
|
44
|
-
* clientId is pre-registered as the default client so that clients can
|
|
45
|
-
* use it directly without registration.
|
|
46
|
-
*/
|
|
47
|
-
export declare class InMemoryClientStore implements OAuthRegisteredClientsStore {
|
|
48
|
-
private clients;
|
|
49
|
-
constructor(xsuaaClientId: string, xsuaaClientSecret: string);
|
|
50
|
-
/**
|
|
51
|
-
* Dynamically add a redirect URI to a client's allow list.
|
|
52
|
-
*
|
|
53
|
-
* The MCP SDK validates redirect_uri with byte-exact matching for
|
|
54
|
-
* non-loopback HTTPS URIs, but two classes of clients need relaxation:
|
|
55
|
-
*
|
|
56
|
-
* 1. Pre-registered XSUAA client: XSUAA itself is the authoritative
|
|
57
|
-
* redirect-URI validator (via xs-security.json wildcard patterns),
|
|
58
|
-
* so any URI is accepted here and forwarded.
|
|
59
|
-
*
|
|
60
|
-
* 2. DCR clients (arc1-*): only accept URIs that are semantically
|
|
61
|
-
* equivalent to one the client registered. This handles clients that
|
|
62
|
-
* use different percent-encoding at /register vs /authorize — notably
|
|
63
|
-
* BAS/Cline via Theia's OAuth proxy, which registers `/callback?x=1`
|
|
64
|
-
* but requests with `/callback%3Fx=1`. Security: hostname, port, and
|
|
65
|
-
* decoded path/query must all match a previously-registered URI.
|
|
66
|
-
*/
|
|
67
|
-
ensureRedirectUri(clientId: string, uri: string): void;
|
|
68
|
-
getClient(clientId: string): Promise<OAuthClientInformationFull | undefined>;
|
|
69
|
-
registerClient(client: Omit<OAuthClientInformationFull, 'client_id' | 'client_id_issued_at'>): Promise<OAuthClientInformationFull>;
|
|
70
|
-
/**
|
|
71
|
-
* Validate a redirect URI against allowed scheme/host policy.
|
|
72
|
-
* Allowed: https://* , http://localhost or 127.0.0.1 or [::1], custom MCP client schemes.
|
|
73
|
-
* Rejected: javascript:, data:, file:, ftp:, and any http:// to non-loopback hosts.
|
|
74
|
-
*/
|
|
75
|
-
private validateRedirectUri;
|
|
76
|
-
}
|
|
77
40
|
/**
|
|
78
41
|
* Verify a JWT token using @sap/xssec.
|
|
79
42
|
*
|
|
@@ -97,8 +60,13 @@ export declare function createChainedTokenVerifier(config: {
|
|
|
97
60
|
oidcIssuer?: string;
|
|
98
61
|
oidcAudience?: string;
|
|
99
62
|
}, xsuaaVerifier?: (token: string) => Promise<AuthInfo>, oidcVerifier?: (token: string) => Promise<AuthInfo>): (token: string) => Promise<AuthInfo>;
|
|
100
|
-
export
|
|
63
|
+
export interface CreateXsuaaOAuthProviderOptions {
|
|
64
|
+
/** Lifetime of issued DCR client_ids in seconds. Falls back to the store's
|
|
65
|
+
* built-in default (30 days) when omitted. */
|
|
66
|
+
dcrTtlSeconds?: number;
|
|
67
|
+
}
|
|
68
|
+
export declare function createXsuaaOAuthProvider(credentials: XsuaaCredentials, appUrl: string, options?: CreateXsuaaOAuthProviderOptions): {
|
|
101
69
|
provider: ProxyOAuthServerProvider;
|
|
102
|
-
clientStore:
|
|
70
|
+
clientStore: StatelessDcrClientStore;
|
|
103
71
|
};
|
|
104
72
|
//# sourceMappingURL=xsuaa.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xsuaa.d.ts","sourceRoot":"","sources":["../../src/server/xsuaa.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"xsuaa.d.ts","sourceRoot":"","sources":["../../src/server/xsuaa.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,EAAE,wBAAwB,EAAE,MAAM,kEAAkE,CAAC;AAC5G,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAM/E,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAatE,2CAA2C;AAC3C,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAID;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,gBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CA6C5G;AA2BD;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE;IACN,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,EACD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,EACpD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,GAClD,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,CAAC,CAuDtC;AAiPD,MAAM,WAAW,+BAA+B;IAC9C;mDAC+C;IAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,gBAAgB,EAC7B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,+BAAoC,GAC5C;IAAE,QAAQ,EAAE,wBAAwB,CAAC;IAAC,WAAW,EAAE,uBAAuB,CAAA;CAAE,CAuB9E"}
|