# How to Add Social Login to Your App — Copy-Paste Prompts Included

- Tool: App Features
- Last updated: July 2026

## TL;DR

Social login means a user clicks 'Sign in with Google' and Supabase Auth handles the OAuth handshake, creates the account, and returns a session — no password required. With Lovable or V0 you can have Google and GitHub login working in 1–2 hours for $0/month. Google OAuth is free, GitHub OAuth is free, and Apple Sign In requires an Apple Developer account ($99/yr) but has no per-login charge.

## What Social Login Actually Does in Your App

Social login delegates authentication to a provider the user already trusts — Google, GitHub, or Apple. When a user clicks 'Sign in with Google', your app redirects to Google's login page, the user approves access, and Google redirects back to your /auth/callback route with a one-time code. Supabase Auth exchanges that code for a session token and creates (or retrieves) an auth.users row. The user never sets a password with your app. The product decision is which providers to offer: Google covers 90% of consumer use cases, GitHub covers developer-focused tools, and Apple is required if you plan to list on the iOS App Store. Each provider has its own credential setup in their respective developer consoles before any Lovable or V0 prompt can make it work.

## Anatomy of the Feature

Six components. Supabase Auth handles the heavy OAuth lifting — your work is the UI buttons, the callback handler, and the profile auto-fill on first login.

- **Supabase Auth OAuth** (backend): supabase.auth.signInWithOAuth({ provider: 'google', options: { redirectTo: 'https://yourapp.com/auth/callback' } }) initiates the OAuth flow. Supabase handles the PKCE challenge, provider redirect, token exchange, and session creation. On first sign-in from a new provider, Supabase creates a row in auth.users and fires any configured auth triggers.
- **OAuth provider credentials** (service): Each provider requires developer credentials registered in their console: Google (Google Cloud Console → APIs & Services → Credentials → OAuth 2.0 Client ID), GitHub (Settings → Developer Settings → OAuth Apps), Apple (Apple Developer account → Certificates → Services IDs + private key). Credentials are stored in Supabase Dashboard → Authentication → Providers — never in your app code.
- **Social login buttons** (ui): One button per provider using the provider's official brand assets. Google requires the exact G logo and white background per their brand guidelines. Apple requires black background with the Apple logo. GitHub uses the Octicon mark. Built with shadcn/ui Button plus SVG icons inline. Never use a generic 'Sign in with Social' button — provider guidelines prohibit it and users distrust unlabeled buttons.
- **OAuth redirect handler** (ui): A /auth/callback route (page in Next.js App Router, or path in Lovable's React Router) that receives the OAuth code from the provider redirect. It calls supabase.auth.exchangeCodeForSession() to trade the code for a Supabase session, then redirects the user to /dashboard or the original page they tried to access before login.
- **Profile auto-fill** (backend): supabase.auth.onAuthStateChange() fires on sign-in and provides user.user_metadata containing name, avatar_url, email, and provider-specific fields from the OAuth token. On first sign-in (check user.created_at within the last few seconds), insert a row into the profiles table populated from user_metadata. On subsequent logins, update the avatar_url to keep it fresh since Google's signed URLs expire.
- **Account linking (multi-provider)** (backend): Supabase Auth automatically links providers to the same account when the email address matches — a user who signed up with email/password and then clicks 'Sign in with Google' using the same email gets linked, not a duplicate account. The user.identities[] array tracks all linked providers. Expose a 'Connected accounts' section in profile settings showing each linked provider with an unlink button.

## Data model

A profiles table stores display name and avatar, and an optional user_providers table tracks linked OAuth accounts for the 'Connected accounts' UI. Run this in the Supabase SQL editor:

```sql
create table public.profiles (
  id uuid primary key references auth.users(id) on delete cascade,
  display_name text,
  avatar_url text,
  provider 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);

-- Optional: track linked providers for 'Connected accounts' UI
create table public.user_providers (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) on delete cascade not null,
  provider text not null,
  provider_user_id text,
  linked_at timestamptz not null default now(),
  unique (user_id, provider)
);

alter table public.user_providers enable row level security;

create policy "Users can view own providers"
  on public.user_providers for select
  using (auth.uid() = user_id);

-- Trigger to auto-create profile on new user
create or replace function public.handle_new_user()
returns trigger language plpgsql security definer set search_path = public
as $$
begin
  insert into public.profiles (id, display_name, avatar_url, provider)
  values (
    new.id,
    new.raw_user_meta_data ->> 'full_name',
    new.raw_user_meta_data ->> 'avatar_url',
    new.raw_app_meta_data ->> 'provider'
  )
  on conflict (id) do nothing;
  return new;
end;
$$;

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

The database trigger is more reliable than client-side profile creation — it fires even if the user's browser closes during the OAuth redirect. The on conflict (id) do nothing ensures re-runs are safe.

## Build paths

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

Fastest path to working social login. Lovable generates the OAuth buttons, callback handler, and profile auto-fill from a single prompt. Only manual step is adding OAuth credentials in the provider consoles and the Supabase Dashboard.

1. Set up Google OAuth credentials: go to console.cloud.google.com → APIs & Services → Credentials → Create OAuth 2.0 Client ID; set Authorized redirect URI to your Lovable published URL + /auth/callback
2. Set up GitHub OAuth app: go to github.com → Settings → Developer Settings → OAuth Apps → New OAuth App; set Authorization callback URL to your published URL + /auth/callback
3. In Supabase Dashboard → Authentication → Providers, enable Google (paste Client ID and Secret) and GitHub (paste Client ID and Secret); add your published URL to Redirect URLs
4. Paste the prompt below into Lovable Agent Mode and let it generate the login page, callback route, and profile creation
5. Click Publish and test OAuth on the published URL — never in the preview iframe

Starter prompt:

```
Add social login to this app using Supabase Auth. On the login and registration pages, add a 'Sign in with Google' button (white background, Google G icon, correct Google brand guidelines) and a 'Sign in with GitHub' button (dark background, GitHub Octicon icon). Both buttons call supabase.auth.signInWithOAuth with the correct provider and redirectTo set to the production URL plus /auth/callback. Create a /auth/callback page that calls supabase.auth.exchangeCodeForSession() and then redirects to /dashboard. On first sign-in from any OAuth provider, auto-create a profiles table row with display_name and avatar_url taken from user.user_metadata. In the user profile settings page, add a 'Connected accounts' section that shows which OAuth providers are currently linked to the account. Add an error state for when the user closes the OAuth popup before completing: show a dismissible banner 'Sign-in cancelled — please try again'. Include a note in the UI to remind users and developers that OAuth only works on the published URL, not in the preview.
```

Limitations:

- OAuth completely fails in the Lovable preview iframe — you must test on the published URL; this is not a bug in the generated code
- Redirect URL in Supabase Auth and in each OAuth app must be updated to match your custom domain after publishing — this step is manual and easy to miss
- Apple OAuth requires an Apple Developer account ($99/yr) and significantly more setup than Google or GitHub — prompt for it separately only when needed

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

V0 generates polished Next.js social login with shadcn/ui buttons and a clean callback handler. Best when social login is part of a larger Next.js app you are building in V0.

1. Set up OAuth credentials in Google Cloud Console and GitHub Developer Settings (same as Lovable path above)
2. In Supabase Dashboard, enable Google and GitHub providers and add your Vercel deployment URL + /auth/callback to Redirect URLs
3. Add environment variables in the V0 Vars panel: NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY
4. Run the profiles table SQL and trigger from this page in the Supabase SQL editor
5. Paste the prompt below into V0 and let it generate the auth components
6. Deploy to Vercel, add OAuth redirect URLs for the Vercel domain, and test OAuth on the live deployment

Starter prompt:

```
Build social login for a Next.js App Router app using Supabase Auth. Login page at /login: 'Sign in with Google' button (white background, SVG Google G logo, Google brand-compliant text) and 'Sign in with GitHub' button (dark background, SVG GitHub Octicon) — both are shadcn/ui Button variants with provider icons. Both buttons call supabase.auth.signInWithOAuth({ provider, options: { redirectTo: process.env.NEXT_PUBLIC_APP_URL + '/auth/callback' } }). Create app/auth/callback/route.ts as a Next.js Route Handler that calls supabase.auth.exchangeCodeForSession(code), handles errors by redirecting to /login?error=oauth_failed, and redirects to /dashboard on success. On the client, listen with supabase.auth.onAuthStateChange: on first SIGNED_IN event, upsert a profiles row with display_name and avatar_url from session.user.user_metadata. Connected accounts section on /profile: read user.identities from supabase.auth.getUser() and display each linked provider with name and linked date. Error state when popup is closed: catch the user_cancelled error from signInWithOAuth and show a toast notification. Protect /dashboard with Next.js middleware that redirects to /login if no session.
```

Limitations:

- V0 does not auto-provision OAuth credentials or configure Supabase providers — both steps are manual before testing
- Auth.js v5 may appear in first-generation code instead of direct Supabase Auth if not explicitly specified — this adds complexity; always specify 'use Supabase Auth directly' in the prompt
- You must add the Vercel production URL to both Supabase Redirect URLs and the OAuth app settings after deploying — redirect_uri_mismatch is the most common post-deploy error

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

Direct Supabase Auth OAuth without AI scaffolding. Only warranted when you need custom OAuth scopes, multi-tenant credentials, or provider token storage for API calls beyond identity.

1. Implement supabase.auth.signInWithOAuth with custom scopes for each provider — e.g., Google Drive scope for file access, GitHub repos scope for repository access
2. Build a token refresh pipeline that persists provider access tokens (returned in session.provider_token) to a secure encrypted column for server-side API calls
3. Implement multi-tenant OAuth where different workspace customers have different OAuth app credentials routed based on the logged-in domain

Limitations:

- More engineering time than AI paths for standard social login — only justified for non-standard requirements like custom scopes or multi-tenant credential routing
- Provider token storage and refresh is security-sensitive code that requires careful key management beyond what pSEO tutorials cover

## Gotchas

- **OAuth redirect fails in Lovable and V0 preview** — signInWithOAuth() opens a redirect to the provider's login page and expects to return to your /auth/callback route at a publicly registered URL. The Lovable preview iframe runs on a dynamic sandbox subdomain that is not registered as a valid OAuth redirect URI. Google and GitHub return an error before the user even sees a login prompt. Fix: Test OAuth exclusively on the published Lovable URL or the deployed Vercel URL. In Lovable, click Publish first, then open the live URL in a new browser tab. In V0, deploy to Vercel before running any OAuth test. Add a note in the Lovable project description so you do not forget this after the next session.
- **redirect_uri_mismatch after deploying to a custom domain** — When you connect a custom domain (yourdomain.com) to Lovable or Vercel, the OAuth callback URL changes. The Google Cloud Console and Supabase Dashboard still have your previous URL (*.lovable.app or *.vercel.app) as the authorized redirect. Every social login attempt returns a 400 redirect_uri_mismatch error. Fix: After every domain change, update two places: (1) Supabase Dashboard → Authentication → Redirect URLs — add the new callback URL; (2) each OAuth provider console (Google Cloud, GitHub Developer Settings) — add the new authorized redirect URI. Both must be updated for each provider. Keep the old URLs too so existing sessions are not broken.
- **Same email from two providers creates duplicate accounts** — A user registered with email/password, then clicks 'Sign in with Google' using the same email address. If the 'Link providers' setting is not enabled in Supabase, Supabase creates a second auth.users row with the same email. The user now has two separate accounts and their data is split between them. Fix: Enable 'Automatically link identities' in Supabase Dashboard → Authentication → Settings. With this enabled, signing in with a new provider that matches an existing email attaches the new identity to the existing user record rather than creating a duplicate. Test this edge case explicitly before launch by registering with email first and then signing in with Google.
- **Apple Sign In privacy relay email breaks profile creation** — Apple lets users hide their real email and provides a randomized @privaterelay.appleid.com address instead. Any profile creation logic that validates email domain format or rejects non-standard domains will reject Apple users. Apple's privacy relay also means you cannot use email as a stable identifier for Apple accounts. Fix: Accept @privaterelay.appleid.com as a valid email address — never validate email domains on account creation. Store Apple's stable user ID (from user.identities[0].id) separately as the primary identifier for Apple users rather than relying on email. Test Apple Sign In with both the 'Share email' and 'Hide email' options to verify both code paths work.
- **Google avatar URL expires and breaks profile pictures** — Google OAuth returns a profile picture URL that is a signed Google CDN link. These links expire. Storing the URL at first sign-in and never updating it means profile pictures show as broken images within days to weeks. Fix: On every sign-in event (not just first sign-in), update avatar_url in the profiles table with the latest value from user.user_metadata.avatar_url. Alternatively, download and re-upload the image to Supabase Storage on first sign-in — you then own the URL and it never expires. The onAuthStateChange handler is the right place to trigger this update.

## Best practices

- Offer Google as the first and most prominent social login — it covers the broadest user base and has the simplest setup
- Register OAuth credentials in provider consoles before generating any code — you need the Client ID and Secret to configure Supabase Auth, and the Supabase redirect URL to configure the OAuth app
- Store the user's intended destination in sessionStorage before triggering OAuth so you can redirect them back to the right page after the callback completes
- Use a Supabase database trigger to create the profiles row on new user creation rather than relying on client-side onAuthStateChange — the trigger fires even if the browser tab closes mid-redirect
- Keep old redirect URLs in Supabase and OAuth app settings when adding a new domain — removing them breaks existing sessions
- Implement a 'Connected accounts' UI so users can see which providers are linked and optionally unlink them — users who switch from Google to email/password need a way to manage this
- For Apple Sign In, always test both the 'Share my email' and 'Hide my email' flows — they produce different user objects and both must work

## Frequently asked questions

### Do I need Google's permission to add 'Sign in with Google'?

You need to register an OAuth 2.0 client in Google Cloud Console, but this is a self-service process that takes about 10 minutes and is free. You do not need Google's approval for standard identity-only OAuth (sign in, get name and email). If you request sensitive scopes beyond identity — like Google Drive or Gmail access — Google requires an OAuth verification review, which can take weeks.

### What is the easiest social login to add — Google or GitHub?

Both take about 10 minutes to configure in their respective consoles. Google has stricter button branding requirements. GitHub's setup is slightly simpler because there is no project or API enablement step — just create an OAuth App in Developer Settings. For a developer-facing tool, GitHub is often more meaningful to users; for a consumer app, Google reaches more people.

### Can a user have both a password and social login?

Yes. Supabase Auth supports multiple identities per user. A user can sign up with email/password and later link their Google account, or vice versa. With 'Automatically link identities' enabled in Supabase Dashboard, signing in with Google after registering with the same email automatically links both methods to a single account.

### What happens to a user's data if they revoke OAuth access?

Revoking OAuth access in Google or GitHub removes that provider's ability to issue new tokens — but it does not delete the user's account or data in your app. Their Supabase auth.users row and all associated data remain intact. The user simply cannot sign in with that provider anymore until they re-authorize. Add a fallback path (email/password or another linked provider) so they do not get locked out.

### Is Apple Sign In required if I want to list my app on the App Store?

If your app offers any third-party social login (Google, Facebook, etc.), Apple requires you to also offer Sign in with Apple when submitting to the App Store. This is Apple's policy for native iOS apps. For web apps accessed through a mobile browser, Apple Sign In is optional. Budget extra time for Apple setup — it requires a $99/yr Developer account, a Services ID, and a private key.

### How do I add more OAuth providers later without breaking existing accounts?

Adding a new provider to Supabase Auth does not affect existing accounts. Enable the new provider in Supabase Dashboard → Authentication → Providers, configure credentials, and add the new button to your login page. Existing users can link the new provider from their profile settings. The only risk is if you fail to handle the case where a new provider returns the same email as an existing account — ensure 'link identities' is enabled to prevent duplicates.

### Can I get the user's Google profile picture?

Yes. After sign-in, user.user_metadata.avatar_url contains the Google profile picture URL. Store it in your profiles table. Be aware that Google's CDN URLs expire — update the avatar_url on each subsequent sign-in event from onAuthStateChange to keep it fresh, or copy the image to Supabase Storage on first sign-in so you control the URL permanently.

### What are Google's brand guidelines for the sign-in button?

Google requires: button text must say exactly 'Sign in with Google' (not 'Login with Google'); the Google G logo must appear in full color on a white or light chip; the button must be at least 40px tall; do not distort or recolor the G logo. Violations can result in your OAuth app being flagged during Google's periodic review. Lovable and V0 both generate compliant buttons from the prompts on this page.

---

Source: https://www.rapidevelopers.com/app-features/social-login
© RapidDev — https://www.rapidevelopers.com/app-features/social-login
