Skip to content

Commit d4bafb4

Browse files
fix(electron): default allowedRedirectProtocols to the renderer scheme (#9043)
Co-authored-by: wobsoriano <sorianorobertc@gmail.com>
1 parent feac5f3 commit d4bafb4

3 files changed

Lines changed: 91 additions & 1 deletion

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/electron': patch
3+
---
4+
5+
`<ClerkProvider>` from `@clerk/electron/react` now allows the renderer's own custom scheme as a redirect protocol by default, so apps no longer need to set `allowedRedirectProtocols={['<scheme>:']}` manually.
6+
7+
This applies when the renderer is served from the custom scheme registered with `createClerkBridge({ renderer })`. Local `file:` renderers are not allowlisted automatically, and explicit `allowedRedirectProtocols` values are still respected.

packages/electron/src/react/__tests__/ClerkProvider.test.tsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ vi.mock('@clerk/shared/loadClerkJsScript', async importOriginal => ({
4040
loadClerkUIScript,
4141
}));
4242

43+
function stubWindowProtocol(protocol: string) {
44+
vi.stubGlobal('window', {
45+
...window,
46+
location: { protocol },
47+
});
48+
}
49+
4350
describe('Electron ClerkProvider', () => {
4451
const tokenCache = {
4552
clearToken: vi.fn(),
@@ -92,6 +99,60 @@ describe('Electron ClerkProvider', () => {
9299
});
93100
});
94101

102+
it('defaults allowedRedirectProtocols to the renderer custom scheme', () => {
103+
stubWindowProtocol('clerk:');
104+
105+
renderToStaticMarkup(<ClerkProvider publishableKey='pk_test_scheme_default'>App</ClerkProvider>);
106+
107+
expect(capturedProviderProps?.allowedRedirectProtocols).toEqual(['clerk:']);
108+
});
109+
110+
it('does not add standard web protocols to allowedRedirectProtocols', () => {
111+
stubWindowProtocol('https:');
112+
113+
renderToStaticMarkup(<ClerkProvider publishableKey='pk_test_https_origin'>App</ClerkProvider>);
114+
115+
expect(capturedProviderProps?.allowedRedirectProtocols).toBeUndefined();
116+
});
117+
118+
it('does not add file protocol to allowedRedirectProtocols', () => {
119+
stubWindowProtocol('file:');
120+
121+
renderToStaticMarkup(<ClerkProvider publishableKey='pk_test_file_origin'>App</ClerkProvider>);
122+
123+
expect(capturedProviderProps?.allowedRedirectProtocols).toBeUndefined();
124+
});
125+
126+
it('respects an explicit allowedRedirectProtocols value over the scheme default', () => {
127+
stubWindowProtocol('clerk:');
128+
129+
renderToStaticMarkup(
130+
<ClerkProvider
131+
allowedRedirectProtocols={['myscheme:']}
132+
publishableKey='pk_test_explicit_protocols'
133+
>
134+
App
135+
</ClerkProvider>,
136+
);
137+
138+
expect(capturedProviderProps?.allowedRedirectProtocols).toEqual(['myscheme:']);
139+
});
140+
141+
it('respects an explicit empty allowedRedirectProtocols array', () => {
142+
stubWindowProtocol('clerk:');
143+
144+
renderToStaticMarkup(
145+
<ClerkProvider
146+
allowedRedirectProtocols={[]}
147+
publishableKey='pk_test_empty_protocols'
148+
>
149+
App
150+
</ClerkProvider>,
151+
);
152+
153+
expect(capturedProviderProps?.allowedRedirectProtocols).toEqual([]);
154+
});
155+
95156
it('registers an OAuth transport backed by the Electron bridge', async () => {
96157
oauthTransport.getRedirectUrl.mockResolvedValue('my-app://renderer/');
97158
oauthTransport.open.mockResolvedValue({ callbackUrl: 'my-app://renderer/?code=123' });

packages/electron/src/react/index.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';
22
import { InternalClerkProvider as ReactClerkProvider } from '@clerk/react/internal';
3+
import { ALLOWED_PROTOCOLS } from '@clerk/shared/internal/clerk-js/windowNavigate';
34
import { loadClerkUIScript } from '@clerk/shared/loadClerkJsScript';
45
import type { ClerkUIConstructor } from '@clerk/shared/ui';
56
import type { ReactNode } from 'react';
@@ -78,7 +79,27 @@ function createOAuthTransport(): ClerkOAuthTransport | undefined {
7879
};
7980
}
8081

81-
export function ClerkProvider({ children, publishableKey, passkeys, ...props }: ClerkProviderProps): JSX.Element {
82+
/**
83+
* Infer the custom renderer scheme registered with `createClerkBridge({ renderer })`.
84+
* Built-in Clerk protocols and local file renderers are not inferred.
85+
*/
86+
function defaultAllowedRedirectProtocols(): string[] | undefined {
87+
const protocol = typeof window !== 'undefined' ? window.location?.protocol : undefined;
88+
89+
if (!protocol || ALLOWED_PROTOCOLS.includes(protocol) || protocol === 'file:') {
90+
return undefined;
91+
}
92+
93+
return [protocol];
94+
}
95+
96+
export function ClerkProvider({
97+
children,
98+
publishableKey,
99+
passkeys,
100+
allowedRedirectProtocols,
101+
...props
102+
}: ClerkProviderProps): JSX.Element {
82103
const clerk = createClerkInstance(publishableKey, passkeys);
83104
const oauthTransport = createOAuthTransport();
84105
const clerkUI = loadClerkUI(publishableKey, props);
@@ -88,6 +109,7 @@ export function ClerkProvider({ children, publishableKey, passkeys, ...props }:
88109
{...props}
89110
Clerk={clerk}
90111
__internal_oauthTransport={oauthTransport}
112+
allowedRedirectProtocols={allowedRedirectProtocols ?? defaultAllowedRedirectProtocols()}
91113
publishableKey={publishableKey}
92114
standardBrowser={false}
93115
ui={{ ClerkUI: clerkUI }}

0 commit comments

Comments
 (0)