Skip to content

Commit 49b51f5

Browse files
committed
test discovery urls list
1 parent 90f6eca commit 49b51f5

File tree

2 files changed

+74
-129
lines changed

2 files changed

+74
-129
lines changed

src/client/auth.test.ts

Lines changed: 73 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { LATEST_PROTOCOL_VERSION } from '../types.js';
22
import {
33
discoverOAuthMetadata,
44
discoverAuthorizationServerMetadata,
5+
buildDiscoveryUrls,
56
startAuthorization,
67
exchangeAuthorization,
78
refreshAuthorization,
@@ -684,6 +685,55 @@ describe("OAuth Authorization", () => {
684685
});
685686
});
686687

688+
describe("buildDiscoveryUrls", () => {
689+
it("generates correct URLs for server without path", () => {
690+
const urls = buildDiscoveryUrls("https://auth.example.com");
691+
692+
expect(urls).toHaveLength(2);
693+
expect(urls.map(u => ({ url: u.url.toString(), type: u.type }))).toEqual([
694+
{
695+
url: "https://auth.example.com/.well-known/oauth-authorization-server",
696+
type: "oauth"
697+
},
698+
{
699+
url: "https://auth.example.com/.well-known/openid-configuration",
700+
type: "oidc"
701+
}
702+
]);
703+
});
704+
705+
it("generates correct URLs for server with path", () => {
706+
const urls = buildDiscoveryUrls("https://auth.example.com/tenant1");
707+
708+
expect(urls).toHaveLength(4);
709+
expect(urls.map(u => ({ url: u.url.toString(), type: u.type }))).toEqual([
710+
{
711+
url: "https://auth.example.com/.well-known/oauth-authorization-server/tenant1",
712+
type: "oauth"
713+
},
714+
{
715+
url: "https://auth.example.com/.well-known/oauth-authorization-server",
716+
type: "oauth"
717+
},
718+
{
719+
url: "https://auth.example.com/.well-known/openid-configuration/tenant1",
720+
type: "oidc"
721+
},
722+
{
723+
url: "https://auth.example.com/tenant1/.well-known/openid-configuration",
724+
type: "oidc"
725+
}
726+
]);
727+
});
728+
729+
it("handles URL object input", () => {
730+
const urls = buildDiscoveryUrls(new URL("https://auth.example.com/tenant1"));
731+
732+
expect(urls).toHaveLength(4);
733+
expect(urls[0].url.toString()).toBe("https://auth.example.com/.well-known/oauth-authorization-server/tenant1");
734+
});
735+
});
736+
687737
describe("discoverAuthorizationServerMetadata", () => {
688738
const validOAuthMetadata = {
689739
issuer: "https://auth.example.com",
@@ -705,138 +755,33 @@ describe("OAuth Authorization", () => {
705755
code_challenge_methods_supported: ["S256"],
706756
};
707757

708-
describe("discovery sequence", () => {
709-
const testCases = [
710-
{
711-
description: "no path - direct OAuth success",
712-
serverUrl: "https://auth.example.com",
713-
responses: [{ success: true, metadata: validOAuthMetadata }],
714-
expectedPaths: [
715-
"https://auth.example.com/.well-known/oauth-authorization-server"
716-
]
717-
},
718-
{
719-
description: "no path - OAuth fails, OIDC succeeds",
720-
serverUrl: "https://auth.example.com",
721-
responses: [
722-
{ success: false, status: 404 },
723-
{ success: true, metadata: validOpenIdMetadata }
724-
],
725-
expectedPaths: [
726-
"https://auth.example.com/.well-known/oauth-authorization-server",
727-
"https://auth.example.com/.well-known/openid-configuration"
728-
]
729-
},
730-
{
731-
description: "with path - path OAuth succeeds",
732-
serverUrl: "https://auth.example.com/tenant1",
733-
responses: [{ success: true, metadata: validOAuthMetadata }],
734-
expectedPaths: [
735-
"https://auth.example.com/.well-known/oauth-authorization-server/tenant1"
736-
]
737-
},
738-
{
739-
description: "with path - path OAuth fails, root OAuth succeeds",
740-
serverUrl: "https://auth.example.com/tenant1",
741-
responses: [
742-
{ success: false, status: 404 },
743-
{ success: true, metadata: validOAuthMetadata }
744-
],
745-
expectedPaths: [
746-
"https://auth.example.com/.well-known/oauth-authorization-server/tenant1",
747-
"https://auth.example.com/.well-known/oauth-authorization-server"
748-
]
749-
},
750-
{
751-
description: "with path - OAuth fails, OIDC path insertion succeeds",
752-
serverUrl: "https://auth.example.com/tenant1",
753-
responses: [
754-
{ success: false, status: 404 }, // OAuth path
755-
{ success: false, status: 404 }, // OAuth root
756-
{ success: true, metadata: validOpenIdMetadata } // OIDC path insertion
757-
],
758-
expectedPaths: [
759-
"https://auth.example.com/.well-known/oauth-authorization-server/tenant1",
760-
"https://auth.example.com/.well-known/oauth-authorization-server",
761-
"https://auth.example.com/.well-known/openid-configuration/tenant1"
762-
]
763-
},
764-
{
765-
description: "with path - OAuth fails, OIDC path appending succeeds",
766-
serverUrl: "https://auth.example.com/tenant1",
767-
responses: [
768-
{ success: false, status: 404 }, // OAuth path
769-
{ success: false, status: 404 }, // OAuth root
770-
{ success: false, status: 404 }, // OIDC path insertion
771-
{ success: true, metadata: validOpenIdMetadata } // OIDC path appending
772-
],
773-
expectedPaths: [
774-
"https://auth.example.com/.well-known/oauth-authorization-server/tenant1",
775-
"https://auth.example.com/.well-known/oauth-authorization-server",
776-
"https://auth.example.com/.well-known/openid-configuration/tenant1",
777-
"https://auth.example.com/tenant1/.well-known/openid-configuration"
778-
]
779-
},
780-
{
781-
description: "with path - all fails, returns undefined",
782-
serverUrl: "https://auth.example.com/tenant1",
783-
responses: [
784-
{ success: false, status: 404 }, // OAuth path
785-
{ success: false, status: 404 }, // OAuth root
786-
{ success: false, status: 404 }, // OIDC path insertion
787-
{ success: false, status: 404 }, // OIDC path appending
788-
],
789-
expectedPaths: [
790-
"https://auth.example.com/.well-known/oauth-authorization-server/tenant1",
791-
"https://auth.example.com/.well-known/oauth-authorization-server",
792-
"https://auth.example.com/.well-known/openid-configuration/tenant1",
793-
"https://auth.example.com/tenant1/.well-known/openid-configuration"
794-
]
795-
}
796-
];
797-
798-
testCases.forEach(({ description, serverUrl, responses, expectedPaths }) => {
799-
it(description, async () => {
800-
// Set up mock responses
801-
responses.forEach(response => {
802-
if (response.success) {
803-
mockFetch.mockResolvedValueOnce({
804-
ok: true,
805-
status: 200,
806-
json: async () => response.metadata
807-
});
808-
} else {
809-
mockFetch.mockResolvedValueOnce({
810-
ok: false,
811-
status: response.status || 404
812-
});
813-
}
814-
});
815-
816-
const metadata = await discoverAuthorizationServerMetadata(
817-
serverUrl
818-
);
819-
820-
// Verify result
821-
const successResponse = responses.find(r => r.success);
822-
if (successResponse) {
823-
expect(metadata).toEqual(successResponse.metadata);
824-
} else {
825-
expect(metadata).toBeUndefined();
826-
}
758+
it("tries URLs in order and returns first successful metadata", async () => {
759+
// First OAuth URL fails with 404
760+
mockFetch.mockResolvedValueOnce({
761+
ok: false,
762+
status: 404,
763+
});
764+
765+
// Second OAuth URL (root) succeeds
766+
mockFetch.mockResolvedValueOnce({
767+
ok: true,
768+
status: 200,
769+
json: async () => validOAuthMetadata,
770+
});
827771

828-
// Verify discovery sequence
829-
const calls = mockFetch.mock.calls;
830-
expect(calls.length).toBe(expectedPaths.length);
772+
const metadata = await discoverAuthorizationServerMetadata(
773+
"https://auth.example.com/tenant1"
774+
);
831775

832-
expectedPaths.forEach((expectedPath, index) => {
833-
expect(calls[index][0].toString()).toBe(expectedPath);
834-
});
835-
});
836-
});
776+
expect(metadata).toEqual(validOAuthMetadata);
777+
778+
// Verify it tried the URLs in the correct order
779+
const calls = mockFetch.mock.calls;
780+
expect(calls.length).toBe(2);
781+
expect(calls[0][0].toString()).toBe("https://auth.example.com/.well-known/oauth-authorization-server/tenant1");
782+
expect(calls[1][0].toString()).toBe("https://auth.example.com/.well-known/oauth-authorization-server");
837783
});
838784

839-
840785
it("throws error when OIDC provider does not support S256 PKCE", async () => {
841786
// OAuth discovery fails
842787
mockFetch.mockResolvedValueOnce({

src/client/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,7 @@ export async function discoverOAuthMetadata(
682682
* 2. OAuth metadata at root (if URL has path)
683683
* 3. OIDC metadata endpoints
684684
*/
685-
function buildDiscoveryUrls(authorizationServerUrl: string | URL): { url: URL; type: 'oauth' | 'oidc' }[] {
685+
export function buildDiscoveryUrls(authorizationServerUrl: string | URL): { url: URL; type: 'oauth' | 'oidc' }[] {
686686
const url = typeof authorizationServerUrl === 'string' ? new URL(authorizationServerUrl) : authorizationServerUrl;
687687
const hasPath = url.pathname !== '/';
688688
const urlsToTry: { url: URL; type: 'oauth' | 'oidc' }[] = [];

0 commit comments

Comments
 (0)