Signup without email verification: why it's a risk and how to require it
By Obscura Security · Updated · Supabase, Firebase, Next.js, Lovable, Bolt, Cursor
Short answer
If your app lets someone sign up with any email address and use it immediately, the address proves nothing. Turn on email confirmation (Supabase 'Confirm email'), enforce emailVerified in Firebase rules and your server code, and never grant access to a team or organisation based on an unverified email domain.
If a stranger can type in any email address, choose a password, and be inside your app a second later, nothing about that account is tied to a real person. The email is a claim, and your app is treating it as a fact.
Why this happens
Email confirmation adds a step, and a step is friction. AI tools building a login screen for a demo often switch it off, or never turn it on, so the flow works on the first try. Nobody goes back to change it when the app goes live.
The effects depend on what an email address means in your app:
- Pretending to be someone else. An attacker signs up as
[email protected]. If teammates, support staff or invite screens trust that address, they may act on it. If your app shows the email as the user’s identity, so does everyone who sees it. - Pre-registering a victim’s address. The attacker creates an account for
[email protected]and sets the password. When the real person later signs up, or signs in with Google, the app may merge the two or refuse to create theirs. If it merges them, the attacker still knows the password. - Claiming a company’s workspace. Any logic like “anyone with an
@acme.comaddress joins the Acme team” is open to everyone if the address was never checked. - Trial and abuse. One person creates unlimited accounts with made-up addresses to reuse free credits, or sends your app’s emails to people who never asked for them.
- Invites and notifications. If you send mail to the address a user typed, your app becomes a way to deliver messages to strangers under your brand.
How to check your own app
Do this with your own app.
- Open the signup form and register with an address you can’t receive mail at, such as
[email protected]. If you land inside the app, signup doesn’t require verification. - In Supabase, run this in the SQL editor to count existing unconfirmed users:
select count(*) from auth.users where email_confirmed_at is null;
- In Firebase, in your browser console or test code, log
auth.currentUser.emailVerifiedafter a fresh signup. Then try an action that your rules should block for unverified users. - Search your code for places that decide access from an email: domain checks, invite acceptance, “auto-join” features.
The fix
Supabase: turn on Confirm email
In the dashboard, open Authentication and find the settings for the Email provider. Switch on Confirm email. A new user then has no session until they open the link in the message.
Test it by signing up with your own address and checking the flow end to end, including the redirect URL you’ve set in the auth URL configuration.
Before launch, configure a custom SMTP provider. The built-in email sender is rate limited and meant for trying things out, and a confirmation email that never arrives looks like a broken app.
Where server code needs to know, check the confirmation yourself:
const { data: { user } } = await supabase.auth.getUser()
if (!user?.email_confirmed_at) {
return new Response('Verify your email first', { status: 403 })
}
Use getUser() on the server, as explained in getSession vs getUser.
Firebase: send the email, then enforce it in rules
On signup, send the verification message:
import { createUserWithEmailAndPassword, sendEmailVerification } from 'firebase/auth'
const cred = await createUserWithEmailAndPassword(auth, email, password)
await sendEmailVerification(cred.user)
Then require it where it matters. A Firestore rule that only lets verified users create documents:
match /projects/{projectId} {
allow create: if request.auth != null
&& request.auth.token.email_verified == true;
}
After the user clicks the link, the token on their device is stale until it refreshes. In the app, call await auth.currentUser.reload() and then await auth.currentUser.getIdToken(true) so the new claim is picked up.
On your own server, check the same claim on the decoded ID token (decoded.email_verified) from the Admin SDK. A check on the frontend alone does not stop anyone, so put the enforcement in rules and server code. For the rules themselves, see Firebase Security Rules that are wide open.
Treat email domains with care
If you grant access by domain, require a verified address first, and check the domain against the verified email only. Also consider whether a person should have to be invited by an existing member instead. Domain-based joining on top of unverified emails is the most damaging version of this problem.
Sign in with an identity provider
Google and similar providers report whether an address is verified. Use that flag. If your app merges accounts that share an email address, merge only when both sides are verified.
Mistakes to avoid
- Checking
emailVerifiedonly in the frontend. It changes the interface and protects nothing. - Turning confirmation off “for now” and shipping. Set a reminder for the day before launch, or turn it on from the start.
- Using the typed email as a permanent identity. Use the user ID your auth provider gives you as the key for data.
- Forgetting people who signed up before you turned it on. Query for them and handle them.
- Sending reset or invite emails to unverified addresses. The same rule applies to password resets.
Common questions
- Is it really a problem if people can sign up without confirming their email?
- It depends on what the account unlocks. For a free tool with nothing private, it may only mean junk accounts. If accounts get a free trial, join teams, receive invites, or see data tied to an email address, then an unverified address lets someone claim an identity they don't own.
- How do I turn on email confirmation in Supabase?
- In the Supabase dashboard, open the Authentication settings, find the Email provider, and switch on 'Confirm email'. With it on, a new user can't sign in until they click the link sent to their address. Set up your own SMTP provider before launch, because the built-in email service is limited and meant for testing.
- How do I require a verified email in Firebase?
- Send a verification email with sendEmailVerification(), then check request.auth.token.email_verified == true in your Security Rules for anything that needs a real address. Checking user.emailVerified in the frontend only changes what is displayed, and the rules do the enforcing.
- Does signing in with Google or GitHub count as verified?
- Usually Google sign-in returns a verified address, but check the provider's flag instead of assuming. Some providers allow unverified addresses. In Supabase and Firebase, look at the email-verified field on the user before you rely on it.
- I already have users who never verified. What now?
- Find them, decide which ones to keep, and require verification before they can use anything sensitive. In Supabase, users with a null email_confirmed_at in the auth.users table haven't confirmed.
Read next
Written by Obscura Security, an Australian practice that checks web apps for problems like this one. If you want a second pair of eyes on yours, email [email protected].