@clerk/electron 0.0.1 → 0.0.2-canary.v20260622192220

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.
Files changed (89) hide show
  1. package/README.md +145 -2
  2. package/dist/index-DdiKCj-P.d.mts +70 -0
  3. package/dist/index-DdiKCj-P.d.mts.map +1 -0
  4. package/dist/index-DdiKCj-P.d.ts +70 -0
  5. package/dist/index-DdiKCj-P.d.ts.map +1 -0
  6. package/dist/index.d.mts +18 -0
  7. package/dist/index.d.mts.map +1 -0
  8. package/dist/index.d.ts +18 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.js +255 -0
  11. package/dist/index.js.map +1 -0
  12. package/dist/index.mjs +253 -0
  13. package/dist/index.mjs.map +1 -0
  14. package/dist/ipc-ByXFj84s.js +37 -0
  15. package/dist/ipc-ByXFj84s.js.map +1 -0
  16. package/dist/ipc-DSk37snY.mjs +19 -0
  17. package/dist/ipc-DSk37snY.mjs.map +1 -0
  18. package/dist/passkeys/index.d.mts +2 -0
  19. package/dist/passkeys/index.d.ts +2 -0
  20. package/dist/passkeys/index.js +287 -0
  21. package/dist/passkeys/index.js.map +1 -0
  22. package/dist/passkeys/index.mjs +284 -0
  23. package/dist/passkeys/index.mjs.map +1 -0
  24. package/dist/preload/index.d.mts +17 -0
  25. package/dist/preload/index.d.mts.map +1 -0
  26. package/dist/preload/index.d.ts +17 -0
  27. package/dist/preload/index.d.ts.map +1 -0
  28. package/dist/preload/index.js +47 -0
  29. package/dist/preload/index.js.map +1 -0
  30. package/dist/preload/index.mjs +45 -0
  31. package/dist/preload/index.mjs.map +1 -0
  32. package/dist/react/index.d.mts +34 -0
  33. package/dist/react/index.d.mts.map +1 -0
  34. package/dist/react/index.d.ts +34 -0
  35. package/dist/react/index.d.ts.map +1 -0
  36. package/dist/react/index.js +104 -0
  37. package/dist/react/index.js.map +1 -0
  38. package/dist/react/index.mjs +97 -0
  39. package/dist/react/index.mjs.map +1 -0
  40. package/dist/storage/index.d.mts +50 -0
  41. package/dist/storage/index.d.mts.map +1 -0
  42. package/dist/{types/storage → storage}/index.d.ts +26 -23
  43. package/dist/storage/index.d.ts.map +1 -0
  44. package/dist/storage/index.js +165 -0
  45. package/dist/storage/index.js.map +1 -0
  46. package/dist/storage/index.mjs +136 -0
  47. package/dist/storage/index.mjs.map +1 -0
  48. package/dist/types-nWjUdPq4.d.mts +52 -0
  49. package/dist/types-nWjUdPq4.d.mts.map +1 -0
  50. package/dist/types-nWjUdPq4.d.ts +52 -0
  51. package/dist/types-nWjUdPq4.d.ts.map +1 -0
  52. package/package.json +43 -30
  53. package/dist/cjs/index.js +0 -172
  54. package/dist/cjs/index.js.map +0 -1
  55. package/dist/cjs/preload/index.js +0 -39
  56. package/dist/cjs/preload/index.js.map +0 -1
  57. package/dist/cjs/react/index.js +0 -76
  58. package/dist/cjs/react/index.js.map +0 -1
  59. package/dist/cjs/storage/index.js +0 -133
  60. package/dist/cjs/storage/index.js.map +0 -1
  61. package/dist/esm/chunk-WJNPPS7B.js +0 -14
  62. package/dist/esm/chunk-WJNPPS7B.js.map +0 -1
  63. package/dist/esm/index.js +0 -157
  64. package/dist/esm/index.js.map +0 -1
  65. package/dist/esm/preload/index.js +0 -24
  66. package/dist/esm/preload/index.js.map +0 -1
  67. package/dist/esm/react/index.js +0 -68
  68. package/dist/esm/react/index.js.map +0 -1
  69. package/dist/esm/storage/index.js +0 -127
  70. package/dist/esm/storage/index.js.map +0 -1
  71. package/dist/types/index.d.ts +0 -3
  72. package/dist/types/index.d.ts.map +0 -1
  73. package/dist/types/main/create-clerk-bridge.d.ts +0 -10
  74. package/dist/types/main/create-clerk-bridge.d.ts.map +0 -1
  75. package/dist/types/main/ipc-handlers.d.ts +0 -3
  76. package/dist/types/main/ipc-handlers.d.ts.map +0 -1
  77. package/dist/types/main/oauth-transport.d.ts +0 -7
  78. package/dist/types/main/oauth-transport.d.ts.map +0 -1
  79. package/dist/types/preload/index.d.ts +0 -8
  80. package/dist/types/preload/index.d.ts.map +0 -1
  81. package/dist/types/react/create-clerk-instance.d.ts +0 -5
  82. package/dist/types/react/create-clerk-instance.d.ts.map +0 -1
  83. package/dist/types/react/index.d.ts +0 -12
  84. package/dist/types/react/index.d.ts.map +0 -1
  85. package/dist/types/shared/ipc.d.ts +0 -10
  86. package/dist/types/shared/ipc.d.ts.map +0 -1
  87. package/dist/types/shared/types.d.ts +0 -45
  88. package/dist/types/shared/types.d.ts.map +0 -1
  89. package/dist/types/storage/index.d.ts.map +0 -1
package/README.md CHANGED
@@ -37,6 +37,7 @@ This package exposes entrypoints for Electron's distinct runtime contexts:
37
37
  - `@clerk/electron/preload` — for use in Electron **preload** scripts.
38
38
  - `@clerk/electron/react` — for use in the Electron **renderer** process.
39
39
  - `@clerk/electron/storage` — default token storage backed by `electron-store`.
40
+ - `@clerk/electron/passkeys` — passkey (WebAuthn) support for the **renderer** process.
40
41
 
41
42
  ```ts
42
43
  // main.ts
@@ -52,6 +53,7 @@ createClerkBridge({
52
53
  scheme: 'my-app',
53
54
  host: 'renderer',
54
55
  },
56
+ passkeys: true,
55
57
  });
56
58
 
57
59
  app.whenReady().then(() => {
@@ -78,16 +80,157 @@ In `my-app://renderer/sign-in`, `my-app` is the scheme, `renderer` is the host,
78
80
  // preload.ts
79
81
  import { exposeClerkBridge } from '@clerk/electron/preload';
80
82
 
81
- exposeClerkBridge();
83
+ exposeClerkBridge({ passkeys: true });
82
84
  ```
83
85
 
84
86
  ```tsx
85
87
  // renderer.tsx
86
88
  import { ClerkProvider } from '@clerk/electron/react';
89
+ import { passkeys } from '@clerk/electron/passkeys';
90
+
91
+ <ClerkProvider
92
+ publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY}
93
+ passkeys={passkeys}
94
+ >
95
+ {/* ... */}
96
+ </ClerkProvider>;
97
+ ```
98
+
99
+ ## Content Security Policy
100
+
101
+ `@clerk/electron` loads Clerk's prebuilt UI from Clerk's CDN at runtime rather than bundling it, so your renderer's Content Security Policy must allow Clerk's Frontend API host. If it doesn't, the UI script fails to load and Clerk components never render.
102
+
103
+ Replace `{fapi_host}` below with your instance's **Frontend API** host, found in the [Clerk Dashboard](https://dashboard.clerk.com) under **API keys**. It includes a `clerk.` segment (for example, `clerk.your-app.com` in production or `your-slug.clerk.accounts.dev` in development). This is _not_ the Account Portal URL (`your-slug.accounts.dev`) — using that host blocks the UI script from loading.
104
+
105
+ ```
106
+ default-src 'self';
107
+ script-src 'self' 'unsafe-inline' https://{fapi_host} https://challenges.cloudflare.com;
108
+ connect-src 'self' https://{fapi_host};
109
+ img-src 'self' https://img.clerk.com data:;
110
+ style-src 'self' 'unsafe-inline';
111
+ worker-src 'self' blob:;
112
+ frame-src 'self' https://challenges.cloudflare.com;
113
+ form-action 'self';
114
+ ```
115
+
116
+ > [!NOTE]
117
+ > This covers sign-in/up and the hotloaded UI. If you use Clerk Billing (Stripe) or other features, you'll need to allow additional origins; see Clerk's [CSP guide](https://clerk.com/docs/guides/secure/best-practices/csp-headers) for the full list.
118
+
119
+ Apply it either with a `<meta>` tag in your renderer HTML:
120
+
121
+ ```html
122
+ <meta
123
+ http-equiv="Content-Security-Policy"
124
+ content="default-src 'self'; script-src 'self' 'unsafe-inline' https://{fapi_host} https://challenges.cloudflare.com; connect-src 'self' https://{fapi_host}; img-src 'self' https://img.clerk.com data:; style-src 'self' 'unsafe-inline'; worker-src 'self' blob:; frame-src 'self' https://challenges.cloudflare.com; form-action 'self';"
125
+ />
126
+ ```
127
+
128
+ or, as [Electron's security guide](https://www.electronjs.org/docs/latest/tutorial/security#7-define-a-content-security-policy) recommends, as a response header from the main process:
129
+
130
+ ```ts
131
+ // main.ts
132
+ import { app, session } from 'electron';
133
+
134
+ const fapiHost = 'clerk.your-app.com';
135
+
136
+ app.whenReady().then(() => {
137
+ session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
138
+ callback({
139
+ responseHeaders: {
140
+ ...details.responseHeaders,
141
+ 'Content-Security-Policy': [
142
+ [
143
+ "default-src 'self'",
144
+ `script-src 'self' 'unsafe-inline' https://${fapiHost} https://challenges.cloudflare.com`,
145
+ `connect-src 'self' https://${fapiHost}`,
146
+ "img-src 'self' https://img.clerk.com data:",
147
+ "style-src 'self' 'unsafe-inline'",
148
+ "worker-src 'self' blob:",
149
+ "frame-src 'self' https://challenges.cloudflare.com",
150
+ "form-action 'self'",
151
+ ].join('; '),
152
+ ],
153
+ },
154
+ });
155
+ });
156
+ });
157
+ ```
158
+
159
+ > [!NOTE]
160
+ > Loading the renderer from a dev server (such as Vite) requires looser rules for HMR: add `'unsafe-eval'` to `script-src` and your dev server's origin to `connect-src` (for example, `ws://localhost:<port> http://localhost:<port>`). Many apps skip CSP during development and apply it only to packaged builds.
161
+
162
+ ## Passkeys
163
+
164
+ Passkey support works in two modes, selected automatically per request:
165
+
166
+ - **Renderer mode**: when your window loads content over `https://` from an origin that matches your passkey RP (Relying Party) ID, the renderer's built-in Chromium WebAuthn is used. Credentials are synced by the OS/browser ecosystem (Windows Hello works out of the box; Touch ID on macOS requires Electron ≥ 42 and [`app.configureWebAuthn`](https://www.electronjs.org/docs/latest/api/app#appconfigurewebauthnoptions-macos)).
167
+ - **Native mode**: when your window loads a local bundle (e.g. `scheme://host`), WebAuthn's origin checks reject the request, so the ceremony is routed over IPC to the main process and serviced by the OS WebAuthn APIs (AuthenticationServices on macOS, `webauthn.dll` on Windows) via the optional [`@clerk/electron-passkeys`](https://github.com/clerk/javascript/tree/main/packages/electron-passkeys) native module.
168
+
169
+ ### Setup
170
+
171
+ Native mode requires the optional native module:
87
172
 
88
- <ClerkProvider publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY}>{/* ... */}</ClerkProvider>;
173
+ ```sh
174
+ pnpm add @clerk/electron-passkeys
89
175
  ```
90
176
 
177
+ ```ts
178
+ // main process
179
+ import { createClerkBridge } from '@clerk/electron';
180
+ import { storage } from '@clerk/electron/storage';
181
+
182
+ createClerkBridge({ storage: storage(), passkeys: true });
183
+ ```
184
+
185
+ ```ts
186
+ // preload script
187
+ import { exposeClerkBridge } from '@clerk/electron/preload';
188
+
189
+ exposeClerkBridge({ passkeys: true });
190
+ ```
191
+
192
+ ```tsx
193
+ // renderer process (React)
194
+ import { ClerkProvider } from '@clerk/electron/react';
195
+ import { passkeys } from '@clerk/electron/passkeys';
196
+
197
+ <ClerkProvider
198
+ publishableKey={publishableKey}
199
+ passkeys={passkeys}
200
+ >
201
+ {/* ... */}
202
+ </ClerkProvider>;
203
+ ```
204
+
205
+ Passkey code is only bundled and initialized when you pass the `passkeys` prop. If you manage the Clerk instance yourself instead of using `ClerkProvider`, wire it up before `clerk.load()`:
206
+
207
+ ```ts
208
+ // renderer process (vanilla)
209
+ import { Clerk } from '@clerk/clerk-js';
210
+ import { createPasskeyProvider } from '@clerk/electron/passkeys';
211
+
212
+ const clerk = new Clerk(publishableKey);
213
+ createPasskeyProvider(clerk);
214
+ await clerk.load();
215
+ ```
216
+
217
+ ### macOS requirements for native mode
218
+
219
+ Like passkeys on iOS, the macOS platform APIs require a verified association between your app and your domain:
220
+
221
+ 1. In the Clerk Dashboard, navigate to the [Native applications](https://dashboard.clerk.com/~/native-applications) page and ensure that the Native API is enabled. This is required to integrate Clerk in your Electron application.
222
+ 2. Add an iOS application to the [Native applications](https://dashboard.clerk.com/~/native-applications) page in the Clerk Dashboard. You will need your app's App ID Prefix and Bundle ID. An Electron macOS app uses the same configuration as iOS applications.
223
+ 3. Sign your app with `com.apple.developer.associated-domains` containing `webcredentials:<rp-domain>`. This is a _restricted_ entitlement: the build must embed a provisioning profile with the Associated Domains capability for the bundle ID, and the entitlements must also include `com.apple.application-identifier` and `com.apple.developer.team-identifier` matching the profile.
224
+
225
+ Quick Tips (each of these failure modes produces the same opaque "not associated with domain" error):
226
+
227
+ - Sign with an **Apple Development** identity (`mac.type: development` in electron-builder) and a **macOS App Development** profile that includes your Mac; also install the profile on the machine (`~/Library/Developer/Xcode/UserData/Provisioning Profiles/<UUID>.provisionprofile`).
228
+ - Copy `.app` bundles with `ditto`. Other copy methods may break the app seal and macOS silently ignores the entitlements of an app whose signature fails `codesign --verify --deep --strict`.
229
+ - The system registers the domain association via `swcd` when the app launches; verify with `sudo swcutil show`. If state gets stuck, `sudo swcutil reset` and relaunch.
230
+ - Prefer the default (production/CDN) association route. `?mode=developer` + `sudo swcutil developer-mode -e true` exists but is often flaky.
231
+
232
+ Windows has no equivalent requirement. On Linux there is no native path; passkeys work in renderer mode only (including external security keys).
233
+
91
234
  ## Support
92
235
 
93
236
  For help, visit our [support page](https://clerk.com/contact/support?utm_source=github&utm_medium=clerk_electron).
@@ -0,0 +1,70 @@
1
+ import { CredentialReturn, PublicKeyCredentialCreationOptionsWithoutExtensions, PublicKeyCredentialRequestOptionsWithoutExtensions, PublicKeyCredentialWithAuthenticatorAssertionResponse, PublicKeyCredentialWithAuthenticatorAttestationResponse } from "@clerk/shared/types";
2
+
3
+ //#region src/passkeys/renderer/strategy.d.ts
4
+ type PasskeyMode = 'auto' | 'renderer' | 'native';
5
+ type PasskeyPath = 'renderer' | 'native' | 'unsupported';
6
+ type StrategyEnv = {
7
+ protocol: string;
8
+ hostname: string;
9
+ hasWebAuthn: boolean;
10
+ nativeAvailable: boolean;
11
+ platform: string;
12
+ electronMajor: number;
13
+ };
14
+ //#endregion
15
+ //#region src/passkeys/index.d.ts
16
+ type CreatePasskeysOptions = {
17
+ /**
18
+ * WebAuthn implementation to use:
19
+ * `auto` chooses renderer WebAuthn for valid HTTPS origins and native WebAuthn otherwise.
20
+ */
21
+ mode?: PasskeyMode;
22
+ };
23
+ type PasskeySupport = {
24
+ create: (publicKey: PublicKeyCredentialCreationOptionsWithoutExtensions) => Promise<CredentialReturn<PublicKeyCredentialWithAuthenticatorAttestationResponse>>;
25
+ get: (args: {
26
+ publicKeyOptions: PublicKeyCredentialRequestOptionsWithoutExtensions;
27
+ }) => Promise<CredentialReturn<PublicKeyCredentialWithAuthenticatorAssertionResponse>>;
28
+ isSupported: () => boolean;
29
+ isAutoFillSupported: () => Promise<boolean>;
30
+ isPlatformAuthenticatorSupported: () => Promise<boolean>;
31
+ };
32
+ type ClerkPasskeyHost = {
33
+ __internal_createPublicCredentials: PasskeySupport['create'] | undefined;
34
+ __internal_getPublicCredentials: PasskeySupport['get'] | undefined;
35
+ __internal_isWebAuthnSupported: PasskeySupport['isSupported'] | undefined;
36
+ __internal_isWebAuthnAutofillSupported: PasskeySupport['isAutoFillSupported'] | undefined;
37
+ __internal_isWebAuthnPlatformAuthenticatorSupported: PasskeySupport['isPlatformAuthenticatorSupported'] | undefined;
38
+ };
39
+ /** Creates an Electron passkey provider for clerk-js. */
40
+ declare function createPasskeys(options?: CreatePasskeysOptions): PasskeySupport;
41
+ /**
42
+ * Ready-to-use passkey implementation for the `ClerkProvider` `passkeys` prop.
43
+ * Chooses renderer or native WebAuthn automatically per request.
44
+ *
45
+ * @example
46
+ * ```tsx
47
+ * import { ClerkProvider } from '@clerk/electron/react';
48
+ * import { passkeys } from '@clerk/electron/passkeys';
49
+ *
50
+ * <ClerkProvider publishableKey={publishableKey} passkeys={passkeys} />
51
+ * ```
52
+ */
53
+ declare const passkeys: PasskeySupport;
54
+ /**
55
+ * Wires passkey support into a Clerk instance. Call before `clerk.load()`.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * import { Clerk } from '@clerk/clerk-js';
60
+ * import { createPasskeyProvider } from '@clerk/electron/passkeys';
61
+ *
62
+ * const clerk = new Clerk(publishableKey);
63
+ * createPasskeyProvider(clerk);
64
+ * await clerk.load();
65
+ * ```
66
+ */
67
+ declare function createPasskeyProvider(clerk: ClerkPasskeyHost, options?: CreatePasskeysOptions): PasskeySupport;
68
+ //#endregion
69
+ export { createPasskeys as a, PasskeyPath as c, createPasskeyProvider as i, StrategyEnv as l, CreatePasskeysOptions as n, passkeys as o, PasskeySupport as r, PasskeyMode as s, ClerkPasskeyHost as t };
70
+ //# sourceMappingURL=index-DdiKCj-P.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-DdiKCj-P.d.mts","names":[],"sources":["../src/passkeys/renderer/strategy.ts","../src/passkeys/index.ts"],"mappings":";;;KAAY,WAAA;AAAA,KACA,WAAA;AAAA,KAEA,WAAA;EACV,QAAA;EACA,QAAA;EACA,WAAA;EACA,eAAA;EACA,QAAA;EACA,aAAA;AAAA;;;KCQU,qBAAA;EDjBW;;;AAAA;ECsBrB,IAAA,GAAO,WAAW;AAAA;AAAA,KAGR,cAAA;EACV,MAAA,GACE,SAAA,EAAW,mDAAA,KACR,OAAA,CAAQ,gBAAA,CAAiB,uDAAA;EAC9B,GAAA,GAAM,IAAA;IACJ,gBAAA,EAAkB,kDAAA;EAAA,MACd,OAAA,CAAQ,gBAAA,CAAiB,qDAAA;EAC/B,WAAA;EACA,mBAAA,QAA2B,OAAA;EAC3B,gCAAA,QAAwC,OAAA;AAAA;AAAA,KAG9B,gBAAA;EACV,kCAAA,EAAoC,cAAA;EACpC,+BAAA,EAAiC,cAAA;EACjC,8BAAA,EAAgC,cAAA;EAChC,sCAAA,EAAwC,cAAA;EACxC,mDAAA,EAAqD,cAAA;AAAA;;iBAyCvC,cAAA,CAAe,OAAA,GAAU,qBAAA,GAAwB,cAAc;AAlE/E;;;;AAKoB;AAGpB;;;;;;;AARA,cAuJa,QAAA,EAAU,cAAiC;;;;;;;;;;;;;;iBAexC,qBAAA,CAAsB,KAAA,EAAO,gBAAA,EAAkB,OAAA,GAAU,qBAAA,GAAwB,cAAA"}
@@ -0,0 +1,70 @@
1
+ import { CredentialReturn, PublicKeyCredentialCreationOptionsWithoutExtensions, PublicKeyCredentialRequestOptionsWithoutExtensions, PublicKeyCredentialWithAuthenticatorAssertionResponse, PublicKeyCredentialWithAuthenticatorAttestationResponse } from "@clerk/shared/types";
2
+
3
+ //#region src/passkeys/renderer/strategy.d.ts
4
+ type PasskeyMode = 'auto' | 'renderer' | 'native';
5
+ type PasskeyPath = 'renderer' | 'native' | 'unsupported';
6
+ type StrategyEnv = {
7
+ protocol: string;
8
+ hostname: string;
9
+ hasWebAuthn: boolean;
10
+ nativeAvailable: boolean;
11
+ platform: string;
12
+ electronMajor: number;
13
+ };
14
+ //#endregion
15
+ //#region src/passkeys/index.d.ts
16
+ type CreatePasskeysOptions = {
17
+ /**
18
+ * WebAuthn implementation to use:
19
+ * `auto` chooses renderer WebAuthn for valid HTTPS origins and native WebAuthn otherwise.
20
+ */
21
+ mode?: PasskeyMode;
22
+ };
23
+ type PasskeySupport = {
24
+ create: (publicKey: PublicKeyCredentialCreationOptionsWithoutExtensions) => Promise<CredentialReturn<PublicKeyCredentialWithAuthenticatorAttestationResponse>>;
25
+ get: (args: {
26
+ publicKeyOptions: PublicKeyCredentialRequestOptionsWithoutExtensions;
27
+ }) => Promise<CredentialReturn<PublicKeyCredentialWithAuthenticatorAssertionResponse>>;
28
+ isSupported: () => boolean;
29
+ isAutoFillSupported: () => Promise<boolean>;
30
+ isPlatformAuthenticatorSupported: () => Promise<boolean>;
31
+ };
32
+ type ClerkPasskeyHost = {
33
+ __internal_createPublicCredentials: PasskeySupport['create'] | undefined;
34
+ __internal_getPublicCredentials: PasskeySupport['get'] | undefined;
35
+ __internal_isWebAuthnSupported: PasskeySupport['isSupported'] | undefined;
36
+ __internal_isWebAuthnAutofillSupported: PasskeySupport['isAutoFillSupported'] | undefined;
37
+ __internal_isWebAuthnPlatformAuthenticatorSupported: PasskeySupport['isPlatformAuthenticatorSupported'] | undefined;
38
+ };
39
+ /** Creates an Electron passkey provider for clerk-js. */
40
+ declare function createPasskeys(options?: CreatePasskeysOptions): PasskeySupport;
41
+ /**
42
+ * Ready-to-use passkey implementation for the `ClerkProvider` `passkeys` prop.
43
+ * Chooses renderer or native WebAuthn automatically per request.
44
+ *
45
+ * @example
46
+ * ```tsx
47
+ * import { ClerkProvider } from '@clerk/electron/react';
48
+ * import { passkeys } from '@clerk/electron/passkeys';
49
+ *
50
+ * <ClerkProvider publishableKey={publishableKey} passkeys={passkeys} />
51
+ * ```
52
+ */
53
+ declare const passkeys: PasskeySupport;
54
+ /**
55
+ * Wires passkey support into a Clerk instance. Call before `clerk.load()`.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * import { Clerk } from '@clerk/clerk-js';
60
+ * import { createPasskeyProvider } from '@clerk/electron/passkeys';
61
+ *
62
+ * const clerk = new Clerk(publishableKey);
63
+ * createPasskeyProvider(clerk);
64
+ * await clerk.load();
65
+ * ```
66
+ */
67
+ declare function createPasskeyProvider(clerk: ClerkPasskeyHost, options?: CreatePasskeysOptions): PasskeySupport;
68
+ //#endregion
69
+ export { createPasskeys as a, PasskeyPath as c, createPasskeyProvider as i, StrategyEnv as l, CreatePasskeysOptions as n, passkeys as o, PasskeySupport as r, PasskeyMode as s, ClerkPasskeyHost as t };
70
+ //# sourceMappingURL=index-DdiKCj-P.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-DdiKCj-P.d.ts","names":[],"sources":["../src/passkeys/renderer/strategy.ts","../src/passkeys/index.ts"],"mappings":";;;KAAY,WAAA;AAAA,KACA,WAAA;AAAA,KAEA,WAAA;EACV,QAAA;EACA,QAAA;EACA,WAAA;EACA,eAAA;EACA,QAAA;EACA,aAAA;AAAA;;;KCQU,qBAAA;EDjBW;;;AAAA;ECsBrB,IAAA,GAAO,WAAW;AAAA;AAAA,KAGR,cAAA;EACV,MAAA,GACE,SAAA,EAAW,mDAAA,KACR,OAAA,CAAQ,gBAAA,CAAiB,uDAAA;EAC9B,GAAA,GAAM,IAAA;IACJ,gBAAA,EAAkB,kDAAA;EAAA,MACd,OAAA,CAAQ,gBAAA,CAAiB,qDAAA;EAC/B,WAAA;EACA,mBAAA,QAA2B,OAAA;EAC3B,gCAAA,QAAwC,OAAA;AAAA;AAAA,KAG9B,gBAAA;EACV,kCAAA,EAAoC,cAAA;EACpC,+BAAA,EAAiC,cAAA;EACjC,8BAAA,EAAgC,cAAA;EAChC,sCAAA,EAAwC,cAAA;EACxC,mDAAA,EAAqD,cAAA;AAAA;;iBAyCvC,cAAA,CAAe,OAAA,GAAU,qBAAA,GAAwB,cAAc;AAlE/E;;;;AAKoB;AAGpB;;;;;;;AARA,cAuJa,QAAA,EAAU,cAAiC;;;;;;;;;;;;;;iBAexC,qBAAA,CAAsB,KAAA,EAAO,gBAAA,EAAkB,OAAA,GAAU,qBAAA,GAAwB,cAAA"}
@@ -0,0 +1,18 @@
1
+ import { a as TokenStorage, i as SetupPasskeysMainReturn, n as CreateClerkBridgeOptions, r as ExposeClerkBridgeOptions, t as ClerkBridge } from "./types-nWjUdPq4.mjs";
2
+
3
+ //#region src/main/create-clerk-bridge.d.ts
4
+ /**
5
+ * Creates the Clerk bridge for Electron's main process.
6
+ *
7
+ * The bridge owns Clerk's main-process IPC handlers, token persistence, and OAuth deep-link
8
+ * transport. Call this before creating renderer windows, and call the returned `cleanup` method
9
+ * when tearing down the app or test environment.
10
+ */
11
+ declare function createClerkBridge(options: CreateClerkBridgeOptions): ClerkBridge;
12
+ //#endregion
13
+ //#region src/main/passkey-handlers.d.ts
14
+ /** Registers IPC handlers for native platform WebAuthn. */
15
+ declare function setupPasskeysMain(): SetupPasskeysMainReturn;
16
+ //#endregion
17
+ export { type ClerkBridge, type CreateClerkBridgeOptions, type ExposeClerkBridgeOptions, type TokenStorage, createClerkBridge, setupPasskeysMain };
18
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/main/create-clerk-bridge.ts","../src/main/passkey-handlers.ts"],"mappings":";;;;;AA4BA;;;;;iBAAgB,iBAAA,CAAkB,OAAA,EAAS,wBAAA,GAA2B,WAAW;;;;iBC+EjE,iBAAA,IAAqB,uBAAuB"}
@@ -0,0 +1,18 @@
1
+ import { a as TokenStorage, i as SetupPasskeysMainReturn, n as CreateClerkBridgeOptions, r as ExposeClerkBridgeOptions, t as ClerkBridge } from "./types-nWjUdPq4.js";
2
+
3
+ //#region src/main/create-clerk-bridge.d.ts
4
+ /**
5
+ * Creates the Clerk bridge for Electron's main process.
6
+ *
7
+ * The bridge owns Clerk's main-process IPC handlers, token persistence, and OAuth deep-link
8
+ * transport. Call this before creating renderer windows, and call the returned `cleanup` method
9
+ * when tearing down the app or test environment.
10
+ */
11
+ declare function createClerkBridge(options: CreateClerkBridgeOptions): ClerkBridge;
12
+ //#endregion
13
+ //#region src/main/passkey-handlers.d.ts
14
+ /** Registers IPC handlers for native platform WebAuthn. */
15
+ declare function setupPasskeysMain(): SetupPasskeysMainReturn;
16
+ //#endregion
17
+ export { type ClerkBridge, type CreateClerkBridgeOptions, type ExposeClerkBridgeOptions, type TokenStorage, createClerkBridge, setupPasskeysMain };
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/main/create-clerk-bridge.ts","../src/main/passkey-handlers.ts"],"mappings":";;;;;AA4BA;;;;;iBAAgB,iBAAA,CAAkB,OAAA,EAAS,wBAAA,GAA2B,WAAW;;;;iBC+EjE,iBAAA,IAAqB,uBAAuB"}
package/dist/index.js ADDED
@@ -0,0 +1,255 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_ipc = require('./ipc-ByXFj84s.js');
3
+ let electron = require("electron");
4
+
5
+ //#region src/main/ipc-handlers.ts
6
+ function setupTokenCacheIpcHandlers(storage) {
7
+ electron.ipcMain.handle(require_ipc.TOKEN_CACHE_CHANNELS.getToken, (_event, key) => {
8
+ return storage.getItem(key);
9
+ });
10
+ electron.ipcMain.handle(require_ipc.TOKEN_CACHE_CHANNELS.saveToken, (_event, key, value) => {
11
+ return storage.setItem(key, value);
12
+ });
13
+ electron.ipcMain.handle(require_ipc.TOKEN_CACHE_CHANNELS.clearToken, (_event, key) => {
14
+ return storage.removeItem(key);
15
+ });
16
+ return () => {
17
+ electron.ipcMain.removeHandler(require_ipc.TOKEN_CACHE_CHANNELS.getToken);
18
+ electron.ipcMain.removeHandler(require_ipc.TOKEN_CACHE_CHANNELS.saveToken);
19
+ electron.ipcMain.removeHandler(require_ipc.TOKEN_CACHE_CHANNELS.clearToken);
20
+ };
21
+ }
22
+
23
+ //#endregion
24
+ //#region src/main/oauth-transport.ts
25
+ const CALLBACK_TIMEOUT_MS = 180 * 1e3;
26
+ function buildRedirectUrl(options) {
27
+ return `${options.renderer.scheme}://${options.renderer.host}/`;
28
+ }
29
+ function isMatchingCallbackUrl(url, redirectUrl) {
30
+ try {
31
+ const callback = new URL(url);
32
+ const expected = new URL(redirectUrl);
33
+ return callback.protocol === expected.protocol && callback.host === expected.host && callback.pathname === expected.pathname;
34
+ } catch {
35
+ return false;
36
+ }
37
+ }
38
+ function assertExternalOAuthUrl(url) {
39
+ const parsedUrl = new URL(url);
40
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") throw new TypeError(`Clerk: refusing to open unsupported OAuth URL protocol: ${parsedUrl.protocol}`);
41
+ }
42
+ function setupOAuthTransportIpcHandlers(options) {
43
+ const redirectUrl = buildRedirectUrl(options);
44
+ let pendingOAuthFlow = null;
45
+ const disposePendingOAuthFlow = (reason) => {
46
+ if (!pendingOAuthFlow) return;
47
+ const pending = pendingOAuthFlow;
48
+ clearTimeout(pendingOAuthFlow.timeout);
49
+ pendingOAuthFlow = null;
50
+ if (reason) pending.reject(reason);
51
+ };
52
+ const handleCallbackUrl = (url) => {
53
+ if (!pendingOAuthFlow || !isMatchingCallbackUrl(url, redirectUrl)) return;
54
+ const pending = pendingOAuthFlow;
55
+ disposePendingOAuthFlow();
56
+ pending.resolve({ callbackUrl: url });
57
+ };
58
+ const openUrlListener = (event, url) => {
59
+ if (!isMatchingCallbackUrl(url, redirectUrl)) return;
60
+ event.preventDefault();
61
+ handleCallbackUrl(url);
62
+ };
63
+ const secondInstanceListener = (_event, argv) => {
64
+ const callbackUrl = argv.find((url) => isMatchingCallbackUrl(url, redirectUrl));
65
+ if (callbackUrl) handleCallbackUrl(callbackUrl);
66
+ };
67
+ electron.app.setAsDefaultProtocolClient(options.renderer.scheme);
68
+ electron.app.on("open-url", openUrlListener);
69
+ electron.app.on("second-instance", secondInstanceListener);
70
+ electron.ipcMain.handle(require_ipc.OAUTH_TRANSPORT_CHANNELS.getRedirectUrl, () => {
71
+ return redirectUrl;
72
+ });
73
+ electron.ipcMain.handle(require_ipc.OAUTH_TRANSPORT_CHANNELS.open, async (_event, url) => {
74
+ if (pendingOAuthFlow) throw new Error("Clerk: an OAuth flow is already pending.");
75
+ assertExternalOAuthUrl(url);
76
+ const callbackPromise = new Promise((resolve, reject) => {
77
+ pendingOAuthFlow = {
78
+ resolve,
79
+ reject,
80
+ timeout: setTimeout(() => {
81
+ disposePendingOAuthFlow();
82
+ reject(/* @__PURE__ */ new Error("Clerk: OAuth callback timed out."));
83
+ }, CALLBACK_TIMEOUT_MS)
84
+ };
85
+ });
86
+ try {
87
+ await electron.shell.openExternal(url);
88
+ } catch (err) {
89
+ disposePendingOAuthFlow(err instanceof Error ? err : new Error(String(err)));
90
+ }
91
+ return callbackPromise;
92
+ });
93
+ return () => {
94
+ disposePendingOAuthFlow(/* @__PURE__ */ new Error("Clerk: OAuth flow was cancelled."));
95
+ electron.app.removeListener("open-url", openUrlListener);
96
+ electron.app.removeListener("second-instance", secondInstanceListener);
97
+ electron.ipcMain.removeHandler(require_ipc.OAUTH_TRANSPORT_CHANNELS.getRedirectUrl);
98
+ electron.ipcMain.removeHandler(require_ipc.OAUTH_TRANSPORT_CHANNELS.open);
99
+ };
100
+ }
101
+
102
+ //#endregion
103
+ //#region src/main/passkey-handlers.ts
104
+ let nativeModulePromise;
105
+ function loadNativeModule() {
106
+ nativeModulePromise ??= import("@clerk/electron-passkeys").then((module) => module.default ?? module, (error) => {
107
+ nativeModulePromise = void 0;
108
+ throw new Error("Clerk: createClerkBridge({ passkeys: true }) requires the optional @clerk/electron-passkeys package. Install it with your package manager to enable native passkey support.", { cause: error });
109
+ });
110
+ return nativeModulePromise;
111
+ }
112
+ const NATIVE_ERROR_CODES = [
113
+ "cancelled",
114
+ "invalid_rp",
115
+ "not_supported",
116
+ "timeout",
117
+ "unknown"
118
+ ];
119
+ function isPasskeyIpcResult(value) {
120
+ if (!value || typeof value !== "object" || typeof value.ok !== "boolean") return false;
121
+ const result = value;
122
+ return result.ok ? result.credential !== void 0 : NATIVE_ERROR_CODES.includes(result.error?.code);
123
+ }
124
+ async function invokeNative(method, event, options) {
125
+ let native;
126
+ try {
127
+ native = await loadNativeModule();
128
+ } catch (error) {
129
+ return {
130
+ ok: false,
131
+ error: {
132
+ code: "not_supported",
133
+ message: error instanceof Error ? error.message : String(error)
134
+ }
135
+ };
136
+ }
137
+ if (!native.isAvailable()) return {
138
+ ok: false,
139
+ error: {
140
+ code: "not_supported",
141
+ message: "Native passkeys are not supported on this platform."
142
+ }
143
+ };
144
+ if (!event.senderFrame || event.senderFrame !== event.sender.mainFrame) return {
145
+ ok: false,
146
+ error: {
147
+ code: "unknown",
148
+ message: "The passkey request did not originate from a window's main frame."
149
+ }
150
+ };
151
+ const window = electron.BrowserWindow.fromWebContents(event.sender);
152
+ if (!window) return {
153
+ ok: false,
154
+ error: {
155
+ code: "unknown",
156
+ message: "The passkey request did not originate from a visible window."
157
+ }
158
+ };
159
+ try {
160
+ const resultJson = await native[method](window.getNativeWindowHandle(), JSON.stringify(options));
161
+ const result = JSON.parse(resultJson);
162
+ if (!isPasskeyIpcResult(result)) return {
163
+ ok: false,
164
+ error: {
165
+ code: "unknown",
166
+ message: "The native module returned an unexpected result."
167
+ }
168
+ };
169
+ return result;
170
+ } catch (error) {
171
+ return {
172
+ ok: false,
173
+ error: {
174
+ code: "unknown",
175
+ message: error instanceof Error ? error.message : String(error)
176
+ }
177
+ };
178
+ }
179
+ }
180
+ /** Registers IPC handlers for native platform WebAuthn. */
181
+ function setupPasskeysMain() {
182
+ loadNativeModule().catch((error) => console.warn(error.message));
183
+ electron.ipcMain.handle(require_ipc.PASSKEY_CHANNELS.create, (event, options) => invokeNative("createCredential", event, options));
184
+ electron.ipcMain.handle(require_ipc.PASSKEY_CHANNELS.get, (event, options) => invokeNative("getCredential", event, options));
185
+ electron.ipcMain.handle(require_ipc.PASSKEY_CHANNELS.capabilities, async () => {
186
+ try {
187
+ const native = await loadNativeModule();
188
+ if (!native.isAvailable()) return {
189
+ available: false,
190
+ platformAuthenticator: false,
191
+ securityKeys: false
192
+ };
193
+ return {
194
+ available: true,
195
+ ...native.capabilities()
196
+ };
197
+ } catch {
198
+ return {
199
+ available: false,
200
+ platformAuthenticator: false,
201
+ securityKeys: false
202
+ };
203
+ }
204
+ });
205
+ return { cleanup() {
206
+ electron.ipcMain.removeHandler(require_ipc.PASSKEY_CHANNELS.create);
207
+ electron.ipcMain.removeHandler(require_ipc.PASSKEY_CHANNELS.get);
208
+ electron.ipcMain.removeHandler(require_ipc.PASSKEY_CHANNELS.capabilities);
209
+ } };
210
+ }
211
+
212
+ //#endregion
213
+ //#region src/main/create-clerk-bridge.ts
214
+ function assertValidRendererOriginConfig(renderer) {
215
+ if (renderer.scheme.includes(":") || renderer.scheme.includes("/")) throw new Error("Clerk: renderer.scheme must be a scheme name like \"my-app\", not a URL or protocol like \"my-app://\".");
216
+ if (renderer.host.includes(":") || renderer.host.includes("/")) throw new Error("Clerk: renderer.host must be a host name like \"renderer\", not a URL or origin like \"my-app://renderer\".");
217
+ }
218
+ /**
219
+ * Creates the Clerk bridge for Electron's main process.
220
+ *
221
+ * The bridge owns Clerk's main-process IPC handlers, token persistence, and OAuth deep-link
222
+ * transport. Call this before creating renderer windows, and call the returned `cleanup` method
223
+ * when tearing down the app or test environment.
224
+ */
225
+ function createClerkBridge(options) {
226
+ if (!options.storage) throw new Error("Clerk: createClerkBridge requires a storage adapter. Pass createClerkBridge({ storage: storage() }) from @clerk/electron/storage, or provide a custom storage adapter.");
227
+ const cleanupTokenPersistence = setupTokenCacheIpcHandlers(options.storage);
228
+ let cleanupOAuthTransport;
229
+ const passkeys = options.passkeys ? setupPasskeysMain() : null;
230
+ if (options.renderer) {
231
+ assertValidRendererOriginConfig(options.renderer);
232
+ electron.protocol.registerSchemesAsPrivileged([{
233
+ scheme: options.renderer.scheme,
234
+ privileges: {
235
+ standard: true,
236
+ secure: true,
237
+ supportFetchAPI: true,
238
+ corsEnabled: true,
239
+ stream: true,
240
+ ...options.renderer.privileges
241
+ }
242
+ }]);
243
+ cleanupOAuthTransport = setupOAuthTransportIpcHandlers({ renderer: options.renderer });
244
+ }
245
+ return { cleanup() {
246
+ cleanupTokenPersistence();
247
+ cleanupOAuthTransport?.();
248
+ passkeys?.cleanup();
249
+ } };
250
+ }
251
+
252
+ //#endregion
253
+ exports.createClerkBridge = createClerkBridge;
254
+ exports.setupPasskeysMain = setupPasskeysMain;
255
+ //# sourceMappingURL=index.js.map