Skip to main content
RapidDev - Software Development Agency
lovable-issues

Resolving Redirect Issues After Auth Flow in Lovable

OAuth redirect loops in Lovable apps happen when the Supabase redirect URL does not match your actual app domain. Fix it by setting the correct Site URL in Supabase Authentication Settings, adding all your domains (including preview and production) to the Redirect URLs list with wildcards, and using the redirectTo option in your signInWithOAuth call.

Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced8 min read~15 minLovable projects using Supabase OAuth (Google, GitHub, etc.)March 2026RapidDev Engineering Team
TL;DR

OAuth redirect loops in Lovable apps happen when the Supabase redirect URL does not match your actual app domain. Fix it by setting the correct Site URL in Supabase Authentication Settings, adding all your domains (including preview and production) to the Redirect URLs list with wildcards, and using the redirectTo option in your signInWithOAuth call.

Why OAuth redirects fail or loop after authentication in Lovable

When a user signs in with Google or GitHub in a Lovable app, the flow involves three parties: your app, Supabase, and the OAuth provider. The user clicks 'Sign in with Google', gets redirected to Google, approves the login, gets redirected to Supabase, and then Supabase redirects back to your app. If any URL in this chain is wrong, the user ends up on the wrong page or in a redirect loop. The most common mistake is leaving the Supabase Site URL set to http://localhost:3000 (the default). After the OAuth provider sends the user to Supabase, Supabase looks at the Site URL to decide where to redirect back. If it points to localhost, the user gets sent to localhost instead of your Lovable app — which either does nothing (blank page) or loops because the session never gets captured. Another frequent issue is having your production domain but not your Lovable preview domain in the redirect URLs list. Testing works on the published app but fails in the Lovable editor preview because the preview uses a different domain that is not whitelisted in Supabase.

  • Supabase Site URL still set to http://localhost:3000 — redirects to localhost instead of your app
  • Production domain not added to Supabase Redirect URLs — Supabase blocks the redirect as a security measure
  • Preview domain and published domain are different — OAuth works on one but not the other
  • Missing redirectTo option in signInWithOAuth — Supabase uses the Site URL instead of the intended page
  • OAuth provider callback URL does not match the Supabase auth endpoint format

Error messages you might see

Unable to exchange external code: invalid_request

The OAuth authorization code exchange failed. This usually means the redirect URI registered with the provider does not match the one Supabase used. Check that your Supabase callback URL is added to the OAuth provider's authorized redirect URIs.

Redirect URL does not match any registered redirect URLs

Supabase rejected the redirect because the target URL is not in the Redirect URLs list. Add the URL (including the protocol and any subdomain) to Supabase Authentication Settings under Redirect URLs.

OAuth callback returned no session

The callback page loaded but Supabase did not return a session. This happens when the auth state listener is not set up in time to capture the session from the URL hash after the redirect.

Error 400: redirect_uri_mismatch (Google OAuth)

The redirect URI sent in the OAuth request does not match any URI in your Google Cloud Console. Add your Supabase callback URL to the authorized redirect URIs in Google Cloud Console.

Before you start

  • A Lovable project with Supabase authentication and at least one OAuth provider enabled
  • Access to your Supabase project dashboard (Authentication Settings)
  • Access to the OAuth provider console (Google Cloud Console, GitHub Settings, etc.)
  • Your app's published URL and preview URL

How to fix it

1

Update the Supabase Site URL to your production domain

Supabase uses the Site URL as the default redirect destination after OAuth — localhost breaks everything

Go to your Supabase project dashboard at supabase.com (not the Lovable Cloud tab). Navigate to Authentication and then URL Configuration. Change the Site URL from http://localhost:3000 to your actual production URL (like https://yourapp.lovable.app or your custom domain). This is the URL Supabase redirects to after a successful OAuth login if no redirectTo is specified in the code.

Expected result: The Site URL points to your live app. After OAuth, users are redirected to your app instead of localhost.

2

Add all domains to the Supabase Redirect URLs list

Supabase rejects redirects to URLs not in this list as a security measure against open redirect attacks

In the same URL Configuration section of Supabase Authentication Settings, scroll down to Redirect URLs. Add every domain your app uses with a wildcard pattern. For Lovable projects, you typically need three entries: your lovable.app subdomain, your custom domain (if any), and the Lovable preview domain. Use the ** wildcard to match all paths under each domain.

typescript
1https://yourapp.lovable.app/**
2https://yourdomain.com/**
3https://id.lovableproject.com/**

Expected result: Supabase accepts redirect requests to any path on your listed domains.

3

Add the redirectTo option in your OAuth sign-in call

Without redirectTo, Supabase redirects to the Site URL instead of the specific callback page in your app

Open the component that handles your OAuth sign-in (usually a login page). Find the supabase.auth.signInWithOAuth call and add the redirectTo option pointing to your app's auth callback page. This tells Supabase exactly where to send the user after they approve the OAuth login. Use window.location.origin to automatically use the current domain, so it works in both preview and production.

Before
typescript
const handleGoogleLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: "google",
});
};
After
typescript
const handleGoogleLogin = async () => {
await supabase.auth.signInWithOAuth({
provider: "google",
options: {
// Redirect back to the current domain's callback page
redirectTo: `${window.location.origin}/auth/callback`,
},
});
};

Expected result: After Google OAuth approval, the user returns to /auth/callback on the correct domain.

4

Create an auth callback page that captures the session

The callback page must extract the session tokens from the URL and redirect to the intended destination

Create a page component for /auth/callback that waits for Supabase to process the OAuth tokens in the URL hash, then redirects the user to the dashboard or their intended page. The session is captured automatically by Supabase's auth listener, but you need to wait for it before navigating. If your auth flow involves multiple OAuth providers, custom scopes, or role-based redirects after login, RapidDev's engineers have resolved this exact pattern across 600+ Lovable projects.

typescript
1import { useEffect } from "react";
2import { useNavigate } from "react-router-dom";
3import { supabase } from "@/integrations/supabase/client";
4
5export default function AuthCallback() {
6 const navigate = useNavigate();
7
8 useEffect(() => {
9 // Supabase processes the OAuth tokens from the URL hash automatically
10 // We just need to wait for the session to be available
11 supabase.auth.onAuthStateChange((event, session) => {
12 if (event === "SIGNED_IN" && session) {
13 // Session captured — redirect to the app
14 navigate("/dashboard", { replace: true });
15 }
16 });
17 }, [navigate]);
18
19 return (
20 <div className="flex items-center justify-center min-h-screen">
21 <p className="text-muted-foreground">Completing sign-in...</p>
22 </div>
23 );
24}

Expected result: After OAuth redirect, the callback page captures the session and sends the user to /dashboard.

Complete code example

src/pages/AuthCallback.tsx
1import { useEffect, useState } from "react";
2import { useNavigate } from "react-router-dom";
3import { supabase } from "@/integrations/supabase/client";
4
5export default function AuthCallback() {
6 const navigate = useNavigate();
7 const [error, setError] = useState<string | null>(null);
8
9 useEffect(() => {
10 const handleAuthCallback = async () => {
11 // Check if we have a session from the OAuth redirect
12 const { data: { session }, error: authError } = await supabase.auth.getSession();
13
14 if (authError) {
15 setError(authError.message);
16 return;
17 }
18
19 if (session) {
20 // Successfully authenticated — go to the dashboard
21 navigate("/dashboard", { replace: true });
22 return;
23 }
24
25 // If no session yet, listen for the auth state change
26 const { data: { subscription } } = supabase.auth.onAuthStateChange(
27 (event, session) => {
28 if (event === "SIGNED_IN" && session) {
29 navigate("/dashboard", { replace: true });
30 } else if (event === "SIGNED_OUT") {
31 setError("Authentication failed. Please try again.");
32 }
33 }
34 );
35
36 // Timeout after 10 seconds to prevent infinite loading
37 const timeout = setTimeout(() => {
38 setError("Authentication timed out. Please try signing in again.");
39 subscription.unsubscribe();
40 }, 10000);
41
42 return () => {
43 clearTimeout(timeout);
44 subscription.unsubscribe();
45 };
46 };
47
48 handleAuthCallback();
49 }, [navigate]);
50
51 if (error) {
52 return (
53 <div className="flex flex-col items-center justify-center min-h-screen p-8">
54 <p className="text-destructive mb-4">{error}</p>
55 <a href="/login" className="text-primary hover:underline">Back to sign in</a>
56 </div>
57 );
58 }
59
60 return (
61 <div className="flex items-center justify-center min-h-screen">
62 <p className="text-muted-foreground">Completing sign-in...</p>
63 </div>
64 );
65}

Best practices to prevent this

  • Always set the Supabase Site URL to your production domain — never leave it as localhost
  • Add all domains (preview, published, custom) to Supabase Redirect URLs with ** wildcard paths
  • Use window.location.origin in redirectTo so the same code works in both preview and production environments
  • Create a dedicated /auth/callback route to handle the OAuth return instead of redirecting to the home page
  • Add a timeout on the callback page so users are not stuck forever if the session capture fails
  • Register the Supabase callback URL (https://your-project.supabase.co/auth/v1/callback) in every OAuth provider console
  • Test the full OAuth flow in an incognito window after every configuration change to avoid cached session interference

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I have a Lovable.dev app using Supabase OAuth (Google sign-in) and users are getting stuck after the OAuth redirect. They either loop back to the login page, land on a blank page, or see an error. Here is my setup: - Supabase Site URL: [paste it] - Supabase Redirect URLs: [paste them] - My signInWithOAuth code: [paste it] - My callback route: [paste it or say 'none'] - App domain: [your domain] Please help me: 1. Identify which URL configuration is wrong 2. Fix the signInWithOAuth call with the correct redirectTo 3. Create a proper callback page that captures the session 4. List the exact URLs I need to add to Supabase and Google Cloud Console

Lovable Prompt

My Google OAuth login redirects users to a blank page instead of the dashboard. Fix the OAuth redirect flow. Check @src/pages/Login.tsx and add the redirectTo option to the signInWithOAuth call using window.location.origin. Create an @src/pages/AuthCallback.tsx component that captures the session and redirects to /dashboard. Add the /auth/callback route to @src/App.tsx.

Frequently asked questions

Why does OAuth redirect to localhost instead of my app?

The Supabase Site URL is still set to http://localhost:3000 (the default). Go to your Supabase dashboard, navigate to Authentication then URL Configuration, and change the Site URL to your actual production domain (like https://yourapp.lovable.app).

Why does OAuth work in Lovable preview but not on my published app?

The published app uses a different domain than the preview. Add the published domain to both the Supabase Redirect URLs list and the OAuth provider's authorized redirect URIs. Use window.location.origin in your redirectTo so it adapts to whatever domain the app is running on.

What URLs do I need to add to Google Cloud Console?

Add your Supabase callback URL: https://your-project.supabase.co/auth/v1/callback. This is the URL Google redirects to after the user approves the login. Supabase then handles the second redirect back to your app.

How do I fix an OAuth redirect loop?

A redirect loop usually means the callback page tries to initiate another sign-in instead of capturing the session. Make sure your callback route checks for an existing session before triggering a new sign-in. Add a 10-second timeout to break infinite loops.

Why does the session disappear after the OAuth redirect?

Supabase puts the session tokens in the URL hash after redirect. The auth state listener (onAuthStateChange) must be active when the callback page loads to capture these tokens. If the listener is not set up in App.tsx, the tokens are lost.

Can I redirect to a specific page after OAuth login?

Yes. Set the redirectTo option in signInWithOAuth to the specific page URL, like window.location.origin + '/dashboard'. The callback page can also read a stored 'return URL' from localStorage if you want to redirect users to wherever they were before signing in.

What if I can't fix this myself?

OAuth redirect flows involving multiple providers, custom domains, and different environments (preview vs production vs custom domain) can get complex. RapidDev's engineers have configured OAuth across 600+ Lovable projects and can resolve redirect issues in a single session.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your issue.

Book a free consultation

Need help with your Lovable project?

Our experts have built 600+ apps and can solve your issue fast. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.