DE EN

Journal

Confirmed addresses only: no sign-in with an unconfirmed email

Since 3 October 2026 no application gets an EAuth account with an unconfirmed address. Why, and what changes for integrations.

Samuel Krauss, Founder · · 6 min read

Since 3 October 2026, EAuth gives no application an account whose email address nobody has confirmed. An authorization code is issued only after the person has opened a link sent to the address, so in a token that comes from a sign-in, email_verified is always true.

Until then registration sent a link as well, but the sign-in went on without waiting for it, and the ID token said email_verified: false until the link was opened. That was honest and, in practice, not enough. Here is why I changed it, what the person and the application see now, and what it costs.

An unconfirmed address is a name anyone can type

Anybody can register with any address. Until the link is opened, the address on the account is a claim by whoever typed it, not a fact about who reads that inbox.

OpenID Connect has a claim for exactly this: email_verified is true when the provider "took affirmative steps to ensure that this e-mail address was controlled by the End-User". The specification puts the information in the token. It cannot make anybody read it. An application that keys anything on the address without checking the claim gives the address's privileges to whoever registered it first:

  • Invitations and shared things. An application that invites people to a project, shares a document or grants a role by address hands it to the account carrying that address. If that account belongs to somebody who typed in a stranger's address, the invitation goes to them.
  • Organisations by domain. EAuth lets an organisation with a verified domain take in everybody whose address is in that domain. That already required a confirmed address, since an address anybody can type is no claim to work at a company. An application doing something similar on its own side had no such check unless it wrote one.
  • Accounts made before their owner. Sudhodanan and Paverd studied this as account pre-hijacking: an attacker sets up an account with the victim's address before the victim arrives, and gets in once the victim starts using it. They found at least 35 of 75 popular services vulnerable to one form of it or another.

Every application could check email_verified itself, and most never looked. Doing the check once, in EAuth, means every application gets it. OWASP's authentication guidance puts the rule in one line: an email address may serve as the username provided it is verified during sign-up.

What the person sees

Registration sends a mail titled "Confirm your email address for" and the application's name. The link works once, for 24 hours, and carries the way back to the sign-in the person started.

Back at the application without having opened it, the person gets no code but a page in the application's colours, "Confirm your email address". It says that the application can use the account once the address is confirmed, and that the link leads back here. Its button, "Send a new link", opens the account page, which sends a fresh link that carries the way back as well. The way back can only point to a page of EAuth itself, the same rule a sign-in follows when it resumes, so the link cannot be turned into a redirect to somewhere else.

Opening the link confirms the address at once and shows "Address confirmed" with a "Continue" button that returns to the sign-in. The application gets its code, or the consent screen comes first if the person has not agreed to its scopes yet.

Confirming on open, without a further click, is deliberate. Mail scanners follow links, and for a confirmation that is fine: whoever can open mail sent to the address is who the claim is about. A password reset link only shows a form when opened, because a scanner must not spend that one.

The account page sends at most three links an hour per account; a fourth request says so and says when to try again. Links are 256 random bits, stored only as digests. A link confirms only while the account still has the address it was sent to. Setting a new password through a reset link counts as confirmation too, since it proves the same thing.

What the application sees

  • A code only after confirmation. email_verified is true in every ID token from a sign-in.
  • prompt=none returns interaction_required, with the description "The account's email address is not confirmed yet." That is the error OpenID Connect defines for a request that cannot finish without showing a page. A silent sign-in that gets it should fall back to a normal redirect, where the person sees the confirmation page.
  • user.created arrives before confirmation. The webhook fires at registration with email_verified: false, because a person who closes the tab after registering never reaches the token exchange. Do not treat it as a sign-in.
  • Single sign-on accounts are confirmed by the provider. An account made through an organisation's SAML provider is confirmed when it is created. If an unconfirmed account with the same address existed, the person the provider vouches for takes it over: its password, passkeys and two-factor are removed, its refresh tokens end, and the application receives session.revoked with the reason "account claimed through single sign-on".
  • Imported accounts keep their flag. An account imported with email_verified false is stopped at its first sign-in with the same page and confirms from there. One imported as true signs in as before.
  • A corrected address is unconfirmed again. Changing a person's address in the console clears the confirmation, unless only the letter case changed.
  • A refresh checks too. Every token EAuth issues, a refreshed one included, is for an account whose address is confirmed. The refresh token of an account whose address is no longer confirmed, such as one corrected in the console, is refused with invalid_grant, and the person signs in again. An access token issued before that runs out within its lifetime, 15 minutes by default.

TestAnApplicationGetsOnlyConfirmedAddresses walks through all of it: register, come back to the confirmation page without a code, get interaction_required with prompt=none, send a new link from the account page and check it differs from the first, open it, find "Continue" pointing at /authorize, and finally get a code.

What it costs

One more step for every new person, and it happens outside the application, in a mailbox. Mail delivery is now part of signing up: a mail that arrives late, lands in a spam folder or goes to a mistyped address stops a person at the door, and three new links an hour is the most they can ask for. Some will give up there.

I think that is the right trade for an account system other applications build on. The alternative was every application deciding on its own whether an address means anything, and most deciding by not deciding. The rule applies to our own console as well, which signs in through the same door.

Sources

  1. OpenID Connect Core 1.0: Standard Claims (email_verified) and Authentication Error Response (interaction_required), OpenID Foundation, read
  2. Authentication Cheat Sheet, OWASP, read
  3. Pre-hijacked accounts: An Empirical Study of Security Failures in User Account Creation on the Web, USENIX Security 2022, Avinash Sudhodanan and Andrew Paverd, read

How can we help?

Call us +41 58 513 63 11 · Weekdays 8 to 18, usually straight away Write to us contact@elchi.dev · A reply within two working days Talk for 15 minutes No commitment, no preparation What does it cost? CHF 6,000. Then CHF 300 a month.