Skip to content

feat: add WebAuthn passkey authentication - #485

Merged
Penielka merged 4 commits into
AetherEdu:mainfrom
Chigybillionz:feat/409-webauthn-passkey-auth
Aug 30, 2026
Merged

feat: add WebAuthn passkey authentication#485
Penielka merged 4 commits into
AetherEdu:mainfrom
Chigybillionz:feat/409-webauthn-passkey-auth

Conversation

@Chigybillionz

Copy link
Copy Markdown
Contributor

PR Title

feat: add WebAuthn passkey authentication

────────────────────────────────────────────────────────────────────────────────

PR Description

Summary

Implement passwordless authentication via WebAuthn passkeys as a primary or second factor, with device management and recovery flows. This adds full FIDO2/WebAuthn support to both backend and frontend while preserving existing password-based authentication for legacy users.

Issue

Closes #409

Root Cause

The platform currently only supports email/password authentication. There was no WebAuthn/passkey infrastructure, making passwordless authentication and hardware security key support impossible.

Solution Implemented

Built a complete WebAuthn passkey system using @simplewebauthn/server (backend) and @simplewebauthn/browser (frontend), following the FIDO2 spec. The implementation covers:

  1. Passkey Registration — Users register passkeys linked to their account with device naming
  2. Passkey Login — Passwordless authentication via browser-native WebAuthn ceremony
  3. MFA Second Factor — Passkeys usable as a second factor after password login
  4. Device Management — List and revoke registered passkeys
  5. Recovery Codes — 10 one-time-use codes generated on registration for account recovery
  6. Fallback Auth — Existing password-based flow completely untouched

Key Changes

  • Added Passkey Mongoose model for storing WebAuthn credentials, counters, and recovery codes
  • Created passkeys.ts service encapsulating all WebAuthn ceremony logic (registration options/verify, authentication options/verify, device CRUD, recovery code verification)
  • Created passkeyAuth.ts routes with 10 endpoints covering registration, login, MFA, device management, and recovery flows
  • Mounted routes at /api/auth/passkeys and /api/v1/auth/passkeys (versioned)
  • Created 5 frontend components: PasskeyRegistration , PasskeyLogin , PasskeyDeviceManagement , PasskeyMFA , PasskeyRecovery
  • Extended AuthContext with loginWithPasskey , registerPasskey , verifyMFA methods
  • Added unit tests for the passkey service

Affected Files

  • backend/src/models/Passkey.ts — New Mongoose model for passkey credentials
  • backend/src/services/auth/passkeys.ts — New WebAuthn service (registration, auth, device mgmt, recovery)
  • backend/src/routes/passkeyAuth.ts — New Express routes for all passkey endpoints
  • backend/src/index.ts — Mounted passkey routes at /api/auth/passkeys and /api/v1/auth/passkeys
  • backend/package.json — Added @simplewebauthn/server dependency
  • frontend/package.json — Added @simplewebauthn/browser dependency
  • frontend/src/components/auth/PasskeyRegistration.tsx — Passkey registration UI with recovery codes display
  • frontend/src/components/auth/PasskeyLogin.tsx — Passwordless login UI with password fallback
  • frontend/src/components/auth/PasskeyDeviceManagement.tsx — Device list and revoke UI
  • frontend/src/components/auth/PasskeyMFA.tsx — MFA second-factor verification UI
  • frontend/src/components/auth/PasskeyRecovery.tsx — Recovery code flow UI
  • frontend/src/contexts/AuthContext.tsx — Extended with passkey auth methods
  • backend/src/tests/passkeyService.test.ts — Unit tests for passkey service

Trade-offs / Considerations

  • Challenge storage: Registration/authentication challenges are stored in-memory on the request object ( req.passkeyChallenge ). In production, this should be moved to Redis with a TTL for multi-server deployments.
  • User lookup for login: The login options endpoint accepts a username but does a broad passkey lookup. In production, resolve the username to a userId first, then scope the credential lookup.
  • Recovery code hashing: Recovery codes are SHA-256 hashed before storage. Only returned in plain text once at generation time.
  • Counter-based clone detection: The WebAuthn counter is updated on each authentication to detect cloned authenticators.

Testing

  • Unit tests written for all service functions (registration options/verify, authentication options/verify, device list/revoke, recovery code verification/regeneration)
  • Tests mock @simplewebauthn/server and PasskeyModel for isolation

CI Verification

  • TypeScript typecheck passes for all new files (only pre-existing errors remain from missing mongoose , ioredis , uuid type declarations)
  • Full CI validation requires npm install to resolve @simplewebauthn/server and @simplewebauthn/browser dependencies

// bash
cd backend && npm install && npm test
cd frontend && npm install

Please kindly review this task. If there are any corrections, improvements, adjustments, or merge conflicts that you notice regarding my implementation, I'd really appreciate your feedback. I'd also love to hear your overall review of my work on this branch.

Thank you!

Chigybillionz and others added 4 commits August 27, 2026 18:55
Implement passwordless authentication via WebAuthn passkeys as primary
or second factor with device management and recovery flows.

- Add Passkey Mongoose model for credential storage
- Create WebAuthn service for registration/authentication ceremonies
- Add passkey auth routes (register, login, MFA, device mgmt, recovery)
- Create frontend components for registration, login, device management, MFA, and recovery
- Extend AuthContext with passkey support
- Preserve existing password-based auth for legacy users

Closes AetherEdu#409

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Update package-lock.json (root) and frontend/package-lock.json to
resolve @simplewebauthn/server and @simplewebauthn/browser dependencies
required by the passkey authentication feature. Fixes CI failures
caused by npm ci not finding the new packages.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
Resolve lock file conflicts and sync with upstream main.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
@Penielka
Penielka merged commit a052447 into AetherEdu:main Aug 30, 2026
11 of 15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Backend] Passkey (WebAuthn) authentication

2 participants