Skip to content

Commit 8fc3fa6

Browse files
committed
refactor: extract common proxyFetch helper in oauth-proxy
1 parent 03dc094 commit 8fc3fa6

File tree

1 file changed

+90
-62
lines changed

1 file changed

+90
-62
lines changed

client/src/lib/oauth-proxy.ts

Lines changed: 90 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import { InspectorConfig } from "./configurationTypes";
1717

1818
/**
1919
* Get proxy headers for authentication
20+
* @param config - Inspector configuration containing proxy authentication settings
21+
* @returns Headers object with Content-Type and optional Bearer token
2022
*/
2123
function getProxyHeaders(config: InspectorConfig): Record<string, string> {
2224
const { token, header } = getMCPProxyAuthToken(config);
@@ -32,121 +34,147 @@ function getProxyHeaders(config: InspectorConfig): Record<string, string> {
3234
}
3335

3436
/**
35-
* Discover OAuth Authorization Server Metadata via proxy
37+
* Common helper for proxying fetch requests
38+
* @param endpoint - The API endpoint path (e.g., "/oauth/metadata")
39+
* @param method - HTTP method (GET or POST)
40+
* @param config - Inspector configuration containing proxy settings
41+
* @param body - Optional request body object
42+
* @param queryParams - Optional query parameters to append to URL
43+
* @returns Promise resolving to the typed response
44+
* @throws Error if the request fails or returns non-ok status
3645
*/
37-
export async function discoverAuthorizationServerMetadataViaProxy(
38-
authServerUrl: URL,
46+
async function proxyFetch<T>(
47+
endpoint: string,
48+
method: "GET" | "POST",
3949
config: InspectorConfig,
40-
): Promise<OAuthMetadata> {
50+
body?: object,
51+
queryParams?: Record<string, string>,
52+
): Promise<T> {
4153
const proxyAddress = getMCPProxyAddress(config);
42-
const url = new URL("/oauth/metadata", proxyAddress);
43-
url.searchParams.set("authServerUrl", authServerUrl.toString());
54+
const url = new URL(endpoint, proxyAddress);
55+
56+
if (queryParams) {
57+
Object.entries(queryParams).forEach(([key, value]) => {
58+
url.searchParams.set(key, value);
59+
});
60+
}
4461

4562
const response = await fetch(url.toString(), {
46-
method: "GET",
63+
method,
4764
headers: getProxyHeaders(config),
65+
...(body && { body: JSON.stringify(body) }),
4866
});
4967

5068
if (!response.ok) {
5169
const error = await response
5270
.json()
5371
.catch(() => ({ error: response.statusText }));
54-
throw new Error(
55-
`Failed to discover OAuth metadata: ${error.error || response.statusText}`,
56-
);
72+
throw new Error(error.error || response.statusText);
5773
}
5874

5975
return await response.json();
6076
}
6177

78+
/**
79+
* Discover OAuth Authorization Server Metadata via proxy
80+
* @param authServerUrl - The OAuth authorization server URL
81+
* @param config - Inspector configuration containing proxy settings
82+
* @returns Promise resolving to OAuth metadata
83+
* @throws Error if metadata discovery fails
84+
*/
85+
export async function discoverAuthorizationServerMetadataViaProxy(
86+
authServerUrl: URL,
87+
config: InspectorConfig,
88+
): Promise<OAuthMetadata> {
89+
try {
90+
return await proxyFetch<OAuthMetadata>(
91+
"/oauth/metadata",
92+
"GET",
93+
config,
94+
undefined,
95+
{ authServerUrl: authServerUrl.toString() },
96+
);
97+
} catch (error) {
98+
throw new Error(
99+
`Failed to discover OAuth metadata: ${error instanceof Error ? error.message : String(error)}`,
100+
);
101+
}
102+
}
103+
62104
/**
63105
* Discover OAuth Protected Resource Metadata via proxy
106+
* @param serverUrl - The MCP server URL
107+
* @param config - Inspector configuration containing proxy settings
108+
* @returns Promise resolving to OAuth protected resource metadata
109+
* @throws Error if resource metadata discovery fails
64110
*/
65111
export async function discoverOAuthProtectedResourceMetadataViaProxy(
66112
serverUrl: string,
67113
config: InspectorConfig,
68114
): Promise<OAuthProtectedResourceMetadata> {
69-
const proxyAddress = getMCPProxyAddress(config);
70-
const url = new URL("/oauth/resource-metadata", proxyAddress);
71-
url.searchParams.set("serverUrl", serverUrl);
72-
73-
const response = await fetch(url.toString(), {
74-
method: "GET",
75-
headers: getProxyHeaders(config),
76-
});
77-
78-
if (!response.ok) {
79-
const error = await response
80-
.json()
81-
.catch(() => ({ error: response.statusText }));
115+
try {
116+
return await proxyFetch<OAuthProtectedResourceMetadata>(
117+
"/oauth/resource-metadata",
118+
"GET",
119+
config,
120+
undefined,
121+
{ serverUrl },
122+
);
123+
} catch (error) {
82124
throw new Error(
83-
`Failed to discover resource metadata: ${error.error || response.statusText}`,
125+
`Failed to discover resource metadata: ${error instanceof Error ? error.message : String(error)}`,
84126
);
85127
}
86-
87-
return await response.json();
88128
}
89129

90130
/**
91131
* Register OAuth client via proxy (Dynamic Client Registration)
132+
* @param registrationEndpoint - The OAuth client registration endpoint URL
133+
* @param clientMetadata - OAuth client metadata for registration
134+
* @param config - Inspector configuration containing proxy settings
135+
* @returns Promise resolving to OAuth client information
136+
* @throws Error if client registration fails
92137
*/
93138
export async function registerClientViaProxy(
94139
registrationEndpoint: string,
95140
clientMetadata: OAuthClientMetadata,
96141
config: InspectorConfig,
97142
): Promise<OAuthClientInformation> {
98-
const proxyAddress = getMCPProxyAddress(config);
99-
const url = new URL("/oauth/register", proxyAddress);
100-
101-
const response = await fetch(url.toString(), {
102-
method: "POST",
103-
headers: getProxyHeaders(config),
104-
body: JSON.stringify({
105-
registrationEndpoint,
106-
clientMetadata,
107-
}),
108-
});
109-
110-
if (!response.ok) {
111-
const error = await response
112-
.json()
113-
.catch(() => ({ error: response.statusText }));
143+
try {
144+
return await proxyFetch<OAuthClientInformation>(
145+
"/oauth/register",
146+
"POST",
147+
config,
148+
{ registrationEndpoint, clientMetadata },
149+
);
150+
} catch (error) {
114151
throw new Error(
115-
`Failed to register client: ${error.error || response.statusText}`,
152+
`Failed to register client: ${error instanceof Error ? error.message : String(error)}`,
116153
);
117154
}
118-
119-
return await response.json();
120155
}
121156

122157
/**
123158
* Exchange authorization code for tokens via proxy
159+
* @param tokenEndpoint - The OAuth token endpoint URL
160+
* @param params - Token exchange parameters (code, client_id, etc.)
161+
* @param config - Inspector configuration containing proxy settings
162+
* @returns Promise resolving to OAuth tokens
163+
* @throws Error if token exchange fails
124164
*/
125165
export async function exchangeAuthorizationViaProxy(
126166
tokenEndpoint: string,
127167
params: Record<string, string>,
128168
config: InspectorConfig,
129169
): Promise<OAuthTokens> {
130-
const proxyAddress = getMCPProxyAddress(config);
131-
const url = new URL("/oauth/token", proxyAddress);
132-
133-
const response = await fetch(url.toString(), {
134-
method: "POST",
135-
headers: getProxyHeaders(config),
136-
body: JSON.stringify({
170+
try {
171+
return await proxyFetch<OAuthTokens>("/oauth/token", "POST", config, {
137172
tokenEndpoint,
138173
params,
139-
}),
140-
});
141-
142-
if (!response.ok) {
143-
const error = await response
144-
.json()
145-
.catch(() => ({ error: response.statusText }));
174+
});
175+
} catch (error) {
146176
throw new Error(
147-
`Failed to exchange authorization code: ${error.error || response.statusText}`,
177+
`Failed to exchange authorization code: ${error instanceof Error ? error.message : String(error)}`,
148178
);
149179
}
150-
151-
return await response.json();
152180
}

0 commit comments

Comments
 (0)