Bind enrolled passkeys to the upstream Gitea identity #1448

Closed
opened 2026-08-26 23:54:12 +00:00 by timmy · 0 comments
Owner

Problem

Enrolled WebAuthn credentials are installation-global. The passkey registry stores no upstream principal, authentication options advertise every credential, and successful verification issues a session for whichever Gitea account is currently configured. Repointing the dashboard can therefore transfer passkey access across accounts.

User flow

Bind registration, discovery, authentication, authorization, listing, and revocation to the stable numeric upstream Gitea principal. Existing unbound credentials must fail closed rather than silently attach to a new account.

Acceptance tests

  • A credential enrolled for principal 101 is stored with that principal and remains retrievable for 101.
  • Authentication options expose only credentials bound to the current upstream principal.
  • Authentication and step-up verification reject a credential whose bound principal differs from the current/session principal.
  • Passkey management lists and revokes only credentials owned by the signed-in principal.
  • Legacy rows migrate without data loss but remain unbound and unusable until re-enrolled.
  • Focused and full test suites pass.

Evidence / non-duplication

src/passkey_store.py has no principal column; src/main.py currently calls store.all() / store.get() without identity filtering. All historical issue titles and recent commits were inspected. Closest shipped work #1372 binds sessions and #1374 binds push delivery; neither binds passkeys.

## Problem Enrolled WebAuthn credentials are installation-global. The passkey registry stores no upstream principal, authentication options advertise every credential, and successful verification issues a session for whichever Gitea account is currently configured. Repointing the dashboard can therefore transfer passkey access across accounts. ## User flow Bind registration, discovery, authentication, authorization, listing, and revocation to the stable numeric upstream Gitea principal. Existing unbound credentials must fail closed rather than silently attach to a new account. ## Acceptance tests - A credential enrolled for principal 101 is stored with that principal and remains retrievable for 101. - Authentication options expose only credentials bound to the current upstream principal. - Authentication and step-up verification reject a credential whose bound principal differs from the current/session principal. - Passkey management lists and revokes only credentials owned by the signed-in principal. - Legacy rows migrate without data loss but remain unbound and unusable until re-enrolled. - Focused and full test suites pass. ## Evidence / non-duplication `src/passkey_store.py` has no principal column; `src/main.py` currently calls `store.all()` / `store.get()` without identity filtering. All historical issue titles and recent commits were inspected. Closest shipped work #1372 binds sessions and #1374 binds push delivery; neither binds passkeys.
timmy added the
P1
P2
labels 2026-08-26 23:54:12 +00:00
timmy closed this issue 2026-08-27 00:25:10 +00:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: stackchain/stackchain-dashboard#1448
No description provided.