@clerk/electron 0.0.15 → 0.0.16-canary.v20260717065019

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -2,15 +2,28 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_ipc = require('./ipc-ByXFj84s.js');
3
3
  let electron = require("electron");
4
4
 
5
+ //#region src/main/validate-sender.ts
6
+ function isMainFrameEvent(event) {
7
+ const { sender, senderFrame } = event;
8
+ return sender.getType() === "window" && Boolean(senderFrame) && senderFrame === sender.mainFrame;
9
+ }
10
+
11
+ //#endregion
5
12
  //#region src/main/ipc-handlers.ts
13
+ function assertMainFrameSender(event) {
14
+ if (!isMainFrameEvent(event)) throw new Error("Clerk: token-cache request did not originate from a window's main frame.");
15
+ }
6
16
  function setupTokenCacheIpcHandlers(storage) {
7
- electron.ipcMain.handle(require_ipc.TOKEN_CACHE_CHANNELS.getToken, (_event, key) => {
17
+ electron.ipcMain.handle(require_ipc.TOKEN_CACHE_CHANNELS.getToken, (event, key) => {
18
+ assertMainFrameSender(event);
8
19
  return storage.getItem(key);
9
20
  });
10
- electron.ipcMain.handle(require_ipc.TOKEN_CACHE_CHANNELS.saveToken, (_event, key, value) => {
21
+ electron.ipcMain.handle(require_ipc.TOKEN_CACHE_CHANNELS.saveToken, (event, key, value) => {
22
+ assertMainFrameSender(event);
11
23
  return storage.setItem(key, value);
12
24
  });
13
- electron.ipcMain.handle(require_ipc.TOKEN_CACHE_CHANNELS.clearToken, (_event, key) => {
25
+ electron.ipcMain.handle(require_ipc.TOKEN_CACHE_CHANNELS.clearToken, (event, key) => {
26
+ assertMainFrameSender(event);
14
27
  return storage.removeItem(key);
15
28
  });
16
29
  return () => {
@@ -67,10 +80,12 @@ function setupOAuthTransportIpcHandlers(options) {
67
80
  electron.app.setAsDefaultProtocolClient(options.renderer.scheme);
68
81
  electron.app.on("open-url", openUrlListener);
69
82
  electron.app.on("second-instance", secondInstanceListener);
70
- electron.ipcMain.handle(require_ipc.OAUTH_TRANSPORT_CHANNELS.getRedirectUrl, () => {
83
+ electron.ipcMain.handle(require_ipc.OAUTH_TRANSPORT_CHANNELS.getRedirectUrl, (event) => {
84
+ if (!isMainFrameEvent(event)) throw new Error("Clerk: OAuth request did not originate from a window's main frame.");
71
85
  return redirectUrl;
72
86
  });
73
- electron.ipcMain.handle(require_ipc.OAUTH_TRANSPORT_CHANNELS.open, async (_event, url) => {
87
+ electron.ipcMain.handle(require_ipc.OAUTH_TRANSPORT_CHANNELS.open, async (event, url) => {
88
+ if (!isMainFrameEvent(event)) throw new Error("Clerk: OAuth request did not originate from a window's main frame.");
74
89
  if (pendingOAuthFlow) throw new Error("Clerk: an OAuth flow is already pending.");
75
90
  assertExternalOAuthUrl(url);
76
91
  const callbackPromise = new Promise((resolve, reject) => {
@@ -141,7 +156,7 @@ async function invokeNative(method, event, options) {
141
156
  message: "Native passkeys are not supported on this platform."
142
157
  }
143
158
  };
144
- if (!event.senderFrame || event.senderFrame !== event.sender.mainFrame) return {
159
+ if (!isMainFrameEvent(event)) return {
145
160
  ok: false,
146
161
  error: {
147
162
  code: "unknown",
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["TOKEN_CACHE_CHANNELS","OAUTH_TRANSPORT_CHANNELS","shell","BrowserWindow","PASSKEY_CHANNELS","app"],"sources":["../../src/main/ipc-handlers.ts","../../src/main/oauth-transport.ts","../../src/main/passkey-handlers.ts","../../src/main/create-clerk-bridge.ts"],"sourcesContent":["import { ipcMain } from 'electron';\n\nimport { TOKEN_CACHE_CHANNELS } from '../shared/ipc';\nimport type { TokenStorage } from '../shared/types';\n\nexport function setupTokenCacheIpcHandlers(storage: TokenStorage): () => void {\n ipcMain.handle(TOKEN_CACHE_CHANNELS.getToken, (_event, key: string) => {\n return storage.getItem(key);\n });\n\n ipcMain.handle(TOKEN_CACHE_CHANNELS.saveToken, (_event, key: string, value: string) => {\n return storage.setItem(key, value);\n });\n\n ipcMain.handle(TOKEN_CACHE_CHANNELS.clearToken, (_event, key: string) => {\n return storage.removeItem(key);\n });\n\n return () => {\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.getToken);\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.saveToken);\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.clearToken);\n };\n}\n","import { app, ipcMain, shell } from 'electron';\n\nimport { OAUTH_TRANSPORT_CHANNELS } from '../shared/ipc';\nimport type { RendererSchemeOptions } from '../shared/types';\n\nconst CALLBACK_TIMEOUT_MS = 3 * 60 * 1000;\n\ntype PendingOAuthFlow = {\n resolve: (value: { callbackUrl: string }) => void;\n reject: (reason: Error) => void;\n timeout: NodeJS.Timeout;\n};\n\ntype OAuthTransportOptions = {\n renderer: RendererSchemeOptions;\n};\n\nfunction buildRedirectUrl(options: OAuthTransportOptions): string {\n return `${options.renderer.scheme}://${options.renderer.host}/`;\n}\n\nfunction isMatchingCallbackUrl(url: string, redirectUrl: string): boolean {\n try {\n const callback = new URL(url);\n const expected = new URL(redirectUrl);\n\n return (\n callback.protocol === expected.protocol &&\n callback.host === expected.host &&\n callback.pathname === expected.pathname\n );\n } catch {\n return false;\n }\n}\n\nfunction assertExternalOAuthUrl(url: string): void {\n const parsedUrl = new URL(url);\n\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n throw new TypeError(`Clerk: refusing to open unsupported OAuth URL protocol: ${parsedUrl.protocol}`);\n }\n}\n\nexport function setupOAuthTransportIpcHandlers(options: OAuthTransportOptions): () => void {\n const redirectUrl = buildRedirectUrl(options);\n let pendingOAuthFlow: PendingOAuthFlow | null = null;\n\n const disposePendingOAuthFlow = (reason?: Error): void => {\n if (!pendingOAuthFlow) {\n return;\n }\n\n const pending = pendingOAuthFlow;\n clearTimeout(pendingOAuthFlow.timeout);\n pendingOAuthFlow = null;\n\n if (reason) {\n pending.reject(reason);\n }\n };\n\n const handleCallbackUrl = (url: string): void => {\n if (!pendingOAuthFlow || !isMatchingCallbackUrl(url, redirectUrl)) {\n return;\n }\n\n const pending = pendingOAuthFlow;\n disposePendingOAuthFlow();\n pending.resolve({ callbackUrl: url });\n };\n\n const openUrlListener = (event: Electron.Event, url: string): void => {\n if (!isMatchingCallbackUrl(url, redirectUrl)) {\n return;\n }\n\n event.preventDefault();\n handleCallbackUrl(url);\n };\n\n const secondInstanceListener = (_event: Electron.Event, argv: string[]): void => {\n const callbackUrl = argv.find(url => isMatchingCallbackUrl(url, redirectUrl));\n\n if (callbackUrl) {\n handleCallbackUrl(callbackUrl);\n }\n };\n\n app.setAsDefaultProtocolClient(options.renderer.scheme);\n app.on('open-url', openUrlListener);\n app.on('second-instance', secondInstanceListener);\n\n ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.getRedirectUrl, () => {\n return redirectUrl;\n });\n\n ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.open, async (_event, url: string) => {\n if (pendingOAuthFlow) {\n throw new Error('Clerk: an OAuth flow is already pending.');\n }\n\n assertExternalOAuthUrl(url);\n\n const callbackPromise = new Promise<{ callbackUrl: string }>((resolve, reject) => {\n const timeout = setTimeout(() => {\n disposePendingOAuthFlow();\n reject(new Error('Clerk: OAuth callback timed out.'));\n }, CALLBACK_TIMEOUT_MS);\n\n pendingOAuthFlow = { resolve, reject, timeout };\n });\n\n try {\n await shell.openExternal(url);\n } catch (err) {\n disposePendingOAuthFlow(err instanceof Error ? err : new Error(String(err)));\n }\n\n return callbackPromise;\n });\n\n return () => {\n disposePendingOAuthFlow(new Error('Clerk: OAuth flow was cancelled.'));\n app.removeListener('open-url', openUrlListener);\n app.removeListener('second-instance', secondInstanceListener);\n ipcMain.removeHandler(OAUTH_TRANSPORT_CHANNELS.getRedirectUrl);\n ipcMain.removeHandler(OAUTH_TRANSPORT_CHANNELS.open);\n };\n}\n","import type { IpcMainInvokeEvent } from 'electron';\nimport { BrowserWindow, ipcMain } from 'electron';\n\nimport { PASSKEY_CHANNELS } from '../shared/ipc';\nimport type {\n AuthenticationResponseJSON,\n PasskeyCapabilities,\n PasskeyIpcResult,\n PasskeyNativeErrorCode,\n RegistrationResponseJSON,\n SerializedPublicKeyCredentialCreationOptions,\n SerializedPublicKeyCredentialRequestOptions,\n SetupPasskeysMainReturn,\n} from '../shared/types';\n\n/**\n * Optional native module. Ceremony failures resolve as JSON envelopes so error\n * codes survive both the FFI and Electron IPC boundaries.\n */\ntype NativePasskeysModule = {\n isAvailable: () => boolean;\n capabilities: () => Omit<PasskeyCapabilities, 'available'>;\n createCredential: (windowHandle: Buffer, optionsJson: string) => Promise<string>;\n getCredential: (windowHandle: Buffer, optionsJson: string) => Promise<string>;\n};\n\nlet nativeModulePromise: Promise<NativePasskeysModule> | undefined;\n\nfunction loadNativeModule(): Promise<NativePasskeysModule> {\n // Keep the native module optional for apps that do not use passkeys.\n nativeModulePromise ??= import('@clerk/electron-passkeys').then(\n (module: { default?: NativePasskeysModule } & NativePasskeysModule) => module.default ?? module,\n error => {\n nativeModulePromise = undefined;\n throw new Error(\n 'Clerk: createClerkBridge({ passkeys: true }) requires the optional @clerk/electron-passkeys package. Install it with your package manager to enable native passkey support.',\n { cause: error },\n );\n },\n );\n return nativeModulePromise;\n}\n\nconst NATIVE_ERROR_CODES: PasskeyNativeErrorCode[] = ['cancelled', 'invalid_rp', 'not_supported', 'timeout', 'unknown'];\n\nfunction isPasskeyIpcResult<T>(value: unknown): value is PasskeyIpcResult<T> {\n if (!value || typeof value !== 'object' || typeof (value as { ok?: unknown }).ok !== 'boolean') {\n return false;\n }\n const result = value as { ok: boolean; credential?: unknown; error?: { code?: unknown } };\n return result.ok\n ? result.credential !== undefined\n : NATIVE_ERROR_CODES.includes(result.error?.code as PasskeyNativeErrorCode);\n}\n\nasync function invokeNative<T>(\n method: 'createCredential' | 'getCredential',\n event: IpcMainInvokeEvent,\n options: SerializedPublicKeyCredentialCreationOptions | SerializedPublicKeyCredentialRequestOptions,\n): Promise<PasskeyIpcResult<T>> {\n let native: NativePasskeysModule;\n try {\n native = await loadNativeModule();\n } catch (error) {\n return {\n ok: false,\n error: { code: 'not_supported', message: error instanceof Error ? error.message : String(error) },\n };\n }\n\n if (!native.isAvailable()) {\n return {\n ok: false,\n error: { code: 'not_supported', message: 'Native passkeys are not supported on this platform.' },\n };\n }\n\n // Subframes and webviews can host third-party content that must not be able\n // to run credential ceremonies for the app's RP ID.\n if (!event.senderFrame || event.senderFrame !== event.sender.mainFrame) {\n return {\n ok: false,\n error: { code: 'unknown', message: \"The passkey request did not originate from a window's main frame.\" },\n };\n }\n\n const window = BrowserWindow.fromWebContents(event.sender);\n if (!window) {\n return {\n ok: false,\n error: { code: 'unknown', message: 'The passkey request did not originate from a visible window.' },\n };\n }\n\n try {\n const resultJson = await native[method](window.getNativeWindowHandle(), JSON.stringify(options));\n const result: unknown = JSON.parse(resultJson);\n if (!isPasskeyIpcResult<T>(result)) {\n return { ok: false, error: { code: 'unknown', message: 'The native module returned an unexpected result.' } };\n }\n return result;\n } catch (error) {\n return { ok: false, error: { code: 'unknown', message: error instanceof Error ? error.message : String(error) } };\n }\n}\n\n/** Registers IPC handlers for native platform WebAuthn. */\nexport function setupPasskeysMain(): SetupPasskeysMainReturn {\n // Surface a missing optional dependency during setup, before the first ceremony.\n loadNativeModule().catch((error: Error) => console.warn(error.message));\n\n ipcMain.handle(\n PASSKEY_CHANNELS.create,\n (\n event,\n options: SerializedPublicKeyCredentialCreationOptions,\n ): Promise<PasskeyIpcResult<RegistrationResponseJSON>> => invokeNative('createCredential', event, options),\n );\n\n ipcMain.handle(\n PASSKEY_CHANNELS.get,\n (\n event,\n options: SerializedPublicKeyCredentialRequestOptions,\n ): Promise<PasskeyIpcResult<AuthenticationResponseJSON>> => invokeNative('getCredential', event, options),\n );\n\n ipcMain.handle(PASSKEY_CHANNELS.capabilities, async (): Promise<PasskeyCapabilities> => {\n try {\n const native = await loadNativeModule();\n if (!native.isAvailable()) {\n return { available: false, platformAuthenticator: false, securityKeys: false };\n }\n return { available: true, ...native.capabilities() };\n } catch {\n return { available: false, platformAuthenticator: false, securityKeys: false };\n }\n });\n\n return {\n cleanup() {\n ipcMain.removeHandler(PASSKEY_CHANNELS.create);\n ipcMain.removeHandler(PASSKEY_CHANNELS.get);\n ipcMain.removeHandler(PASSKEY_CHANNELS.capabilities);\n },\n };\n}\n","import { app, protocol } from 'electron';\n\nimport type { ClerkBridge, CreateClerkBridgeOptions } from '../shared/types';\nimport { setupTokenCacheIpcHandlers } from './ipc-handlers';\nimport { setupOAuthTransportIpcHandlers } from './oauth-transport';\nimport { setupPasskeysMain } from './passkey-handlers';\n\nfunction assertValidRendererOriginConfig(renderer: NonNullable<CreateClerkBridgeOptions['renderer']>): void {\n if (renderer.scheme.includes(':') || renderer.scheme.includes('/')) {\n throw new Error(\n 'Clerk: renderer.scheme must be a scheme name like \"my-app\", not a URL or protocol like \"my-app://\".',\n );\n }\n\n if (renderer.host.includes(':') || renderer.host.includes('/')) {\n throw new Error(\n 'Clerk: renderer.host must be a host name like \"renderer\", not a URL or origin like \"my-app://renderer\".',\n );\n }\n}\n\nfunction buildUserAgentFallback(defaultUserAgent: string, productToken: string): string {\n const platformCommentStart = defaultUserAgent.indexOf('(');\n const platformCommentEnd = defaultUserAgent.indexOf(')', platformCommentStart + 1);\n\n if (platformCommentStart === -1 || platformCommentEnd === -1) {\n return productToken;\n }\n\n const platformComment = defaultUserAgent.slice(platformCommentStart, platformCommentEnd + 1);\n\n // Clerk's session activity parser preserves the platform comment and treats the\n // token after Gecko as the app/browser name.\n return `Mozilla/5.0 ${platformComment} Gecko/20100101 ${productToken}`;\n}\n\n/**\n * Creates the Clerk bridge for Electron's main process.\n *\n * The bridge owns Clerk's main-process IPC handlers, token persistence, and OAuth deep-link\n * transport. Call this before creating renderer windows, and call the returned `cleanup` method\n * when tearing down the app or test environment.\n */\nexport function createClerkBridge(options: CreateClerkBridgeOptions): ClerkBridge {\n if (!options.storage) {\n throw new Error(\n 'Clerk: createClerkBridge requires a storage adapter. Pass createClerkBridge({ storage: storage() }) from @clerk/electron/storage, or provide a custom storage adapter.',\n );\n }\n\n const cleanupTokenPersistence = setupTokenCacheIpcHandlers(options.storage);\n let cleanupOAuthTransport: (() => void) | undefined;\n const passkeys = options.passkeys ? setupPasskeysMain() : null;\n\n if (options.userAgent) {\n app.userAgentFallback = buildUserAgentFallback(app.userAgentFallback, options.userAgent);\n }\n\n if (options.renderer) {\n assertValidRendererOriginConfig(options.renderer);\n\n protocol.registerSchemesAsPrivileged([\n {\n scheme: options.renderer.scheme,\n privileges: {\n standard: true,\n secure: true,\n supportFetchAPI: true,\n corsEnabled: true,\n stream: true,\n ...options.renderer.privileges,\n },\n },\n ]);\n\n cleanupOAuthTransport = setupOAuthTransportIpcHandlers({\n renderer: options.renderer,\n });\n }\n\n return {\n cleanup() {\n cleanupTokenPersistence();\n cleanupOAuthTransport?.();\n passkeys?.cleanup();\n },\n };\n}\n"],"mappings":";;;;;AAKA,SAAgB,2BAA2B,SAAmC;CAC5E,iBAAQ,OAAOA,iCAAqB,WAAW,QAAQ,QAAgB;EACrE,OAAO,QAAQ,QAAQ,GAAG;CAC5B,CAAC;CAED,iBAAQ,OAAOA,iCAAqB,YAAY,QAAQ,KAAa,UAAkB;EACrF,OAAO,QAAQ,QAAQ,KAAK,KAAK;CACnC,CAAC;CAED,iBAAQ,OAAOA,iCAAqB,aAAa,QAAQ,QAAgB;EACvE,OAAO,QAAQ,WAAW,GAAG;CAC/B,CAAC;CAED,aAAa;EACX,iBAAQ,cAAcA,iCAAqB,QAAQ;EACnD,iBAAQ,cAAcA,iCAAqB,SAAS;EACpD,iBAAQ,cAAcA,iCAAqB,UAAU;CACvD;AACF;;;;AClBA,MAAM,sBAAsB,MAAS;AAYrC,SAAS,iBAAiB,SAAwC;CAChE,OAAO,GAAG,QAAQ,SAAS,OAAO,KAAK,QAAQ,SAAS,KAAK;AAC/D;AAEA,SAAS,sBAAsB,KAAa,aAA8B;CACxE,IAAI;EACF,MAAM,WAAW,IAAI,IAAI,GAAG;EAC5B,MAAM,WAAW,IAAI,IAAI,WAAW;EAEpC,OACE,SAAS,aAAa,SAAS,YAC/B,SAAS,SAAS,SAAS,QAC3B,SAAS,aAAa,SAAS;CAEnC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,uBAAuB,KAAmB;CACjD,MAAM,YAAY,IAAI,IAAI,GAAG;CAE7B,IAAI,UAAU,aAAa,WAAW,UAAU,aAAa,UAC3D,MAAM,IAAI,UAAU,2DAA2D,UAAU,UAAU;AAEvG;AAEA,SAAgB,+BAA+B,SAA4C;CACzF,MAAM,cAAc,iBAAiB,OAAO;CAC5C,IAAI,mBAA4C;CAEhD,MAAM,2BAA2B,WAAyB;EACxD,IAAI,CAAC,kBACH;EAGF,MAAM,UAAU;EAChB,aAAa,iBAAiB,OAAO;EACrC,mBAAmB;EAEnB,IAAI,QACF,QAAQ,OAAO,MAAM;CAEzB;CAEA,MAAM,qBAAqB,QAAsB;EAC/C,IAAI,CAAC,oBAAoB,CAAC,sBAAsB,KAAK,WAAW,GAC9D;EAGF,MAAM,UAAU;EAChB,wBAAwB;EACxB,QAAQ,QAAQ,EAAE,aAAa,IAAI,CAAC;CACtC;CAEA,MAAM,mBAAmB,OAAuB,QAAsB;EACpE,IAAI,CAAC,sBAAsB,KAAK,WAAW,GACzC;EAGF,MAAM,eAAe;EACrB,kBAAkB,GAAG;CACvB;CAEA,MAAM,0BAA0B,QAAwB,SAAyB;EAC/E,MAAM,cAAc,KAAK,MAAK,QAAO,sBAAsB,KAAK,WAAW,CAAC;EAE5E,IAAI,aACF,kBAAkB,WAAW;CAEjC;CAEA,aAAI,2BAA2B,QAAQ,SAAS,MAAM;CACtD,aAAI,GAAG,YAAY,eAAe;CAClC,aAAI,GAAG,mBAAmB,sBAAsB;CAEhD,iBAAQ,OAAOC,qCAAyB,sBAAsB;EAC5D,OAAO;CACT,CAAC;CAED,iBAAQ,OAAOA,qCAAyB,MAAM,OAAO,QAAQ,QAAgB;EAC3E,IAAI,kBACF,MAAM,IAAI,MAAM,0CAA0C;EAG5D,uBAAuB,GAAG;EAE1B,MAAM,kBAAkB,IAAI,SAAkC,SAAS,WAAW;GAMhF,mBAAmB;IAAE;IAAS;IAAQ,SALtB,iBAAiB;KAC/B,wBAAwB;KACxB,uBAAO,IAAI,MAAM,kCAAkC,CAAC;IACtD,GAAG,mBAEyC;GAAE;EAChD,CAAC;EAED,IAAI;GACF,MAAMC,eAAM,aAAa,GAAG;EAC9B,SAAS,KAAK;GACZ,wBAAwB,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;EAC7E;EAEA,OAAO;CACT,CAAC;CAED,aAAa;EACX,wCAAwB,IAAI,MAAM,kCAAkC,CAAC;EACrE,aAAI,eAAe,YAAY,eAAe;EAC9C,aAAI,eAAe,mBAAmB,sBAAsB;EAC5D,iBAAQ,cAAcD,qCAAyB,cAAc;EAC7D,iBAAQ,cAAcA,qCAAyB,IAAI;CACrD;AACF;;;;ACvGA,IAAI;AAEJ,SAAS,mBAAkD;CAEzD,wBAAwB,OAAO,2BAA2B,CAAC,MACxD,WAAsE,OAAO,WAAW,SACzF,UAAS;EACP,sBAAsB;EACtB,MAAM,IAAI,MACR,+KACA,EAAE,OAAO,MAAM,CACjB;CACF,CACF;CACA,OAAO;AACT;AAEA,MAAM,qBAA+C;CAAC;CAAa;CAAc;CAAiB;CAAW;AAAS;AAEtH,SAAS,mBAAsB,OAA8C;CAC3E,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,OAAQ,MAA2B,OAAO,WACnF,OAAO;CAET,MAAM,SAAS;CACf,OAAO,OAAO,KACV,OAAO,eAAe,SACtB,mBAAmB,SAAS,OAAO,OAAO,IAA8B;AAC9E;AAEA,eAAe,aACb,QACA,OACA,SAC8B;CAC9B,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,iBAAiB;CAClC,SAAS,OAAO;EACd,OAAO;GACL,IAAI;GACJ,OAAO;IAAE,MAAM;IAAiB,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAAE;EAClG;CACF;CAEA,IAAI,CAAC,OAAO,YAAY,GACtB,OAAO;EACL,IAAI;EACJ,OAAO;GAAE,MAAM;GAAiB,SAAS;EAAsD;CACjG;CAKF,IAAI,CAAC,MAAM,eAAe,MAAM,gBAAgB,MAAM,OAAO,WAC3D,OAAO;EACL,IAAI;EACJ,OAAO;GAAE,MAAM;GAAW,SAAS;EAAoE;CACzG;CAGF,MAAM,SAASE,uBAAc,gBAAgB,MAAM,MAAM;CACzD,IAAI,CAAC,QACH,OAAO;EACL,IAAI;EACJ,OAAO;GAAE,MAAM;GAAW,SAAS;EAA+D;CACpG;CAGF,IAAI;EACF,MAAM,aAAa,MAAM,OAAO,OAAO,CAAC,OAAO,sBAAsB,GAAG,KAAK,UAAU,OAAO,CAAC;EAC/F,MAAM,SAAkB,KAAK,MAAM,UAAU;EAC7C,IAAI,CAAC,mBAAsB,MAAM,GAC/B,OAAO;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAW,SAAS;GAAmD;EAAE;EAE9G,OAAO;CACT,SAAS,OAAO;EACd,OAAO;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAW,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAAE;EAAE;CAClH;AACF;;AAGA,SAAgB,oBAA6C;CAE3D,iBAAiB,CAAC,CAAC,OAAO,UAAiB,QAAQ,KAAK,MAAM,OAAO,CAAC;CAEtE,iBAAQ,OACNC,6BAAiB,SAEf,OACA,YACwD,aAAa,oBAAoB,OAAO,OAAO,CAC3G;CAEA,iBAAQ,OACNA,6BAAiB,MAEf,OACA,YAC0D,aAAa,iBAAiB,OAAO,OAAO,CAC1G;CAEA,iBAAQ,OAAOA,6BAAiB,cAAc,YAA0C;EACtF,IAAI;GACF,MAAM,SAAS,MAAM,iBAAiB;GACtC,IAAI,CAAC,OAAO,YAAY,GACtB,OAAO;IAAE,WAAW;IAAO,uBAAuB;IAAO,cAAc;GAAM;GAE/E,OAAO;IAAE,WAAW;IAAM,GAAG,OAAO,aAAa;GAAE;EACrD,QAAQ;GACN,OAAO;IAAE,WAAW;IAAO,uBAAuB;IAAO,cAAc;GAAM;EAC/E;CACF,CAAC;CAED,OAAO,EACL,UAAU;EACR,iBAAQ,cAAcA,6BAAiB,MAAM;EAC7C,iBAAQ,cAAcA,6BAAiB,GAAG;EAC1C,iBAAQ,cAAcA,6BAAiB,YAAY;CACrD,EACF;AACF;;;;AC3IA,SAAS,gCAAgC,UAAmE;CAC1G,IAAI,SAAS,OAAO,SAAS,GAAG,KAAK,SAAS,OAAO,SAAS,GAAG,GAC/D,MAAM,IAAI,MACR,yGACF;CAGF,IAAI,SAAS,KAAK,SAAS,GAAG,KAAK,SAAS,KAAK,SAAS,GAAG,GAC3D,MAAM,IAAI,MACR,6GACF;AAEJ;AAEA,SAAS,uBAAuB,kBAA0B,cAA8B;CACtF,MAAM,uBAAuB,iBAAiB,QAAQ,GAAG;CACzD,MAAM,qBAAqB,iBAAiB,QAAQ,KAAK,uBAAuB,CAAC;CAEjF,IAAI,yBAAyB,MAAM,uBAAuB,IACxD,OAAO;CAOT,OAAO,eAJiB,iBAAiB,MAAM,sBAAsB,qBAAqB,CAItD,EAAE,kBAAkB;AAC1D;;;;;;;;AASA,SAAgB,kBAAkB,SAAgD;CAChF,IAAI,CAAC,QAAQ,SACX,MAAM,IAAI,MACR,wKACF;CAGF,MAAM,0BAA0B,2BAA2B,QAAQ,OAAO;CAC1E,IAAI;CACJ,MAAM,WAAW,QAAQ,WAAW,kBAAkB,IAAI;CAE1D,IAAI,QAAQ,WACV,aAAI,oBAAoB,uBAAuBC,aAAI,mBAAmB,QAAQ,SAAS;CAGzF,IAAI,QAAQ,UAAU;EACpB,gCAAgC,QAAQ,QAAQ;EAEhD,kBAAS,4BAA4B,CACnC;GACE,QAAQ,QAAQ,SAAS;GACzB,YAAY;IACV,UAAU;IACV,QAAQ;IACR,iBAAiB;IACjB,aAAa;IACb,QAAQ;IACR,GAAG,QAAQ,SAAS;GACtB;EACF,CACF,CAAC;EAED,wBAAwB,+BAA+B,EACrD,UAAU,QAAQ,SACpB,CAAC;CACH;CAEA,OAAO,EACL,UAAU;EACR,wBAAwB;EACxB,wBAAwB;EACxB,UAAU,QAAQ;CACpB,EACF;AACF"}
1
+ {"version":3,"file":"index.js","names":["TOKEN_CACHE_CHANNELS","OAUTH_TRANSPORT_CHANNELS","shell","BrowserWindow","PASSKEY_CHANNELS","app"],"sources":["../../src/main/validate-sender.ts","../../src/main/ipc-handlers.ts","../../src/main/oauth-transport.ts","../../src/main/passkey-handlers.ts","../../src/main/create-clerk-bridge.ts"],"sourcesContent":["import type { IpcMainInvokeEvent } from 'electron';\n\n// Restricts privileged IPC to a top-level BrowserWindow's main frame. The frame check alone\n// only proves the sender is the top document of *its own* WebContents, so a <webview> (a separate\n// WebContents) sharing the Clerk preload would still pass; requiring getType() === 'window' rejects\n// <webview> guests and BrowserViews, and the frame check rejects in-page iframes.\nexport function isMainFrameEvent(event: IpcMainInvokeEvent): boolean {\n const { sender, senderFrame } = event;\n return sender.getType() === 'window' && Boolean(senderFrame) && senderFrame === sender.mainFrame;\n}\n","import type { IpcMainInvokeEvent } from 'electron';\nimport { ipcMain } from 'electron';\n\nimport { TOKEN_CACHE_CHANNELS } from '../shared/ipc';\nimport type { TokenStorage } from '../shared/types';\nimport { isMainFrameEvent } from './validate-sender';\n\nfunction assertMainFrameSender(event: IpcMainInvokeEvent): void {\n if (!isMainFrameEvent(event)) {\n throw new Error(\"Clerk: token-cache request did not originate from a window's main frame.\");\n }\n}\n\nexport function setupTokenCacheIpcHandlers(storage: TokenStorage): () => void {\n ipcMain.handle(TOKEN_CACHE_CHANNELS.getToken, (event, key: string) => {\n assertMainFrameSender(event);\n return storage.getItem(key);\n });\n\n ipcMain.handle(TOKEN_CACHE_CHANNELS.saveToken, (event, key: string, value: string) => {\n assertMainFrameSender(event);\n return storage.setItem(key, value);\n });\n\n ipcMain.handle(TOKEN_CACHE_CHANNELS.clearToken, (event, key: string) => {\n assertMainFrameSender(event);\n return storage.removeItem(key);\n });\n\n return () => {\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.getToken);\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.saveToken);\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.clearToken);\n };\n}\n","import { app, ipcMain, shell } from 'electron';\n\nimport { OAUTH_TRANSPORT_CHANNELS } from '../shared/ipc';\nimport type { RendererSchemeOptions } from '../shared/types';\nimport { isMainFrameEvent } from './validate-sender';\n\nconst CALLBACK_TIMEOUT_MS = 3 * 60 * 1000;\n\ntype PendingOAuthFlow = {\n resolve: (value: { callbackUrl: string }) => void;\n reject: (reason: Error) => void;\n timeout: NodeJS.Timeout;\n};\n\ntype OAuthTransportOptions = {\n renderer: RendererSchemeOptions;\n};\n\nfunction buildRedirectUrl(options: OAuthTransportOptions): string {\n return `${options.renderer.scheme}://${options.renderer.host}/`;\n}\n\nfunction isMatchingCallbackUrl(url: string, redirectUrl: string): boolean {\n try {\n const callback = new URL(url);\n const expected = new URL(redirectUrl);\n\n return (\n callback.protocol === expected.protocol &&\n callback.host === expected.host &&\n callback.pathname === expected.pathname\n );\n } catch {\n return false;\n }\n}\n\nfunction assertExternalOAuthUrl(url: string): void {\n const parsedUrl = new URL(url);\n\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n throw new TypeError(`Clerk: refusing to open unsupported OAuth URL protocol: ${parsedUrl.protocol}`);\n }\n}\n\nexport function setupOAuthTransportIpcHandlers(options: OAuthTransportOptions): () => void {\n const redirectUrl = buildRedirectUrl(options);\n let pendingOAuthFlow: PendingOAuthFlow | null = null;\n\n const disposePendingOAuthFlow = (reason?: Error): void => {\n if (!pendingOAuthFlow) {\n return;\n }\n\n const pending = pendingOAuthFlow;\n clearTimeout(pendingOAuthFlow.timeout);\n pendingOAuthFlow = null;\n\n if (reason) {\n pending.reject(reason);\n }\n };\n\n const handleCallbackUrl = (url: string): void => {\n if (!pendingOAuthFlow || !isMatchingCallbackUrl(url, redirectUrl)) {\n return;\n }\n\n const pending = pendingOAuthFlow;\n disposePendingOAuthFlow();\n pending.resolve({ callbackUrl: url });\n };\n\n const openUrlListener = (event: Electron.Event, url: string): void => {\n if (!isMatchingCallbackUrl(url, redirectUrl)) {\n return;\n }\n\n event.preventDefault();\n handleCallbackUrl(url);\n };\n\n const secondInstanceListener = (_event: Electron.Event, argv: string[]): void => {\n const callbackUrl = argv.find(url => isMatchingCallbackUrl(url, redirectUrl));\n\n if (callbackUrl) {\n handleCallbackUrl(callbackUrl);\n }\n };\n\n app.setAsDefaultProtocolClient(options.renderer.scheme);\n app.on('open-url', openUrlListener);\n app.on('second-instance', secondInstanceListener);\n\n ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.getRedirectUrl, event => {\n if (!isMainFrameEvent(event)) {\n throw new Error(\"Clerk: OAuth request did not originate from a window's main frame.\");\n }\n return redirectUrl;\n });\n\n ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.open, async (event, url: string) => {\n if (!isMainFrameEvent(event)) {\n throw new Error(\"Clerk: OAuth request did not originate from a window's main frame.\");\n }\n\n if (pendingOAuthFlow) {\n throw new Error('Clerk: an OAuth flow is already pending.');\n }\n\n assertExternalOAuthUrl(url);\n\n const callbackPromise = new Promise<{ callbackUrl: string }>((resolve, reject) => {\n const timeout = setTimeout(() => {\n disposePendingOAuthFlow();\n reject(new Error('Clerk: OAuth callback timed out.'));\n }, CALLBACK_TIMEOUT_MS);\n\n pendingOAuthFlow = { resolve, reject, timeout };\n });\n\n try {\n await shell.openExternal(url);\n } catch (err) {\n disposePendingOAuthFlow(err instanceof Error ? err : new Error(String(err)));\n }\n\n return callbackPromise;\n });\n\n return () => {\n disposePendingOAuthFlow(new Error('Clerk: OAuth flow was cancelled.'));\n app.removeListener('open-url', openUrlListener);\n app.removeListener('second-instance', secondInstanceListener);\n ipcMain.removeHandler(OAUTH_TRANSPORT_CHANNELS.getRedirectUrl);\n ipcMain.removeHandler(OAUTH_TRANSPORT_CHANNELS.open);\n };\n}\n","import type { IpcMainInvokeEvent } from 'electron';\nimport { BrowserWindow, ipcMain } from 'electron';\n\nimport { PASSKEY_CHANNELS } from '../shared/ipc';\nimport type {\n AuthenticationResponseJSON,\n PasskeyCapabilities,\n PasskeyIpcResult,\n PasskeyNativeErrorCode,\n RegistrationResponseJSON,\n SerializedPublicKeyCredentialCreationOptions,\n SerializedPublicKeyCredentialRequestOptions,\n SetupPasskeysMainReturn,\n} from '../shared/types';\nimport { isMainFrameEvent } from './validate-sender';\n\n/**\n * Optional native module. Ceremony failures resolve as JSON envelopes so error\n * codes survive both the FFI and Electron IPC boundaries.\n */\ntype NativePasskeysModule = {\n isAvailable: () => boolean;\n capabilities: () => Omit<PasskeyCapabilities, 'available'>;\n createCredential: (windowHandle: Buffer, optionsJson: string) => Promise<string>;\n getCredential: (windowHandle: Buffer, optionsJson: string) => Promise<string>;\n};\n\nlet nativeModulePromise: Promise<NativePasskeysModule> | undefined;\n\nfunction loadNativeModule(): Promise<NativePasskeysModule> {\n // Keep the native module optional for apps that do not use passkeys.\n nativeModulePromise ??= import('@clerk/electron-passkeys').then(\n (module: { default?: NativePasskeysModule } & NativePasskeysModule) => module.default ?? module,\n error => {\n nativeModulePromise = undefined;\n throw new Error(\n 'Clerk: createClerkBridge({ passkeys: true }) requires the optional @clerk/electron-passkeys package. Install it with your package manager to enable native passkey support.',\n { cause: error },\n );\n },\n );\n return nativeModulePromise;\n}\n\nconst NATIVE_ERROR_CODES: PasskeyNativeErrorCode[] = ['cancelled', 'invalid_rp', 'not_supported', 'timeout', 'unknown'];\n\nfunction isPasskeyIpcResult<T>(value: unknown): value is PasskeyIpcResult<T> {\n if (!value || typeof value !== 'object' || typeof (value as { ok?: unknown }).ok !== 'boolean') {\n return false;\n }\n const result = value as { ok: boolean; credential?: unknown; error?: { code?: unknown } };\n return result.ok\n ? result.credential !== undefined\n : NATIVE_ERROR_CODES.includes(result.error?.code as PasskeyNativeErrorCode);\n}\n\nasync function invokeNative<T>(\n method: 'createCredential' | 'getCredential',\n event: IpcMainInvokeEvent,\n options: SerializedPublicKeyCredentialCreationOptions | SerializedPublicKeyCredentialRequestOptions,\n): Promise<PasskeyIpcResult<T>> {\n let native: NativePasskeysModule;\n try {\n native = await loadNativeModule();\n } catch (error) {\n return {\n ok: false,\n error: { code: 'not_supported', message: error instanceof Error ? error.message : String(error) },\n };\n }\n\n if (!native.isAvailable()) {\n return {\n ok: false,\n error: { code: 'not_supported', message: 'Native passkeys are not supported on this platform.' },\n };\n }\n\n // Subframes and webviews can host third-party content that must not be able\n // to run credential ceremonies for the app's RP ID.\n if (!isMainFrameEvent(event)) {\n return {\n ok: false,\n error: { code: 'unknown', message: \"The passkey request did not originate from a window's main frame.\" },\n };\n }\n\n const window = BrowserWindow.fromWebContents(event.sender);\n if (!window) {\n return {\n ok: false,\n error: { code: 'unknown', message: 'The passkey request did not originate from a visible window.' },\n };\n }\n\n try {\n const resultJson = await native[method](window.getNativeWindowHandle(), JSON.stringify(options));\n const result: unknown = JSON.parse(resultJson);\n if (!isPasskeyIpcResult<T>(result)) {\n return { ok: false, error: { code: 'unknown', message: 'The native module returned an unexpected result.' } };\n }\n return result;\n } catch (error) {\n return { ok: false, error: { code: 'unknown', message: error instanceof Error ? error.message : String(error) } };\n }\n}\n\n/** Registers IPC handlers for native platform WebAuthn. */\nexport function setupPasskeysMain(): SetupPasskeysMainReturn {\n // Surface a missing optional dependency during setup, before the first ceremony.\n loadNativeModule().catch((error: Error) => console.warn(error.message));\n\n ipcMain.handle(\n PASSKEY_CHANNELS.create,\n (\n event,\n options: SerializedPublicKeyCredentialCreationOptions,\n ): Promise<PasskeyIpcResult<RegistrationResponseJSON>> => invokeNative('createCredential', event, options),\n );\n\n ipcMain.handle(\n PASSKEY_CHANNELS.get,\n (\n event,\n options: SerializedPublicKeyCredentialRequestOptions,\n ): Promise<PasskeyIpcResult<AuthenticationResponseJSON>> => invokeNative('getCredential', event, options),\n );\n\n ipcMain.handle(PASSKEY_CHANNELS.capabilities, async (): Promise<PasskeyCapabilities> => {\n try {\n const native = await loadNativeModule();\n if (!native.isAvailable()) {\n return { available: false, platformAuthenticator: false, securityKeys: false };\n }\n return { available: true, ...native.capabilities() };\n } catch {\n return { available: false, platformAuthenticator: false, securityKeys: false };\n }\n });\n\n return {\n cleanup() {\n ipcMain.removeHandler(PASSKEY_CHANNELS.create);\n ipcMain.removeHandler(PASSKEY_CHANNELS.get);\n ipcMain.removeHandler(PASSKEY_CHANNELS.capabilities);\n },\n };\n}\n","import { app, protocol } from 'electron';\n\nimport type { ClerkBridge, CreateClerkBridgeOptions } from '../shared/types';\nimport { setupTokenCacheIpcHandlers } from './ipc-handlers';\nimport { setupOAuthTransportIpcHandlers } from './oauth-transport';\nimport { setupPasskeysMain } from './passkey-handlers';\n\nfunction assertValidRendererOriginConfig(renderer: NonNullable<CreateClerkBridgeOptions['renderer']>): void {\n if (renderer.scheme.includes(':') || renderer.scheme.includes('/')) {\n throw new Error(\n 'Clerk: renderer.scheme must be a scheme name like \"my-app\", not a URL or protocol like \"my-app://\".',\n );\n }\n\n if (renderer.host.includes(':') || renderer.host.includes('/')) {\n throw new Error(\n 'Clerk: renderer.host must be a host name like \"renderer\", not a URL or origin like \"my-app://renderer\".',\n );\n }\n}\n\nfunction buildUserAgentFallback(defaultUserAgent: string, productToken: string): string {\n const platformCommentStart = defaultUserAgent.indexOf('(');\n const platformCommentEnd = defaultUserAgent.indexOf(')', platformCommentStart + 1);\n\n if (platformCommentStart === -1 || platformCommentEnd === -1) {\n return productToken;\n }\n\n const platformComment = defaultUserAgent.slice(platformCommentStart, platformCommentEnd + 1);\n\n // Clerk's session activity parser preserves the platform comment and treats the\n // token after Gecko as the app/browser name.\n return `Mozilla/5.0 ${platformComment} Gecko/20100101 ${productToken}`;\n}\n\n/**\n * Creates the Clerk bridge for Electron's main process.\n *\n * The bridge owns Clerk's main-process IPC handlers, token persistence, and OAuth deep-link\n * transport. Call this before creating renderer windows, and call the returned `cleanup` method\n * when tearing down the app or test environment.\n */\nexport function createClerkBridge(options: CreateClerkBridgeOptions): ClerkBridge {\n if (!options.storage) {\n throw new Error(\n 'Clerk: createClerkBridge requires a storage adapter. Pass createClerkBridge({ storage: storage() }) from @clerk/electron/storage, or provide a custom storage adapter.',\n );\n }\n\n const cleanupTokenPersistence = setupTokenCacheIpcHandlers(options.storage);\n let cleanupOAuthTransport: (() => void) | undefined;\n const passkeys = options.passkeys ? setupPasskeysMain() : null;\n\n if (options.userAgent) {\n app.userAgentFallback = buildUserAgentFallback(app.userAgentFallback, options.userAgent);\n }\n\n if (options.renderer) {\n assertValidRendererOriginConfig(options.renderer);\n\n protocol.registerSchemesAsPrivileged([\n {\n scheme: options.renderer.scheme,\n privileges: {\n standard: true,\n secure: true,\n supportFetchAPI: true,\n corsEnabled: true,\n stream: true,\n ...options.renderer.privileges,\n },\n },\n ]);\n\n cleanupOAuthTransport = setupOAuthTransportIpcHandlers({\n renderer: options.renderer,\n });\n }\n\n return {\n cleanup() {\n cleanupTokenPersistence();\n cleanupOAuthTransport?.();\n passkeys?.cleanup();\n },\n };\n}\n"],"mappings":"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"}
@@ -25,7 +25,7 @@ function createClerkInstance(publishableKey, passkeys) {
25
25
  clerk.__internal_onBeforeRequest(async (request) => {
26
26
  request.credentials = "omit";
27
27
  request.url?.searchParams.append("_is_native", "1");
28
- request.url?.searchParams.append("_electron_sdk_version", "0.0.15");
28
+ request.url?.searchParams.append("_electron_sdk_version", "0.0.16-canary.v20260717065019");
29
29
  const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);
30
30
  if (token) {
31
31
  const headers = new Headers(request.headers);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Clerk","ALLOWED_PROTOCOLS","ReactClerkProvider"],"sources":["../../../src/react/create-clerk-instance.ts","../../../src/react/index.tsx"],"sourcesContent":["import { Clerk } from '@clerk/clerk-js';\n\n// Type-only import: the passkeys module is only bundled when the app imports\n// `@clerk/electron/passkeys` itself.\nimport type { PasskeySupport } from '../passkeys';\n\nconst CLERK_CLIENT_JWT_KEY = '__clerk_client_jwt';\n\ntype ClerkInstance = InstanceType<typeof Clerk>;\n\nlet cached: { instance: ClerkInstance; publishableKey: string } | null = null;\n\nfunction attachPasskeys(clerk: ClerkInstance, passkeys: PasskeySupport): void {\n clerk.__internal_createPublicCredentials = passkeys.create;\n clerk.__internal_getPublicCredentials = passkeys.get;\n clerk.__internal_isWebAuthnSupported = passkeys.isSupported;\n clerk.__internal_isWebAuthnAutofillSupported = passkeys.isAutoFillSupported;\n clerk.__internal_isWebAuthnPlatformAuthenticatorSupported = passkeys.isPlatformAuthenticatorSupported;\n}\n\nexport function createClerkInstance(publishableKey: string, passkeys?: PasskeySupport): ClerkInstance {\n if (cached?.publishableKey === publishableKey) {\n if (passkeys) {\n attachPasskeys(cached.instance, passkeys);\n }\n return cached.instance;\n }\n\n const clerk = new Clerk(publishableKey);\n\n if (passkeys) {\n attachPasskeys(clerk, passkeys);\n }\n\n clerk.__internal_onBeforeRequest(async request => {\n request.credentials = 'omit';\n request.url?.searchParams.append('_is_native', '1');\n request.url?.searchParams.append('_electron_sdk_version', PACKAGE_VERSION);\n\n const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);\n if (token) {\n const headers = new Headers(request.headers);\n headers.set('Authorization', `Bearer ${token}`);\n request.headers = headers;\n }\n });\n\n clerk.__internal_onAfterResponse(async (_request, response) => {\n const authorization = (response as Response).headers.get('Authorization');\n if (!authorization) {\n return;\n }\n\n const token = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : authorization;\n await window.__clerk_internal_electron?.tokenCache.saveToken(CLERK_CLIENT_JWT_KEY, token);\n });\n\n cached = { instance: clerk, publishableKey };\n return clerk;\n}\n","import type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';\nimport { InternalClerkProvider as ReactClerkProvider } from '@clerk/react/internal';\nimport { ALLOWED_PROTOCOLS } from '@clerk/shared/internal/clerk-js/windowNavigate';\nimport { loadClerkUIScript } from '@clerk/shared/loadClerkJsScript';\nimport type { ClerkUIConstructor } from '@clerk/shared/ui';\nimport type { ReactNode } from 'react';\n\nimport type { PasskeySupport } from '../passkeys';\nimport { createClerkInstance } from './create-clerk-instance';\n\ntype ClerkOAuthTransport = NonNullable<ReactClerkProviderProps['__internal_oauthTransport']>;\n\nexport type ClerkProviderProps = Omit<\n ReactClerkProviderProps,\n 'Clerk' | 'children' | 'publishableKey' | 'standardBrowser' | 'ui' | '__internal_oauthTransport'\n> & {\n children: ReactNode;\n /**\n * Your Clerk publishable key, available in the Clerk Dashboard.\n */\n publishableKey: string;\n /**\n * Enables passkey support. Pass the `passkeys` export from `@clerk/electron/passkeys`;\n * when omitted, no passkey code is bundled or initialized.\n *\n * @example\n * ```tsx\n * import { passkeys } from '@clerk/electron/passkeys';\n *\n * <ClerkProvider publishableKey={publishableKey} passkeys={passkeys} />\n * ```\n */\n passkeys?: PasskeySupport;\n};\n\nlet cachedClerkUI: { promise: Promise<ClerkUIConstructor>; publishableKey: string } | null = null;\n\nfunction loadClerkUI(publishableKey: string, props: Partial<ClerkProviderProps>): Promise<ClerkUIConstructor> {\n if (cachedClerkUI?.publishableKey === publishableKey) {\n return cachedClerkUI.promise;\n }\n\n // Undocumented escape hatch for self-hosting/proxying the UI bundle; not part of the public props.\n const { __internal_clerkUIUrl, __internal_clerkUIVersion } = props as {\n __internal_clerkUIUrl?: string;\n __internal_clerkUIVersion?: string;\n };\n\n const promise = loadClerkUIScript({\n publishableKey,\n proxyUrl: typeof props.proxyUrl === 'string' ? props.proxyUrl : undefined,\n domain: typeof props.domain === 'string' ? props.domain : undefined,\n nonce: props.nonce,\n __internal_clerkUIUrl,\n __internal_clerkUIVersion,\n }).then(() => {\n if (!window.__internal_ClerkUICtor) {\n throw new Error(\n 'Clerk: Failed to load Clerk UI from the CDN. Ensure your Content Security Policy allows the Clerk Frontend API host in `script-src`. Contact support@clerk.com.',\n );\n }\n return window.__internal_ClerkUICtor;\n });\n\n cachedClerkUI = { promise, publishableKey };\n return promise;\n}\n\nfunction createOAuthTransport(): ClerkOAuthTransport | undefined {\n const bridge = window.__clerk_internal_electron?.oauthTransport;\n\n if (!bridge) {\n return undefined;\n }\n\n return {\n getRedirectUrl: () => bridge.getRedirectUrl(),\n open: url => bridge.open(url.href),\n };\n}\n\n/**\n * Infer the custom renderer scheme registered with `createClerkBridge({ renderer })`.\n * Built-in Clerk protocols and local file renderers are not inferred.\n */\nfunction defaultAllowedRedirectProtocols(): string[] | undefined {\n const protocol = typeof window !== 'undefined' ? window.location?.protocol : undefined;\n\n if (!protocol || ALLOWED_PROTOCOLS.includes(protocol) || protocol === 'file:') {\n return undefined;\n }\n\n return [protocol];\n}\n\nexport function ClerkProvider({\n children,\n publishableKey,\n passkeys,\n allowedRedirectProtocols,\n ...props\n}: ClerkProviderProps): JSX.Element {\n const clerk = createClerkInstance(publishableKey, passkeys);\n const oauthTransport = createOAuthTransport();\n const clerkUI = loadClerkUI(publishableKey, props);\n\n return (\n <ReactClerkProvider\n {...props}\n Clerk={clerk}\n __internal_oauthTransport={oauthTransport}\n allowedRedirectProtocols={allowedRedirectProtocols ?? defaultAllowedRedirectProtocols()}\n publishableKey={publishableKey}\n standardBrowser={false}\n ui={{ ClerkUI: clerkUI }}\n >\n {children}\n </ReactClerkProvider>\n );\n}\n\nexport * from '@clerk/react';\n"],"mappings":";;;;;;;;AAMA,MAAM,uBAAuB;AAI7B,IAAI,SAAqE;AAEzE,SAAS,eAAe,OAAsB,UAAgC;CAC5E,MAAM,qCAAqC,SAAS;CACpD,MAAM,kCAAkC,SAAS;CACjD,MAAM,iCAAiC,SAAS;CAChD,MAAM,yCAAyC,SAAS;CACxD,MAAM,sDAAsD,SAAS;AACvE;AAEA,SAAgB,oBAAoB,gBAAwB,UAA0C;CACpG,IAAI,QAAQ,mBAAmB,gBAAgB;EAC7C,IAAI,UACF,eAAe,OAAO,UAAU,QAAQ;EAE1C,OAAO,OAAO;CAChB;CAEA,MAAM,QAAQ,IAAIA,sBAAM,cAAc;CAEtC,IAAI,UACF,eAAe,OAAO,QAAQ;CAGhC,MAAM,2BAA2B,OAAM,YAAW;EAChD,QAAQ,cAAc;EACtB,QAAQ,KAAK,aAAa,OAAO,cAAc,GAAG;EAClD,QAAQ,KAAK,aAAa,OAAO,iCAAwC;EAEzE,MAAM,QAAQ,MAAM,OAAO,2BAA2B,WAAW,SAAS,oBAAoB;EAC9F,IAAI,OAAO;GACT,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;GAC3C,QAAQ,IAAI,iBAAiB,UAAU,OAAO;GAC9C,QAAQ,UAAU;EACpB;CACF,CAAC;CAED,MAAM,2BAA2B,OAAO,UAAU,aAAa;EAC7D,MAAM,gBAAiB,SAAsB,QAAQ,IAAI,eAAe;EACxE,IAAI,CAAC,eACH;EAGF,MAAM,QAAQ,cAAc,WAAW,SAAS,IAAI,cAAc,MAAM,CAAgB,IAAI;EAC5F,MAAM,OAAO,2BAA2B,WAAW,UAAU,sBAAsB,KAAK;CAC1F,CAAC;CAED,SAAS;EAAE,UAAU;EAAO;CAAe;CAC3C,OAAO;AACT;;;;ACxBA,IAAI,gBAAyF;AAE7F,SAAS,YAAY,gBAAwB,OAAiE;CAC5G,IAAI,eAAe,mBAAmB,gBACpC,OAAO,cAAc;CAIvB,MAAM,EAAE,uBAAuB,8BAA8B;CAK7D,MAAM,iEAA4B;EAChC;EACA,UAAU,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;EAChE,QAAQ,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;EAC1D,OAAO,MAAM;EACb;EACA;CACF,CAAC,CAAC,CAAC,WAAW;EACZ,IAAI,CAAC,OAAO,wBACV,MAAM,IAAI,MACR,iKACF;EAEF,OAAO,OAAO;CAChB,CAAC;CAED,gBAAgB;EAAE;EAAS;CAAe;CAC1C,OAAO;AACT;AAEA,SAAS,uBAAwD;CAC/D,MAAM,SAAS,OAAO,2BAA2B;CAEjD,IAAI,CAAC,QACH;CAGF,OAAO;EACL,sBAAsB,OAAO,eAAe;EAC5C,OAAM,QAAO,OAAO,KAAK,IAAI,IAAI;CACnC;AACF;;;;;AAMA,SAAS,kCAAwD;CAC/D,MAAM,WAAW,OAAO,WAAW,cAAc,OAAO,UAAU,WAAW;CAE7E,IAAI,CAAC,YAAYC,iEAAkB,SAAS,QAAQ,KAAK,aAAa,SACpE;CAGF,OAAO,CAAC,QAAQ;AAClB;AAEA,SAAgB,cAAc,EAC5B,UACA,gBACA,UACA,0BACA,GAAG,SAC+B;CAClC,MAAM,QAAQ,oBAAoB,gBAAgB,QAAQ;CAC1D,MAAM,iBAAiB,qBAAqB;CAC5C,MAAM,UAAU,YAAY,gBAAgB,KAAK;CAEjD,OACE,2CAACC,6CAAD;EACE,GAAI;EACJ,OAAO;EACP,2BAA2B;EAC3B,0BAA0B,4BAA4B,gCAAgC;EACtE;EAChB,iBAAiB;EACjB,IAAI,EAAE,SAAS,QAAQ;EAEtB;CACiB;AAExB"}
1
+ {"version":3,"file":"index.js","names":["Clerk","ALLOWED_PROTOCOLS","ReactClerkProvider"],"sources":["../../../src/react/create-clerk-instance.ts","../../../src/react/index.tsx"],"sourcesContent":["import { Clerk } from '@clerk/clerk-js';\n\n// Type-only import: the passkeys module is only bundled when the app imports\n// `@clerk/electron/passkeys` itself.\nimport type { PasskeySupport } from '../passkeys';\n\nconst CLERK_CLIENT_JWT_KEY = '__clerk_client_jwt';\n\ntype ClerkInstance = InstanceType<typeof Clerk>;\n\nlet cached: { instance: ClerkInstance; publishableKey: string } | null = null;\n\nfunction attachPasskeys(clerk: ClerkInstance, passkeys: PasskeySupport): void {\n clerk.__internal_createPublicCredentials = passkeys.create;\n clerk.__internal_getPublicCredentials = passkeys.get;\n clerk.__internal_isWebAuthnSupported = passkeys.isSupported;\n clerk.__internal_isWebAuthnAutofillSupported = passkeys.isAutoFillSupported;\n clerk.__internal_isWebAuthnPlatformAuthenticatorSupported = passkeys.isPlatformAuthenticatorSupported;\n}\n\nexport function createClerkInstance(publishableKey: string, passkeys?: PasskeySupport): ClerkInstance {\n if (cached?.publishableKey === publishableKey) {\n if (passkeys) {\n attachPasskeys(cached.instance, passkeys);\n }\n return cached.instance;\n }\n\n const clerk = new Clerk(publishableKey);\n\n if (passkeys) {\n attachPasskeys(clerk, passkeys);\n }\n\n clerk.__internal_onBeforeRequest(async request => {\n request.credentials = 'omit';\n request.url?.searchParams.append('_is_native', '1');\n request.url?.searchParams.append('_electron_sdk_version', PACKAGE_VERSION);\n\n const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);\n if (token) {\n const headers = new Headers(request.headers);\n headers.set('Authorization', `Bearer ${token}`);\n request.headers = headers;\n }\n });\n\n clerk.__internal_onAfterResponse(async (_request, response) => {\n const authorization = (response as Response).headers.get('Authorization');\n if (!authorization) {\n return;\n }\n\n const token = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : authorization;\n await window.__clerk_internal_electron?.tokenCache.saveToken(CLERK_CLIENT_JWT_KEY, token);\n });\n\n cached = { instance: clerk, publishableKey };\n return clerk;\n}\n","import type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';\nimport { InternalClerkProvider as ReactClerkProvider } from '@clerk/react/internal';\nimport { ALLOWED_PROTOCOLS } from '@clerk/shared/internal/clerk-js/windowNavigate';\nimport { loadClerkUIScript } from '@clerk/shared/loadClerkJsScript';\nimport type { ClerkUIConstructor } from '@clerk/shared/ui';\nimport type { ReactNode } from 'react';\n\nimport type { PasskeySupport } from '../passkeys';\nimport { createClerkInstance } from './create-clerk-instance';\n\ntype ClerkOAuthTransport = NonNullable<ReactClerkProviderProps['__internal_oauthTransport']>;\n\nexport type ClerkProviderProps = Omit<\n ReactClerkProviderProps,\n 'Clerk' | 'children' | 'publishableKey' | 'standardBrowser' | 'ui' | '__internal_oauthTransport'\n> & {\n children: ReactNode;\n /**\n * Your Clerk publishable key, available in the Clerk Dashboard.\n */\n publishableKey: string;\n /**\n * Enables passkey support. Pass the `passkeys` export from `@clerk/electron/passkeys`;\n * when omitted, no passkey code is bundled or initialized.\n *\n * @example\n * ```tsx\n * import { passkeys } from '@clerk/electron/passkeys';\n *\n * <ClerkProvider publishableKey={publishableKey} passkeys={passkeys} />\n * ```\n */\n passkeys?: PasskeySupport;\n};\n\nlet cachedClerkUI: { promise: Promise<ClerkUIConstructor>; publishableKey: string } | null = null;\n\nfunction loadClerkUI(publishableKey: string, props: Partial<ClerkProviderProps>): Promise<ClerkUIConstructor> {\n if (cachedClerkUI?.publishableKey === publishableKey) {\n return cachedClerkUI.promise;\n }\n\n // Undocumented escape hatch for self-hosting/proxying the UI bundle; not part of the public props.\n const { __internal_clerkUIUrl, __internal_clerkUIVersion } = props as {\n __internal_clerkUIUrl?: string;\n __internal_clerkUIVersion?: string;\n };\n\n const promise = loadClerkUIScript({\n publishableKey,\n proxyUrl: typeof props.proxyUrl === 'string' ? props.proxyUrl : undefined,\n domain: typeof props.domain === 'string' ? props.domain : undefined,\n nonce: props.nonce,\n __internal_clerkUIUrl,\n __internal_clerkUIVersion,\n }).then(() => {\n if (!window.__internal_ClerkUICtor) {\n throw new Error(\n 'Clerk: Failed to load Clerk UI from the CDN. Ensure your Content Security Policy allows the Clerk Frontend API host in `script-src`. Contact support@clerk.com.',\n );\n }\n return window.__internal_ClerkUICtor;\n });\n\n cachedClerkUI = { promise, publishableKey };\n return promise;\n}\n\nfunction createOAuthTransport(): ClerkOAuthTransport | undefined {\n const bridge = window.__clerk_internal_electron?.oauthTransport;\n\n if (!bridge) {\n return undefined;\n }\n\n return {\n getRedirectUrl: () => bridge.getRedirectUrl(),\n open: url => bridge.open(url.href),\n };\n}\n\n/**\n * Infer the custom renderer scheme registered with `createClerkBridge({ renderer })`.\n * Built-in Clerk protocols and local file renderers are not inferred.\n */\nfunction defaultAllowedRedirectProtocols(): string[] | undefined {\n const protocol = typeof window !== 'undefined' ? window.location?.protocol : undefined;\n\n if (!protocol || ALLOWED_PROTOCOLS.includes(protocol) || protocol === 'file:') {\n return undefined;\n }\n\n return [protocol];\n}\n\nexport function ClerkProvider({\n children,\n publishableKey,\n passkeys,\n allowedRedirectProtocols,\n ...props\n}: ClerkProviderProps): JSX.Element {\n const clerk = createClerkInstance(publishableKey, passkeys);\n const oauthTransport = createOAuthTransport();\n const clerkUI = loadClerkUI(publishableKey, props);\n\n return (\n <ReactClerkProvider\n {...props}\n Clerk={clerk}\n __internal_oauthTransport={oauthTransport}\n allowedRedirectProtocols={allowedRedirectProtocols ?? defaultAllowedRedirectProtocols()}\n publishableKey={publishableKey}\n standardBrowser={false}\n ui={{ ClerkUI: clerkUI }}\n >\n {children}\n </ReactClerkProvider>\n );\n}\n\nexport * from '@clerk/react';\n"],"mappings":";;;;;;;;AAMA,MAAM,uBAAuB;AAI7B,IAAI,SAAqE;AAEzE,SAAS,eAAe,OAAsB,UAAgC;CAC5E,MAAM,qCAAqC,SAAS;CACpD,MAAM,kCAAkC,SAAS;CACjD,MAAM,iCAAiC,SAAS;CAChD,MAAM,yCAAyC,SAAS;CACxD,MAAM,sDAAsD,SAAS;AACvE;AAEA,SAAgB,oBAAoB,gBAAwB,UAA0C;CACpG,IAAI,QAAQ,mBAAmB,gBAAgB;EAC7C,IAAI,UACF,eAAe,OAAO,UAAU,QAAQ;EAE1C,OAAO,OAAO;CAChB;CAEA,MAAM,QAAQ,IAAIA,sBAAM,cAAc;CAEtC,IAAI,UACF,eAAe,OAAO,QAAQ;CAGhC,MAAM,2BAA2B,OAAM,YAAW;EAChD,QAAQ,cAAc;EACtB,QAAQ,KAAK,aAAa,OAAO,cAAc,GAAG;EAClD,QAAQ,KAAK,aAAa,OAAO,wDAAwC;EAEzE,MAAM,QAAQ,MAAM,OAAO,2BAA2B,WAAW,SAAS,oBAAoB;EAC9F,IAAI,OAAO;GACT,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;GAC3C,QAAQ,IAAI,iBAAiB,UAAU,OAAO;GAC9C,QAAQ,UAAU;EACpB;CACF,CAAC;CAED,MAAM,2BAA2B,OAAO,UAAU,aAAa;EAC7D,MAAM,gBAAiB,SAAsB,QAAQ,IAAI,eAAe;EACxE,IAAI,CAAC,eACH;EAGF,MAAM,QAAQ,cAAc,WAAW,SAAS,IAAI,cAAc,MAAM,CAAgB,IAAI;EAC5F,MAAM,OAAO,2BAA2B,WAAW,UAAU,sBAAsB,KAAK;CAC1F,CAAC;CAED,SAAS;EAAE,UAAU;EAAO;CAAe;CAC3C,OAAO;AACT;;;;ACxBA,IAAI,gBAAyF;AAE7F,SAAS,YAAY,gBAAwB,OAAiE;CAC5G,IAAI,eAAe,mBAAmB,gBACpC,OAAO,cAAc;CAIvB,MAAM,EAAE,uBAAuB,8BAA8B;CAK7D,MAAM,iEAA4B;EAChC;EACA,UAAU,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;EAChE,QAAQ,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;EAC1D,OAAO,MAAM;EACb;EACA;CACF,CAAC,CAAC,CAAC,WAAW;EACZ,IAAI,CAAC,OAAO,wBACV,MAAM,IAAI,MACR,iKACF;EAEF,OAAO,OAAO;CAChB,CAAC;CAED,gBAAgB;EAAE;EAAS;CAAe;CAC1C,OAAO;AACT;AAEA,SAAS,uBAAwD;CAC/D,MAAM,SAAS,OAAO,2BAA2B;CAEjD,IAAI,CAAC,QACH;CAGF,OAAO;EACL,sBAAsB,OAAO,eAAe;EAC5C,OAAM,QAAO,OAAO,KAAK,IAAI,IAAI;CACnC;AACF;;;;;AAMA,SAAS,kCAAwD;CAC/D,MAAM,WAAW,OAAO,WAAW,cAAc,OAAO,UAAU,WAAW;CAE7E,IAAI,CAAC,YAAYC,iEAAkB,SAAS,QAAQ,KAAK,aAAa,SACpE;CAGF,OAAO,CAAC,QAAQ;AAClB;AAEA,SAAgB,cAAc,EAC5B,UACA,gBACA,UACA,0BACA,GAAG,SAC+B;CAClC,MAAM,QAAQ,oBAAoB,gBAAgB,QAAQ;CAC1D,MAAM,iBAAiB,qBAAqB;CAC5C,MAAM,UAAU,YAAY,gBAAgB,KAAK;CAEjD,OACE,2CAACC,6CAAD;EACE,GAAI;EACJ,OAAO;EACP,2BAA2B;EAC3B,0BAA0B,4BAA4B,gCAAgC;EACtE;EAChB,iBAAiB;EACjB,IAAI,EAAE,SAAS,QAAQ;EAEtB;CACiB;AAExB"}
package/dist/esm/index.js CHANGED
@@ -1,15 +1,28 @@
1
1
  import { n as PASSKEY_CHANNELS, r as TOKEN_CACHE_CHANNELS, t as OAUTH_TRANSPORT_CHANNELS } from "./ipc-DSk37snY.js";
2
2
  import { BrowserWindow, app, ipcMain, protocol, shell } from "electron";
3
3
 
4
+ //#region src/main/validate-sender.ts
5
+ function isMainFrameEvent(event) {
6
+ const { sender, senderFrame } = event;
7
+ return sender.getType() === "window" && Boolean(senderFrame) && senderFrame === sender.mainFrame;
8
+ }
9
+
10
+ //#endregion
4
11
  //#region src/main/ipc-handlers.ts
12
+ function assertMainFrameSender(event) {
13
+ if (!isMainFrameEvent(event)) throw new Error("Clerk: token-cache request did not originate from a window's main frame.");
14
+ }
5
15
  function setupTokenCacheIpcHandlers(storage) {
6
- ipcMain.handle(TOKEN_CACHE_CHANNELS.getToken, (_event, key) => {
16
+ ipcMain.handle(TOKEN_CACHE_CHANNELS.getToken, (event, key) => {
17
+ assertMainFrameSender(event);
7
18
  return storage.getItem(key);
8
19
  });
9
- ipcMain.handle(TOKEN_CACHE_CHANNELS.saveToken, (_event, key, value) => {
20
+ ipcMain.handle(TOKEN_CACHE_CHANNELS.saveToken, (event, key, value) => {
21
+ assertMainFrameSender(event);
10
22
  return storage.setItem(key, value);
11
23
  });
12
- ipcMain.handle(TOKEN_CACHE_CHANNELS.clearToken, (_event, key) => {
24
+ ipcMain.handle(TOKEN_CACHE_CHANNELS.clearToken, (event, key) => {
25
+ assertMainFrameSender(event);
13
26
  return storage.removeItem(key);
14
27
  });
15
28
  return () => {
@@ -66,10 +79,12 @@ function setupOAuthTransportIpcHandlers(options) {
66
79
  app.setAsDefaultProtocolClient(options.renderer.scheme);
67
80
  app.on("open-url", openUrlListener);
68
81
  app.on("second-instance", secondInstanceListener);
69
- ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.getRedirectUrl, () => {
82
+ ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.getRedirectUrl, (event) => {
83
+ if (!isMainFrameEvent(event)) throw new Error("Clerk: OAuth request did not originate from a window's main frame.");
70
84
  return redirectUrl;
71
85
  });
72
- ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.open, async (_event, url) => {
86
+ ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.open, async (event, url) => {
87
+ if (!isMainFrameEvent(event)) throw new Error("Clerk: OAuth request did not originate from a window's main frame.");
73
88
  if (pendingOAuthFlow) throw new Error("Clerk: an OAuth flow is already pending.");
74
89
  assertExternalOAuthUrl(url);
75
90
  const callbackPromise = new Promise((resolve, reject) => {
@@ -140,7 +155,7 @@ async function invokeNative(method, event, options) {
140
155
  message: "Native passkeys are not supported on this platform."
141
156
  }
142
157
  };
143
- if (!event.senderFrame || event.senderFrame !== event.sender.mainFrame) return {
158
+ if (!isMainFrameEvent(event)) return {
144
159
  ok: false,
145
160
  error: {
146
161
  code: "unknown",
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/main/ipc-handlers.ts","../../src/main/oauth-transport.ts","../../src/main/passkey-handlers.ts","../../src/main/create-clerk-bridge.ts"],"sourcesContent":["import { ipcMain } from 'electron';\n\nimport { TOKEN_CACHE_CHANNELS } from '../shared/ipc';\nimport type { TokenStorage } from '../shared/types';\n\nexport function setupTokenCacheIpcHandlers(storage: TokenStorage): () => void {\n ipcMain.handle(TOKEN_CACHE_CHANNELS.getToken, (_event, key: string) => {\n return storage.getItem(key);\n });\n\n ipcMain.handle(TOKEN_CACHE_CHANNELS.saveToken, (_event, key: string, value: string) => {\n return storage.setItem(key, value);\n });\n\n ipcMain.handle(TOKEN_CACHE_CHANNELS.clearToken, (_event, key: string) => {\n return storage.removeItem(key);\n });\n\n return () => {\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.getToken);\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.saveToken);\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.clearToken);\n };\n}\n","import { app, ipcMain, shell } from 'electron';\n\nimport { OAUTH_TRANSPORT_CHANNELS } from '../shared/ipc';\nimport type { RendererSchemeOptions } from '../shared/types';\n\nconst CALLBACK_TIMEOUT_MS = 3 * 60 * 1000;\n\ntype PendingOAuthFlow = {\n resolve: (value: { callbackUrl: string }) => void;\n reject: (reason: Error) => void;\n timeout: NodeJS.Timeout;\n};\n\ntype OAuthTransportOptions = {\n renderer: RendererSchemeOptions;\n};\n\nfunction buildRedirectUrl(options: OAuthTransportOptions): string {\n return `${options.renderer.scheme}://${options.renderer.host}/`;\n}\n\nfunction isMatchingCallbackUrl(url: string, redirectUrl: string): boolean {\n try {\n const callback = new URL(url);\n const expected = new URL(redirectUrl);\n\n return (\n callback.protocol === expected.protocol &&\n callback.host === expected.host &&\n callback.pathname === expected.pathname\n );\n } catch {\n return false;\n }\n}\n\nfunction assertExternalOAuthUrl(url: string): void {\n const parsedUrl = new URL(url);\n\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n throw new TypeError(`Clerk: refusing to open unsupported OAuth URL protocol: ${parsedUrl.protocol}`);\n }\n}\n\nexport function setupOAuthTransportIpcHandlers(options: OAuthTransportOptions): () => void {\n const redirectUrl = buildRedirectUrl(options);\n let pendingOAuthFlow: PendingOAuthFlow | null = null;\n\n const disposePendingOAuthFlow = (reason?: Error): void => {\n if (!pendingOAuthFlow) {\n return;\n }\n\n const pending = pendingOAuthFlow;\n clearTimeout(pendingOAuthFlow.timeout);\n pendingOAuthFlow = null;\n\n if (reason) {\n pending.reject(reason);\n }\n };\n\n const handleCallbackUrl = (url: string): void => {\n if (!pendingOAuthFlow || !isMatchingCallbackUrl(url, redirectUrl)) {\n return;\n }\n\n const pending = pendingOAuthFlow;\n disposePendingOAuthFlow();\n pending.resolve({ callbackUrl: url });\n };\n\n const openUrlListener = (event: Electron.Event, url: string): void => {\n if (!isMatchingCallbackUrl(url, redirectUrl)) {\n return;\n }\n\n event.preventDefault();\n handleCallbackUrl(url);\n };\n\n const secondInstanceListener = (_event: Electron.Event, argv: string[]): void => {\n const callbackUrl = argv.find(url => isMatchingCallbackUrl(url, redirectUrl));\n\n if (callbackUrl) {\n handleCallbackUrl(callbackUrl);\n }\n };\n\n app.setAsDefaultProtocolClient(options.renderer.scheme);\n app.on('open-url', openUrlListener);\n app.on('second-instance', secondInstanceListener);\n\n ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.getRedirectUrl, () => {\n return redirectUrl;\n });\n\n ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.open, async (_event, url: string) => {\n if (pendingOAuthFlow) {\n throw new Error('Clerk: an OAuth flow is already pending.');\n }\n\n assertExternalOAuthUrl(url);\n\n const callbackPromise = new Promise<{ callbackUrl: string }>((resolve, reject) => {\n const timeout = setTimeout(() => {\n disposePendingOAuthFlow();\n reject(new Error('Clerk: OAuth callback timed out.'));\n }, CALLBACK_TIMEOUT_MS);\n\n pendingOAuthFlow = { resolve, reject, timeout };\n });\n\n try {\n await shell.openExternal(url);\n } catch (err) {\n disposePendingOAuthFlow(err instanceof Error ? err : new Error(String(err)));\n }\n\n return callbackPromise;\n });\n\n return () => {\n disposePendingOAuthFlow(new Error('Clerk: OAuth flow was cancelled.'));\n app.removeListener('open-url', openUrlListener);\n app.removeListener('second-instance', secondInstanceListener);\n ipcMain.removeHandler(OAUTH_TRANSPORT_CHANNELS.getRedirectUrl);\n ipcMain.removeHandler(OAUTH_TRANSPORT_CHANNELS.open);\n };\n}\n","import type { IpcMainInvokeEvent } from 'electron';\nimport { BrowserWindow, ipcMain } from 'electron';\n\nimport { PASSKEY_CHANNELS } from '../shared/ipc';\nimport type {\n AuthenticationResponseJSON,\n PasskeyCapabilities,\n PasskeyIpcResult,\n PasskeyNativeErrorCode,\n RegistrationResponseJSON,\n SerializedPublicKeyCredentialCreationOptions,\n SerializedPublicKeyCredentialRequestOptions,\n SetupPasskeysMainReturn,\n} from '../shared/types';\n\n/**\n * Optional native module. Ceremony failures resolve as JSON envelopes so error\n * codes survive both the FFI and Electron IPC boundaries.\n */\ntype NativePasskeysModule = {\n isAvailable: () => boolean;\n capabilities: () => Omit<PasskeyCapabilities, 'available'>;\n createCredential: (windowHandle: Buffer, optionsJson: string) => Promise<string>;\n getCredential: (windowHandle: Buffer, optionsJson: string) => Promise<string>;\n};\n\nlet nativeModulePromise: Promise<NativePasskeysModule> | undefined;\n\nfunction loadNativeModule(): Promise<NativePasskeysModule> {\n // Keep the native module optional for apps that do not use passkeys.\n nativeModulePromise ??= import('@clerk/electron-passkeys').then(\n (module: { default?: NativePasskeysModule } & NativePasskeysModule) => module.default ?? module,\n error => {\n nativeModulePromise = undefined;\n throw new Error(\n 'Clerk: createClerkBridge({ passkeys: true }) requires the optional @clerk/electron-passkeys package. Install it with your package manager to enable native passkey support.',\n { cause: error },\n );\n },\n );\n return nativeModulePromise;\n}\n\nconst NATIVE_ERROR_CODES: PasskeyNativeErrorCode[] = ['cancelled', 'invalid_rp', 'not_supported', 'timeout', 'unknown'];\n\nfunction isPasskeyIpcResult<T>(value: unknown): value is PasskeyIpcResult<T> {\n if (!value || typeof value !== 'object' || typeof (value as { ok?: unknown }).ok !== 'boolean') {\n return false;\n }\n const result = value as { ok: boolean; credential?: unknown; error?: { code?: unknown } };\n return result.ok\n ? result.credential !== undefined\n : NATIVE_ERROR_CODES.includes(result.error?.code as PasskeyNativeErrorCode);\n}\n\nasync function invokeNative<T>(\n method: 'createCredential' | 'getCredential',\n event: IpcMainInvokeEvent,\n options: SerializedPublicKeyCredentialCreationOptions | SerializedPublicKeyCredentialRequestOptions,\n): Promise<PasskeyIpcResult<T>> {\n let native: NativePasskeysModule;\n try {\n native = await loadNativeModule();\n } catch (error) {\n return {\n ok: false,\n error: { code: 'not_supported', message: error instanceof Error ? error.message : String(error) },\n };\n }\n\n if (!native.isAvailable()) {\n return {\n ok: false,\n error: { code: 'not_supported', message: 'Native passkeys are not supported on this platform.' },\n };\n }\n\n // Subframes and webviews can host third-party content that must not be able\n // to run credential ceremonies for the app's RP ID.\n if (!event.senderFrame || event.senderFrame !== event.sender.mainFrame) {\n return {\n ok: false,\n error: { code: 'unknown', message: \"The passkey request did not originate from a window's main frame.\" },\n };\n }\n\n const window = BrowserWindow.fromWebContents(event.sender);\n if (!window) {\n return {\n ok: false,\n error: { code: 'unknown', message: 'The passkey request did not originate from a visible window.' },\n };\n }\n\n try {\n const resultJson = await native[method](window.getNativeWindowHandle(), JSON.stringify(options));\n const result: unknown = JSON.parse(resultJson);\n if (!isPasskeyIpcResult<T>(result)) {\n return { ok: false, error: { code: 'unknown', message: 'The native module returned an unexpected result.' } };\n }\n return result;\n } catch (error) {\n return { ok: false, error: { code: 'unknown', message: error instanceof Error ? error.message : String(error) } };\n }\n}\n\n/** Registers IPC handlers for native platform WebAuthn. */\nexport function setupPasskeysMain(): SetupPasskeysMainReturn {\n // Surface a missing optional dependency during setup, before the first ceremony.\n loadNativeModule().catch((error: Error) => console.warn(error.message));\n\n ipcMain.handle(\n PASSKEY_CHANNELS.create,\n (\n event,\n options: SerializedPublicKeyCredentialCreationOptions,\n ): Promise<PasskeyIpcResult<RegistrationResponseJSON>> => invokeNative('createCredential', event, options),\n );\n\n ipcMain.handle(\n PASSKEY_CHANNELS.get,\n (\n event,\n options: SerializedPublicKeyCredentialRequestOptions,\n ): Promise<PasskeyIpcResult<AuthenticationResponseJSON>> => invokeNative('getCredential', event, options),\n );\n\n ipcMain.handle(PASSKEY_CHANNELS.capabilities, async (): Promise<PasskeyCapabilities> => {\n try {\n const native = await loadNativeModule();\n if (!native.isAvailable()) {\n return { available: false, platformAuthenticator: false, securityKeys: false };\n }\n return { available: true, ...native.capabilities() };\n } catch {\n return { available: false, platformAuthenticator: false, securityKeys: false };\n }\n });\n\n return {\n cleanup() {\n ipcMain.removeHandler(PASSKEY_CHANNELS.create);\n ipcMain.removeHandler(PASSKEY_CHANNELS.get);\n ipcMain.removeHandler(PASSKEY_CHANNELS.capabilities);\n },\n };\n}\n","import { app, protocol } from 'electron';\n\nimport type { ClerkBridge, CreateClerkBridgeOptions } from '../shared/types';\nimport { setupTokenCacheIpcHandlers } from './ipc-handlers';\nimport { setupOAuthTransportIpcHandlers } from './oauth-transport';\nimport { setupPasskeysMain } from './passkey-handlers';\n\nfunction assertValidRendererOriginConfig(renderer: NonNullable<CreateClerkBridgeOptions['renderer']>): void {\n if (renderer.scheme.includes(':') || renderer.scheme.includes('/')) {\n throw new Error(\n 'Clerk: renderer.scheme must be a scheme name like \"my-app\", not a URL or protocol like \"my-app://\".',\n );\n }\n\n if (renderer.host.includes(':') || renderer.host.includes('/')) {\n throw new Error(\n 'Clerk: renderer.host must be a host name like \"renderer\", not a URL or origin like \"my-app://renderer\".',\n );\n }\n}\n\nfunction buildUserAgentFallback(defaultUserAgent: string, productToken: string): string {\n const platformCommentStart = defaultUserAgent.indexOf('(');\n const platformCommentEnd = defaultUserAgent.indexOf(')', platformCommentStart + 1);\n\n if (platformCommentStart === -1 || platformCommentEnd === -1) {\n return productToken;\n }\n\n const platformComment = defaultUserAgent.slice(platformCommentStart, platformCommentEnd + 1);\n\n // Clerk's session activity parser preserves the platform comment and treats the\n // token after Gecko as the app/browser name.\n return `Mozilla/5.0 ${platformComment} Gecko/20100101 ${productToken}`;\n}\n\n/**\n * Creates the Clerk bridge for Electron's main process.\n *\n * The bridge owns Clerk's main-process IPC handlers, token persistence, and OAuth deep-link\n * transport. Call this before creating renderer windows, and call the returned `cleanup` method\n * when tearing down the app or test environment.\n */\nexport function createClerkBridge(options: CreateClerkBridgeOptions): ClerkBridge {\n if (!options.storage) {\n throw new Error(\n 'Clerk: createClerkBridge requires a storage adapter. Pass createClerkBridge({ storage: storage() }) from @clerk/electron/storage, or provide a custom storage adapter.',\n );\n }\n\n const cleanupTokenPersistence = setupTokenCacheIpcHandlers(options.storage);\n let cleanupOAuthTransport: (() => void) | undefined;\n const passkeys = options.passkeys ? setupPasskeysMain() : null;\n\n if (options.userAgent) {\n app.userAgentFallback = buildUserAgentFallback(app.userAgentFallback, options.userAgent);\n }\n\n if (options.renderer) {\n assertValidRendererOriginConfig(options.renderer);\n\n protocol.registerSchemesAsPrivileged([\n {\n scheme: options.renderer.scheme,\n privileges: {\n standard: true,\n secure: true,\n supportFetchAPI: true,\n corsEnabled: true,\n stream: true,\n ...options.renderer.privileges,\n },\n },\n ]);\n\n cleanupOAuthTransport = setupOAuthTransportIpcHandlers({\n renderer: options.renderer,\n });\n }\n\n return {\n cleanup() {\n cleanupTokenPersistence();\n cleanupOAuthTransport?.();\n passkeys?.cleanup();\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/main/validate-sender.ts","../../src/main/ipc-handlers.ts","../../src/main/oauth-transport.ts","../../src/main/passkey-handlers.ts","../../src/main/create-clerk-bridge.ts"],"sourcesContent":["import type { IpcMainInvokeEvent } from 'electron';\n\n// Restricts privileged IPC to a top-level BrowserWindow's main frame. The frame check alone\n// only proves the sender is the top document of *its own* WebContents, so a <webview> (a separate\n// WebContents) sharing the Clerk preload would still pass; requiring getType() === 'window' rejects\n// <webview> guests and BrowserViews, and the frame check rejects in-page iframes.\nexport function isMainFrameEvent(event: IpcMainInvokeEvent): boolean {\n const { sender, senderFrame } = event;\n return sender.getType() === 'window' && Boolean(senderFrame) && senderFrame === sender.mainFrame;\n}\n","import type { IpcMainInvokeEvent } from 'electron';\nimport { ipcMain } from 'electron';\n\nimport { TOKEN_CACHE_CHANNELS } from '../shared/ipc';\nimport type { TokenStorage } from '../shared/types';\nimport { isMainFrameEvent } from './validate-sender';\n\nfunction assertMainFrameSender(event: IpcMainInvokeEvent): void {\n if (!isMainFrameEvent(event)) {\n throw new Error(\"Clerk: token-cache request did not originate from a window's main frame.\");\n }\n}\n\nexport function setupTokenCacheIpcHandlers(storage: TokenStorage): () => void {\n ipcMain.handle(TOKEN_CACHE_CHANNELS.getToken, (event, key: string) => {\n assertMainFrameSender(event);\n return storage.getItem(key);\n });\n\n ipcMain.handle(TOKEN_CACHE_CHANNELS.saveToken, (event, key: string, value: string) => {\n assertMainFrameSender(event);\n return storage.setItem(key, value);\n });\n\n ipcMain.handle(TOKEN_CACHE_CHANNELS.clearToken, (event, key: string) => {\n assertMainFrameSender(event);\n return storage.removeItem(key);\n });\n\n return () => {\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.getToken);\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.saveToken);\n ipcMain.removeHandler(TOKEN_CACHE_CHANNELS.clearToken);\n };\n}\n","import { app, ipcMain, shell } from 'electron';\n\nimport { OAUTH_TRANSPORT_CHANNELS } from '../shared/ipc';\nimport type { RendererSchemeOptions } from '../shared/types';\nimport { isMainFrameEvent } from './validate-sender';\n\nconst CALLBACK_TIMEOUT_MS = 3 * 60 * 1000;\n\ntype PendingOAuthFlow = {\n resolve: (value: { callbackUrl: string }) => void;\n reject: (reason: Error) => void;\n timeout: NodeJS.Timeout;\n};\n\ntype OAuthTransportOptions = {\n renderer: RendererSchemeOptions;\n};\n\nfunction buildRedirectUrl(options: OAuthTransportOptions): string {\n return `${options.renderer.scheme}://${options.renderer.host}/`;\n}\n\nfunction isMatchingCallbackUrl(url: string, redirectUrl: string): boolean {\n try {\n const callback = new URL(url);\n const expected = new URL(redirectUrl);\n\n return (\n callback.protocol === expected.protocol &&\n callback.host === expected.host &&\n callback.pathname === expected.pathname\n );\n } catch {\n return false;\n }\n}\n\nfunction assertExternalOAuthUrl(url: string): void {\n const parsedUrl = new URL(url);\n\n if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {\n throw new TypeError(`Clerk: refusing to open unsupported OAuth URL protocol: ${parsedUrl.protocol}`);\n }\n}\n\nexport function setupOAuthTransportIpcHandlers(options: OAuthTransportOptions): () => void {\n const redirectUrl = buildRedirectUrl(options);\n let pendingOAuthFlow: PendingOAuthFlow | null = null;\n\n const disposePendingOAuthFlow = (reason?: Error): void => {\n if (!pendingOAuthFlow) {\n return;\n }\n\n const pending = pendingOAuthFlow;\n clearTimeout(pendingOAuthFlow.timeout);\n pendingOAuthFlow = null;\n\n if (reason) {\n pending.reject(reason);\n }\n };\n\n const handleCallbackUrl = (url: string): void => {\n if (!pendingOAuthFlow || !isMatchingCallbackUrl(url, redirectUrl)) {\n return;\n }\n\n const pending = pendingOAuthFlow;\n disposePendingOAuthFlow();\n pending.resolve({ callbackUrl: url });\n };\n\n const openUrlListener = (event: Electron.Event, url: string): void => {\n if (!isMatchingCallbackUrl(url, redirectUrl)) {\n return;\n }\n\n event.preventDefault();\n handleCallbackUrl(url);\n };\n\n const secondInstanceListener = (_event: Electron.Event, argv: string[]): void => {\n const callbackUrl = argv.find(url => isMatchingCallbackUrl(url, redirectUrl));\n\n if (callbackUrl) {\n handleCallbackUrl(callbackUrl);\n }\n };\n\n app.setAsDefaultProtocolClient(options.renderer.scheme);\n app.on('open-url', openUrlListener);\n app.on('second-instance', secondInstanceListener);\n\n ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.getRedirectUrl, event => {\n if (!isMainFrameEvent(event)) {\n throw new Error(\"Clerk: OAuth request did not originate from a window's main frame.\");\n }\n return redirectUrl;\n });\n\n ipcMain.handle(OAUTH_TRANSPORT_CHANNELS.open, async (event, url: string) => {\n if (!isMainFrameEvent(event)) {\n throw new Error(\"Clerk: OAuth request did not originate from a window's main frame.\");\n }\n\n if (pendingOAuthFlow) {\n throw new Error('Clerk: an OAuth flow is already pending.');\n }\n\n assertExternalOAuthUrl(url);\n\n const callbackPromise = new Promise<{ callbackUrl: string }>((resolve, reject) => {\n const timeout = setTimeout(() => {\n disposePendingOAuthFlow();\n reject(new Error('Clerk: OAuth callback timed out.'));\n }, CALLBACK_TIMEOUT_MS);\n\n pendingOAuthFlow = { resolve, reject, timeout };\n });\n\n try {\n await shell.openExternal(url);\n } catch (err) {\n disposePendingOAuthFlow(err instanceof Error ? err : new Error(String(err)));\n }\n\n return callbackPromise;\n });\n\n return () => {\n disposePendingOAuthFlow(new Error('Clerk: OAuth flow was cancelled.'));\n app.removeListener('open-url', openUrlListener);\n app.removeListener('second-instance', secondInstanceListener);\n ipcMain.removeHandler(OAUTH_TRANSPORT_CHANNELS.getRedirectUrl);\n ipcMain.removeHandler(OAUTH_TRANSPORT_CHANNELS.open);\n };\n}\n","import type { IpcMainInvokeEvent } from 'electron';\nimport { BrowserWindow, ipcMain } from 'electron';\n\nimport { PASSKEY_CHANNELS } from '../shared/ipc';\nimport type {\n AuthenticationResponseJSON,\n PasskeyCapabilities,\n PasskeyIpcResult,\n PasskeyNativeErrorCode,\n RegistrationResponseJSON,\n SerializedPublicKeyCredentialCreationOptions,\n SerializedPublicKeyCredentialRequestOptions,\n SetupPasskeysMainReturn,\n} from '../shared/types';\nimport { isMainFrameEvent } from './validate-sender';\n\n/**\n * Optional native module. Ceremony failures resolve as JSON envelopes so error\n * codes survive both the FFI and Electron IPC boundaries.\n */\ntype NativePasskeysModule = {\n isAvailable: () => boolean;\n capabilities: () => Omit<PasskeyCapabilities, 'available'>;\n createCredential: (windowHandle: Buffer, optionsJson: string) => Promise<string>;\n getCredential: (windowHandle: Buffer, optionsJson: string) => Promise<string>;\n};\n\nlet nativeModulePromise: Promise<NativePasskeysModule> | undefined;\n\nfunction loadNativeModule(): Promise<NativePasskeysModule> {\n // Keep the native module optional for apps that do not use passkeys.\n nativeModulePromise ??= import('@clerk/electron-passkeys').then(\n (module: { default?: NativePasskeysModule } & NativePasskeysModule) => module.default ?? module,\n error => {\n nativeModulePromise = undefined;\n throw new Error(\n 'Clerk: createClerkBridge({ passkeys: true }) requires the optional @clerk/electron-passkeys package. Install it with your package manager to enable native passkey support.',\n { cause: error },\n );\n },\n );\n return nativeModulePromise;\n}\n\nconst NATIVE_ERROR_CODES: PasskeyNativeErrorCode[] = ['cancelled', 'invalid_rp', 'not_supported', 'timeout', 'unknown'];\n\nfunction isPasskeyIpcResult<T>(value: unknown): value is PasskeyIpcResult<T> {\n if (!value || typeof value !== 'object' || typeof (value as { ok?: unknown }).ok !== 'boolean') {\n return false;\n }\n const result = value as { ok: boolean; credential?: unknown; error?: { code?: unknown } };\n return result.ok\n ? result.credential !== undefined\n : NATIVE_ERROR_CODES.includes(result.error?.code as PasskeyNativeErrorCode);\n}\n\nasync function invokeNative<T>(\n method: 'createCredential' | 'getCredential',\n event: IpcMainInvokeEvent,\n options: SerializedPublicKeyCredentialCreationOptions | SerializedPublicKeyCredentialRequestOptions,\n): Promise<PasskeyIpcResult<T>> {\n let native: NativePasskeysModule;\n try {\n native = await loadNativeModule();\n } catch (error) {\n return {\n ok: false,\n error: { code: 'not_supported', message: error instanceof Error ? error.message : String(error) },\n };\n }\n\n if (!native.isAvailable()) {\n return {\n ok: false,\n error: { code: 'not_supported', message: 'Native passkeys are not supported on this platform.' },\n };\n }\n\n // Subframes and webviews can host third-party content that must not be able\n // to run credential ceremonies for the app's RP ID.\n if (!isMainFrameEvent(event)) {\n return {\n ok: false,\n error: { code: 'unknown', message: \"The passkey request did not originate from a window's main frame.\" },\n };\n }\n\n const window = BrowserWindow.fromWebContents(event.sender);\n if (!window) {\n return {\n ok: false,\n error: { code: 'unknown', message: 'The passkey request did not originate from a visible window.' },\n };\n }\n\n try {\n const resultJson = await native[method](window.getNativeWindowHandle(), JSON.stringify(options));\n const result: unknown = JSON.parse(resultJson);\n if (!isPasskeyIpcResult<T>(result)) {\n return { ok: false, error: { code: 'unknown', message: 'The native module returned an unexpected result.' } };\n }\n return result;\n } catch (error) {\n return { ok: false, error: { code: 'unknown', message: error instanceof Error ? error.message : String(error) } };\n }\n}\n\n/** Registers IPC handlers for native platform WebAuthn. */\nexport function setupPasskeysMain(): SetupPasskeysMainReturn {\n // Surface a missing optional dependency during setup, before the first ceremony.\n loadNativeModule().catch((error: Error) => console.warn(error.message));\n\n ipcMain.handle(\n PASSKEY_CHANNELS.create,\n (\n event,\n options: SerializedPublicKeyCredentialCreationOptions,\n ): Promise<PasskeyIpcResult<RegistrationResponseJSON>> => invokeNative('createCredential', event, options),\n );\n\n ipcMain.handle(\n PASSKEY_CHANNELS.get,\n (\n event,\n options: SerializedPublicKeyCredentialRequestOptions,\n ): Promise<PasskeyIpcResult<AuthenticationResponseJSON>> => invokeNative('getCredential', event, options),\n );\n\n ipcMain.handle(PASSKEY_CHANNELS.capabilities, async (): Promise<PasskeyCapabilities> => {\n try {\n const native = await loadNativeModule();\n if (!native.isAvailable()) {\n return { available: false, platformAuthenticator: false, securityKeys: false };\n }\n return { available: true, ...native.capabilities() };\n } catch {\n return { available: false, platformAuthenticator: false, securityKeys: false };\n }\n });\n\n return {\n cleanup() {\n ipcMain.removeHandler(PASSKEY_CHANNELS.create);\n ipcMain.removeHandler(PASSKEY_CHANNELS.get);\n ipcMain.removeHandler(PASSKEY_CHANNELS.capabilities);\n },\n };\n}\n","import { app, protocol } from 'electron';\n\nimport type { ClerkBridge, CreateClerkBridgeOptions } from '../shared/types';\nimport { setupTokenCacheIpcHandlers } from './ipc-handlers';\nimport { setupOAuthTransportIpcHandlers } from './oauth-transport';\nimport { setupPasskeysMain } from './passkey-handlers';\n\nfunction assertValidRendererOriginConfig(renderer: NonNullable<CreateClerkBridgeOptions['renderer']>): void {\n if (renderer.scheme.includes(':') || renderer.scheme.includes('/')) {\n throw new Error(\n 'Clerk: renderer.scheme must be a scheme name like \"my-app\", not a URL or protocol like \"my-app://\".',\n );\n }\n\n if (renderer.host.includes(':') || renderer.host.includes('/')) {\n throw new Error(\n 'Clerk: renderer.host must be a host name like \"renderer\", not a URL or origin like \"my-app://renderer\".',\n );\n }\n}\n\nfunction buildUserAgentFallback(defaultUserAgent: string, productToken: string): string {\n const platformCommentStart = defaultUserAgent.indexOf('(');\n const platformCommentEnd = defaultUserAgent.indexOf(')', platformCommentStart + 1);\n\n if (platformCommentStart === -1 || platformCommentEnd === -1) {\n return productToken;\n }\n\n const platformComment = defaultUserAgent.slice(platformCommentStart, platformCommentEnd + 1);\n\n // Clerk's session activity parser preserves the platform comment and treats the\n // token after Gecko as the app/browser name.\n return `Mozilla/5.0 ${platformComment} Gecko/20100101 ${productToken}`;\n}\n\n/**\n * Creates the Clerk bridge for Electron's main process.\n *\n * The bridge owns Clerk's main-process IPC handlers, token persistence, and OAuth deep-link\n * transport. Call this before creating renderer windows, and call the returned `cleanup` method\n * when tearing down the app or test environment.\n */\nexport function createClerkBridge(options: CreateClerkBridgeOptions): ClerkBridge {\n if (!options.storage) {\n throw new Error(\n 'Clerk: createClerkBridge requires a storage adapter. Pass createClerkBridge({ storage: storage() }) from @clerk/electron/storage, or provide a custom storage adapter.',\n );\n }\n\n const cleanupTokenPersistence = setupTokenCacheIpcHandlers(options.storage);\n let cleanupOAuthTransport: (() => void) | undefined;\n const passkeys = options.passkeys ? setupPasskeysMain() : null;\n\n if (options.userAgent) {\n app.userAgentFallback = buildUserAgentFallback(app.userAgentFallback, options.userAgent);\n }\n\n if (options.renderer) {\n assertValidRendererOriginConfig(options.renderer);\n\n protocol.registerSchemesAsPrivileged([\n {\n scheme: options.renderer.scheme,\n privileges: {\n standard: true,\n secure: true,\n supportFetchAPI: true,\n corsEnabled: true,\n stream: true,\n ...options.renderer.privileges,\n },\n },\n ]);\n\n cleanupOAuthTransport = setupOAuthTransportIpcHandlers({\n renderer: options.renderer,\n });\n }\n\n return {\n cleanup() {\n cleanupTokenPersistence();\n cleanupOAuthTransport?.();\n passkeys?.cleanup();\n },\n };\n}\n"],"mappings":";;;;AAMA,SAAgB,iBAAiB,OAAoC;CACnE,MAAM,EAAE,QAAQ,gBAAgB;CAChC,OAAO,OAAO,QAAQ,MAAM,YAAY,QAAQ,WAAW,KAAK,gBAAgB,OAAO;AACzF;;;;ACFA,SAAS,sBAAsB,OAAiC;CAC9D,IAAI,CAAC,iBAAiB,KAAK,GACzB,MAAM,IAAI,MAAM,0EAA0E;AAE9F;AAEA,SAAgB,2BAA2B,SAAmC;CAC5E,QAAQ,OAAO,qBAAqB,WAAW,OAAO,QAAgB;EACpE,sBAAsB,KAAK;EAC3B,OAAO,QAAQ,QAAQ,GAAG;CAC5B,CAAC;CAED,QAAQ,OAAO,qBAAqB,YAAY,OAAO,KAAa,UAAkB;EACpF,sBAAsB,KAAK;EAC3B,OAAO,QAAQ,QAAQ,KAAK,KAAK;CACnC,CAAC;CAED,QAAQ,OAAO,qBAAqB,aAAa,OAAO,QAAgB;EACtE,sBAAsB,KAAK;EAC3B,OAAO,QAAQ,WAAW,GAAG;CAC/B,CAAC;CAED,aAAa;EACX,QAAQ,cAAc,qBAAqB,QAAQ;EACnD,QAAQ,cAAc,qBAAqB,SAAS;EACpD,QAAQ,cAAc,qBAAqB,UAAU;CACvD;AACF;;;;AC5BA,MAAM,sBAAsB,MAAS;AAYrC,SAAS,iBAAiB,SAAwC;CAChE,OAAO,GAAG,QAAQ,SAAS,OAAO,KAAK,QAAQ,SAAS,KAAK;AAC/D;AAEA,SAAS,sBAAsB,KAAa,aAA8B;CACxE,IAAI;EACF,MAAM,WAAW,IAAI,IAAI,GAAG;EAC5B,MAAM,WAAW,IAAI,IAAI,WAAW;EAEpC,OACE,SAAS,aAAa,SAAS,YAC/B,SAAS,SAAS,SAAS,QAC3B,SAAS,aAAa,SAAS;CAEnC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,uBAAuB,KAAmB;CACjD,MAAM,YAAY,IAAI,IAAI,GAAG;CAE7B,IAAI,UAAU,aAAa,WAAW,UAAU,aAAa,UAC3D,MAAM,IAAI,UAAU,2DAA2D,UAAU,UAAU;AAEvG;AAEA,SAAgB,+BAA+B,SAA4C;CACzF,MAAM,cAAc,iBAAiB,OAAO;CAC5C,IAAI,mBAA4C;CAEhD,MAAM,2BAA2B,WAAyB;EACxD,IAAI,CAAC,kBACH;EAGF,MAAM,UAAU;EAChB,aAAa,iBAAiB,OAAO;EACrC,mBAAmB;EAEnB,IAAI,QACF,QAAQ,OAAO,MAAM;CAEzB;CAEA,MAAM,qBAAqB,QAAsB;EAC/C,IAAI,CAAC,oBAAoB,CAAC,sBAAsB,KAAK,WAAW,GAC9D;EAGF,MAAM,UAAU;EAChB,wBAAwB;EACxB,QAAQ,QAAQ,EAAE,aAAa,IAAI,CAAC;CACtC;CAEA,MAAM,mBAAmB,OAAuB,QAAsB;EACpE,IAAI,CAAC,sBAAsB,KAAK,WAAW,GACzC;EAGF,MAAM,eAAe;EACrB,kBAAkB,GAAG;CACvB;CAEA,MAAM,0BAA0B,QAAwB,SAAyB;EAC/E,MAAM,cAAc,KAAK,MAAK,QAAO,sBAAsB,KAAK,WAAW,CAAC;EAE5E,IAAI,aACF,kBAAkB,WAAW;CAEjC;CAEA,IAAI,2BAA2B,QAAQ,SAAS,MAAM;CACtD,IAAI,GAAG,YAAY,eAAe;CAClC,IAAI,GAAG,mBAAmB,sBAAsB;CAEhD,QAAQ,OAAO,yBAAyB,iBAAgB,UAAS;EAC/D,IAAI,CAAC,iBAAiB,KAAK,GACzB,MAAM,IAAI,MAAM,oEAAoE;EAEtF,OAAO;CACT,CAAC;CAED,QAAQ,OAAO,yBAAyB,MAAM,OAAO,OAAO,QAAgB;EAC1E,IAAI,CAAC,iBAAiB,KAAK,GACzB,MAAM,IAAI,MAAM,oEAAoE;EAGtF,IAAI,kBACF,MAAM,IAAI,MAAM,0CAA0C;EAG5D,uBAAuB,GAAG;EAE1B,MAAM,kBAAkB,IAAI,SAAkC,SAAS,WAAW;GAMhF,mBAAmB;IAAE;IAAS;IAAQ,SALtB,iBAAiB;KAC/B,wBAAwB;KACxB,uBAAO,IAAI,MAAM,kCAAkC,CAAC;IACtD,GAAG,mBAEyC;GAAE;EAChD,CAAC;EAED,IAAI;GACF,MAAM,MAAM,aAAa,GAAG;EAC9B,SAAS,KAAK;GACZ,wBAAwB,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;EAC7E;EAEA,OAAO;CACT,CAAC;CAED,aAAa;EACX,wCAAwB,IAAI,MAAM,kCAAkC,CAAC;EACrE,IAAI,eAAe,YAAY,eAAe;EAC9C,IAAI,eAAe,mBAAmB,sBAAsB;EAC5D,QAAQ,cAAc,yBAAyB,cAAc;EAC7D,QAAQ,cAAc,yBAAyB,IAAI;CACrD;AACF;;;;AC9GA,IAAI;AAEJ,SAAS,mBAAkD;CAEzD,wBAAwB,OAAO,2BAA2B,CAAC,MACxD,WAAsE,OAAO,WAAW,SACzF,UAAS;EACP,sBAAsB;EACtB,MAAM,IAAI,MACR,+KACA,EAAE,OAAO,MAAM,CACjB;CACF,CACF;CACA,OAAO;AACT;AAEA,MAAM,qBAA+C;CAAC;CAAa;CAAc;CAAiB;CAAW;AAAS;AAEtH,SAAS,mBAAsB,OAA8C;CAC3E,IAAI,CAAC,SAAS,OAAO,UAAU,YAAY,OAAQ,MAA2B,OAAO,WACnF,OAAO;CAET,MAAM,SAAS;CACf,OAAO,OAAO,KACV,OAAO,eAAe,SACtB,mBAAmB,SAAS,OAAO,OAAO,IAA8B;AAC9E;AAEA,eAAe,aACb,QACA,OACA,SAC8B;CAC9B,IAAI;CACJ,IAAI;EACF,SAAS,MAAM,iBAAiB;CAClC,SAAS,OAAO;EACd,OAAO;GACL,IAAI;GACJ,OAAO;IAAE,MAAM;IAAiB,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAAE;EAClG;CACF;CAEA,IAAI,CAAC,OAAO,YAAY,GACtB,OAAO;EACL,IAAI;EACJ,OAAO;GAAE,MAAM;GAAiB,SAAS;EAAsD;CACjG;CAKF,IAAI,CAAC,iBAAiB,KAAK,GACzB,OAAO;EACL,IAAI;EACJ,OAAO;GAAE,MAAM;GAAW,SAAS;EAAoE;CACzG;CAGF,MAAM,SAAS,cAAc,gBAAgB,MAAM,MAAM;CACzD,IAAI,CAAC,QACH,OAAO;EACL,IAAI;EACJ,OAAO;GAAE,MAAM;GAAW,SAAS;EAA+D;CACpG;CAGF,IAAI;EACF,MAAM,aAAa,MAAM,OAAO,OAAO,CAAC,OAAO,sBAAsB,GAAG,KAAK,UAAU,OAAO,CAAC;EAC/F,MAAM,SAAkB,KAAK,MAAM,UAAU;EAC7C,IAAI,CAAC,mBAAsB,MAAM,GAC/B,OAAO;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAW,SAAS;GAAmD;EAAE;EAE9G,OAAO;CACT,SAAS,OAAO;EACd,OAAO;GAAE,IAAI;GAAO,OAAO;IAAE,MAAM;IAAW,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;GAAE;EAAE;CAClH;AACF;;AAGA,SAAgB,oBAA6C;CAE3D,iBAAiB,CAAC,CAAC,OAAO,UAAiB,QAAQ,KAAK,MAAM,OAAO,CAAC;CAEtE,QAAQ,OACN,iBAAiB,SAEf,OACA,YACwD,aAAa,oBAAoB,OAAO,OAAO,CAC3G;CAEA,QAAQ,OACN,iBAAiB,MAEf,OACA,YAC0D,aAAa,iBAAiB,OAAO,OAAO,CAC1G;CAEA,QAAQ,OAAO,iBAAiB,cAAc,YAA0C;EACtF,IAAI;GACF,MAAM,SAAS,MAAM,iBAAiB;GACtC,IAAI,CAAC,OAAO,YAAY,GACtB,OAAO;IAAE,WAAW;IAAO,uBAAuB;IAAO,cAAc;GAAM;GAE/E,OAAO;IAAE,WAAW;IAAM,GAAG,OAAO,aAAa;GAAE;EACrD,QAAQ;GACN,OAAO;IAAE,WAAW;IAAO,uBAAuB;IAAO,cAAc;GAAM;EAC/E;CACF,CAAC;CAED,OAAO,EACL,UAAU;EACR,QAAQ,cAAc,iBAAiB,MAAM;EAC7C,QAAQ,cAAc,iBAAiB,GAAG;EAC1C,QAAQ,cAAc,iBAAiB,YAAY;CACrD,EACF;AACF;;;;AC5IA,SAAS,gCAAgC,UAAmE;CAC1G,IAAI,SAAS,OAAO,SAAS,GAAG,KAAK,SAAS,OAAO,SAAS,GAAG,GAC/D,MAAM,IAAI,MACR,yGACF;CAGF,IAAI,SAAS,KAAK,SAAS,GAAG,KAAK,SAAS,KAAK,SAAS,GAAG,GAC3D,MAAM,IAAI,MACR,6GACF;AAEJ;AAEA,SAAS,uBAAuB,kBAA0B,cAA8B;CACtF,MAAM,uBAAuB,iBAAiB,QAAQ,GAAG;CACzD,MAAM,qBAAqB,iBAAiB,QAAQ,KAAK,uBAAuB,CAAC;CAEjF,IAAI,yBAAyB,MAAM,uBAAuB,IACxD,OAAO;CAOT,OAAO,eAJiB,iBAAiB,MAAM,sBAAsB,qBAAqB,CAItD,EAAE,kBAAkB;AAC1D;;;;;;;;AASA,SAAgB,kBAAkB,SAAgD;CAChF,IAAI,CAAC,QAAQ,SACX,MAAM,IAAI,MACR,wKACF;CAGF,MAAM,0BAA0B,2BAA2B,QAAQ,OAAO;CAC1E,IAAI;CACJ,MAAM,WAAW,QAAQ,WAAW,kBAAkB,IAAI;CAE1D,IAAI,QAAQ,WACV,IAAI,oBAAoB,uBAAuB,IAAI,mBAAmB,QAAQ,SAAS;CAGzF,IAAI,QAAQ,UAAU;EACpB,gCAAgC,QAAQ,QAAQ;EAEhD,SAAS,4BAA4B,CACnC;GACE,QAAQ,QAAQ,SAAS;GACzB,YAAY;IACV,UAAU;IACV,QAAQ;IACR,iBAAiB;IACjB,aAAa;IACb,QAAQ;IACR,GAAG,QAAQ,SAAS;GACtB;EACF,CACF,CAAC;EAED,wBAAwB,+BAA+B,EACrD,UAAU,QAAQ,SACpB,CAAC;CACH;CAEA,OAAO,EACL,UAAU;EACR,wBAAwB;EACxB,wBAAwB;EACxB,UAAU,QAAQ;CACpB,EACF;AACF"}
@@ -26,7 +26,7 @@ function createClerkInstance(publishableKey, passkeys) {
26
26
  clerk.__internal_onBeforeRequest(async (request) => {
27
27
  request.credentials = "omit";
28
28
  request.url?.searchParams.append("_is_native", "1");
29
- request.url?.searchParams.append("_electron_sdk_version", "0.0.15");
29
+ request.url?.searchParams.append("_electron_sdk_version", "0.0.16-canary.v20260717065019");
30
30
  const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);
31
31
  if (token) {
32
32
  const headers = new Headers(request.headers);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["ReactClerkProvider"],"sources":["../../../src/react/create-clerk-instance.ts","../../../src/react/index.tsx"],"sourcesContent":["import { Clerk } from '@clerk/clerk-js';\n\n// Type-only import: the passkeys module is only bundled when the app imports\n// `@clerk/electron/passkeys` itself.\nimport type { PasskeySupport } from '../passkeys';\n\nconst CLERK_CLIENT_JWT_KEY = '__clerk_client_jwt';\n\ntype ClerkInstance = InstanceType<typeof Clerk>;\n\nlet cached: { instance: ClerkInstance; publishableKey: string } | null = null;\n\nfunction attachPasskeys(clerk: ClerkInstance, passkeys: PasskeySupport): void {\n clerk.__internal_createPublicCredentials = passkeys.create;\n clerk.__internal_getPublicCredentials = passkeys.get;\n clerk.__internal_isWebAuthnSupported = passkeys.isSupported;\n clerk.__internal_isWebAuthnAutofillSupported = passkeys.isAutoFillSupported;\n clerk.__internal_isWebAuthnPlatformAuthenticatorSupported = passkeys.isPlatformAuthenticatorSupported;\n}\n\nexport function createClerkInstance(publishableKey: string, passkeys?: PasskeySupport): ClerkInstance {\n if (cached?.publishableKey === publishableKey) {\n if (passkeys) {\n attachPasskeys(cached.instance, passkeys);\n }\n return cached.instance;\n }\n\n const clerk = new Clerk(publishableKey);\n\n if (passkeys) {\n attachPasskeys(clerk, passkeys);\n }\n\n clerk.__internal_onBeforeRequest(async request => {\n request.credentials = 'omit';\n request.url?.searchParams.append('_is_native', '1');\n request.url?.searchParams.append('_electron_sdk_version', PACKAGE_VERSION);\n\n const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);\n if (token) {\n const headers = new Headers(request.headers);\n headers.set('Authorization', `Bearer ${token}`);\n request.headers = headers;\n }\n });\n\n clerk.__internal_onAfterResponse(async (_request, response) => {\n const authorization = (response as Response).headers.get('Authorization');\n if (!authorization) {\n return;\n }\n\n const token = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : authorization;\n await window.__clerk_internal_electron?.tokenCache.saveToken(CLERK_CLIENT_JWT_KEY, token);\n });\n\n cached = { instance: clerk, publishableKey };\n return clerk;\n}\n","import type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';\nimport { InternalClerkProvider as ReactClerkProvider } from '@clerk/react/internal';\nimport { ALLOWED_PROTOCOLS } from '@clerk/shared/internal/clerk-js/windowNavigate';\nimport { loadClerkUIScript } from '@clerk/shared/loadClerkJsScript';\nimport type { ClerkUIConstructor } from '@clerk/shared/ui';\nimport type { ReactNode } from 'react';\n\nimport type { PasskeySupport } from '../passkeys';\nimport { createClerkInstance } from './create-clerk-instance';\n\ntype ClerkOAuthTransport = NonNullable<ReactClerkProviderProps['__internal_oauthTransport']>;\n\nexport type ClerkProviderProps = Omit<\n ReactClerkProviderProps,\n 'Clerk' | 'children' | 'publishableKey' | 'standardBrowser' | 'ui' | '__internal_oauthTransport'\n> & {\n children: ReactNode;\n /**\n * Your Clerk publishable key, available in the Clerk Dashboard.\n */\n publishableKey: string;\n /**\n * Enables passkey support. Pass the `passkeys` export from `@clerk/electron/passkeys`;\n * when omitted, no passkey code is bundled or initialized.\n *\n * @example\n * ```tsx\n * import { passkeys } from '@clerk/electron/passkeys';\n *\n * <ClerkProvider publishableKey={publishableKey} passkeys={passkeys} />\n * ```\n */\n passkeys?: PasskeySupport;\n};\n\nlet cachedClerkUI: { promise: Promise<ClerkUIConstructor>; publishableKey: string } | null = null;\n\nfunction loadClerkUI(publishableKey: string, props: Partial<ClerkProviderProps>): Promise<ClerkUIConstructor> {\n if (cachedClerkUI?.publishableKey === publishableKey) {\n return cachedClerkUI.promise;\n }\n\n // Undocumented escape hatch for self-hosting/proxying the UI bundle; not part of the public props.\n const { __internal_clerkUIUrl, __internal_clerkUIVersion } = props as {\n __internal_clerkUIUrl?: string;\n __internal_clerkUIVersion?: string;\n };\n\n const promise = loadClerkUIScript({\n publishableKey,\n proxyUrl: typeof props.proxyUrl === 'string' ? props.proxyUrl : undefined,\n domain: typeof props.domain === 'string' ? props.domain : undefined,\n nonce: props.nonce,\n __internal_clerkUIUrl,\n __internal_clerkUIVersion,\n }).then(() => {\n if (!window.__internal_ClerkUICtor) {\n throw new Error(\n 'Clerk: Failed to load Clerk UI from the CDN. Ensure your Content Security Policy allows the Clerk Frontend API host in `script-src`. Contact support@clerk.com.',\n );\n }\n return window.__internal_ClerkUICtor;\n });\n\n cachedClerkUI = { promise, publishableKey };\n return promise;\n}\n\nfunction createOAuthTransport(): ClerkOAuthTransport | undefined {\n const bridge = window.__clerk_internal_electron?.oauthTransport;\n\n if (!bridge) {\n return undefined;\n }\n\n return {\n getRedirectUrl: () => bridge.getRedirectUrl(),\n open: url => bridge.open(url.href),\n };\n}\n\n/**\n * Infer the custom renderer scheme registered with `createClerkBridge({ renderer })`.\n * Built-in Clerk protocols and local file renderers are not inferred.\n */\nfunction defaultAllowedRedirectProtocols(): string[] | undefined {\n const protocol = typeof window !== 'undefined' ? window.location?.protocol : undefined;\n\n if (!protocol || ALLOWED_PROTOCOLS.includes(protocol) || protocol === 'file:') {\n return undefined;\n }\n\n return [protocol];\n}\n\nexport function ClerkProvider({\n children,\n publishableKey,\n passkeys,\n allowedRedirectProtocols,\n ...props\n}: ClerkProviderProps): JSX.Element {\n const clerk = createClerkInstance(publishableKey, passkeys);\n const oauthTransport = createOAuthTransport();\n const clerkUI = loadClerkUI(publishableKey, props);\n\n return (\n <ReactClerkProvider\n {...props}\n Clerk={clerk}\n __internal_oauthTransport={oauthTransport}\n allowedRedirectProtocols={allowedRedirectProtocols ?? defaultAllowedRedirectProtocols()}\n publishableKey={publishableKey}\n standardBrowser={false}\n ui={{ ClerkUI: clerkUI }}\n >\n {children}\n </ReactClerkProvider>\n );\n}\n\nexport * from '@clerk/react';\n"],"mappings":";;;;;;;;;AAMA,MAAM,uBAAuB;AAI7B,IAAI,SAAqE;AAEzE,SAAS,eAAe,OAAsB,UAAgC;CAC5E,MAAM,qCAAqC,SAAS;CACpD,MAAM,kCAAkC,SAAS;CACjD,MAAM,iCAAiC,SAAS;CAChD,MAAM,yCAAyC,SAAS;CACxD,MAAM,sDAAsD,SAAS;AACvE;AAEA,SAAgB,oBAAoB,gBAAwB,UAA0C;CACpG,IAAI,QAAQ,mBAAmB,gBAAgB;EAC7C,IAAI,UACF,eAAe,OAAO,UAAU,QAAQ;EAE1C,OAAO,OAAO;CAChB;CAEA,MAAM,QAAQ,IAAI,MAAM,cAAc;CAEtC,IAAI,UACF,eAAe,OAAO,QAAQ;CAGhC,MAAM,2BAA2B,OAAM,YAAW;EAChD,QAAQ,cAAc;EACtB,QAAQ,KAAK,aAAa,OAAO,cAAc,GAAG;EAClD,QAAQ,KAAK,aAAa,OAAO,iCAAwC;EAEzE,MAAM,QAAQ,MAAM,OAAO,2BAA2B,WAAW,SAAS,oBAAoB;EAC9F,IAAI,OAAO;GACT,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;GAC3C,QAAQ,IAAI,iBAAiB,UAAU,OAAO;GAC9C,QAAQ,UAAU;EACpB;CACF,CAAC;CAED,MAAM,2BAA2B,OAAO,UAAU,aAAa;EAC7D,MAAM,gBAAiB,SAAsB,QAAQ,IAAI,eAAe;EACxE,IAAI,CAAC,eACH;EAGF,MAAM,QAAQ,cAAc,WAAW,SAAS,IAAI,cAAc,MAAM,CAAgB,IAAI;EAC5F,MAAM,OAAO,2BAA2B,WAAW,UAAU,sBAAsB,KAAK;CAC1F,CAAC;CAED,SAAS;EAAE,UAAU;EAAO;CAAe;CAC3C,OAAO;AACT;;;;ACxBA,IAAI,gBAAyF;AAE7F,SAAS,YAAY,gBAAwB,OAAiE;CAC5G,IAAI,eAAe,mBAAmB,gBACpC,OAAO,cAAc;CAIvB,MAAM,EAAE,uBAAuB,8BAA8B;CAK7D,MAAM,UAAU,kBAAkB;EAChC;EACA,UAAU,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;EAChE,QAAQ,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;EAC1D,OAAO,MAAM;EACb;EACA;CACF,CAAC,CAAC,CAAC,WAAW;EACZ,IAAI,CAAC,OAAO,wBACV,MAAM,IAAI,MACR,iKACF;EAEF,OAAO,OAAO;CAChB,CAAC;CAED,gBAAgB;EAAE;EAAS;CAAe;CAC1C,OAAO;AACT;AAEA,SAAS,uBAAwD;CAC/D,MAAM,SAAS,OAAO,2BAA2B;CAEjD,IAAI,CAAC,QACH;CAGF,OAAO;EACL,sBAAsB,OAAO,eAAe;EAC5C,OAAM,QAAO,OAAO,KAAK,IAAI,IAAI;CACnC;AACF;;;;;AAMA,SAAS,kCAAwD;CAC/D,MAAM,WAAW,OAAO,WAAW,cAAc,OAAO,UAAU,WAAW;CAE7E,IAAI,CAAC,YAAY,kBAAkB,SAAS,QAAQ,KAAK,aAAa,SACpE;CAGF,OAAO,CAAC,QAAQ;AAClB;AAEA,SAAgB,cAAc,EAC5B,UACA,gBACA,UACA,0BACA,GAAG,SAC+B;CAClC,MAAM,QAAQ,oBAAoB,gBAAgB,QAAQ;CAC1D,MAAM,iBAAiB,qBAAqB;CAC5C,MAAM,UAAU,YAAY,gBAAgB,KAAK;CAEjD,OACE,oBAACA,uBAAD;EACE,GAAI;EACJ,OAAO;EACP,2BAA2B;EAC3B,0BAA0B,4BAA4B,gCAAgC;EACtE;EAChB,iBAAiB;EACjB,IAAI,EAAE,SAAS,QAAQ;EAEtB;CACiB;AAExB"}
1
+ {"version":3,"file":"index.js","names":["ReactClerkProvider"],"sources":["../../../src/react/create-clerk-instance.ts","../../../src/react/index.tsx"],"sourcesContent":["import { Clerk } from '@clerk/clerk-js';\n\n// Type-only import: the passkeys module is only bundled when the app imports\n// `@clerk/electron/passkeys` itself.\nimport type { PasskeySupport } from '../passkeys';\n\nconst CLERK_CLIENT_JWT_KEY = '__clerk_client_jwt';\n\ntype ClerkInstance = InstanceType<typeof Clerk>;\n\nlet cached: { instance: ClerkInstance; publishableKey: string } | null = null;\n\nfunction attachPasskeys(clerk: ClerkInstance, passkeys: PasskeySupport): void {\n clerk.__internal_createPublicCredentials = passkeys.create;\n clerk.__internal_getPublicCredentials = passkeys.get;\n clerk.__internal_isWebAuthnSupported = passkeys.isSupported;\n clerk.__internal_isWebAuthnAutofillSupported = passkeys.isAutoFillSupported;\n clerk.__internal_isWebAuthnPlatformAuthenticatorSupported = passkeys.isPlatformAuthenticatorSupported;\n}\n\nexport function createClerkInstance(publishableKey: string, passkeys?: PasskeySupport): ClerkInstance {\n if (cached?.publishableKey === publishableKey) {\n if (passkeys) {\n attachPasskeys(cached.instance, passkeys);\n }\n return cached.instance;\n }\n\n const clerk = new Clerk(publishableKey);\n\n if (passkeys) {\n attachPasskeys(clerk, passkeys);\n }\n\n clerk.__internal_onBeforeRequest(async request => {\n request.credentials = 'omit';\n request.url?.searchParams.append('_is_native', '1');\n request.url?.searchParams.append('_electron_sdk_version', PACKAGE_VERSION);\n\n const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);\n if (token) {\n const headers = new Headers(request.headers);\n headers.set('Authorization', `Bearer ${token}`);\n request.headers = headers;\n }\n });\n\n clerk.__internal_onAfterResponse(async (_request, response) => {\n const authorization = (response as Response).headers.get('Authorization');\n if (!authorization) {\n return;\n }\n\n const token = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : authorization;\n await window.__clerk_internal_electron?.tokenCache.saveToken(CLERK_CLIENT_JWT_KEY, token);\n });\n\n cached = { instance: clerk, publishableKey };\n return clerk;\n}\n","import type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';\nimport { InternalClerkProvider as ReactClerkProvider } from '@clerk/react/internal';\nimport { ALLOWED_PROTOCOLS } from '@clerk/shared/internal/clerk-js/windowNavigate';\nimport { loadClerkUIScript } from '@clerk/shared/loadClerkJsScript';\nimport type { ClerkUIConstructor } from '@clerk/shared/ui';\nimport type { ReactNode } from 'react';\n\nimport type { PasskeySupport } from '../passkeys';\nimport { createClerkInstance } from './create-clerk-instance';\n\ntype ClerkOAuthTransport = NonNullable<ReactClerkProviderProps['__internal_oauthTransport']>;\n\nexport type ClerkProviderProps = Omit<\n ReactClerkProviderProps,\n 'Clerk' | 'children' | 'publishableKey' | 'standardBrowser' | 'ui' | '__internal_oauthTransport'\n> & {\n children: ReactNode;\n /**\n * Your Clerk publishable key, available in the Clerk Dashboard.\n */\n publishableKey: string;\n /**\n * Enables passkey support. Pass the `passkeys` export from `@clerk/electron/passkeys`;\n * when omitted, no passkey code is bundled or initialized.\n *\n * @example\n * ```tsx\n * import { passkeys } from '@clerk/electron/passkeys';\n *\n * <ClerkProvider publishableKey={publishableKey} passkeys={passkeys} />\n * ```\n */\n passkeys?: PasskeySupport;\n};\n\nlet cachedClerkUI: { promise: Promise<ClerkUIConstructor>; publishableKey: string } | null = null;\n\nfunction loadClerkUI(publishableKey: string, props: Partial<ClerkProviderProps>): Promise<ClerkUIConstructor> {\n if (cachedClerkUI?.publishableKey === publishableKey) {\n return cachedClerkUI.promise;\n }\n\n // Undocumented escape hatch for self-hosting/proxying the UI bundle; not part of the public props.\n const { __internal_clerkUIUrl, __internal_clerkUIVersion } = props as {\n __internal_clerkUIUrl?: string;\n __internal_clerkUIVersion?: string;\n };\n\n const promise = loadClerkUIScript({\n publishableKey,\n proxyUrl: typeof props.proxyUrl === 'string' ? props.proxyUrl : undefined,\n domain: typeof props.domain === 'string' ? props.domain : undefined,\n nonce: props.nonce,\n __internal_clerkUIUrl,\n __internal_clerkUIVersion,\n }).then(() => {\n if (!window.__internal_ClerkUICtor) {\n throw new Error(\n 'Clerk: Failed to load Clerk UI from the CDN. Ensure your Content Security Policy allows the Clerk Frontend API host in `script-src`. Contact support@clerk.com.',\n );\n }\n return window.__internal_ClerkUICtor;\n });\n\n cachedClerkUI = { promise, publishableKey };\n return promise;\n}\n\nfunction createOAuthTransport(): ClerkOAuthTransport | undefined {\n const bridge = window.__clerk_internal_electron?.oauthTransport;\n\n if (!bridge) {\n return undefined;\n }\n\n return {\n getRedirectUrl: () => bridge.getRedirectUrl(),\n open: url => bridge.open(url.href),\n };\n}\n\n/**\n * Infer the custom renderer scheme registered with `createClerkBridge({ renderer })`.\n * Built-in Clerk protocols and local file renderers are not inferred.\n */\nfunction defaultAllowedRedirectProtocols(): string[] | undefined {\n const protocol = typeof window !== 'undefined' ? window.location?.protocol : undefined;\n\n if (!protocol || ALLOWED_PROTOCOLS.includes(protocol) || protocol === 'file:') {\n return undefined;\n }\n\n return [protocol];\n}\n\nexport function ClerkProvider({\n children,\n publishableKey,\n passkeys,\n allowedRedirectProtocols,\n ...props\n}: ClerkProviderProps): JSX.Element {\n const clerk = createClerkInstance(publishableKey, passkeys);\n const oauthTransport = createOAuthTransport();\n const clerkUI = loadClerkUI(publishableKey, props);\n\n return (\n <ReactClerkProvider\n {...props}\n Clerk={clerk}\n __internal_oauthTransport={oauthTransport}\n allowedRedirectProtocols={allowedRedirectProtocols ?? defaultAllowedRedirectProtocols()}\n publishableKey={publishableKey}\n standardBrowser={false}\n ui={{ ClerkUI: clerkUI }}\n >\n {children}\n </ReactClerkProvider>\n );\n}\n\nexport * from '@clerk/react';\n"],"mappings":";;;;;;;;;AAMA,MAAM,uBAAuB;AAI7B,IAAI,SAAqE;AAEzE,SAAS,eAAe,OAAsB,UAAgC;CAC5E,MAAM,qCAAqC,SAAS;CACpD,MAAM,kCAAkC,SAAS;CACjD,MAAM,iCAAiC,SAAS;CAChD,MAAM,yCAAyC,SAAS;CACxD,MAAM,sDAAsD,SAAS;AACvE;AAEA,SAAgB,oBAAoB,gBAAwB,UAA0C;CACpG,IAAI,QAAQ,mBAAmB,gBAAgB;EAC7C,IAAI,UACF,eAAe,OAAO,UAAU,QAAQ;EAE1C,OAAO,OAAO;CAChB;CAEA,MAAM,QAAQ,IAAI,MAAM,cAAc;CAEtC,IAAI,UACF,eAAe,OAAO,QAAQ;CAGhC,MAAM,2BAA2B,OAAM,YAAW;EAChD,QAAQ,cAAc;EACtB,QAAQ,KAAK,aAAa,OAAO,cAAc,GAAG;EAClD,QAAQ,KAAK,aAAa,OAAO,wDAAwC;EAEzE,MAAM,QAAQ,MAAM,OAAO,2BAA2B,WAAW,SAAS,oBAAoB;EAC9F,IAAI,OAAO;GACT,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;GAC3C,QAAQ,IAAI,iBAAiB,UAAU,OAAO;GAC9C,QAAQ,UAAU;EACpB;CACF,CAAC;CAED,MAAM,2BAA2B,OAAO,UAAU,aAAa;EAC7D,MAAM,gBAAiB,SAAsB,QAAQ,IAAI,eAAe;EACxE,IAAI,CAAC,eACH;EAGF,MAAM,QAAQ,cAAc,WAAW,SAAS,IAAI,cAAc,MAAM,CAAgB,IAAI;EAC5F,MAAM,OAAO,2BAA2B,WAAW,UAAU,sBAAsB,KAAK;CAC1F,CAAC;CAED,SAAS;EAAE,UAAU;EAAO;CAAe;CAC3C,OAAO;AACT;;;;ACxBA,IAAI,gBAAyF;AAE7F,SAAS,YAAY,gBAAwB,OAAiE;CAC5G,IAAI,eAAe,mBAAmB,gBACpC,OAAO,cAAc;CAIvB,MAAM,EAAE,uBAAuB,8BAA8B;CAK7D,MAAM,UAAU,kBAAkB;EAChC;EACA,UAAU,OAAO,MAAM,aAAa,WAAW,MAAM,WAAW;EAChE,QAAQ,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;EAC1D,OAAO,MAAM;EACb;EACA;CACF,CAAC,CAAC,CAAC,WAAW;EACZ,IAAI,CAAC,OAAO,wBACV,MAAM,IAAI,MACR,iKACF;EAEF,OAAO,OAAO;CAChB,CAAC;CAED,gBAAgB;EAAE;EAAS;CAAe;CAC1C,OAAO;AACT;AAEA,SAAS,uBAAwD;CAC/D,MAAM,SAAS,OAAO,2BAA2B;CAEjD,IAAI,CAAC,QACH;CAGF,OAAO;EACL,sBAAsB,OAAO,eAAe;EAC5C,OAAM,QAAO,OAAO,KAAK,IAAI,IAAI;CACnC;AACF;;;;;AAMA,SAAS,kCAAwD;CAC/D,MAAM,WAAW,OAAO,WAAW,cAAc,OAAO,UAAU,WAAW;CAE7E,IAAI,CAAC,YAAY,kBAAkB,SAAS,QAAQ,KAAK,aAAa,SACpE;CAGF,OAAO,CAAC,QAAQ;AAClB;AAEA,SAAgB,cAAc,EAC5B,UACA,gBACA,UACA,0BACA,GAAG,SAC+B;CAClC,MAAM,QAAQ,oBAAoB,gBAAgB,QAAQ;CAC1D,MAAM,iBAAiB,qBAAqB;CAC5C,MAAM,UAAU,YAAY,gBAAgB,KAAK;CAEjD,OACE,oBAACA,uBAAD;EACE,GAAI;EACJ,OAAO;EACP,2BAA2B;EAC3B,0BAA0B,4BAA4B,gCAAgC;EACtE;EAChB,iBAAiB;EACjB,IAAI,EAAE,SAAS,QAAQ;EAEtB;CACiB;AAExB"}
@@ -1 +1 @@
1
- {"version":3,"file":"ipc-handlers.d.ts","sourceRoot":"","sources":["../../../src/main/ipc-handlers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,IAAI,CAkB5E"}
1
+ {"version":3,"file":"ipc-handlers.d.ts","sourceRoot":"","sources":["../../../src/main/ipc-handlers.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AASpD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,IAAI,CAqB5E"}
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-transport.d.ts","sourceRoot":"","sources":["../../../src/main/oauth-transport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAU7D,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,EAAE,qBAAqB,CAAC;CACjC,CAAC;AA6BF,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,IAAI,CAqFzF"}
1
+ {"version":3,"file":"oauth-transport.d.ts","sourceRoot":"","sources":["../../../src/main/oauth-transport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAW7D,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,EAAE,qBAAqB,CAAC;CACjC,CAAC;AA6BF,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,IAAI,CA4FzF"}
@@ -1 +1 @@
1
- {"version":3,"file":"passkey-handlers.d.ts","sourceRoot":"","sources":["../../../src/main/passkey-handlers.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAQV,uBAAuB,EACxB,MAAM,iBAAiB,CAAC;AA6FzB,2DAA2D;AAC3D,wBAAgB,iBAAiB,IAAI,uBAAuB,CAuC3D"}
1
+ {"version":3,"file":"passkey-handlers.d.ts","sourceRoot":"","sources":["../../../src/main/passkey-handlers.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAQV,uBAAuB,EACxB,MAAM,iBAAiB,CAAC;AA8FzB,2DAA2D;AAC3D,wBAAgB,iBAAiB,IAAI,uBAAuB,CAuC3D"}
@@ -0,0 +1,3 @@
1
+ import type { IpcMainInvokeEvent } from 'electron';
2
+ export declare function isMainFrameEvent(event: IpcMainInvokeEvent): boolean;
3
+ //# sourceMappingURL=validate-sender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-sender.d.ts","sourceRoot":"","sources":["../../../src/main/validate-sender.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAMnD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAGnE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clerk/electron",
3
- "version": "0.0.15",
3
+ "version": "0.0.16-canary.v20260717065019",
4
4
  "description": "Clerk SDK for Electron",
5
5
  "keywords": [
6
6
  "clerk",
@@ -84,18 +84,18 @@
84
84
  ],
85
85
  "dependencies": {
86
86
  "tslib": "2.8.1",
87
- "@clerk/clerk-js": "^6.25.5",
88
- "@clerk/react": "^6.12.5",
89
- "@clerk/shared": "^4.25.5"
87
+ "@clerk/clerk-js": "6.25.6-canary.v20260717065019",
88
+ "@clerk/react": "6.12.6-canary.v20260717065019",
89
+ "@clerk/shared": "4.25.6-canary.v20260717065019"
90
90
  },
91
91
  "devDependencies": {
92
92
  "@types/node": "^22.19.17",
93
93
  "electron": "^39.2.6",
94
94
  "electron-store": "^8.2.0",
95
- "@clerk/electron-passkeys": "0.0.3"
95
+ "@clerk/electron-passkeys": "0.0.4-canary.v20260717065019"
96
96
  },
97
97
  "peerDependencies": {
98
- "@clerk/electron-passkeys": "*",
98
+ "@clerk/electron-passkeys": "0.0.4-canary.v20260717065019",
99
99
  "electron": ">=28",
100
100
  "electron-store": "^8.2.0",
101
101
  "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0",