Passwords Are a 60-Year-Old Mistake

The password was introduced to computing in 1961 at MIT’s Compatible Time-Sharing System. It was a temporary hack. Sixty-four years later, it remains the dominant authentication mechanism on the internet — and it’s responsible for over 80% of data breaches.

The problems are well-documented:

  • Humans reuse passwords across services
  • Phishing tricks users into handing them over
  • Credential stuffing automates breach exploitation at scale
  • Password managers help but add complexity most users avoid
  • Even hashed passwords in databases get cracked with modern GPUs

Passkeys are the industry’s answer. Backed by Apple, Google, Microsoft, and the FIDO Alliance, they replace the shared-secret model entirely.

How Passwords Actually Fail

Traditional Password Flow:
User → [password] → Network → Server stores hash

Attack vectors:
  ✗ Phishing       → User gives password to fake site
  ✗ Credential DB  → Server breach exposes hashes
  ✗ Reuse          → One breach compromises all accounts
  ✗ Brute force    → Weak passwords cracked offline
  ✗ MitM           → SSL-strip + fake login page
  ✗ Keylogger      → Malware captures keystrokes

Every step in the password lifecycle is an attack surface. The secret must be transmitted, stored, and compared — each operation is a vulnerability.

What Are Passkeys?

Passkeys are FIDO2/WebAuthn credentials — public-key cryptography applied to authentication. Instead of a shared secret, you generate a unique key pair for each service:

Passkey Flow:
Registration:
  Device generates keypair (private + public)
  Private key → stored on device (never leaves)
  Public key → sent to server

Authentication:
  Server sends challenge (random nonce)
  Device signs challenge with private key
  Server verifies signature with stored public key

The private key never leaves your device. There’s no secret to phish, no hash to crack, no credential to stuff.

The Cryptography Under the Hood

WebAuthn uses asymmetric cryptography with COSE (CBOR Object Signing and Encryption) algorithms:

Supported algorithms:
  - ES256 (ECDSA with P-256 curve) — most common
  - RS256 (RSASSA-PKCS1-v1_5)
  - Ed25519 (Edwards-curve) — gaining adoption

Registration ceremony:
  1. Server → Client: challenge, user info, relying party ID
  2. Client → Authenticator: create credential request
  3. Authenticator → generates keypair, stores private key
  4. Authenticator → Client: attestation object
     (public key + credential ID + signature)
  5. Client → Server: attestation for verification and storage

Authentication ceremony:
  1. Server → Client: challenge + allowed credential IDs
  2. Client → Authenticator: get assertion request
  3. Authenticator → biometric/PIN verification
  4. Authenticator → signs challenge with private key
  5. Client → Server: assertion (signature + authenticator data)
  6. Server → verifies signature against stored public key

Why Passkeys Win

Phishing Resistant

Passkeys are origin-bound. The credential is cryptographically tied to the domain that created it. A phishing site at g00gle.com cannot use a credential created for google.com:

Credential scope:
  rpId: "google.com"
  origin: "https://google.com"

Phishing attempt from "https://g00gle.com":
  → Authenticator refuses to sign
  → Attack fails silently

This is the single biggest advantage over passwords. Phishing resistance is built into the protocol, not dependent on user vigilance.

No Server-Side Secrets

Password database breach:
  Attacker gets: bcrypt($password, $salt)
  Risk: Offline cracking → plaintext passwords

Passkey database breach:
  Attacker gets: public keys
  Risk: None. Public keys are... public.

No Credential Reuse

Each passkey is unique to a service. Compromising one reveals nothing about others:

google.com  → unique keypair A
github.com  → unique keypair B
bank.com    → unique keypair C

Breach of google.com → keypair A's public key leaked
Impact on github.com → zero
Impact on bank.com   → zero

Replay Resistant

Each authentication includes a server-generated challenge (nonce) and a signature counter:

{
  "challenge": "dGhpcyBpcyBhIHJhbmRvbSBjaGFsbGVuZ2U",
  "signCount": 42,
  "signature": "MEUCIQC..."
}

Replaying an old authentication response fails because the challenge and counter have changed.

Implementing Passkeys (Server-Side)

Registration Endpoint

// Node.js with @simplewebauthn/server
import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
} from '@simplewebauthn/server';

// Step 1: Generate registration options
app.post('/api/passkey/register/options', async (req, res) => {
  const user = await getUser(req.session.userId);

  const options = await generateRegistrationOptions({
    rpName: 'MyApp',
    rpID: 'myapp.com',
    userID: user.id,
    userName: user.email,
    attestationType: 'none',
    authenticatorSelection: {
      residentKey: 'preferred',
      userVerification: 'preferred',
    },
  });

  // Store challenge for verification
  req.session.currentChallenge = options.challenge;
  res.json(options);
});

// Step 2: Verify registration response
app.post('/api/passkey/register/verify', async (req, res) => {
  const verification = await verifyRegistrationResponse({
    response: req.body,
    expectedChallenge: req.session.currentChallenge,
    expectedOrigin: 'https://myapp.com',
    expectedRPID: 'myapp.com',
  });

  if (verification.verified) {
    // Store credential in database
    await storeCredential(req.session.userId, {
      credentialID: verification.registrationInfo.credentialID,
      publicKey: verification.registrationInfo.credentialPublicKey,
      counter: verification.registrationInfo.counter,
    });
  }

  res.json({ verified: verification.verified });
});

Client-Side Integration

// Browser-side passkey registration
async function registerPasskey() {
  // Get options from server
  const optionsRes = await fetch('/api/passkey/register/options', {
    method: 'POST',
  });
  const options = await optionsRes.json();

  // Create credential via WebAuthn API
  const credential = await navigator.credentials.create({
    publicKey: {
      ...options,
      challenge: base64URLToBuffer(options.challenge),
      user: {
        ...options.user,
        id: base64URLToBuffer(options.user.id),
      },
    },
  });

  // Send to server for verification
  await fetch('/api/passkey/register/verify', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(credential),
  });
}

The Transition Problem

Passkeys aren’t a drop-in replacement. The transition creates real challenges:

Account Recovery

With passwords, you reset via email. With passkeys, if you lose all your devices:

Recovery options:
  1. Synced passkeys (iCloud Keychain, Google Password Manager)
     → Survives device loss if you keep cloud access
  2. Multiple registered authenticators
     → Hardware key as backup
  3. Fallback to password + MFA
     → Defeats the purpose but prevents lockout
  4. Recovery codes
     → Old school but effective

Platform Fragmentation

Apple ecosystem  → iCloud Keychain sync
Google ecosystem → Google Password Manager sync
Windows          → Windows Hello
Cross-platform   → Hardware keys (YubiKey), 1Password, Bitwarden

Problem: Passkey created on iPhone doesn't auto-sync to Windows PC
Solution: QR-code cross-device authentication (CTAP 2.2)

Enterprise Considerations

  • Attestation requirements (which authenticators are allowed?)
  • Managed device policies
  • Compliance with regulations that mandate specific auth methods
  • Migration strategy for millions of existing password-based accounts

Passkeys vs MFA

A common question: “If I have passwords + TOTP, isn’t that enough?”

Password + TOTP:
  ✓ Two factors (knowledge + possession)
  ✗ Password still phishable
  ✗ TOTP codes are phishable in real-time (Evilginx)
  ✗ User friction (typing codes, managing apps)

Passkey alone:
  ✓ Two factors built-in (possession + biometric/PIN)
  ✓ Phishing resistant by design
  ✓ Better UX (tap fingerprint, done)
  ✗ Single device dependency (mitigated by sync)

A single passkey is cryptographically stronger than password + SMS + TOTP combined.

The Future

The password isn’t dying tomorrow. But the trajectory is clear:

  • 2024-2025: Major platforms support passkeys alongside passwords
  • 2025-2027: Passkey-first authentication becomes default for new accounts
  • 2027-2030: Password-optional accounts become the norm
  • 2030+: Passwords relegated to legacy system access

The shared secret had a good run. Sixty years is enough. The future is asymmetric.