Skip to content

Commit 177b62c

Browse files
committed
chore(client): normalize trailing slashes in discovery paths
1 parent 2c0c481 commit 177b62c

2 files changed

Lines changed: 31 additions & 9 deletions

File tree

packages/client/src/client/auth.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,10 +1035,9 @@ function buildWellKnownPath(
10351035
pathname: string = '',
10361036
options: { prependPathname?: boolean } = {}
10371037
): string {
1038-
// Strip trailing slash from pathname to avoid double slashes
1039-
if (pathname.endsWith('/')) {
1040-
pathname = pathname.slice(0, -1);
1041-
}
1038+
// Strip trailing slashes from pathname to avoid malformed discovery paths like
1039+
// "/foo//.well-known/oauth-authorization-server".
1040+
pathname = pathname.replace(/\/+$/, '');
10421041

10431042
return options.prependPathname ? `${pathname}/.well-known/${wellKnownPrefix}` : `/.well-known/${wellKnownPrefix}${pathname}`;
10441043
}
@@ -1173,11 +1172,8 @@ export function buildDiscoveryUrls(authorizationServerUrl: string | URL): { url:
11731172
return urlsToTry;
11741173
}
11751174

1176-
// Strip trailing slash from pathname to avoid double slashes
1177-
let pathname = url.pathname;
1178-
if (pathname.endsWith('/')) {
1179-
pathname = pathname.slice(0, -1);
1180-
}
1175+
// Strip trailing slashes from pathname to avoid malformed discovery paths.
1176+
let pathname = url.pathname.replace(/\/+$/, '');
11811177

11821178
urlsToTry.push(
11831179
// 1. OAuth metadata at the given URL

packages/client/test/client/auth.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,21 @@ describe('OAuth Authorization', () => {
265265
expect(url.toString()).toBe('https://resource.example.com/.well-known/oauth-protected-resource/path/name');
266266
});
267267

268+
it('normalizes duplicate trailing slashes in path before metadata discovery', async () => {
269+
mockFetch.mockResolvedValueOnce({
270+
ok: true,
271+
status: 200,
272+
json: async () => validMetadata
273+
});
274+
275+
const metadata = await discoverOAuthProtectedResourceMetadata('https://resource.example.com/path/name//');
276+
expect(metadata).toEqual(validMetadata);
277+
const calls = mockFetch.mock.calls;
278+
expect(calls.length).toBe(1);
279+
const [url] = calls[0]!;
280+
expect(url.toString()).toBe('https://resource.example.com/.well-known/oauth-protected-resource/path/name');
281+
});
282+
268283
it('preserves query parameters in path-aware discovery', async () => {
269284
mockFetch.mockResolvedValueOnce({
270285
ok: true,
@@ -853,6 +868,17 @@ describe('OAuth Authorization', () => {
853868
]);
854869
});
855870

871+
it('normalizes trailing slashes in server URLs before discovery', () => {
872+
const urls = buildDiscoveryUrls('https://auth.example.com/tenant1//');
873+
874+
expect(urls).toHaveLength(3);
875+
expect(urls.map(u => u.url.toString())).toEqual([
876+
'https://auth.example.com/.well-known/oauth-authorization-server/tenant1',
877+
'https://auth.example.com/.well-known/openid-configuration/tenant1',
878+
'https://auth.example.com/tenant1/.well-known/openid-configuration'
879+
]);
880+
});
881+
856882
it('handles URL object input', () => {
857883
const urls = buildDiscoveryUrls(new URL('https://auth.example.com/tenant1'));
858884

0 commit comments

Comments
 (0)