Skip to content

Merge duplicate 2FA entries with the same name and code - #263

Merged
danielehrhardt merged 1 commit into
mainfrom
fix/totp-dedupe
Oct 29, 2026
Merged

danielehrhardt merged 1 commit into
mainfrom
fix/totp-dedupe

Conversation

@danielehrhardt

Copy link
Copy Markdown
Contributor

The 2FA page showed the same code twice (e.g. "Coolify Zeus" once in Global and once in the Solakon GmbH workspace).

What changes

  • mergeDuplicateTotp() in packages/core/src/vault/totp.ts: entries with the same issuer + account name (case-insensitive) and the same secret, algorithm, digits and period are merged when they sit in the same scope, or in a workspace while the same entry is also global.
  • Survivor: the global entry first, then the one a login is linked to, then the oldest. It takes over the login link and the agent totpIds permissions of the removed copy.
  • Kept as is: copies linked to two different logins (the link is 1:1), and copies in two different workspaces without a global one (merging would widen access).
  • Runs on start (shared work only, so the primary of a device group), whenever the vault unlocks, and after create, update and import. Importing into a workspace now skips codes that already exist globally ("already exists").

Tests

  • New test in packages/core/test/totp.test.ts covering global + workspace merge, link and permission transfer, idempotency, create/import of an existing code, linked twins and separate workspaces.
  • Vault suites (totp, totp-guess, missing-login-totp, credentials, vault-routes, password-import, missing-logins): 123 pass. mcp, agents, workspaces, remote-snapshot, agents-routes: 179 pass. tsc --noEmit clean.

When the same authenticator code (same issuer, account name and secret) exists
twice in the same scope, or in a workspace while it is also global, keep one:
the global entry first, then the one a login uses, then the oldest. The
survivor takes over the login link and agent permissions. Copies linked to a
different login, and copies in two different workspaces, stay.

Runs when the vault unlocks, after create, update and import; importing into a
workspace now skips codes that already exist globally.
@danielehrhardt
danielehrhardt merged commit 1f49e43 into main Oct 29, 2026
8 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.

1 participant