KeyFoldAccount identity atlas
Field notes

Two issuers can become one lookup key.

The subject string is only part of an identity. Changing the other part of the lookup key can bring previously separate account rows together.

Checked October 25, 2026 · Better Auth 1.7.2 → 1.7.3

Three fictional rows, one useful control.

Exact synthetic input used by the first example
RowProvider / subjectIssuer / user
Acompany / subject-42north.example / person-a
Bcompany / subject-42south.example / person-b
Cgithub / subject-42github.example / person-a

The complete example JSON uses fictional HTTPS issuer strings. In the old grouping, A and B have different issuer/accountId tuples. In the target grouping they share ["company","subject-42"]. Row C stays separate because its provider is different, even though its subject string matches.

Expected result: three old keys, two target keys, one duplicated target key. The duplicated group contains two different user labels. Select 1.7.2, then 1.7.3 on the map to see the regrouping.

Why the same user is still a duplicate.

Change B's user label to person-a. There are now two account rows pointing to one user label. That does not make the lookup unique: the pinned 1.7.3 native adapter rejects more than one matching account row. The second built-in example reproduces this case. It is deliberately not described as an account takeover or as evidence that two people exist.

Duplicate source row IDs are different: KeyFold rejects the input because the review would no longer have an unambiguous row reference. Duplicate account keys remain valid input and become findings.

What a row inspection tells you.

The source position is the one-based position in your imported array. It is not a database primary key or a line number in a formatted JSON file. The detail view shows the supplied row ID separately, followed by the old and target tuples. Default aliases preserve grouping within each field; use the reveal control only when the underlying strings are appropriate to show on screen.

What to investigate next.

Take the affected source rows back to the trusted database and provider configuration. Establish the intended provider-to-issuer mapping and ownership before editing anything. A matching email is not enough to merge identities. If the old map also has duplicate keys, the export may include repeated snapshots or a different scope; old-key duplication is an observation, not a claim that the 1.7.2 lookup rejected it.

Missing issuer values remain unknown in the old view. KeyFold never substitutes the provider label as an invented issuer. For the target view, an absent issuer does not prevent grouping by provider and account ID.

A clean target map is a hypothesis, not a migration.

Start with the three rows above. Suppose a trusted configuration review confirms that north.example and south.example really are separate identity providers, each with its own intended users. An original candidate projection gives row A the provider label company-north and row B company-south. Row C remains github. The subject and user labels are unchanged.

Download that fictional candidate projection and open it in KeyFold. It has three target keys and no duplicated target group. That is the expected projection result. The file is not an update script, a database backup or proof that either provider configuration has changed.

Keep four separate lines in the decision record.

Identity evidence
Which trusted provider configuration associates each issuer with its provider ID? Preserve the evidence outside the public report. Similar-looking email addresses do not answer this question.
Rows affected
In this example, only A and B change provider labels. All three row IDs, account IDs and user labels remain. Record before and after tuples so another reviewer can find an accidentally renamed control row.
Application configuration
The provider labels used during sign-in must agree with the intended account lookup. A collision-free JSON file alone does not establish that agreement.
Observed staging result
Use separate controlled identities for the two providers. Record which existing user each reaches and whether a new unintended account appears. Keep successful sign-in distinct from proof that every historical row has been covered.

The official upgrade guide says to check duplicate provider/account pairs and keep different users separate when distinct issuers share a provider ID. This example illustrates that reasoning with invented data; it does not choose the correct mapping for your deployment.

When the candidate is wrong.

If A and B are duplicated snapshots of the same database row, assigning two new provider labels hides an export problem. If they are two real rows for one user, the same-user label still does not make the lookup unique. If ownership is unknown, keep the group unresolved. None of these cases justifies deleting a row merely because the map would look simpler.

Compare row counts as well as group counts. Dropping B also removes the visible collision, but it leaves only two rows and loses the second identity. That is a different operation from separating provider namespaces. A useful review should explain both the target uniqueness and why every retained user relationship is still correct.

Contract: pinned 1.7.3 internal adapter. Native behavior was reproduced with both published packages; see the method.

Open the identity map ↗