TL;DR: Cloud-synced passkeys + services skipping 2FA = your cloud account becomes a single point of failure. I built LocalPasskey, a macOS app that stores passkeys in Secure Enclave with no cloud sync.
Passkeys Skip 2FA
Many servicesâGoogle, GitHub, and othersâskip TOTP and other second-factor verification when you authenticate with a passkey. Passkeys are treated as a strong single factor because they're phishing-resistant and cryptographically secure.
This seems reasonable. But combined with how passkeys are currently implemented, it creates a serious security hole.
Cloud Sync by Default
iCloud Keychain, Google Password Manager, 1Password, Bitwardenâevery major passkey implementation is built around cloud synchronization.
There is no option to store passkeys locally.
The Attack Scenario
Consider this:
- You enable 2FA on GitHub, Google, and other critical services
- You register passkeys for these services, stored in iCloud Keychain
- Service providers skip 2FA when you use passkeys
- An attacker compromises your Apple account
- All your passkeys sync to the attacker's device
- The attacker now has access to all your servicesâbypassing the 2FA you set up
The 2FA you carefully configured becomes useless. Your Apple account is now a single point of failure for everything.
The Root Cause
The WebAuthn spec actually includes BE (Backup Eligible) and BS (Backup State) flags. Service providers can use these to distinguish between cloud-synced and device-bound passkeys. This enables risk-based authentication: require additional verification for synced passkeys, trust device-bound ones.
But two problems prevent this from working:
- Service providers ignore the flags: Most services skip 2FA regardless of passkey type
- No local-only option exists: Neither Apple nor major password managers offer a way to create device-bound passkeys
Unlike the first problemâwhich requires service providers to actâthe second problem can be solved on the client side.
I Built LocalPasskey
Shameless plug: I built a macOS app that solves this.
LocalPasskey
A macOS passkey manager that stores credentials locally in the Secure Enclave.
Motivation
Modern passkey solutionsâwhether Apple's iCloud Keychain or third-party password managers like 1Password, Bitwarden, and Dashlaneâare designed around cloud synchronization. While this provides convenience, it turns your cloud account into a single point of failure for all your passkey-protected services.
The Single Point of Failure Problem
Many service providers skip TOTP or other second-factor verification when users authenticate with passkeys, treating them as a strong single factor. This seems reasonableâpasskeys are phishing-resistant and cryptographically secure. However, when passkeys are synced to the cloud, this creates a dangerous single point of failure:
- You enable 2FA on important services (GitHub, Google, etc.)
- You register passkeys for these services, stored in iCloud Keychain
- Service providers skip 2FA when you use passkeys
- An attacker compromises your Apple account
- All your passkeys sync to the attacker's device
- The attacker now has accessâŚ
Private keys are stored in the Secure Enclave:
- Cannot be extracted, even with root access
- Can only be used for signing after biometric verification
- Never synced to iCloud
Even if your Apple account is compromised, your passkeys remain safe.
Implementation Details
The key settings are:
-
kSecAttrTokenIDSecureEnclave: Generate the key inside the Secure Enclave -
kSecAttrAccessibleWhenUnlockedThisDeviceOnly: Only accessible on this device (prevents iCloud sync) -
.privateKeyUsage, .biometryAny: Require biometric authentication to use the private key
Even if the app has vulnerabilities, even if an attacker gains root access to your machine, the private keys cannot be extracted.
let accessControl = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
[.privateKeyUsage, .biometryAny],
nil
)!
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrAccessControl as String: accessControl,
kSecAttrAccessGroup as String: accessGroup,
],
]
var error: Unmanaged<CFError>?
guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
throw error!.takeRetainedValue()
}
Usage
Installation
Trusting an individual developer to improve your security is questionable at best. I recommend reading the code and building it yourself.
You only need to check here. Verify that keys are stored in a way that prevents extraction.
If you do trust me, you can download the dmg from https://github.com/malt03/local-passkey-manager/releases.
Setup
- Open System Settings > General > AutoFill & Passwords
- Enable LocalPasskey under "AutoFill from"
Known Bugs (Apple's Fault)
Apple's Credential Provider Extension has bugs that prevent LocalPasskey from reporting accurate credential information. These don't affect securityâyour private keys are still protectedâbut the metadata is wrong:
- BE/BS flags are forced to 1: Relying parties can't distinguish device-bound from cloud-synced credentials (https://developer.apple.com/forums/thread/813844)
- AAGUID is overwritten to zeros: Relying parties can't identify which passkey manager created a credential (https://developer.apple.com/forums/thread/814547)
Apple hasn't responded to either issue. If you care about proper passkey implementation on macOS, please boost these threads.
Final Thoughts
This app shouldn't need to exist. If Apple added a "store locally only" option to their Passwords app, that would be the end of it.
If this post gets enough attention and Apple decides to implement this natively, that would be the best outcome.
Further Reading
- Device-Device-Bound vs. Synced Credentials: A Comparative Evaluation of Passkey Authentication - University of Oslo
- Detecting Compromise of Passkey Storage on the Cloud - Microsoft Research
- Your (Synced) Passkey is Weak - DEFCON 33
- How Attackers Bypass Synced Passkeys - The Hacker News

Top comments (0)