Social login (OAuth)

How OAuth sign-in works on SupaHost: the callback URL to give each provider, enabling a provider in the dashboard, signInWithOAuth on the web and on the server, scopes and provider tokens.

Social login lets users sign in with an account they already have, such as Google or GitHub. Supabase Auth acts as the OAuth client: it sends the user to the provider, receives them back on its callback URL, creates or finds the matching user, and then redirects to your app with a session.

SupaHost supports 19 providers: Google, GitHub, Apple, Microsoft (Azure), GitLab, Bitbucket, Discord, Facebook, Figma, Kakao, Keycloak, LinkedIn, Notion, Slack, Spotify, Twitch, X (Twitter), WorkOS and Zoom. Step-by-step guides exist for Google, GitHub and Apple, and the provider reference covers the rest.

The flow

  1. Your app calls signInWithOAuth({ provider }). The client library builds a URL on your project's /auth/v1/authorize endpoint and sends the browser there.
  2. Auth redirects to the provider's consent screen.
  3. The provider sends the user back to Auth's callback URL, /auth/v1/callback, with an authorization code.
  4. Auth swaps that code for the user's profile, creates the user if needed, and redirects to your redirectTo URL (or the Site URL).
  5. Your app ends up with a session, either from the URL fragment (browser apps) or by exchanging a ?code= in your callback route (server apps using PKCE).

Two different redirect URLs are involved, and mixing them up is the most common OAuth mistake:

URL Where you put it Example
Auth's callback URL In the provider's console, as the redirect or callback URL https://app-one.acme.supahost.space/auth/v1/callback
Your app's landing page In your code as redirectTo, and in SupaHost's Redirect URLs list https://chat.example.com/auth/callback

Step 1: copy the callback URL

Open Dashboard → your project → Auth tab → OAuth providers. The card shows "Callback URL for every provider" with a copy button. It has this shape:

https://<project host or active custom domain>/auth/v1/callback

For project app-one on instance acme that's https://app-one.acme.supahost.space/auth/v1/callback. If the project has an active custom domain, the callback uses the oldest active custom domain instead, for example https://api.chat.example.com/auth/v1/callback. When you add a custom domain later, update the callback URL in every provider's console, or sign-in stops working. See OAuth broke after adding a custom domain.

Step 2: create the app at the provider

In the provider's developer console, create an OAuth app (the name varies: "OAuth client", "OAuth app", "application"), paste the callback URL, and copy the client ID and client secret it gives you.

Step 3: enable the provider in SupaHost

Still in the OAuth providers card:

  1. Click the provider's row to open it.
  2. Switch on Enabled.
  3. Paste the Client ID (some providers label it differently, such as "Services ID" for Apple or "App ID" for Facebook).
  4. Paste the Client secret. It's stored encrypted in Vault and never shown again. To keep a saved secret when you edit other fields, leave this field empty.
  5. Fill the URL field if the provider has one (Keycloak's "Realm URL" is required; the Azure, GitLab and WorkOS URL fields are optional).
  6. Click the save button, which is named after the provider (for example Save GitHub).

The provider works within about a minute. The row's badge changes from "Off" to "Enabled".

Step 4: add your landing page to Redirect URLs

On the Auth card, add every URL you'll pass as redirectTo, one per line, and click Save Auth settings:

http://localhost:3000/**
https://chat.example.com/**

See Site URL and redirect URLs for wildcard rules.

Step 5: sign in from your app

In a browser app:

const { error } = await supabase.auth.signInWithOAuth({
  provider: 'github',
  options: { redirectTo: `${window.location.origin}/auth/callback` },
})

The provider value is the provider's key: google, github, apple, azure, gitlab, bitbucket, discord, facebook, figma, kakao, keycloak, linkedin_oidc, notion, slack_oidc, spotify, twitch, twitter, workos or zoom.

In a server-rendered app, start the flow on the server and redirect the browser to the URL you get back:

const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: { redirectTo: 'https://chat.example.com/auth/callback' },
})
if (data.url) redirect(data.url) // your framework's redirect

Then exchange the code in /auth/callback with supabase.auth.exchangeCodeForSession(code). Server-side auth and Auth in Next.js have complete routes.

Scopes and provider data

Each provider returns basic profile data (name, email, avatar) by default. That data lands in the user's user_metadata and in auth.identities.identity_data.

Ask for more with scopes, a space-separated string in the provider's own format:

await supabase.auth.signInWithOAuth({
  provider: 'github',
  options: { scopes: 'read:org', redirectTo },
})

Extra query parameters for the provider go in queryParams. For example, Google needs these to hand out a refresh token:

options: { queryParams: { access_type: 'offline', prompt: 'consent' } }

Calling the provider's API

Right after sign-in, the session includes provider_token (and sometimes provider_refresh_token), which you can use to call the provider's API on the user's behalf. Auth doesn't store or refresh these tokens for you, and they aren't present in sessions restored later. If you need them, save them yourself at sign-in time, in a table with tight RLS or, better, server-side only.

supabase.auth.onAuthStateChange((event, session) => {
  if (event === 'SIGNED_IN' && session?.provider_token) {
    saveGithubToken(session.provider_token) // your server endpoint
  }
})

One person, several providers

If someone signs in with Google and later with GitHub using the same verified email address, Auth links both identities to one user automatically. To let signed-in users connect more providers themselves, turn on "Manual identity linking". See Linking identities.

When it fails

Updated 11 November 2026 · View as Markdown · Something wrong or missing? Tell us