Skip to content

Commit a30e0d1

Browse files
authored
Merge pull request #684 from 2underscores/dcr-fallback-fix
Fix: Pre-registered Client ID as fallback to DCR
2 parents 2feac76 + ddd06c2 commit a30e0d1

File tree

2 files changed

+106
-5
lines changed

2 files changed

+106
-5
lines changed

client/src/components/__tests__/AuthDebugger.test.tsx

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,103 @@ describe("AuthDebugger", () => {
442442
});
443443
});
444444

445+
describe("Client Registration behavior", () => {
446+
it("uses preregistered (static) client information without calling DCR", async () => {
447+
const preregClientInfo = {
448+
client_id: "static_client_id",
449+
client_secret: "static_client_secret",
450+
redirect_uris: ["http://localhost:3000/oauth/callback/debug"],
451+
};
452+
453+
// Return preregistered client info for the server-specific key
454+
sessionStorageMock.getItem.mockImplementation((key) => {
455+
if (
456+
key ===
457+
`[${defaultProps.serverUrl}] ${SESSION_KEYS.PREREGISTERED_CLIENT_INFORMATION}`
458+
) {
459+
return JSON.stringify(preregClientInfo);
460+
}
461+
return null;
462+
});
463+
464+
const updateAuthState = jest.fn();
465+
466+
await act(async () => {
467+
renderAuthDebugger({
468+
updateAuthState,
469+
authState: {
470+
...defaultAuthState,
471+
isInitiatingAuth: false,
472+
oauthStep: "client_registration",
473+
oauthMetadata: mockOAuthMetadata as unknown as OAuthMetadata,
474+
},
475+
});
476+
});
477+
478+
// Proceed from client_registration → authorization_redirect
479+
await act(async () => {
480+
fireEvent.click(screen.getByText("Continue"));
481+
});
482+
483+
// Should NOT attempt dynamic client registration
484+
expect(mockRegisterClient).not.toHaveBeenCalled();
485+
486+
// Should advance with the preregistered client info
487+
expect(updateAuthState).toHaveBeenCalledWith(
488+
expect.objectContaining({
489+
oauthClientInfo: expect.objectContaining({
490+
client_id: "static_client_id",
491+
}),
492+
oauthStep: "authorization_redirect",
493+
}),
494+
);
495+
});
496+
497+
it("falls back to DCR when no static client information is available", async () => {
498+
// No preregistered or dynamic client info present in session storage
499+
sessionStorageMock.getItem.mockImplementation(() => null);
500+
501+
// DCR returns a new client
502+
mockRegisterClient.mockResolvedValueOnce(mockOAuthClientInfo);
503+
504+
const updateAuthState = jest.fn();
505+
506+
await act(async () => {
507+
renderAuthDebugger({
508+
updateAuthState,
509+
authState: {
510+
...defaultAuthState,
511+
isInitiatingAuth: false,
512+
oauthStep: "client_registration",
513+
oauthMetadata: mockOAuthMetadata as unknown as OAuthMetadata,
514+
},
515+
});
516+
});
517+
518+
await act(async () => {
519+
fireEvent.click(screen.getByText("Continue"));
520+
});
521+
522+
expect(mockRegisterClient).toHaveBeenCalledTimes(1);
523+
524+
// Should save and advance with the DCR client info
525+
expect(updateAuthState).toHaveBeenCalledWith(
526+
expect.objectContaining({
527+
oauthClientInfo: expect.objectContaining({
528+
client_id: "test_client_id",
529+
}),
530+
oauthStep: "authorization_redirect",
531+
}),
532+
);
533+
534+
// Verify the dynamically registered client info was persisted
535+
expect(sessionStorage.setItem).toHaveBeenCalledWith(
536+
`[${defaultProps.serverUrl}] ${SESSION_KEYS.CLIENT_INFORMATION}`,
537+
expect.any(String),
538+
);
539+
});
540+
});
541+
445542
describe("OAuth State Persistence", () => {
446543
it("should store auth state to sessionStorage before redirect in Quick OAuth Flow", async () => {
447544
const updateAuthState = jest.fn();

client/src/lib/oauth-state-machine.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,16 @@ export const oauthTransitions: Record<OAuthStep, StateTransition> = {
8989
clientMetadata.scope = scopesSupported.join(" ");
9090
}
9191

92-
const fullInformation = await registerClient(context.serverUrl, {
93-
metadata,
94-
clientMetadata,
95-
});
92+
// Try Static client first, with DCR as fallback
93+
let fullInformation = await context.provider.clientInformation();
94+
if (!fullInformation) {
95+
fullInformation = await registerClient(context.serverUrl, {
96+
metadata,
97+
clientMetadata,
98+
});
99+
context.provider.saveClientInformation(fullInformation);
100+
}
96101

97-
context.provider.saveClientInformation(fullInformation);
98102
context.updateState({
99103
oauthClientInfo: fullInformation,
100104
oauthStep: "authorization_redirect",

0 commit comments

Comments
 (0)