Passkeys vs OIDC vs Magic Links: Which Authentication Method Should I Use?
A practical developer’s guide to choosing between passkeys, OpenID Connect, and magic links. Compare security, user experience, and implementation effort with concrete examples and a decision framework.
TL;DR: Passkeys offer the strongest phishing resistance and fastest UX but require device/browser support. OIDC (e.g., Sign in with Google) reduces password fatigue and implementation overhead but ties you to an identity provider. Magic links are simple to build but suffer from email deliverability issues and token theft risks. Choose passkeys for high-security apps, OIDC for enterprise or social login, and magic links only as a fallback or for low-risk onboarding.
Core Mechanics: How Each Method Works
Passkeys use public-key cryptography with a device-protected private key, magic links rely on email-delivered tokens, and OpenID Connect (OIDC) delegates authentication to an identity provider using the Authorization Code flow with PKCE.
Passkeys generate a key pair on the client during registration. The private key stays on the user’s device (and can be synced across devices via platform keychains like iCloud Keychain). The server stores only the public key. Authentication requires a local biometric or device PIN check, then a cryptographic signature that proves possession of the private key without revealing it. A typical WebAuthn registration call looks like:
const credential = await navigator.credentials.create({
publicKey: {
challenge: new Uint8Array(serverChallenge),
rp: { name: "Example App" },
user: { id: userId, name: userEmail, displayName: userEmail },
pubKeyCredParams: [{ type: "public-key", alg: -7 }],
}
});
// Send credential.response to server; server stores the public key.
Magic links send a one-time URL to the user’s email. The link contains a unique token. When clicked, the server validates the token (ideally comparing a hash of the token against a stored hash) and creates a session. The entire flow depends on email delivery and the security of the inbox. A secure implementation hashes the token before storage:
const token = crypto.randomBytes(32).toString('hex');
const digest = crypto.createHash('sha256').update(token).digest('hex');
await db.insert({ email, token: digest, expiresAt: Date.now() + 15*60*1000 });
// Send email with link: https://app.example.com/verify?token=<token>
OpenID Connect (OIDC) is an identity protocol built on OAuth 2.0. The user is redirected to an identity provider (IdP) where they authenticate (often with a password, passkey, or social account). The IdP returns an authorization code, which the application exchanges for an ID token and access token. The ID token contains claims about the user. Using the Authorization Code flow with PKCE prevents interception of the authorization code. The initial redirect includes a code challenge:
GET /authorize?response_type=code
&client_id=CLIENT_ID
&redirect_uri=https://app.example.com/callback
&scope=openid+profile+email
&code_challenge=CHALLENGE
&code_challenge_method=S256
The server then exchanges the code for tokens via a back-channel POST, including the code verifier.
Security Comparison: Threats and Mitigations
Passkeys provide the strongest security posture, inherently resisting phishing and never exposing shared secrets, while magic links are the most vulnerable due to their reliance on email security; OIDC's security is entirely dependent on the identity provider's implementation.
Phishing resistance: Passkeys are bound to the origin (relying party ID) by the browser, so a fake site cannot complete the authentication ceremony. Magic links are highly susceptible to phishing—an attacker can trick a user into clicking a malicious link that forwards the token, leading to account takeover. OIDC's resistance depends on the IdP; if the IdP supports phishing-resistant authentication (e.g., passkeys or hardware tokens), the overall flow can be phishing-resistant.
Token security: Magic link tokens travel through email, which is not end-to-end encrypted by default. If an attacker intercepts the email or gains access to the inbox, they can sign in. Storing a hashed token (e.g., SHA-256) in the database prevents token reuse if the database is breached, but the token in transit remains vulnerable. OIDC authorization codes are short-lived and can be bound to a PKCE code_challenge, making intercepted codes useless without the code_verifier. Passkeys never expose a shared secret; the private key never leaves the device.
Account recovery: Magic link recovery is often just another magic link, so email compromise is catastrophic. OIDC recovery depends on the IdP's recovery process. Passkey recovery relies on platform keychain sync or pre-configured recovery methods; if a user loses all synced devices and has no recovery key, account access can be lost.
User Experience Trade-offs
Magic links trade password memory for email dependency, passkeys offer the fastest login but stumble on enrollment and cross-device flows, and OIDC reduces credential fatigue at the cost of redirect friction and privacy concerns. Magic links force users to leave the application, wait for an email that may be delayed or land in spam, and then click a link that can open in the wrong browser on shared devices. This inbox clutter and context switching turns a supposedly seamless flow into a frustrating wait. Passkeys deliver a near-instant biometric scan—often completing authentication in seconds—but the initial enrollment can confuse if system prompts aren't explained. Cross-device authentication via QR code scanning fails when users don't understand they must use the same device that holds the passkey. Common UX mistakes include silently falling back to passwords and showing cryptic errors when a passkey isn't available, eroding trust. OIDC provides a familiar “Sign in with Google/Apple” button, eliminating the need to create and remember another password. However, the redirect to the identity provider and consent screen adds extra steps, and users may hesitate to grant data access to the application. In enterprise settings, OIDC enables single sign-on across multiple services, which is a significant UX win by reducing repeated logins.
Implementation Complexity and Pitfalls
Magic links are the simplest to implement but the hardest to secure correctly; OIDC with PKCE adds moderate backend complexity; passkeys offer the strongest security but demand careful cross-device UX design. Each method has distinct pitfalls that can undermine the entire flow.
For magic links, the critical mistake is storing the plaintext token. Always hash the token before persisting it, as shown below, and set a short expiration (e.g., 10 minutes) while invalidating the token immediately after use.
const crypto = require('crypto');
const token = crypto.randomBytes(32).toString('hex');
const digest = crypto.createHash('sha256').update(token).digest('hex');
await db.insert({ email, token: digest, expiresAt: Date.now() + 600000 });
const link = `https://example.com/verify?token=${token}&email=${email}`;
OIDC with PKCE requires a two-step flow. The client first redirects to the authorization endpoint with a code_challenge and code_challenge_method=S256, then exchanges the returned code for tokens on the backend, including the code_verifier. A common pitfall is using the implicit flow (response_type=id_token), which cannot use PKCE and exposes tokens in the URL. The correct redirect is:
https://idp.example.com/authorize?
response_type=code&
client_id=CLIENT_ID&
redirect_uri=https://example.com/callback&
scope=openid profile email&
code_challenge=CODE_CHALLENGE&
code_challenge_method=S256&
state=STATE
Passkeys rely on the WebAuthn API: navigator.credentials.create() for registration and navigator.credentials.get() for authentication. Server-side libraries handle challenge generation and credential verification. The biggest pitfalls are failing to offer a cross-device QR flow for users who registered on a different device, not handling errors gracefully (e.g., when a user cancels the biometric prompt), and neglecting to test across multiple platforms and browsers.
When to Use Which: Decision Framework
Prioritize passkeys for phishing-resistant, high-conversion flows; use OIDC for enterprise SSO and broad device coverage; reserve magic links for low-risk, one-time access or as a last-resort fallback. The decision tree is: if the device supports passkeys, offer them first; otherwise, present OIDC (e.g., “Sign in with Google”) and a magic link option, ensuring no user is locked out.
A typical client-side check:
if (window.PublicKeyCredential) {
// Primary: passkey authentication
showPasskeyButton();
} else {
// Fallbacks: OIDC and magic link
showOIDCButton('google');
showMagicLinkOption();
}
On the server, always provide a fallback endpoint. For sensitive applications, never rely solely on magic links—email deliverability and security issues make them unsuitable as the only method. OIDC serves as a strong fallback because it leverages existing identity providers and supports enterprise SSO, while magic links can handle edge cases like one-time access or newsletter sign-in. This layered approach ensures every user has a viable, secure path.
FAQ
Are passkeys really phishing-resistant?
Yes. Passkeys are bound to the origin (relying party ID) that created them. The browser will not complete the authentication ceremony on a lookalike site, so an attacker cannot steal the credential even if the user is tricked.
Can I use magic links securely?
You can reduce risk by hashing the token in the database, enforcing single use and short expiration, and sending the link over HTTPS. However, the token still transits through the user’s email, which is not end-to-end encrypted. Email account compromise or network interception can lead to account takeover.
Does OIDC eliminate passwords entirely?
Not necessarily. OIDC delegates authentication to an identity provider, which may still use passwords. However, if the IdP supports passwordless methods (like passkeys), the end user can have a fully passwordless experience. OIDC itself is a protocol, not an authentication mechanism.
What happens if a user loses their passkey device?
Modern passkeys sync across devices via platform keychains (e.g., iCloud Keychain, Google Password Manager). If a user loses all synced devices and has no recovery method set up, they may be locked out. Always provide account recovery options, such as a backup magic link or a recovery code.
Which method is easiest to implement?
Magic links are the simplest to prototype—just generate a token, send an email, and verify the hash. OIDC requires integrating an SDK or handling the authorization code flow, but libraries exist for most frameworks. Passkeys involve WebAuthn API calls and server-side challenge management, which is more complex but well-supported by libraries like SimpleWebAuthn.
References for further reading
Sources consulted while researching this guide, included so you can verify the details and go deeper. Listing them is not a claim that every line was independently fact-checked.
- Passwordless Authentication: Magic Links vs Passkeys vs ...
- Magic Links vs Passkeys: What's the Difference?
- Magic Links - A Guide to Passwordless Authentication
- OTP vs Magic link: Choosing the right passwordless ...
- The top 7 passwordless authentication solutions for developers
I packaged the setup above into a ready-to-use kit — **Passkeys-First Authentication Architecture Pack (16 Items)* — for anyone who'd rather copy-paste than wire it from scratch: https://unfairhq.gumroad.com/l/tbefx.*
Top comments (0)