EmDash Passkey Authentication: No Passwords, No Sessions
Every WordPress site has a wp-login.php page. Attackers know this. They hammer it with credential stuffing attacks, phishing campaigns, and brute-force attempts. This is so common it's basically background noise for anyone who runs a WordPress site — you see it in the logs every day. EmDash takes a fundamentally different approach: there is no password. There is no login form. There is no session cookie. Authentication is built on passkeys, and the attack surface for the entire authentication system is a fraction of what traditional CMS platforms carry.
What Passkeys Actually Are
A passkey is a hardware-backed cryptographic credential. When you register a passkey, your device generates a public/private key pair. The private key stays on your device — protected by your device's secure enclave, never transmitted, never stored on a server. The public key is registered with the service. When you authenticate, your device signs a challenge with the private key. The server verifies the signature with the public key. No password is exchanged. No secret leaves your device.
The underlying standard is WebAuthn (Web Authentication), part of the FIDO2 specification. Every major browser and operating system supports it: Touch ID on Mac and iPhone, Face ID on iPhone, Windows Hello on PC, and hardware security keys (YubiKey, etc.) everywhere. If your device has a biometric sensor or a PIN, it supports passkeys.
How EmDash Uses Passkeys
The first time you navigate to /_emdash/admin on a fresh EmDash installation, a setup wizard runs. It walks you through creating your admin account: you provide a name and email, then your browser prompts you to create a passkey using your device's authentication method. Touch ID on a Mac, Face ID on an iPhone, Windows Hello on a PC. That passkey is your permanent credential. There's no password ever created, stored, or required.
Every subsequent admin login is the same: navigate to /_emdash/admin, your browser prompts for your passkey, you authenticate with a tap or glance, and you're in. The entire flow takes under 5 seconds and requires no password manager.
What This Eliminates
Passkey authentication eliminates entire categories of attacks that are standard threats for password-based CMS platforms:
- Credential stuffing: no credentials to stuff. There's no password that could be leaked from a data breach elsewhere and tried here.
- Phishing: passkeys are bound to the specific domain they were created on. A fake login page on a different domain can't steal them.
- Brute force: there's no password to guess. The cryptographic challenge/response means every login attempt requires possession of the physical device.
- Session hijacking: EmDash doesn't issue long-lived session cookies. Authentication state is short-lived and backed by cryptographic proof.
- CSRF attacks: without session cookies, traditional CSRF attack vectors don't apply.
Passkeys don't just make authentication harder to attack — they eliminate entire attack categories. Credential stuffing, phishing, brute force, session hijacking: all of these require a password or session token to exist. Remove those, and the attacks have nothing to target.
Passkeys vs Passwords: A Direct Comparison
| Property | EmDash (Passkeys) | WordPress (Passwords) |
| Credential storage | Private key stays on device, never transmitted | Hashed password stored in database |
| Phishing risk | None — domain-bound | High — fake login pages work |
| Brute force risk | None — no guessable secret | High without lockout plugins |
| Session cookies | Not used for auth state | Long-lived, hijackable |
| CSRF exposure | Eliminated by design | Requires nonce management |
| Login UX | One tap (Touch ID / Face ID / Hello) | Username + password, possibly 2FA |
Device Support
Passkeys work on any modern device. On macOS and iOS, that's Touch ID or Face ID via Safari, Chrome, or Firefox. On Windows, it's Windows Hello (facial recognition, fingerprint, or PIN). On Android, it's the device biometric. Hardware security keys like YubiKey work everywhere as a fallback and are the right choice for high-security environments. The only requirement is a device manufactured in the last several years — passkey support is ubiquitous in 2024 and beyond.
What Happens If You Lose Your Device
This is the question everyone asks. With passkeys, losing your device doesn't mean you're locked out permanently — but it requires planning. The recommended approach is to register multiple passkeys: your primary device, a secondary device, and a hardware key stored somewhere secure. EmDash allows multiple passkeys per account for this reason.
If you lose access to all your registered devices without a backup, account recovery requires direct database access. This is intentional — it means there's no "forgot password" email flow that an attacker could abuse. Recovery is an admin-level operation, not a self-service one. For most site owners, having a backup passkey on a second device (or a YubiKey in a drawer) is the right operational posture.
The Security Model in Practice
I've been running EmDash sites since early access. What I notice most in the logs is what's missing: there are no failed login attempts, no bot traffic to the admin URL, no rate limit events. The attackers probe /wp-login.php and /admin and find nothing they can work with. The admin endpoint exists, but without a password field to attack, the entire class of automated credential attacks becomes irrelevant.
This is one of the places where EmDash's design philosophy shows clearly: instead of adding security on top of an insecure foundation, it removes the insecure foundation entirely. You can't harden a password-based auth system into being as secure as a system that never had passwords.
# What WordPress admin logs look like (typical daily sample)
# GET /wp-login.php - 404.21.103.45 - 401 - bot
# POST /wp-login.php - 191.101.22.8 - 401 - credential stuffing
# POST /wp-login.php - 103.56.91.2 - 401 - credential stuffing
# POST /xmlrpc.php - 45.142.120.3 - 403 - brute force
# What EmDash admin logs look like
# POST /_emdash/api/auth/begin - 73.xxx.xxx.xxx - 200 - passkey challenge
# POST /_emdash/api/auth/complete - 73.xxx.xxx.xxx - 200 - verified