# How to Add User Auth and Registration to Your App (Copy-Paste Prompts Included)

- Tool: App Features
- Last updated: July 2026

## TL;DR

User auth needs three layers: an auth provider (Supabase Auth handles sign-up, JWT issuance, and email verification for free), protected routes that redirect unauthenticated visitors, and a profiles table with RLS so users only read their own data. With Lovable you can have email + Google login working in 1–2 hours for $0/month up to 50,000 monthly active users.

## What User Authentication and Registration Actually Is

Authentication answers the question 'who are you?' and registration is the moment a new user first answers it. Together they cover sign-up with email and password, email verification so you know addresses are real, social OAuth so users don't have to remember yet another password, session management so login persists across page refreshes, and password reset when they forget. Supabase Auth handles all of this behind a clean JavaScript SDK — you never store a raw password or roll your own JWT logic. The real product decisions are which OAuth providers to support, whether email verification is required at signup, and what a user can see before their first login.

## Anatomy of the Feature

Six components work together. Supabase Auth handles the hard parts (hashing, JWT, refresh) — your job is to wire the UI and protect the routes.

- **Auth provider — Supabase Auth** (backend): Handles sign-up, sign-in, JWT issuance and rotation, session refresh, email verification, and password reset. Replaces building bcrypt hashing, session tables, and token rotation from scratch. Configured in Supabase Dashboard under Authentication → Providers and Settings.
- **Sign-up and login forms** (ui): React forms using shadcn/ui Form, react-hook-form, and zod for validation. Sign-up: email, password, confirm-password fields with inline error messages. Login: email, password, and a 'Forgot password?' link. Submission calls supabase.auth.signUp() or supabase.auth.signInWithPassword().
- **OAuth buttons** (ui): Single button per provider that calls supabase.auth.signInWithOAuth({ provider: 'google', options: { redirectTo: window.location.origin + '/auth/callback' } }). Opens the provider's login page in a full redirect — never inside an iframe.
- **Auth state listener** (backend): supabase.auth.onAuthStateChange() fires on every sign-in, sign-out, and token refresh. Used to update a React auth context that all components read, trigger profile creation on SIGNED_IN with no existing profile, and redirect users after OAuth callback.
- **Protected route guard** (ui): A React component (or Next.js middleware) that reads the current session from auth context and either renders children or redirects to /login. In Next.js App Router, a middleware.ts file at the root intercepts unauthenticated requests to /dashboard/* before any page renders.
- **Profiles table with RLS** (data): Supabase auto-populates auth.users on sign-up but it is not directly queryable by the client. A public.profiles table mirrors it with user-facing fields: display_name, avatar_url, created_at. An INSERT policy triggered by auth state or an Auth hook creates the profile row on first sign-in.

## Data model

One profiles table is all most apps need. Run this in the Supabase SQL editor (Dashboard → SQL Editor → New Query):

```sql
create table public.profiles (
  id uuid primary key references auth.users(id) on delete cascade,
  display_name text,
  avatar_url text,
  created_at timestamptz not null default now(),
  updated_at timestamptz not null default now()
);

alter table public.profiles enable row level security;

create policy "Users can view own profile"
  on public.profiles for select
  using (auth.uid() = id);

create policy "Users can update own profile"
  on public.profiles for update
  using (auth.uid() = id);

create policy "Users can insert own profile"
  on public.profiles for insert
  with check (auth.uid() = id);

-- Auto-create profile row when a new user signs up
create or replace function public.handle_new_user()
returns trigger as $$
begin
  insert into public.profiles (id, display_name, avatar_url)
  values (
    new.id,
    new.raw_user_meta_data->>'full_name',
    new.raw_user_meta_data->>'avatar_url'
  );
  return new;
end;
$$ language plpgsql security definer;

create trigger on_auth_user_created
  after insert on auth.users
  for each row execute procedure public.handle_new_user();
```

The trigger auto-populates display_name and avatar_url from OAuth metadata (Google, GitHub) so the profile is filled in on first social login without any extra code.

## Build paths

### Lovable — fit 5/10, 1–2 hours

Best first choice for non-technical founders: Lovable generates full Supabase Auth integration — email sign-up, Google OAuth, protected routes, and profiles table — from a single detailed prompt. Auto-creates the Edge Functions and RLS policies.

1. Create a new Lovable project and connect Lovable Cloud (Cloud tab → Database) so Supabase is provisioned automatically
2. Paste the prompt below in Agent Mode and let Lovable build the auth pages, callback handler, and protected dashboard route
3. Open Cloud tab → Secrets and verify VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY are present
4. Click Publish (top-right icon) to get a real HTTPS URL — OAuth and email verification only work on the published URL, not the preview
5. In Lovable Cloud tab → Database, go to Auth → Redirect URLs and add your published URL (e.g., https://yourapp.lovable.app)

Starter prompt:

```
Build a complete user authentication system using Supabase Auth. Pages needed: 1) /register — sign-up form with email, password (min 8 chars), confirm-password fields; inline zod validation showing errors under each field; on submit call supabase.auth.signUp() and redirect to /verify-email. 2) /verify-email — static page saying 'Check your inbox and click the link to continue'; include a 'Resend email' button. 3) /login — email and password fields with a 'Forgot password?' link that calls supabase.auth.resetPasswordForEmail(); Google OAuth button that calls signInWithOAuth({ provider: 'google' }) using a full redirect (not popup); error message if credentials are wrong. 4) /auth/callback — page that calls supabase.auth.exchangeCodeForSession() from the URL, then redirects to /dashboard. 5) /dashboard — protected route that redirects unauthenticated users to /login; shows 'Welcome, {display_name}' from the profiles table. 6) /profile — shows user email and display_name from profiles table with an edit form. Create a profiles table (id uuid references auth.users, display_name text, avatar_url text, created_at timestamptz) with RLS: users can only SELECT and UPDATE their own row. Use a Supabase database trigger on auth.users INSERT to auto-create the profile row. Loading state while session check runs on first page load. Google OAuth button should work on the published URL after redirect URL is configured.
```

Limitations:

- Google OAuth callback fails in the Lovable preview iframe — always test on the published URL
- Lovable Cloud Supabase instance is not visible in the Supabase Dashboard; manage auth settings from the Cloud tab
- Iterating heavily on auth UI design burns credits quickly — finalize the design before prompting auth logic

### V0 — fit 4/10, 2–3 hours

Best if you need polished, production-quality Next.js auth UI with shadcn/ui components. V0 generates clean TypeScript; pair it with Supabase Auth or Clerk for the backend.

1. Prompt V0 with the spec below to generate the auth pages and middleware
2. Open the Vars panel in V0 and add NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY
3. Run the profiles table SQL (from the Data Model section above) in your Supabase SQL editor
4. Add your Supabase project URL to the OAuth callback URL in Supabase Dashboard → Authentication → Redirect URLs
5. Publish to Vercel and set the same environment variables in the Vercel Dashboard under Settings → Environment Variables

Starter prompt:

```
Build a complete Next.js App Router authentication system using @supabase/ssr and @supabase/supabase-js. Create: 1) app/register/page.tsx — shadcn/ui form with email, password, confirm-password; zod validation schema with min 8 chars and password match; calls supabase.auth.signUp() and redirects to /verify-email on success; inline field-level error messages. 2) app/login/page.tsx — email + password form with 'Forgot password?' link that calls supabase.auth.resetPasswordForEmail(); Google OAuth button that calls signInWithOAuth with redirectTo set to /auth/callback; error state for 'Invalid login credentials'. 3) app/auth/callback/route.ts — Route Handler that calls exchangeCodeForSession() and redirects to /dashboard. 4) middleware.ts — protects /dashboard/* routes by checking session with createServerClient from @supabase/ssr; redirects to /login if no session. 5) app/dashboard/page.tsx — Server Component that reads session from supabase.auth.getUser() and shows display_name from the profiles table. All session reads in Server Components must use createServerClient (not createBrowserClient) to avoid SSR localStorage errors.
```

Limitations:

- V0 does not auto-provision Supabase — you must run the SQL schema manually and configure OAuth credentials in Supabase Dashboard
- V0 sometimes defaults to Clerk when auth provider is not specified — state 'use Supabase Auth' explicitly in the prompt
- shadcn registry mismatches on form components occasionally require a follow-up 'fix the import paths' prompt

### Flutterflow — fit 4/10, 2–4 hours

FlutterFlow has native Firebase Auth and Supabase Auth widgets with pre-built login and registration page templates. Handles iOS/Android auth UX including secure storage of session tokens.

1. In FlutterFlow, go to Settings → Firebase or Supabase and connect your backend; FlutterFlow generates the Auth config files automatically
2. Add a pre-built Auth page template from the Page Templates panel — FlutterFlow ships login, register, and forgot-password layouts
3. In the Action editor on the sign-up button, select 'Create Account' action and map the email and password fields
4. For Google OAuth, enable Google Sign-In in Settings → Firebase → Authentication and add a Social Login action to a button
5. Set up a Supabase profiles table and add a 'Create Row' action triggered by the 'On Auth State Changed' app state listener in App Settings
6. Test on a real device via the FlutterFlow mobile preview — OAuth flows cannot run in the browser-based canvas

Limitations:

- Firebase Auth integration is tightest in FlutterFlow; Supabase Auth requires REST API actions and more manual wiring
- Email template customization (verification, reset) requires going to Firebase Console or Supabase Dashboard directly
- Dependency conflicts on code export can break the supabase_flutter package version pinning

### Custom — fit 3/10, 3–5 days

Full control over session expiry, SAML/SSO, custom email templates, and GDPR deletion flows. Required only when Supabase Auth defaults don't meet your compliance or enterprise requirements.

1. Implement Supabase Auth client-side with @supabase/supabase-js for sign-up, sign-in, and OAuth
2. Write RLS policies on all data tables — not just the profiles table — scoped to auth.uid()
3. Configure email templates in Supabase Dashboard → Authentication → Email Templates for branded verification and reset emails
4. Implement GDPR right-to-erasure by cascading deletion through all user-linked tables via an Edge Function with service role key
5. Add sign-in audit logging to a separate table for compliance requirements

Limitations:

- No AI scaffolding for the JWT expiry and refresh token rotation logic — must understand Supabase session internals
- SAML/SSO requires Supabase Enterprise or a third-party identity provider like Auth0 or Okta with custom integration work

## Gotchas

- **Google OAuth fails silently in the Lovable preview iframe** — supabase.auth.signInWithOAuth() opens a redirect to accounts.google.com. The Lovable preview panel is an iframe with a sandboxed origin — Google's OAuth server blocks callbacks to non-registered URLs, so the redirect either fails with a blank page or throws a redirect_uri_mismatch error with no visible message in the preview. Fix: Test Google OAuth only on the published Lovable URL. Click Publish (top-right icon), open the live URL in a real browser tab, and add that URL to Supabase Dashboard → Authentication → Redirect URLs before testing.
- **RLS blocks all data after auth is wired up** — AI tools generate the auth flow and often create the profiles table, but frequently skip adding RLS policies. Without a SELECT policy, supabase.from('profiles').select() returns an empty array even for the logged-in user — not an error, just silence. This makes it look like the profile was never created. Fix: After Lovable or V0 generates the schema, open Supabase Dashboard → Table Editor → profiles → RLS and verify there is a SELECT policy with USING (auth.uid() = id). If missing, add it manually or prompt: 'Add an RLS SELECT policy on profiles that allows users to read only their own row.'
- **Email not confirmed error loops at login** — Supabase requires email verification by default. AI-generated apps sometimes skip building the /verify-email page and the re-send logic, leaving new users stranded after sign-up with no clear next step. They try to log in, get 'Email not confirmed', and have no way to resend the email. Fix: Add a /verify-email page that shows a 'Check your inbox' message and a 'Resend verification email' button calling supabase.auth.resend(). During development, disable email confirmation in Supabase Dashboard → Authentication → Settings → 'Enable email confirmations' (re-enable before launch).
- **Session lost on page refresh in V0 Next.js apps** — V0 sometimes generates session reads using localStorage or supabase.auth.getSession() inside a useEffect that runs only after hydration. During SSR, the server renders the page as if no user is logged in, sends it to the browser, then the client re-checks — causing a flash of the login screen for authenticated users. Fix: Use @supabase/ssr createServerClient in Server Components and route handlers to read the session from cookies, not localStorage. Never access the Supabase auth session from a Server Component using createBrowserClient.
- **Duplicate auth.users records from interrupted OAuth** — If a user starts a Google OAuth flow and closes the browser tab before the callback completes, Supabase may create a partial auth.users record. Re-attempting sign-in with the same Google account sometimes creates a second record because the first didn't complete the email verification handshake. Fix: Add an onAuthStateChange listener that checks for an existing profile row before treating a SIGNED_IN event as a new user. Supabase Auth deduplicates by email when the same OAuth provider is used — the real risk is a user who also registered with email/password first, creating two accounts for the same email.

## Best practices

- Always protect routes at the middleware level (Next.js) or with an AuthGuard component (React Router) — never rely only on hiding UI elements
- Store JWTs in httpOnly cookies via @supabase/ssr rather than localStorage to prevent XSS token theft
- Require email verification before granting access to the main app, but make the verification step clear and include a resend button
- Write RLS policies on every table that contains user data — auth alone is not enough if the Supabase REST API is accessible
- Use the Supabase Auth trigger pattern to auto-create profile rows on sign-up, so profile reads never return null for real users
- Set redirect URLs to exact production domains in both Supabase Dashboard and each OAuth provider's console — wildcard redirects are a security risk
- Add a loading state while the initial session check runs to prevent flashing the login screen to authenticated users on page load
- Customize email templates in Supabase Dashboard → Authentication → Email Templates before launch — the default 'Supabase' branding erodes trust

## Frequently asked questions

### Is Supabase Auth free for small apps?

Yes. Supabase Auth is free up to 50,000 monthly active users on the free tier. For most early-stage apps this covers everything indefinitely. The Supabase Pro plan ($25/mo) adds higher MAU limits, better email delivery rates, and production SLA — worth it once you have paying users.

### How do I add Google login to my Lovable app?

Three steps: 1) Go to Google Cloud Console, create an OAuth 2.0 credential, and copy the Client ID and Secret. 2) In Supabase Dashboard → Authentication → Providers, enable Google and paste the credentials. 3) Add your published Lovable URL to Supabase Dashboard → Authentication → Redirect URLs. Then prompt Lovable to add a Google OAuth button. Test on the published URL — OAuth does not work in the preview iframe.

### What's the difference between authentication and authorization?

Authentication is proving who you are (logging in). Authorization is deciding what you're allowed to do once logged in. Your sign-up and login system handles authentication. Row Level Security policies and custom roles handle authorization — they determine whether the logged-in user can read a specific row in a specific table.

### Do I need email verification for my MVP?

It depends on your risk tolerance. Without verification, users can register with fake or typo'd emails — you lose the ability to contact them and your email reputation suffers if you later send to bad addresses. For a private beta or internal tool, disabling it speeds up testing. For any app collecting user data or sending transactional emails, enable it before launch.

### How do I protect pages so only logged-in users can see them?

In Next.js (V0), add a middleware.ts file that checks the Supabase session on every request to /dashboard/* and redirects to /login if none exists. In Lovable (React Router), create an AuthGuard component that reads the session from auth context and returns a redirect element if the session is null. Both checks should happen before any protected page content renders.

### Can I use both email and Google login at the same time?

Yes, and Supabase Auth handles the account linking automatically. If a user registers with email/password and then tries to sign in with Google using the same email, Supabase links both providers to the same auth.users record. You can see the linked identities in user.identities[] in the session object.

### What happens to user data if I delete an account?

Deleting a row from auth.users does not automatically delete data in your public tables unless you set up ON DELETE CASCADE on the foreign key references. The profiles table schema above includes ON DELETE CASCADE — but any other tables (posts, orders, files) need it too. For GDPR compliance, use a Supabase Edge Function with the service role key to cascade-delete across all tables in a single transaction.

### How do I customize the verification email template?

Go to Supabase Dashboard → Authentication → Email Templates. You can edit the subject line, HTML body, and the confirmation link placeholder. Add your logo, brand colors, and a clear call to action. Custom SMTP (via Resend) removes the 3 emails/hour free tier limit and lets you send from your own domain, which improves deliverability significantly.

---

Source: https://www.rapidevelopers.com/app-features/user-authentication-and-registration
© RapidDev — https://www.rapidevelopers.com/app-features/user-authentication-and-registration
