Configure passkeys for Electron, Beta
Passkeys are a secure, passwordless sign-in method: users authenticate with biometrics and a physical device. In Electron, the Clerk Electron SDK chooses one of two WebAuthn implementations per request:
- Native mode is the path for the typical Electron app, whose window loads a local bundle from a custom scheme (or the dev server) rather than a page on your domain. Clerk routes the ceremony over IPC to the main process, and the OS services it: AuthenticationServices on macOS and
webauthn.dllon Windows, through the optional@clerk/electron-passkeysnative module. There's no native path on Linux. - Renderer mode uses Chromium's built-in WebAuthn. It only works when the window's origin can satisfy the passkey's relying party (RP) ID, which Clerk sets to your instance's Frontend API host for native clients: in practice, an
https://page served from your own domain. It doesn't work onhttp://localhostor a custom scheme; there, the sign-in form reports "Passkeys are not supported on this device." Windows Hello works out of the box; Touch ID on macOS requires Electron 42 or later andapp.configureWebAuthn().
Passkey autofill (conditional UI) is only available in renderer mode. In native mode, passkeys are offered through the explicit Use passkey action; the sign-in form never opens a passkey prompt on its own.
Enable passkeys
In the Clerk Dashboard, navigate to the User & authentication page and enable Sign-in with passkey.
Pass passkeys to <ClerkProvider>
Passkey code is only bundled and initialized when the passkeys prop is set. This alone is enough for renderer mode; native mode needs the next two steps as well.
import { passkeys } from '@clerk/electron/passkeys'
import { ClerkProvider } from '@clerk/electron/react'
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'
import './index.css'
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
if (!PUBLISHABLE_KEY) {
throw new Error('Add your Clerk Publishable Key to the .env file')
}
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ClerkProvider publishableKey={PUBLISHABLE_KEY} passkeys={passkeys}>
<App />
</ClerkProvider>
</StrictMode>,
)Renderer mode now works on https:// origins that match your RP ID. Local bundles, custom schemes, and the dev server need native mode; the remaining steps add it.
Install the native module
Install the package:
npm install @clerk/electron-passkeyspnpm add @clerk/electron-passkeysyarn add @clerk/electron-passkeysbun add @clerk/electron-passkeysThe package ships prebuilt binaries for macOS (arm64, x64) and Windows (arm64, x64) as optional platform packages. Install it before the next step: createClerkBridge({ passkeys: true }) loads the module at startup, and when it's missing, native requests report passkey_not_supported and every launch logs:
Clerk: createClerkBridge({ passkeys: true }) requires the optional @clerk/electron-passkeys package. Install it with your package manager to enable native passkey support.Enable the native bridge in the main and preload processes
Set passkeys: true in both processes:
import { createClerkBridge } from '@clerk/electron'
import { storage } from '@clerk/electron/storage'
createClerkBridge({
passkeys: true,
storage: storage(),
})import { exposeClerkBridge } from '@clerk/electron/preload'
exposeClerkBridge({ passkeys: true })Associate your macOS app with your domain
Like passkeys on iOS, the macOS platform APIs require a verified association between your app and your Clerk domain. Windows has no equivalent requirement.
- In the Clerk Dashboard, navigate to the Native applications page and ensure the Native API is enabled.
- Select the iOS tab, then select Add iOS App. Enter your app's App ID Prefix and Bundle ID. An Electron macOS app uses the same configuration as an iOS app.
- Sign your app with the
com.apple.developer.associated-domainsentitlement containingwebcredentials:<your Frontend API host>. 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 includecom.apple.application-identifierandcom.apple.developer.team-identifiermatching the profile.
- Name
createPasskeyProvider(clerk, options?)- Type
(clerk: Clerk, options?: { mode?: 'auto' | 'renderer' | 'native' }) => PasskeySupport- Description
Wires passkey support into a
@clerk/clerk-jsinstance you manage yourself. Call it beforeclerk.load().
- Name
createPasskeys(options?)- Type
(options?: { mode?: 'auto' | 'renderer' | 'native' }) => PasskeySupport- Description
Creates a passkey provider with an explicit mode.
auto(the default) takes the renderer path when the window's origin can satisfy the requested RP ID (anhttps://origin matching it) and the native path otherwise; on macOS with Electron older than 42 it prefers native when the module is available. Inautomode, a renderer request that fails with an RP ID mismatch or an unsupported-authenticator error is retried natively when the native module is available; user cancellation isn't retried.
- Name
passkeys- Type
PasskeySupport- Description
Ready-to-use passkey implementation for the
<ClerkProvider>passkeysprop. Chooses renderer or native WebAuthn automatically per request.
Use passkeys without <ClerkProvider>
The following example wires passkey support into a @clerk/clerk-js instance you manage yourself:
import { Clerk } from '@clerk/clerk-js'
import { createPasskeyProvider } from '@clerk/electron/passkeys'
const clerk = new Clerk(PUBLISHABLE_KEY)
createPasskeyProvider(clerk)
await clerk.load()Usage
To learn how to create, delete, and authenticate with passkeys in your own UI, see the custom flow guide.
Feedback
Last updated on