DEV Community

Mahdi BEN RHOUMA
Mahdi BEN RHOUMA

Posted on Originally published at iloveblogs.blog

Sign In to Supabase Without Email Verification (2026)

The friction

You are building locally. You sign up a test user, Supabase replies
"Check your email to confirm your account", and now you have to open the
inbox, click the link, and only then can you log in. Every. Single. Iteration.

On hosted Supabase, email confirmation is on by default. On self-hosted
and local (supabase start) it is off by default. So the friction is worst
when you are developing against a hosted project — which is most people.

The original ask for this is supabase/supabase#5113:
a flag to let users sign in without email confirmation. The flag exists in
three forms, depending on whether you want to disable it globally or just
confirm one user.

Option 1 — Turn it off in the dashboard (hosted dev)

Dashboard → Authentication → Sign In / Providers → Email → Confirm email:
OFF

URL: https://supabase.com/dashboard/project/_/auth/providers

New signups can now sign in immediately. No confirmation email is sent.

This is project-wide. It affects every signup on that project, so use it
only on a dev project, not production.

Option 2 — Turn it off in config (self-hosted / local)

In supabase/config.toml:

[auth.email]
enable_confirmations = false
Enter fullscreen mode Exit fullscreen mode

Restart the auth container:

supabase stop && supabase start
Enter fullscreen mode Exit fullscreen mode

Self-hosted and local projects ship with enable_confirmations = false, so
you only need this if you previously turned it on.

Option 3 — Confirm one user with the admin API (production-safe)

When you want to keep confirmation on globally but pre-confirm one user — a
team member, an invited customer, a test account — use the admin API with the
service role key:

// server-only — NEVER expose the service role key to the browser
import { createClient } from '@supabase/supabase-js'

const supabaseAdmin = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!, // service role, not anon
)

const { data, error } = await supabaseAdmin.auth.admin.createUser({
  email: 'teammate@example.com',
  password: 'a-strong-temporary-password',
  email_confirm: true, // ✅ user is already confirmed, no email sent
})
Enter fullscreen mode Exit fullscreen mode

email_confirm: true writes email_confirmed_at for you. The user can sign
in immediately. This is the right tool for production invites.

Option 4 — Confirm an existing user in SQL

For a user who already exists but is stuck unconfirmed:

update auth.users
set email_confirmed_at = now(),
    confirmed_at = now()
where email = 'teammate@example.com';
Enter fullscreen mode Exit fullscreen mode

Run this in the SQL editor with the service role. It is the surgical option —
no setting changes, no API call, just flip the column. Useful when a
confirmation email bounced or a user lost access to their inbox.

⚠️ Do not expose this as a client-callable function. Anyone who can run it
against auth.users can confirm any email — which is account takeover. It
is a server-side escape hatch, not a feature.

When you must keep confirmation on

In production, keep email confirmation on. Three reasons:

  1. Account takeover. Without confirmation, anyone can sign up with an email they do not own and lock the real owner out of that address on your app.
  2. Spam signups. Bots fill your auth.users table with garbage, which costs you rows and skews analytics.
  3. Abuse of free tier. Unverified signups are the main vector for Supabase free-tier quota abuse; a project with confirmation off is a magnet for it.

The production pattern: confirmation on, plus the admin API for pre-
confirmed invites. Disable confirmation only on dev and staging projects.

The redirect after sign-in

Once confirmation is off (or the user is confirmed), the next friction is
where they land after signInWithPassword. If the post-login redirect is
broken, the user is authenticated but staring at the wrong page — see
fix Supabase auth redirects that land on the wrong URL.
And if the session itself does not persist across refreshes, it is the
Supabase auth session persistence
issue, not email confirmation.

Common mistakes

  • Disabling confirmation in production. It is a security control, not a convenience. Use the admin API to pre-confirm individuals instead.
  • Using the service role key in the browser. The service role bypasses RLS. If it ships in client code, your database is public. The getSession() security warning covers the same boundary from the read side.
  • Forgetting the OAuth path still verifies. Turning off email confirmation does not affect OAuth providers — Google, GitHub already verify the email upstream, so email_confirmed_at is set on the OAuth callback regardless.
  • Treating confirmed_at and email_confirmed_at as different. Set both in SQL; Supabase reads both depending on the flow.

TL;DR

  • Hosted dev: dashboard → Auth Providers → Email → Confirm email OFF.
  • Local/self-hosted: auth.email.enable_confirmations = false in config.toml.
  • Production: keep it ON. Pre-confirm individuals with the admin API (email_confirm: true) or SQL (update auth.users set email_confirmed_at = now()).

Related Articles


Originally published at https://www.iloveblogs.blog

Top comments (0)