Skip to content

Email Claim Policy

The email emitted by /oauth/userinfo is the user's primary email at the time of the request. It is not the email the user typed to log in, and it is not a value frozen for your RP forever.

ClaimTypeMeaning
emailstringThe primary email at request time. Mutable — the user can change it in logi settings at any time.
email_verifiedbooleanWhether the primary email comes from a verified source (verified credential / Apple / Google SSO).

Three core rules:

  1. email is not an identifier. Key your accounts on sub. The same user may arrive with a different email on their next login.
  2. id_token carries no email claim. Call userinfo when you need the email.
  3. Login email ≠ email claim. A logi account can hold several login emails (legacy email_address, Apple/Google/Kakao SSO emails, verified extra emails); whichever one the user logs in with, userinfo always emits the primary email.

When does email change?

  • The user changes their primary email in the logi app (Settings → Account → Email).
  • Accounts that sign up via SSO (Apple/Google) get their primary email explicitly pinned to that SSO email at signup (policy since 2026-06-11). After that, only an explicit pick changes it — adding another email never silently flips what your RP receives.
  • Apple Hide-My-Email users may present a relay address (@privaterelay.appleid.com). Apple guarantees delivery, so it is email_verified: true.

Choose Snapshot or Follow — explicitly

Most OAuth sample code stores the email once, at account creation. That silently makes you a Snapshot RP: when the user later changes their primary email in logi, your app keeps showing the signup-time address forever — and users report that as a bug. Either strategy is legitimate; make the choice visible in code and docs.

On every SSO login, if the verified email differs from your stored value, update it. This is login-time adoption, not background sync — no extra infrastructure needed.

ruby
# Right after resolving the existing user by sub (Rails example)
def refresh_email_if_changed!(user)
  return unless @email.present? && @email_verified
  return if user.email_address == @email

  # Another local account already owns the address — skip, but let login proceed
  if User.where.not(id: user.id).where(email_address: @email).exists?
    Rails.logger.warn("[sso] email refresh skipped: collision user=#{user.id}")
    return
  end

  user.update!(email_address: @email)
rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid
  # TOCTOU race with a concurrent signup — refresh is best-effort; never fail the login
  Rails.logger.warn("[sso] email refresh skipped: race user=#{user.id}")
end

Caveats:

  • Scope this to the logi (one_pass) provider only. Applying the same pattern to Apple logins lets Hide-My-Email relay addresses overwrite real emails.
  • Never adopt a value with email_verified: false (account-takeover vector).
  • If your local email doubles as the password-login identifier, the user will log in locally with the new email + existing password after a refresh. That is the intended "IdP is the source of truth" behavior, but consider telling the user.

Strategy B — Snapshot: keep the signup-time value, display-only

If you treat email purely as contact/display data and let users manage it inside your RP, Snapshot is fine. But:

  • Leave a comment saying the non-sync is intentional.
  • Provide an email-edit UI in your RP, or at least make it clear that changing the email in logi will not propagate here.

Pitfall summary

PitfallConsequenceAvoidance
Keying accounts on emailPrimary change splits one person into two accountsKey on sub (Sub policy)
Implicit Snapshot"I changed my primary but the RP didn't update" bug reportsAdopt Follow, or document Snapshot
Adopting unverified emailImpersonation → account-link takeoverConsume only when email_verified == true
Follow applied to AppleRelay address overwrites the real emailFollow is one_pass-only
Refresh failure breaks loginConcurrency race turns login into a 500Refresh is best-effort; rescue and proceed

See also

Identity가 제품의 신뢰를 만듭니다.