@slashfi/agents-sdk 0.90.4 → 0.90.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/config-store.js +123 -50
- package/dist/cjs/config-store.js.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/mcp-client.js +77 -24
- package/dist/cjs/mcp-client.js.map +1 -1
- package/dist/config-store.d.ts.map +1 -1
- package/dist/config-store.js +123 -50
- package/dist/config-store.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-client.d.ts +5 -2
- package/dist/mcp-client.d.ts.map +1 -1
- package/dist/mcp-client.js +77 -24
- package/dist/mcp-client.js.map +1 -1
- package/package.json +1 -1
- package/src/config-store.test.ts +133 -0
- package/src/config-store.ts +179 -71
- package/src/index.ts +4 -1
- package/src/mcp-client.test.ts +80 -0
- package/src/mcp-client.ts +114 -31
package/dist/mcp-client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-client.js","sourceRoot":"","sources":["../src/mcp-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;
|
|
1
|
+
{"version":3,"file":"mcp-client.js","sourceRoot":"","sources":["../src/mcp-client.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/config-store.test.ts
CHANGED
|
@@ -2099,3 +2099,136 @@ describe("isRefAuthComplete + cached authFields", () => {
|
|
|
2099
2099
|
expect(result).toBe(false);
|
|
2100
2100
|
});
|
|
2101
2101
|
});
|
|
2102
|
+
|
|
2103
|
+
describe("ADK ref.refreshToken() manifest-aware token endpoint", () => {
|
|
2104
|
+
let registryServer: AgentServer;
|
|
2105
|
+
let discoveryServer: ReturnType<typeof Bun.serve>;
|
|
2106
|
+
let tokenServer: ReturnType<typeof Bun.serve>;
|
|
2107
|
+
const REG_PORT = 19950;
|
|
2108
|
+
const AS_PORT = 19951;
|
|
2109
|
+
const TOKEN_PORT = 19952;
|
|
2110
|
+
let tokenRefreshCount = 0;
|
|
2111
|
+
|
|
2112
|
+
beforeAll(async () => {
|
|
2113
|
+
tokenServer = Bun.serve({
|
|
2114
|
+
port: TOKEN_PORT,
|
|
2115
|
+
async fetch(req) {
|
|
2116
|
+
tokenRefreshCount++;
|
|
2117
|
+
const params = new URLSearchParams(await req.text());
|
|
2118
|
+
if (
|
|
2119
|
+
params.get("grant_type") !== "refresh_token" ||
|
|
2120
|
+
params.get("refresh_token") !== "discovery-refresh-token" ||
|
|
2121
|
+
params.get("client_id") !== "discovery-client-id"
|
|
2122
|
+
) {
|
|
2123
|
+
return new Response(JSON.stringify({ error: "invalid_grant" }), {
|
|
2124
|
+
status: 400,
|
|
2125
|
+
});
|
|
2126
|
+
}
|
|
2127
|
+
return new Response(
|
|
2128
|
+
JSON.stringify({
|
|
2129
|
+
access_token: "discovery-refreshed-token",
|
|
2130
|
+
token_type: "Bearer",
|
|
2131
|
+
expires_in: 3600,
|
|
2132
|
+
}),
|
|
2133
|
+
{ headers: { "Content-Type": "application/json" } },
|
|
2134
|
+
);
|
|
2135
|
+
},
|
|
2136
|
+
});
|
|
2137
|
+
|
|
2138
|
+
discoveryServer = Bun.serve({
|
|
2139
|
+
port: AS_PORT,
|
|
2140
|
+
fetch(req) {
|
|
2141
|
+
const path = new URL(req.url).pathname;
|
|
2142
|
+
if (path === "/.well-known/oauth-authorization-server") {
|
|
2143
|
+
return Response.json({
|
|
2144
|
+
issuer: `http://localhost:${AS_PORT}`,
|
|
2145
|
+
authorization_endpoint: `http://localhost:${AS_PORT}/oauth/authorize`,
|
|
2146
|
+
token_endpoint: `http://localhost:${TOKEN_PORT}`,
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
return new Response("not found", { status: 404 });
|
|
2150
|
+
},
|
|
2151
|
+
});
|
|
2152
|
+
|
|
2153
|
+
const stubTool = defineTool({
|
|
2154
|
+
name: "noop",
|
|
2155
|
+
description: "Unused in this test",
|
|
2156
|
+
inputSchema: { type: "object" as const, properties: {} },
|
|
2157
|
+
execute: async () => ({ ok: true }),
|
|
2158
|
+
});
|
|
2159
|
+
const agent = defineAgent({
|
|
2160
|
+
path: "discovery-oauth-agent",
|
|
2161
|
+
entrypoint: "Discovery OAuth agent",
|
|
2162
|
+
tools: [stubTool],
|
|
2163
|
+
visibility: "public",
|
|
2164
|
+
config: {
|
|
2165
|
+
security: {
|
|
2166
|
+
type: "oauth2",
|
|
2167
|
+
discoveryUrl: `http://localhost:${AS_PORT}/.well-known/oauth-authorization-server`,
|
|
2168
|
+
flows: {
|
|
2169
|
+
authorizationCode: {
|
|
2170
|
+
authorizationUrl: `http://localhost:${AS_PORT}/oauth/authorize`,
|
|
2171
|
+
},
|
|
2172
|
+
},
|
|
2173
|
+
},
|
|
2174
|
+
},
|
|
2175
|
+
});
|
|
2176
|
+
const registry = createAgentRegistry();
|
|
2177
|
+
registry.register(agent);
|
|
2178
|
+
registryServer = createAgentServer(registry, { port: REG_PORT });
|
|
2179
|
+
await registryServer.start();
|
|
2180
|
+
});
|
|
2181
|
+
|
|
2182
|
+
afterAll(async () => {
|
|
2183
|
+
await registryServer.stop();
|
|
2184
|
+
discoveryServer.stop();
|
|
2185
|
+
tokenServer.stop();
|
|
2186
|
+
});
|
|
2187
|
+
|
|
2188
|
+
test("ref.refreshToken() discovers token endpoint from discoveryUrl when tokenUrl is absent", async () => {
|
|
2189
|
+
tokenRefreshCount = 0;
|
|
2190
|
+
|
|
2191
|
+
const fs = createMemoryFs();
|
|
2192
|
+
const adk = createAdk(fs, {
|
|
2193
|
+
encryptionKey: "test-key-32-chars-long-enough!!",
|
|
2194
|
+
});
|
|
2195
|
+
|
|
2196
|
+
await adk.registry.add({
|
|
2197
|
+
name: "discovery-reg",
|
|
2198
|
+
url: `http://localhost:${REG_PORT}`,
|
|
2199
|
+
});
|
|
2200
|
+
await adk.ref.add({
|
|
2201
|
+
ref: "discovery-oauth-agent",
|
|
2202
|
+
sourceRegistry: {
|
|
2203
|
+
url: `http://localhost:${REG_PORT}`,
|
|
2204
|
+
agentPath: "discovery-oauth-agent",
|
|
2205
|
+
},
|
|
2206
|
+
});
|
|
2207
|
+
|
|
2208
|
+
const config = await adk.readConfig();
|
|
2209
|
+
await adk.writeConfig({
|
|
2210
|
+
...config,
|
|
2211
|
+
refs: config.refs?.map((r: any) => {
|
|
2212
|
+
if (r.ref === "discovery-oauth-agent") {
|
|
2213
|
+
return {
|
|
2214
|
+
...r,
|
|
2215
|
+
config: {
|
|
2216
|
+
...r.config,
|
|
2217
|
+
access_token: "stale-token",
|
|
2218
|
+
refresh_token: "discovery-refresh-token",
|
|
2219
|
+
client_id: "discovery-client-id",
|
|
2220
|
+
},
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
return r;
|
|
2224
|
+
}),
|
|
2225
|
+
});
|
|
2226
|
+
|
|
2227
|
+
const refreshed = await adk.ref.refreshToken("discovery-oauth-agent");
|
|
2228
|
+
expect(refreshed).toEqual({ accessToken: "discovery-refreshed-token" });
|
|
2229
|
+
expect(tokenRefreshCount).toBe(1);
|
|
2230
|
+
|
|
2231
|
+
const updated = await adk.ref.get("discovery-oauth-agent");
|
|
2232
|
+
expect(typeof updated?.config?.expires_at).toBe("string");
|
|
2233
|
+
});
|
|
2234
|
+
});
|
package/src/config-store.ts
CHANGED
|
@@ -33,6 +33,7 @@ import type { RegistryAuthRequirement } from "./define-config.js";
|
|
|
33
33
|
import type { FetchFn } from "./fetch-types.js";
|
|
34
34
|
import type { Logger } from "./logger.js";
|
|
35
35
|
import {
|
|
36
|
+
type OAuthClientAuthMethod,
|
|
36
37
|
buildOAuthAuthorizeUrl,
|
|
37
38
|
discoverOAuthMetadata,
|
|
38
39
|
dynamicClientRegistration,
|
|
@@ -1040,7 +1041,9 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
1040
1041
|
const raw = (security as { authFields?: unknown }).authFields;
|
|
1041
1042
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return undefined;
|
|
1042
1043
|
const out: Record<string, RegistryCacheAuthField> = {};
|
|
1043
|
-
for (const [field, meta] of Object.entries(
|
|
1044
|
+
for (const [field, meta] of Object.entries(
|
|
1045
|
+
raw as Record<string, unknown>,
|
|
1046
|
+
)) {
|
|
1044
1047
|
if (!meta || typeof meta !== "object" || Array.isArray(meta)) continue;
|
|
1045
1048
|
const m = meta as Record<string, unknown>;
|
|
1046
1049
|
if (typeof m.required !== "boolean" || typeof m.automated !== "boolean") {
|
|
@@ -1227,6 +1230,7 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
1227
1230
|
codeVerifier: string;
|
|
1228
1231
|
clientId: string;
|
|
1229
1232
|
clientSecret?: string;
|
|
1233
|
+
clientAuthMethod?: OAuthClientAuthMethod;
|
|
1230
1234
|
tokenEndpoint: string;
|
|
1231
1235
|
redirectUri: string;
|
|
1232
1236
|
createdAt: number;
|
|
@@ -1434,6 +1438,109 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
1434
1438
|
}
|
|
1435
1439
|
}
|
|
1436
1440
|
|
|
1441
|
+
/**
|
|
1442
|
+
* Resolve OAuth server metadata from a registry security scheme.
|
|
1443
|
+
* Shared by `ref.auth` and `ref.refreshToken` so both paths discover
|
|
1444
|
+
* token endpoints the same way — explicit manifest URLs, RFC 8414
|
|
1445
|
+
* discovery via `discoveryUrl`, authorization-server discovery, and
|
|
1446
|
+
* finally the MCP upstream URL for redirect-mode agents.
|
|
1447
|
+
*/
|
|
1448
|
+
async function resolveOAuthMetadataFromSecurity(
|
|
1449
|
+
security: SecuritySchemeSummary | null | undefined,
|
|
1450
|
+
opts?: { serverUrl?: string },
|
|
1451
|
+
): Promise<import("./mcp-client.js").OAuthServerMetadata | null> {
|
|
1452
|
+
if (!security || security.type !== "oauth2") return null;
|
|
1453
|
+
|
|
1454
|
+
const securityExt = security as {
|
|
1455
|
+
discoveryUrl?: string;
|
|
1456
|
+
flows?: {
|
|
1457
|
+
authorizationCode?: {
|
|
1458
|
+
authorizationUrl?: string;
|
|
1459
|
+
tokenUrl?: string;
|
|
1460
|
+
refreshUrl?: string;
|
|
1461
|
+
scopes?: Record<string, string>;
|
|
1462
|
+
};
|
|
1463
|
+
};
|
|
1464
|
+
};
|
|
1465
|
+
const authCodeFlow = securityExt.flows?.authorizationCode;
|
|
1466
|
+
|
|
1467
|
+
const explicitEndpoint = authCodeFlow?.refreshUrl ?? authCodeFlow?.tokenUrl;
|
|
1468
|
+
if (explicitEndpoint) {
|
|
1469
|
+
const flowScopes = (authCodeFlow as Record<string, unknown> | undefined)
|
|
1470
|
+
?.scopes as Record<string, string> | undefined;
|
|
1471
|
+
const authUrl = authCodeFlow?.authorizationUrl;
|
|
1472
|
+
return {
|
|
1473
|
+
issuer: authUrl
|
|
1474
|
+
? new URL(authUrl).origin
|
|
1475
|
+
: new URL(explicitEndpoint).origin,
|
|
1476
|
+
authorization_endpoint: authUrl ?? explicitEndpoint,
|
|
1477
|
+
token_endpoint: explicitEndpoint,
|
|
1478
|
+
scopes_supported: flowScopes ? Object.keys(flowScopes) : undefined,
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
if (securityExt.discoveryUrl) {
|
|
1483
|
+
const fromDiscovery =
|
|
1484
|
+
(await tryFetchOAuthMetadata(securityExt.discoveryUrl)) ??
|
|
1485
|
+
(await discoverOAuthMetadata(securityExt.discoveryUrl));
|
|
1486
|
+
if (fromDiscovery) return fromDiscovery;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
const authUrl = authCodeFlow?.authorizationUrl;
|
|
1490
|
+
if (authUrl) {
|
|
1491
|
+
let metadata = await tryFetchOAuthMetadata(authUrl);
|
|
1492
|
+
if (!metadata) {
|
|
1493
|
+
metadata = await discoverOAuthMetadata(new URL(authUrl).origin);
|
|
1494
|
+
}
|
|
1495
|
+
if (metadata) return metadata;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
const serverUrl = opts?.serverUrl;
|
|
1499
|
+
if (serverUrl) {
|
|
1500
|
+
let metadata = await discoverOAuthMetadata(serverUrl);
|
|
1501
|
+
if (!metadata) {
|
|
1502
|
+
metadata = await discoverOAuthMetadata(
|
|
1503
|
+
serverUrl.replace(/\/(mcp|sse)$/, ""),
|
|
1504
|
+
);
|
|
1505
|
+
}
|
|
1506
|
+
if (metadata) return metadata;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
return null;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
function resolveClientAuthMethod(
|
|
1513
|
+
security: SecuritySchemeSummary | null | undefined,
|
|
1514
|
+
metadata: OAuthServerMetadata | null,
|
|
1515
|
+
): OAuthClientAuthMethod {
|
|
1516
|
+
const flowAuth = (
|
|
1517
|
+
security as {
|
|
1518
|
+
flows?: {
|
|
1519
|
+
authorizationCode?: { clientAuth?: OAuthClientAuthMethod };
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
).flows?.authorizationCode?.clientAuth;
|
|
1523
|
+
if (flowAuth) return flowAuth;
|
|
1524
|
+
|
|
1525
|
+
const supported = metadata?.token_endpoint_auth_methods_supported;
|
|
1526
|
+
if (supported?.length === 1 && supported[0] === "client_secret_basic") {
|
|
1527
|
+
return "client_secret_basic";
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
const tokenEndpoint = metadata?.token_endpoint;
|
|
1531
|
+
if (tokenEndpoint) {
|
|
1532
|
+
try {
|
|
1533
|
+
if (new URL(tokenEndpoint).hostname === "api.x.com") {
|
|
1534
|
+
return "client_secret_basic";
|
|
1535
|
+
}
|
|
1536
|
+
} catch {
|
|
1537
|
+
/* ignore malformed token endpoint */
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
return "client_secret_post";
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1437
1544
|
/**
|
|
1438
1545
|
* Build a registryConsumer from the current config.
|
|
1439
1546
|
* Decrypts secret: values in registry headers/auth before connecting.
|
|
@@ -1568,7 +1675,10 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
1568
1675
|
});
|
|
1569
1676
|
if (!found) return false;
|
|
1570
1677
|
for (const r of registries) {
|
|
1571
|
-
if (
|
|
1678
|
+
if (
|
|
1679
|
+
typeof r !== "string" &&
|
|
1680
|
+
(registryDisplayName(r) === nameOrUrl || registryUrl(r) === nameOrUrl)
|
|
1681
|
+
) {
|
|
1572
1682
|
await mutate(r);
|
|
1573
1683
|
}
|
|
1574
1684
|
}
|
|
@@ -2803,7 +2913,12 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
2803
2913
|
format: "basic" as const,
|
|
2804
2914
|
parts: [
|
|
2805
2915
|
{ name: "username", label: "Username", secret: false },
|
|
2806
|
-
{
|
|
2916
|
+
{
|
|
2917
|
+
name: "password",
|
|
2918
|
+
label: "Password",
|
|
2919
|
+
secret: true,
|
|
2920
|
+
optional: true,
|
|
2921
|
+
},
|
|
2807
2922
|
],
|
|
2808
2923
|
}),
|
|
2809
2924
|
};
|
|
@@ -2987,8 +3102,11 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
2987
3102
|
const username =
|
|
2988
3103
|
opts?.credentials?.["username"] ?? (await tryResolve("username"));
|
|
2989
3104
|
const password =
|
|
2990
|
-
opts?.credentials?.["password"] ??
|
|
2991
|
-
|
|
3105
|
+
opts?.credentials?.["password"] ??
|
|
3106
|
+
(await tryResolve("password")) ??
|
|
3107
|
+
"";
|
|
3108
|
+
const hasUsername =
|
|
3109
|
+
username !== undefined && username !== null && username !== "";
|
|
2992
3110
|
if (!hasUsername) {
|
|
2993
3111
|
return {
|
|
2994
3112
|
type: "http",
|
|
@@ -3049,23 +3167,9 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
3049
3167
|
}
|
|
3050
3168
|
|
|
3051
3169
|
const authUrl = authCodeFlow.authorizationUrl;
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
metadata = await discoverOAuthMetadata(origin);
|
|
3056
|
-
}
|
|
3057
|
-
// Fallback: construct metadata from the security scheme's explicit URLs
|
|
3058
|
-
if (!metadata && authCodeFlow.tokenUrl) {
|
|
3059
|
-
const flowScopes = (authCodeFlow as Record<string, unknown>).scopes as
|
|
3060
|
-
| Record<string, string>
|
|
3061
|
-
| undefined;
|
|
3062
|
-
metadata = {
|
|
3063
|
-
issuer: new URL(authUrl).origin,
|
|
3064
|
-
authorization_endpoint: authUrl,
|
|
3065
|
-
token_endpoint: authCodeFlow.tokenUrl,
|
|
3066
|
-
scopes_supported: flowScopes ? Object.keys(flowScopes) : undefined,
|
|
3067
|
-
} as import("./mcp-client.js").OAuthServerMetadata;
|
|
3068
|
-
}
|
|
3170
|
+
const metadata = await resolveOAuthMetadataFromSecurity(security, {
|
|
3171
|
+
serverUrl: entry.url,
|
|
3172
|
+
});
|
|
3069
3173
|
if (!metadata) {
|
|
3070
3174
|
throw new Error(`Could not discover OAuth metadata from ${authUrl}`);
|
|
3071
3175
|
}
|
|
@@ -3176,11 +3280,13 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
3176
3280
|
});
|
|
3177
3281
|
|
|
3178
3282
|
// Persist pending state so handleCallback works across processes
|
|
3283
|
+
const clientAuthMethod = resolveClientAuthMethod(security, metadata);
|
|
3179
3284
|
await storePendingOAuth(state, {
|
|
3180
3285
|
refName: name,
|
|
3181
3286
|
codeVerifier,
|
|
3182
3287
|
clientId,
|
|
3183
3288
|
clientSecret,
|
|
3289
|
+
clientAuthMethod,
|
|
3184
3290
|
tokenEndpoint: metadata.token_endpoint,
|
|
3185
3291
|
redirectUri,
|
|
3186
3292
|
createdAt: Date.now(),
|
|
@@ -3351,54 +3457,50 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
3351
3457
|
|
|
3352
3458
|
const status = await ref.authStatus(name);
|
|
3353
3459
|
const security = status.security;
|
|
3354
|
-
const
|
|
3355
|
-
|
|
3356
|
-
? (
|
|
3357
|
-
security as {
|
|
3358
|
-
flows?: Record<
|
|
3359
|
-
string,
|
|
3360
|
-
{ tokenUrl?: string; refreshUrl?: string }
|
|
3361
|
-
>;
|
|
3362
|
-
}
|
|
3363
|
-
).flows
|
|
3364
|
-
: undefined;
|
|
3365
|
-
const authCodeFlow = flows?.authorizationCode;
|
|
3366
|
-
const tokenUrl = authCodeFlow?.refreshUrl ?? authCodeFlow?.tokenUrl;
|
|
3367
|
-
if (!tokenUrl) return null;
|
|
3368
|
-
|
|
3369
|
-
const oauthClient = await resolveOAuthClient({ name, entry, security });
|
|
3370
|
-
if (!oauthClient) return null;
|
|
3371
|
-
const { clientId, clientSecret } = oauthClient;
|
|
3372
|
-
|
|
3373
|
-
// POST to the token endpoint with grant_type=refresh_token
|
|
3374
|
-
const body = new URLSearchParams({
|
|
3375
|
-
grant_type: "refresh_token",
|
|
3376
|
-
refresh_token: refreshToken,
|
|
3377
|
-
client_id: clientId,
|
|
3460
|
+
const metadata = await resolveOAuthMetadataFromSecurity(security, {
|
|
3461
|
+
serverUrl: entry.url,
|
|
3378
3462
|
});
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3463
|
+
const tokenEndpoint = metadata?.token_endpoint;
|
|
3464
|
+
if (!tokenEndpoint) return null;
|
|
3465
|
+
|
|
3466
|
+
const oauthClient = await resolveOAuthClient({
|
|
3467
|
+
name,
|
|
3468
|
+
entry,
|
|
3469
|
+
security,
|
|
3470
|
+
metadata,
|
|
3387
3471
|
});
|
|
3472
|
+
if (!oauthClient) return null;
|
|
3388
3473
|
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3474
|
+
const clientAuthMethod = resolveClientAuthMethod(security, metadata);
|
|
3475
|
+
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
3476
|
+
let tokens: Awaited<ReturnType<typeof refreshAccessToken>>;
|
|
3477
|
+
try {
|
|
3478
|
+
tokens = await refreshAccessToken(
|
|
3479
|
+
tokenEndpoint,
|
|
3480
|
+
{
|
|
3481
|
+
refreshToken,
|
|
3482
|
+
clientId: oauthClient.clientId,
|
|
3483
|
+
clientSecret: oauthClient.clientSecret,
|
|
3484
|
+
clientAuthMethod,
|
|
3485
|
+
},
|
|
3486
|
+
fetchFn,
|
|
3487
|
+
);
|
|
3488
|
+
} catch {
|
|
3489
|
+
return null;
|
|
3490
|
+
}
|
|
3394
3491
|
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3492
|
+
await storeRefSecret(name, "access_token", tokens.accessToken);
|
|
3493
|
+
if (tokens.refreshToken) {
|
|
3494
|
+
await storeRefSecret(name, "refresh_token", tokens.refreshToken);
|
|
3495
|
+
}
|
|
3496
|
+
if (tokens.expiresIn) {
|
|
3497
|
+
const expiresAt = new Date(
|
|
3498
|
+
Date.now() + tokens.expiresIn * 1000,
|
|
3499
|
+
).toISOString();
|
|
3500
|
+
await storeRefSecret(name, "expires_at", expiresAt);
|
|
3399
3501
|
}
|
|
3400
3502
|
|
|
3401
|
-
return { accessToken:
|
|
3503
|
+
return { accessToken: tokens.accessToken };
|
|
3402
3504
|
},
|
|
3403
3505
|
};
|
|
3404
3506
|
|
|
@@ -3419,13 +3521,19 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
|
|
|
3419
3521
|
throw new Error(`No pending OAuth flow for state "${params.state}".`);
|
|
3420
3522
|
}
|
|
3421
3523
|
|
|
3422
|
-
const
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3524
|
+
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
3525
|
+
const tokens = await exchangeCodeForTokens(
|
|
3526
|
+
pending.tokenEndpoint,
|
|
3527
|
+
{
|
|
3528
|
+
code: params.code,
|
|
3529
|
+
codeVerifier: pending.codeVerifier,
|
|
3530
|
+
clientId: pending.clientId,
|
|
3531
|
+
clientSecret: pending.clientSecret,
|
|
3532
|
+
redirectUri: pending.redirectUri,
|
|
3533
|
+
clientAuthMethod: pending.clientAuthMethod,
|
|
3534
|
+
},
|
|
3535
|
+
fetchFn,
|
|
3536
|
+
);
|
|
3429
3537
|
|
|
3430
3538
|
await storeRefSecret(pending.refName, "access_token", tokens.accessToken);
|
|
3431
3539
|
if (tokens.refreshToken) {
|
package/src/index.ts
CHANGED
|
@@ -352,7 +352,10 @@ export {
|
|
|
352
352
|
exchangeCodeForTokens,
|
|
353
353
|
refreshAccessToken as refreshMcpAccessToken,
|
|
354
354
|
} from "./mcp-client.js";
|
|
355
|
-
export type {
|
|
355
|
+
export type {
|
|
356
|
+
OAuthClientAuthMethod,
|
|
357
|
+
OAuthServerMetadata,
|
|
358
|
+
} from "./mcp-client.js";
|
|
356
359
|
|
|
357
360
|
// ============================================
|
|
358
361
|
// Serialized Agent Definitions
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "bun:test";
|
|
2
|
+
import { exchangeCodeForTokens, refreshAccessToken } from "./mcp-client.js";
|
|
3
|
+
|
|
4
|
+
describe("exchangeCodeForTokens", () => {
|
|
5
|
+
it("uses HTTP Basic auth for client_secret_basic", async () => {
|
|
6
|
+
const fetch = vi.fn(async () =>
|
|
7
|
+
Response.json({ access_token: "at", refresh_token: "rt" }),
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
await exchangeCodeForTokens(
|
|
11
|
+
"https://api.x.com/2/oauth2/token",
|
|
12
|
+
{
|
|
13
|
+
code: "auth-code",
|
|
14
|
+
codeVerifier: "verifier",
|
|
15
|
+
clientId: "client-id",
|
|
16
|
+
clientSecret: "client-secret",
|
|
17
|
+
redirectUri: "https://example.com/callback",
|
|
18
|
+
clientAuthMethod: "client_secret_basic",
|
|
19
|
+
},
|
|
20
|
+
fetch,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
|
24
|
+
const [, init] = fetch.mock.calls[0] as [string, RequestInit];
|
|
25
|
+
const headers = init.headers as Record<string, string>;
|
|
26
|
+
expect(headers.Authorization).toBe(
|
|
27
|
+
`Basic ${Buffer.from("client-id:client-secret").toString("base64")}`,
|
|
28
|
+
);
|
|
29
|
+
expect(String(init.body)).not.toContain("client_secret");
|
|
30
|
+
expect(String(init.body)).not.toContain("client_id");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("uses client_secret_post by default", async () => {
|
|
34
|
+
const fetch = vi.fn(async () => Response.json({ access_token: "at" }));
|
|
35
|
+
|
|
36
|
+
await exchangeCodeForTokens(
|
|
37
|
+
"https://example.com/token",
|
|
38
|
+
{
|
|
39
|
+
code: "auth-code",
|
|
40
|
+
codeVerifier: "verifier",
|
|
41
|
+
clientId: "client-id",
|
|
42
|
+
clientSecret: "client-secret",
|
|
43
|
+
redirectUri: "https://example.com/callback",
|
|
44
|
+
},
|
|
45
|
+
fetch,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const [, init] = fetch.mock.calls[0] as [string, RequestInit];
|
|
49
|
+
const headers = init.headers as Record<string, string>;
|
|
50
|
+
expect(headers.Authorization).toBeUndefined();
|
|
51
|
+
expect(String(init.body)).toContain("client_secret=client-secret");
|
|
52
|
+
expect(String(init.body)).toContain("client_id=client-id");
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("refreshAccessToken", () => {
|
|
57
|
+
it("uses HTTP Basic auth for client_secret_basic", async () => {
|
|
58
|
+
const fetch = vi.fn(async () => Response.json({ access_token: "at" }));
|
|
59
|
+
|
|
60
|
+
await refreshAccessToken(
|
|
61
|
+
"https://api.x.com/2/oauth2/token",
|
|
62
|
+
{
|
|
63
|
+
refreshToken: "refresh-token",
|
|
64
|
+
clientId: "client-id",
|
|
65
|
+
clientSecret: "client-secret",
|
|
66
|
+
clientAuthMethod: "client_secret_basic",
|
|
67
|
+
},
|
|
68
|
+
fetch,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const [, init] = fetch.mock.calls[0] as [string, RequestInit];
|
|
72
|
+
const headers = init.headers as Record<string, string>;
|
|
73
|
+
expect(headers.Authorization).toBe(
|
|
74
|
+
`Basic ${Buffer.from("client-id:client-secret").toString("base64")}`,
|
|
75
|
+
);
|
|
76
|
+
expect(String(init.body)).toBe(
|
|
77
|
+
"grant_type=refresh_token&refresh_token=refresh-token",
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
});
|