# Resolving Redirect Issues After Auth Flow in Lovable

- Tool: Lovable
- Difficulty: Advanced
- Fix time: ~15 min
- Compatibility: Lovable projects using Supabase OAuth (Google, GitHub, etc.)
- Last updated: March 2026

## 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.

After:

```
https://yourapp.lovable.app/**
https://yourdomain.com/**
https://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:

```
const handleGoogleLogin = async () => {
  await supabase.auth.signInWithOAuth({
    provider: "google",
  });
};
```

After:

```
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.

After:

```
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { supabase } from "@/integrations/supabase/client";

export default function AuthCallback() {
  const navigate = useNavigate();

  useEffect(() => {
    // Supabase processes the OAuth tokens from the URL hash automatically
    // We just need to wait for the session to be available
    supabase.auth.onAuthStateChange((event, session) => {
      if (event === "SIGNED_IN" && session) {
        // Session captured — redirect to the app
        navigate("/dashboard", { replace: true });
      }
    });
  }, [navigate]);

  return (
    <div className="flex items-center justify-center min-h-screen">
      <p className="text-muted-foreground">Completing sign-in...</p>
    </div>
  );
}
```

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

## Complete code example

File: `src/pages/AuthCallback.tsx`

```typescript
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { supabase } from "@/integrations/supabase/client";

export default function AuthCallback() {
  const navigate = useNavigate();
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const handleAuthCallback = async () => {
      // Check if we have a session from the OAuth redirect
      const { data: { session }, error: authError } = await supabase.auth.getSession();

      if (authError) {
        setError(authError.message);
        return;
      }

      if (session) {
        // Successfully authenticated — go to the dashboard
        navigate("/dashboard", { replace: true });
        return;
      }

      // If no session yet, listen for the auth state change
      const { data: { subscription } } = supabase.auth.onAuthStateChange(
        (event, session) => {
          if (event === "SIGNED_IN" && session) {
            navigate("/dashboard", { replace: true });
          } else if (event === "SIGNED_OUT") {
            setError("Authentication failed. Please try again.");
          }
        }
      );

      // Timeout after 10 seconds to prevent infinite loading
      const timeout = setTimeout(() => {
        setError("Authentication timed out. Please try signing in again.");
        subscription.unsubscribe();
      }, 10000);

      return () => {
        clearTimeout(timeout);
        subscription.unsubscribe();
      };
    };

    handleAuthCallback();
  }, [navigate]);

  if (error) {
    return (
      <div className="flex flex-col items-center justify-center min-h-screen p-8">
        <p className="text-destructive mb-4">{error}</p>
        <a href="/login" className="text-primary hover:underline">Back to sign in</a>
      </div>
    );
  }

  return (
    <div className="flex items-center justify-center min-h-screen">
      <p className="text-muted-foreground">Completing sign-in...</p>
    </div>
  );
}
```

## Best practices

- 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

## 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.

---

Source: https://www.rapidevelopers.com/lovable-issues/resolving-redirect-issues-after-auth-flow-in-lovable
© RapidDev — https://www.rapidevelopers.com/lovable-issues/resolving-redirect-issues-after-auth-flow-in-lovable
