Skip to content

email_link throws IntegrityError when user already has a connection to the same source (CILogon / multi-identity provider broker scenario) #20975

@groundsada

Description

@groundsada

Describe the bug

When user_matching_mode = email_link is configured, and a user who already has a UserSourceConnection to a source logs in via the same source with a different identifier (same email), authentik attempts to INSERT a second UserSourceConnection record. This causes a database integrity error and crashes the login flow.

How to reproduce

  1. Configure an OAuth source (e.g., CILogon) with user_matching_mode: email_link.
  2. User logs in via the source for the first time through IdP A (e.g., campus SSO). → UserSourceConnection created with identifier = sub_A.
  3. Same user logs in through the same OAuth source via IdP B (e.g., Google) with the same email → different sub_B.
  4. authentik matches the user by email and then attempts to INSERT UserSourceConnection(user_id=X, source_id=Y, identifier=sub_B)
  5. Error below:
django.db.utils.IntegrityError: duplicate key value violates unique constraint
"authentik_core_usersourc_user_id_source_id_ad1f5aa7_uniq"
DETAIL: Key (user_id, source_id)=(56, 4eefecf7-b868-48f7-9f9d-e83a87d6b0fa) already exists.
at authentik/core/sources/stage.py line 25: connection.save()

Expected behavior

The behavior that I expect to see is:

When email_link matches an existing user who already has a connection to this source (different identifier, same user+source), authentik should authenticate the user rather than failing.

For example, at sources/stage.py, before calling connection.save() there should be a check:

existing = UserSourceConnection.objects.filter(
user=connection.user, source=connection.source
).first()
if existing:
# User already linked to this source — authenticate, don't re-insert
# Optionally update the identifier if policy allows it
return existing

Alternatively, changing the unique constraint from UNIQUE(user_id, source_id) to UNIQUE(user_id, source_id, identifier) would allow multiple connections per source and let users link multiple IdPs through the same broker source.

Screenshots

No response

Additional context

This affects CILogon, a widely-used OIDC federation broker for research/academic institutions. CILogon presents as a single OAuth source in authentik but fronts many backing IdPs (campus SSOs, Google, GitHub, ORCID, etc.). Each backing IdP produces a different sub for the same user. Users often forget which IdP they originally enrolled with and try a different one, triggering this error.

Deployment Method

Kubernetes

Version

2025.10.3

Relevant log output

django.db.utils.IntegrityError: duplicate key value violates unique constraint
"authentik_core_usersourc_user_id_source_id_ad1f5aa7_uniq"
DETAIL: Key (user_id, source_id)=(56, 4eefecf7-b868-48f7-9f9d-e83a87d6b0fa) already exists.
at authentik/core/sources/stage.py line 25: connection.save()

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageAdd this label to issues that need to be triaged

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions