
Research
/Security News
11 Malicious NuGet Tools Pose as Game Cheats to Drop a Windows Host-Surveillance Payload
11 malicious NuGet tools pose as game cheats to deploy Windows payloads, track hosts, and use Google Sheets for telemetry and control.
@clerk/electron
Advanced tools
Clerk is the easiest way to add authentication and user management to your Electron application.
[!WARNING]
@clerk/electronis under active development and is not yet ready for production use. The API is incomplete and subject to change.
This package exposes entrypoints for Electron's distinct runtime contexts:
@clerk/electron — for use in the Electron main process.@clerk/electron/preload — for use in Electron preload scripts.@clerk/electron/react — for use in the Electron renderer process.@clerk/electron/storage — default token storage backed by electron-store.@clerk/electron/passkeys — passkey (WebAuthn) support for the renderer process.// main.ts
import { app, BrowserWindow, net, protocol } from 'electron';
import { createClerkBridge } from '@clerk/electron';
import { storage } from '@clerk/electron/storage';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';
createClerkBridge({
storage: storage(),
renderer: {
scheme: 'my-app',
host: 'renderer',
},
passkeys: true,
});
app.whenReady().then(() => {
protocol.handle('my-app', request => {
const url = new URL(request.url);
const file = url.pathname === '/' ? 'index.html' : url.pathname;
return net.fetch(pathToFileURL(join(__dirname, '../renderer', file)).toString());
});
const win = new BrowserWindow({
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
},
});
win.loadURL('my-app://renderer/');
});
In my-app://renderer/sign-in, my-app is the scheme, renderer is the host, my-app://renderer is the origin, and /sign-in is the path. If your renderer uses path-based routing, serve every route from the same origin and fall back to your renderer entrypoint as needed.
// preload.ts
import { exposeClerkBridge } from '@clerk/electron/preload';
exposeClerkBridge({ passkeys: true });
// renderer.tsx
import { ClerkProvider } from '@clerk/electron/react';
import { passkeys } from '@clerk/electron/passkeys';
<ClerkProvider
publishableKey={import.meta.env.VITE_CLERK_PUBLISHABLE_KEY}
passkeys={passkeys}
>
{/* ... */}
</ClerkProvider>;
Pass userAgent to createClerkBridge before creating renderer windows to set an app-specific product token, such as Acme Co/1.0.0. Clerk preserves Electron's platform details, such as Macintosh or Windows, while using the product token for UserProfile session activity attribution:
createClerkBridge({
storage: storage(),
renderer: { scheme: 'my-app', host: 'renderer' },
userAgent: 'Acme Co/1.0.0',
});
@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.
Replace {fapi_host} below with your instance's Frontend API host, found in the Clerk Dashboard 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.
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';
[!NOTE] 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 for the full list.
Apply it either with a <meta> tag in your renderer HTML:
<meta
http-equiv="Content-Security-Policy"
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';"
/>
or, as Electron's security guide recommends, as a response header from the main process:
// main.ts
import { app, session } from 'electron';
const fapiHost = 'clerk.your-app.com';
app.whenReady().then(() => {
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
'Content-Security-Policy': [
[
"default-src 'self'",
`script-src 'self' 'unsafe-inline' https://${fapiHost} https://challenges.cloudflare.com`,
`connect-src 'self' https://${fapiHost}`,
"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'",
].join('; '),
],
},
});
});
});
[!NOTE] Loading the renderer from a dev server (such as Vite) requires looser rules for HMR: add
'unsafe-eval'toscript-srcand your dev server's origin toconnect-src(for example,ws://localhost:<port> http://localhost:<port>). Many apps skip CSP during development and apply it only to packaged builds.
Passkey support works in two modes, selected automatically per request:
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).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 native module.Native mode requires the optional native module:
pnpm add @clerk/electron-passkeys
// main process
import { createClerkBridge } from '@clerk/electron';
import { storage } from '@clerk/electron/storage';
createClerkBridge({ storage: storage(), passkeys: true });
// preload script
import { exposeClerkBridge } from '@clerk/electron/preload';
exposeClerkBridge({ passkeys: true });
// renderer process (React)
import { ClerkProvider } from '@clerk/electron/react';
import { passkeys } from '@clerk/electron/passkeys';
<ClerkProvider
publishableKey={publishableKey}
passkeys={passkeys}
>
{/* ... */}
</ClerkProvider>;
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():
// renderer process (vanilla)
import { Clerk } from '@clerk/clerk-js';
import { createPasskeyProvider } from '@clerk/electron/passkeys';
const clerk = new Clerk(publishableKey);
createPasskeyProvider(clerk);
await clerk.load();
Like passkeys on iOS, the macOS platform APIs require a verified association between your app and your domain:
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.Quick Tips (each of these failure modes produces the same opaque "not associated with domain" error):
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)..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.swcd when the app launches; verify with sudo swcutil show. If state gets stuck, sudo swcutil reset and relaunch.?mode=developer + sudo swcutil developer-mode -e true exists but is often flaky.Windows has no equivalent requirement. On Linux there is no native path; passkeys work in renderer mode only (including external security keys).
For help, visit our support page.
We're open to all community contributions! If you'd like to contribute in any way, please read our contribution guidelines and code of conduct.
@clerk/electron follows good practices of security, but 100% security cannot be assured.
@clerk/electron is provided "as is" without any warranty. Use at your own risk.
For more information and to report security issues, please refer to our security documentation.
This project is licensed under the MIT license.
See LICENSE for more information.
FAQs
Clerk SDK for Electron
We found that @clerk/electron demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 6 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
11 malicious NuGet tools pose as game cheats to deploy Windows payloads, track hosts, and use Google Sheets for telemetry and control.

Research
/Security News
4 compromised asyncapi packages deliver miasma botnet loader on macOS, Linux and Windows.

Research
/Security News
A compromised jscrambler npm release added a malicious preinstall hook that runs hidden native binaries on Linux, macOS, and Windows.