Skip to content

Commit 5a8dd80

Browse files
committed
use client info scope
1 parent 590d484 commit 5a8dd80

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

src/client/auth.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ global.fetch = mockFetch;
1717
describe("OAuth Authorization", () => {
1818
beforeEach(() => {
1919
mockFetch.mockReset();
20+
jest.clearAllMocks();
2021
});
2122

2223
describe("extractResourceMetadataUrl", () => {
@@ -625,6 +626,7 @@ describe("OAuth Authorization", () => {
625626
});
626627
});
627628

629+
628630
describe("registerClient", () => {
629631
const validClientMetadata = {
630632
redirect_uris: ["http://localhost:3000/callback"],
@@ -712,6 +714,7 @@ describe("OAuth Authorization", () => {
712714
});
713715
});
714716

717+
715718
describe("auth function", () => {
716719
const mockProvider: OAuthClientProvider = {
717720
get redirectUrl() { return "http://localhost:3000/callback"; },
@@ -727,12 +730,79 @@ describe("OAuth Authorization", () => {
727730
redirectToAuthorization: jest.fn(),
728731
saveCodeVerifier: jest.fn(),
729732
codeVerifier: jest.fn(),
733+
saveClientInformation: jest.fn(),
734+
};
735+
736+
const validMetadata = {
737+
issuer: "https://auth.example.com",
738+
authorization_endpoint: "https://auth.example.com/authorize",
739+
token_endpoint: "https://auth.example.com/token",
740+
registration_endpoint: "https://auth.example.com/register",
741+
response_types_supported: ["code"],
742+
code_challenge_methods_supported: ["S256"],
743+
};
744+
745+
const validClientInfo = {
746+
client_id: "client123",
747+
client_secret: "secret123",
748+
redirect_uris: ["http://localhost:3000/callback"],
749+
client_name: "Test Client",
730750
};
731751

732752
beforeEach(() => {
733753
jest.clearAllMocks();
734754
});
735755

756+
757+
it("uses scope from registered client information if not present in clientMetadata", async () => {
758+
// Mock fetch for metadata discovery and registration
759+
mockFetch
760+
.mockResolvedValueOnce({
761+
ok: true,
762+
status: 200,
763+
json: async () => ({}),
764+
}) // protected resource metadata
765+
.mockResolvedValueOnce({
766+
ok: true,
767+
status: 200,
768+
json: async () => validMetadata,
769+
}) // discovery
770+
.mockResolvedValueOnce({
771+
ok: true,
772+
status: 200,
773+
json: async () => ({
774+
...validClientInfo,
775+
scope: "dynamic scope from registration",
776+
}),
777+
}); // registration
778+
779+
// Provider: clientInformation returns undefined first, then fullInformation after registration
780+
const fullInformation = {
781+
...validClientInfo,
782+
scope: "dynamic scope from registration",
783+
};
784+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
785+
const clientInformationMock = jest
786+
.fn()
787+
.mockResolvedValueOnce(undefined)
788+
.mockResolvedValueOnce(fullInformation);
789+
790+
791+
792+
await auth(mockProvider, {
793+
serverUrl: "https://auth.example.com",
794+
});
795+
796+
// Check saveClientInformation was called with fullInformation
797+
expect(mockProvider.saveClientInformation).toHaveBeenCalledWith(fullInformation);
798+
799+
// Check that redirectToAuthorization was called with a URL containing the correct scope from registration
800+
expect(mockProvider.redirectToAuthorization).toHaveBeenCalledTimes(1);
801+
const urlArg = (mockProvider.redirectToAuthorization as jest.Mock).mock.calls[0][0];
802+
expect(urlArg).toBeInstanceOf(URL);
803+
expect(urlArg.searchParams.get("scope")).toBe("dynamic scope from registration");
804+
});
805+
736806
it("falls back to /.well-known/oauth-authorization-server when no protected-resource-metadata", async () => {
737807
// Setup: First call to protected resource metadata fails (404)
738808
// Second call to auth server metadata succeeds

src/client/auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ export async function auth(
175175
clientInformation,
176176
state,
177177
redirectUrl: provider.redirectUrl,
178-
scope: scope || provider.clientMetadata.scope,
178+
scope: scope || provider.clientMetadata.scope || (clientInformation as OAuthClientInformationFull).scope,
179179
});
180180

181181
await provider.saveCodeVerifier(codeVerifier);

0 commit comments

Comments
 (0)