Skip to content

Commit 71086e2

Browse files
committed
fix(electron): avoid an unhandled rejection when a launching OAuth flow is replaced
1 parent db95d2f commit 71086e2

3 files changed

Lines changed: 31 additions & 1 deletion

File tree

‎.changeset/electron-replace-stale-oauth-flow.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
'@clerk/electron': patch
33
---
44

5-
Fix OAuth sign-in staying blocked after a user abandons the browser tab. Starting a new OAuth sign-in now replaces the pending one instead of failing with `Clerk: an OAuth flow is already pending.` until the 3-minute callback timeout. An unsupported OAuth URL no longer cancels a pending flow, and a replaced flow's browser launch failure no longer rejects the new flow.
5+
Fix OAuth sign-in staying blocked after a user abandons the browser tab. Starting a new OAuth sign-in now replaces the pending one instead of failing with `Clerk: an OAuth flow is already pending.` until the 3-minute callback timeout. An unsupported OAuth URL no longer cancels a pending flow, a replaced flow's browser launch failure no longer rejects the new flow, and replacing a flow while its browser is still launching no longer causes an unhandled promise rejection in the main process.

‎packages/electron/src/main/__tests__/create-clerk-bridge.test.ts‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,4 +781,33 @@ describe('createClerkBridge', () => {
781781
openUrlListener?.({ preventDefault: vi.fn() } as unknown as Electron.Event, 'my-app://renderer/?code=123');
782782
await expect(secondPromise).resolves.toEqual({ callbackUrl: 'my-app://renderer/?code=123' });
783783
});
784+
785+
it('does not leave an unhandled rejection when a flow is replaced while its browser is launching', async () => {
786+
let resolveFirstLaunch: () => void = () => {};
787+
vi.mocked(shell.openExternal)
788+
.mockImplementationOnce(
789+
() =>
790+
new Promise<void>(resolve => {
791+
resolveFirstLaunch = resolve;
792+
}),
793+
)
794+
.mockResolvedValueOnce(undefined);
795+
createClerkBridge({
796+
storage,
797+
renderer: {
798+
host: 'renderer',
799+
scheme: 'my-app',
800+
},
801+
});
802+
803+
const openHandler = vi.mocked(ipcMain.handle).mock.calls.find(([channel]) => {
804+
return channel === OAUTH_TRANSPORT_CHANNELS.open;
805+
})?.[1];
806+
const firstPromise = openHandler?.(mainFrameEvent, 'https://accounts.example.com/oauth?attempt=1');
807+
void openHandler?.(mainFrameEvent, 'https://accounts.example.com/oauth?attempt=2');
808+
await new Promise(resolve => setTimeout(resolve, 0));
809+
810+
resolveFirstLaunch();
811+
await expect(firstPromise).rejects.toThrow('replaced by a new one');
812+
});
784813
});

‎packages/electron/src/main/oauth-transport.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ export function setupOAuthTransportIpcHandlers(options: OAuthTransportOptions):
138138
flow = { resolve, reject, timeout, window };
139139
pendingOAuthFlow = flow;
140140
});
141+
callbackPromise.catch(() => {});
141142

142143
try {
143144
await shell.openExternal(url);

0 commit comments

Comments
 (0)