Back
Supabase

Social Authentication with Supabase: Google, GitHub and More

Implement social authentication in Supabase with Google, GitHub, and Discord. Step-by-step setup, session handling, and route protection.

Francisco ZapataWritten by Francisco Zapata
January 12, 20269 min read
Social Authentication with Supabase: Google, GitHub and More

Social authentication allows users to sign in with their existing Google, GitHub, Discord, and other provider accounts. Supabase makes this integration easy with its GoTrue-based Auth module. In this guide, we will implement complete OAuth authentication step by step.

How Does OAuth Work in Supabase?

The OAuth flow in Supabase follows the standard OAuth 2.0:

1. User clicks "Sign in with Google"

2. Supabase redirects to the OAuth provider (Google)

3. User authorizes the application

4. Google redirects back to Supabase with a code

5. Supabase exchanges the code for tokens

6. Supabase creates or updates the user in auth.users

7. User is redirected to your application with an active session

Provider Configuration

Google OAuth

1. Create Credentials in Google Cloud Console

1. Go to Google Cloud Console

2. Create a new project or select an existing one

3. Navigate to APIs & Services > Credentials

4. Click Create Credentials > OAuth 2.0 Client ID

5. Select Web Application

6. Add the Authorized redirect URIs:

   https://<your-project>.supabase.co/auth/v1/callback
   

7. Copy the Client ID and Client Secret

2. Configure in Supabase Dashboard

1. Go to Authentication > Providers in your Supabase dashboard

2. Enable Google

3. Paste the Client ID and Client Secret

4. Save changes

3. Implement in Your Application

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.SUPABASE_URL,
  process.env.SUPABASE_ANON_KEY
)

async function signInWithGoogle() {
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: 'google',
    options: {
      redirectTo: 'http://localhost:3000/auth/callback',
      queryParams: {
        access_type: 'offline',
        prompt: 'consent'
      }
    }
  })

  if (error) {
    console.error('Authentication error:', error.message)
  }
}

GitHub OAuth

async function signInWithGitHub() {
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: 'github',
    options: {
      redirectTo: 'http://localhost:3000/auth/callback',
      scopes: 'read:user user:email'
    }
  })
}

Discord OAuth

async function signInWithDiscord() {
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider: 'discord',
    options: {
      redirectTo: 'http://localhost:3000/auth/callback',
      scopes: 'identify email'
    }
  })
}

Session Handling

Callback Handler

After OAuth authentication, the user is redirected to your callback URL:

async function handleCallback() {
  const { data: { session }, error } = await supabase.auth.getSession()

  if (error) {
    console.error('Callback error:', error.message)
    return
  }

  if (session) {
    window.location.href = '/dashboard'
  }
}

Listen for Session Changes

supabase.auth.onAuthStateChange((event, session) => {
  switch (event) {
    case 'SIGNED_IN':
      console.log('User signed in', session.user)
      break
    case 'SIGNED_OUT':
      console.log('User signed out')
      break
    case 'TOKEN_REFRESHED':
      console.log('Token refreshed')
      break
    case 'USER_UPDATED':
      console.log('User data updated')
      break
  }
})

Get User Data

const { data: { user } } = await supabase.auth.getUser()

console.log(user.id)
console.log(user.email)
console.log(user.app_metadata.provider)
console.log(user.user_metadata)

Route Protection

In Vue/Nuxt

// middleware/auth.ts (Nuxt)
export default defineNuxtRouteMiddleware(async (to) => {
  const supabase = useSupabaseClient()
  const { data: { session } } = await supabase.auth.getSession()

  if (!session && to.path !== '/login') {
    return navigateTo('/login')
  }
})

In React/Next.js

import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'

export async function middleware(req) {
  const res = NextResponse.next()
  const supabase = createMiddlewareClient({ req, res })
  const { data: { session } } = await supabase.auth.getSession()

  if (!session && req.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', req.url))
  }

  return res
}

User Metadata and Profiles

OAuth providers supply useful metadata. You can use it to create a profile automatically:

CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS trigger AS $$
BEGIN
  INSERT INTO public.profiles (id, full_name, avatar_url, email)
  VALUES (
    NEW.id,
    NEW.raw_user_meta_data ->> 'full_name',
    NEW.raw_user_meta_data ->> 'avatar_url',
    NEW.email
  );
  RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER on_auth_user_created
  AFTER INSERT ON auth.users
  FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();

Account Linking

A user can link multiple providers to the same account:

const { data, error } = await supabase.auth.linkIdentity({
  provider: 'github'
})

Sign Out

async function signOut() {
  const { error } = await supabase.auth.signOut()
  if (!error) {
    window.location.href = '/'
  }
}

// Sign out from all devices
async function signOutAll() {
  const { error } = await supabase.auth.signOut({ scope: 'global' })
}

Security Best Practices

1. Always validate sessions on the server, not just the client

2. Use HTTPS for all redirect URIs

3. Configure allowed domains in Supabase Dashboard > URL Configuration

4. Never expose the Service Role Key on the client

5. Implement RLS to protect data at the database level

6. Handle errors by showing clear messages to users

Conclusion

Social authentication with Supabase is straightforward and powerful. With a few steps you can offer your users login with Google, GitHub, Discord, and more. The integration with RLS and PostgreSQL triggers lets you create complete registration flows with automatic profiles.

Comments (0)

Leave a comment

Be the first to comment