feat(auth): add passkeys (WebAuthn) like monk
Nobody has claimed this yet.
- Dominant language
- HTML
- Stars
- 4
- Forks
- 0
- Avg merge
- 4h 4m
- Merged PRs (30d)
- 57
Description
Goal
Bring Fanout to parity with monk's auth stack: offer passkeys (WebAuthn / FIDO2) as a primary login method alongside the existing email-code flow.
Reference implementation: monk/server/src/monk/auth/webauthn.py + @simplewebauthn/browser on the client. ~440 lines of auth code total.
Why
- Phishing-resistant by design (origin-bound credentials)
- No password / OTP to type or intercept — users tap Touch ID / Windows Hello / security key
- Platform support is now universal (Safari 16+, Chrome, Firefox, Edge, iOS 16+, Android)
- Monk already ships it; feature parity is the bar
Proposed scope
Go side (est. ~400 LOC)
- Library:
github.com/go-webauthn/webauthn(FIDO2-compliant, maintained) - New SQLite tables (Atlas migration):
webauthn_credentials(id, user_id, credential_id, public_key, sign_count, aaguid, transports, created_at, last_used_at, name)webauthn_challenges(id, user_id, challenge, purpose, expires_at)— ephemeral, 5 min TTL
- New package:
internal/auth/webauthn.go— helpers for registration + authentication ceremonies - New API endpoints in
internal/api/auth.go:POST /api/auth/passkey/register/options(admin-authenticated — starts registration)POST /api/auth/passkey/register/verify(completes registration, persists credential)POST /api/auth/passkey/login/options(anonymous — returns challenge for existing user)POST /api/auth/passkey/login/verify(completes authentication, issues JWTs)GET /api/auth/passkeys(list registered passkeys for current user)DELETE /api/auth/passkeys/:id(revoke a passkey)
Config (env vars)
WEBAUTHN_RP_ID— Relying Party ID, e.g.fanout.labstack.comWEBAUTHN_RP_NAME— display name, e.g.Fanout- Origins derived from
HTTP_ADDR+WEBAUTHN_RP_ID
Client side (est. ~200 LOC)
- Dep:
@simplewebauthn/browser(already used by monk) - LoginPage: "Sign in with a passkey" button alongside the email input. If
localStoragehas a "prefer passkey" flag from a prior login, auto-launch the ceremony. - SettingsPage: new "Passkeys" section — list existing credentials, allow rename + revoke, "Add a passkey" button that triggers the registration ceremony.
Tests
- Unit tests for the registration/authentication helpers
- API tests for the four ceremony endpoints (challenge issuance, verification, replay rejection, counter-rollback detection)
Non-goals
- Cross-device sign-in — out of scope for v1; default to platform authenticators
- Passwordless-only mode (forcing passkey + disabling email codes) — keep both paths available
- Account recovery via passkey — email codes remain the recovery path
Monk reference files (for implementation guidance)
monk/server/src/monk/auth/webauthn.py— ceremony helpers (89 lines)monk/server/src/monk/auth/models.py— credential model (50 lines)monk/server/src/monk/auth/dependencies.py— FastAPI auth deps (146 lines)monk/web/src/...— client-side registration + login components
Open questions
- Should passkey be required for new admin accounts (strict) or opt-in after email setup (flexible)?
- Do we allow multiple passkeys per user from day one? (Monk does; recommended.)
- Rename / revoke UX — simple list + buttons, or dedicated manage-passkeys page?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reviewing internal/api/auth.go, the proposed internal/auth/webauthn.go package, Atlas migrations, and the monk WebAuthn reference files. Confirm the open questions about account policy, multiple passkeys, and rename/revoke UX before implementation. Done means the registration, login, listing, rename, and revoke flows work across LoginPage and SettingsPage, with ceremony, replay, and counter-rollback tests passing.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, javascript, sqlite
- Domain
- api, authentication, frontend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100