Hire Lovable Xperts
Backend & Database

Lovable Login Loop: Why You Get Redirected Back to the Login Page (and the 5-Minute Fix)

If you sign in to your Lovable app and immediately get redirected back to the login page, you are in an auth redirect loop. In almost every case it is one of four things: a deadlock inside onAuthStateChange, a wrong Supabase Site URL or redirect URL, a session that is not being persisted, or RLS blocking the profile read your route guard depends on. This guide walks the 5-minute fix for each.

By Founder Name · Last verified: 2026-06-25

Why does my Lovable app redirect me back to the login page after I sign in?

Because your route guard thinks you are still logged out. Sign-in succeeds at Supabase, but the app fails to read or persist the resulting session before your protected route checks it, so the guard sends you straight back to /login. The credentials are fine. The bug is in how the session is loaded, stored, or read after login, not in the login itself.

There are only four root causes worth checking, and they account for nearly every Lovable login loop we see in rescue work. First, a deadlock inside the onAuthStateChange callback where an await on a Supabase query never resolves. Second, a wrong Site URL or redirect URL in Supabase Auth settings that throws the session away on the OAuth or magic-link round trip. Third, a session that is created but never persisted, so a page refresh or new tab lands logged out. Fourth, a Row Level Security policy blocking the profiles read that your guard treats as the proof you are authenticated.

We call this the Login Doom Loop: each sign-in attempt looks like it worked for a fraction of a second, then bounces, and because the URL flickers between /login and your dashboard it is easy to assume the password is wrong. It is not. Open DevTools, watch the Network and Application tabs while you log in once, and the real cause becomes obvious in under a minute.

Lovable Login Loop — Cause to Fix Map
Symptom you seeMost likely cause5-minute fix
App hangs on a spinner after login, then bounces to /loginDeadlock: an await inside the onAuthStateChange callback never resolvesMove data fetches OUT of the callback; only set session state inside it
Login works locally but loops on the deployed URLWrong Site URL / missing redirect URL in Supabase Auth settingsAdd your production URL to Site URL and Redirect URLs, then redeploy
Login works, but a refresh or new tab logs you out and loopsSession not persisted to localStorage (persistSession off or wrong storage)Set persistSession: true and autoRefreshToken: true in the client
You reach the app for a blink, then get kicked back to /loginRLS blocks the profiles/roles read your route guard requiresAdd a SELECT policy: auth.uid() = id on the profiles table
OAuth (Google/GitHub) returns to login, never to the dashboardProvider callback URL points at a stale or wrong domainMatch Supabase redirect URL and provider callback to the live domain
Magic link opens, flashes the app, then loopsdetectSessionInUrl disabled or the link domain != Site URLEnable detectSessionInUrl and align the magic-link domain with Site URL

How do I tell which of the four causes is breaking my login?

Log in once with DevTools open and read three things. The Network tab tells you whether the token request succeeded. The Application tab (Local Storage) tells you whether a session was saved. The Console tells you whether a query hung or an RLS error fired. Two minutes of observation points you at exactly one of the four causes — no guessing, no wasted credits.

  1. Open DevTools (F12) and switch to the Network tab. Filter for token. Submit your login.
  2. Confirm the POST to /auth/v1/token returns 200 with an access_token in the response. If it does, your credentials and Supabase keys are fine — the bug is after login.
  3. Switch to Application > Local Storage. Look for a key like sb-<project-ref>-auth-token. If it is missing, your session is not being persisted (cause 3).
  4. Switch to Console. A spinner that never resolves with no error points to an onAuthStateChange deadlock (cause 1).
  5. A red line like 'new row violates row-level security policy' or a 401/406 on a profiles request points to RLS (cause 4).
  6. If the token request is 200 but the URL still bounces on the deployed site only, it is a Site URL / redirect URL mismatch (cause 2).
Run the whole sequence on the deployed pop-out build, not just the editor preview. Auth redirect loops frequently appear only on the live domain because the editor uses a different origin that is already whitelisted in Supabase.

How do I fix the onAuthStateChange deadlock that freezes login?

Never call an async Supabase query directly inside the onAuthStateChange callback and await it there. Supabase serialises auth events, so an awaited query inside the callback can deadlock — the session set never completes, your guard never sees a user, and you loop. The fix is to set session state synchronously in the callback and run any data fetch outside it, after the state updates.

This is the single most common Lovable login loop, because the AI loves to generate a tidy-looking callback that fetches the user's profile inline. It works in the demo and deadlocks the moment a real session restores on page load. The rule from the Supabase docs is explicit: do not use other Supabase functions inside the onAuthStateChange callback with await.

Here is the broken pattern Lovable tends to generate, followed by the corrected version. Paste the corrected one over your auth provider or hook:

BROKEN — deadlocks on session restore: supabase.auth.onAuthStateChange(async (event, session) => { // This await inside the callback can hang forever const { data } = await supabase.from('profiles').select('*').single(); setProfile(data); setSession(session); });

FIXED — set state in the callback, fetch outside it: supabase.auth.onAuthStateChange((event, session) => { setSession(session); setUser(session?.user ?? null); if (session?.user) { // defer the fetch so it runs AFTER the auth event resolves setTimeout(() => { void loadProfile(session.user.id); }, 0); } });

Also confirm you call supabase.auth.getSession() once on mount to hydrate the initial state, and that you unsubscribe on unmount. A guard that reads loading-state-true forever, because getSession was never called, produces the same loop.

Do not ask Lovable to 'fix the login loop' as a single prompt while this deadlock is live. It will often regenerate the same inline-await pattern under a different name — a false-fixed hallucination where the response says it is solved but the callback still blocks. Apply the corrected pattern by hand.

Why does login work in the editor but loop on my real deployed site?

Because Supabase rejects the session whenever the redirect lands on a URL that is not whitelisted. The editor preview origin is already trusted, but your production domain is not until you add it. When the OAuth callback or magic link returns to an unlisted URL, Supabase discards the session and your guard bounces you to /login on the live site only.

Open your Supabase dashboard, go to Authentication > URL Configuration, and fix two fields. Set Site URL to your exact production URL (https://yourapp.com, no trailing slash). Then add every URL the user can land on after auth to Redirect URLs — including your custom domain, the lovable.app preview domain, and localhost for development. A single missing entry is enough to throw the session away.

If you use Google or GitHub OAuth, the provider's own callback URL must also point at https://<project-ref>.supabase.co/auth/v1/callback, and the post-login redirectTo in your code must be a URL that appears in the Redirect URLs list. A mismatch here is The Vanishing Session — the token is issued, then immediately orphaned because there is nowhere whitelisted to deliver it.

After changing URL configuration, redeploy and test in an incognito window so no stale token masks the result. If you recently moved to a custom domain and the loop started then, the custom-domain switch is almost certainly the trigger.

Related: fix a Lovable custom domain that is stuck or looping · stop Lovable environment variables vanishing on deploy

Why am I logged out and looped every time I refresh the page?

Because the session was never written to storage, so a refresh starts with no token and your guard treats you as logged out. The Supabase client must be configured to persist the session and auto-refresh the token. If persistSession is off, or the wrong storage is passed, or two client instances exist, the session lives only in memory and dies on reload — producing a loop on every refresh.

Check how your Supabase client is created. There should be exactly one createClient call in the whole app, exported from a single module and imported everywhere. Two client instances each manage their own session and fight over storage, which Lovable's generated code does surprisingly often.

The correct client configuration looks like this: import { createClient } from '@supabase/supabase-js'; export const supabase = createClient( import.meta.env.VITE_SUPABASE_URL, import.meta.env.VITE_SUPABASE_ANON_KEY, { auth: { persistSession: true, autoRefreshToken: true, detectSessionInUrl: true, storage: localStorage, }, } );

Then verify in DevTools: after login, Application > Local Storage should contain a key named sb-<project-ref>-auth-token holding your access and refresh tokens. If that key never appears, persistSession is being overridden or you are in a context with no localStorage (some embedded or SSR setups). Fix the configuration before touching the route guard — a guard cannot read a session that was never stored.

Can a Supabase RLS policy cause the login redirect loop?

Yes — and it is the most overlooked cause. If your route guard treats a successful profiles or roles read as proof of login, and Row Level Security blocks that read, the guard sees empty data and bounces you to /login even though your session is perfectly valid. Auth succeeds; the authorization read fails silently; you loop.

The pattern is almost always the same: a guard does select from profiles where id = auth.uid(), gets back an empty array because no SELECT policy permits it, and concludes the user does not exist. RLS denials do not throw a loud error by default — they return zero rows or a 406, which a naive guard misreads as logged out.

The fix is a SELECT policy that lets a user read their own row. On the profiles table, add: create policy "Users can read own profile" on public.profiles for select using ( auth.uid() = id );

If you store roles in a separate table, it needs its own read policy too. Test the policy in the Supabase SQL editor with the impersonate-user option before redeploying. RLS is the right tool — it should stay on — but every table your guard reads on login needs a policy that the authenticated user can pass. Our deeper walkthrough of getting these policies right is below.

Related: fix Lovable Supabase RLS and permission-denied errors · Lovable RLS and auth best practices

How do I confirm the login loop is actually gone?

Run a four-point verification before you call it fixed. The most common false fix is one that works on a fresh login but breaks on refresh, in a new tab, or after the token expires. Test all four paths in an incognito window so no stale token hides a still-broken flow.

  1. Fresh login: sign in from a clean incognito window and confirm you land on the dashboard and stay there.
  2. Refresh: hard-refresh the dashboard (Cmd/Ctrl+Shift+R) and confirm you are not bounced to /login.
  3. New tab: open the app URL in a second tab and confirm the session carries over without a re-login.
  4. Sign out / sign in: log out fully, then log back in, and confirm no loop and no leftover session in Local Storage after logout.
  5. Check the Console during all four — zero red RLS errors, zero hanging requests, and the sb-<project-ref>-auth-token key present after login and cleared after logout.
If the loop returns only after about an hour, the issue is token refresh, not initial login. Confirm autoRefreshToken is true and that your guard re-reads the session on the TOKEN_REFRESHED event rather than caching a stale user object.

When should I stop debugging this myself and get help?

If you have applied all four fixes and the loop still appears — or if the loop only happens for some users, or only on the live domain, or only after a token refresh — the cause is structural and worth a specialist. An auth loop that resists the obvious fixes usually hides a deeper RLS, session-storage, or multi-client problem that compounds the longer it is prompted at.

We fix Lovable login loops as part of our app rescue work, usually within 24 to 48 hours: we reproduce the loop with DevTools, isolate which of the four causes (or which combination) is firing, apply the fix by hand rather than by prompt, and harden the RLS policies so the loop cannot silently return. You keep full ownership of your code and Supabase project throughout.

Because login loops so often trace back to RLS and session handling, they are also a signal to audit the rest of your auth surface — many apps with a login loop also have tables readable by anyone or service keys exposed in the client. If that describes your situation, a focused security audit is the faster route than fixing one loop at a time.

Related: Lovable App Rescue service · Lovable Security Audit service

Frequently asked questions

Why does my Lovable app keep redirecting me back to the login page after I log in?
Your login succeeds at Supabase, but the app fails to read or persist the resulting session before your route guard checks it, so the guard sends you back to /login. It is almost always one of four things: a deadlock inside onAuthStateChange, a wrong Supabase Site URL, a session that is not persisted, or an RLS policy blocking your profile read. Open DevTools and log in once to spot which.
How do I stop the onAuthStateChange callback from deadlocking?
Do not await a Supabase query inside the onAuthStateChange callback. Supabase serialises auth events, so an awaited query there can hang and the session never sets. Set your session and user state synchronously inside the callback, and defer any profile fetch to run after it (for example with setTimeout 0). Also call getSession once on mount to hydrate the initial state.
Why does my login work in the Lovable editor but loop on my real domain?
Because your production URL is not whitelisted in Supabase. The editor preview origin is already trusted, but your live domain is not until you add it under Authentication > URL Configuration. Set Site URL to your exact production URL and add every post-login URL to Redirect URLs. A single missing entry makes Supabase discard the session and bounce you to /login on the live site only.
Why am I logged out every time I refresh the page?
Your session is living in memory instead of storage, so a refresh starts with no token. Set persistSession: true and autoRefreshToken: true in your Supabase client, and make sure there is only one createClient instance in the whole app. After login, DevTools > Application > Local Storage should show an sb-<project-ref>-auth-token key. If it is missing, the session was never persisted.
Can a Row Level Security policy really cause a login redirect loop?
Yes, and it is the most overlooked cause. If your route guard treats a successful profiles read as proof of login, and RLS blocks that read, the guard sees empty data and bounces you to /login even though your session is valid. Add a SELECT policy using auth.uid() = id so each user can read their own row. Our RLS and permissions guide covers this in full.
Does fixing the login loop cost Lovable credits?
Only if you fix it by prompting. Editing the Supabase client config, URL settings, and RLS policies by hand costs nothing and is more reliable. Prompting Lovable to 'fix the login loop' often regenerates the same broken inline-await callback and burns credits without solving it. Apply the corrected onAuthStateChange pattern and RLS policy directly, then verify in DevTools.
My Google or GitHub OAuth login loops back instead of reaching the dashboard. What is wrong?
Your provider callback URL or post-login redirectTo does not match what Supabase has whitelisted. The provider callback must point at https://<project-ref>.supabase.co/auth/v1/callback, and the redirectTo in your code must appear in the Supabase Redirect URLs list. Any mismatch orphans the issued token, so the session is discarded and you loop. Align all three: provider callback, Supabase redirect URLs, and your code.
The login loop only comes back after about an hour. Why?
That is a token refresh problem, not an initial-login problem. The first session works, then the access token expires and is not refreshed, so your guard sees a stale or empty session and loops. Confirm autoRefreshToken: true in the client and that your code re-reads the session on the TOKEN_REFRESHED event rather than caching a user object indefinitely.
How do I know the login loop is truly fixed and not just hidden?
Test four paths in an incognito window: a fresh login, a hard refresh of the dashboard, opening the app in a second tab, and a full sign-out then sign-in. All four must stay logged in with no bounce to /login. Watch the Console for RLS errors or hanging requests, and confirm the auth-token key appears after login and clears after logout.
I have tried all four fixes and it still loops. Can you fix it for me?
Yes. A login loop that resists the obvious fixes usually hides a deeper RLS, session-storage, or multi-client issue. We reproduce it with DevTools, isolate the exact cause, fix it by hand rather than by prompt, and harden your auth so it cannot silently return — usually within 24 to 48 hours. Book a call and tell us what you are seeing and we will triage it the same day.

App down or leaking data? Get an expert on it within 24–48h.

Book a free 30-minute audit call. We'll diagnose what's wrong and tell you exactly what it costs to fix.

Get emergency help