Skip to content

fix(auth): use standard OIDC email claim for subscriber email#1689

Open
kvdb wants to merge 1 commit into
thunderbird:mainfrom
kvdb:fix/oidc-prefer-email-claim
Open

fix(auth): use standard OIDC email claim for subscriber email#1689
kvdb wants to merge 1 commit into
thunderbird:mainfrom
kvdb:fix/oidc-prefer-email-claim

Conversation

@kvdb

@kvdb kvdb commented May 29, 2026

Copy link
Copy Markdown

What changed?

The OIDC token endpoint (backend/src/appointment/routes/auth.py) now derives a
new subscriber's email from the standard OIDC email claim, falling back to
preferred_username/username only when it's absent.

Adds two integration tests in backend/test/integration/test_auth.py for both
paths (email claim present; falls back to preferred_username).

Why?

The scheme derived the email from preferred_username, assuming
preferred_username == email. That only holds for Thunderbird/Firefox Accounts.
For other IdPs (Keycloak, Kanidm, ...) preferred_username is a display handle,
not an email, so subscribers got a bogus email, breaking host notification
emails. Per OIDC Core 1.0 §5.1 preferred_username "MUST NOT be relied upon" as
an identifier; the email claim (§5.4) is the authoritative source.

Ref: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims

Limitations and Notes

Trusts the introspected email claim without checking email_verified (matching
prior behavior). Verified locally: full backend suite passes (403 passed, 1
skipped), ruff check clean.

The generic `oidc` auth scheme derived the subscriber email from
`preferred_username`, assuming `preferred_username == email`. That only
holds for Thunderbird Accounts / Firefox Accounts, where the username is
the email. For any other OIDC IdP (Keycloak, Kanidm, ...),
`preferred_username` is a display/login handle, not an email, so the
subscriber was created with a bogus email (the short username), breaking
host notification emails.

Per OpenID Connect Core 1.0 §5.1 (Standard Claims), `preferred_username`
is the "Shorthand name by which the End-User wishes to be referred to ...
It MUST NOT be relied upon to be unique by the RP" - i.e. a display
handle, explicitly not an email. The dedicated `email` claim (provided by
the `email` scope, §5.4) is the authoritative email source.

Prefer the `email` claim, falling back to `preferred_username`/`username`
to preserve Thunderbird Accounts compatibility:

    email = token_data.get('email') or token_data.get('preferred_username') or token_data.get('username')

Add integration tests covering both paths: when the introspected token
carries an `email` claim the subscriber email comes from it (not from
`preferred_username`); when `email` is absent it falls back to
`preferred_username`.

Ref: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
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