OAuth Providers
OAuth Flow trong Supabase
Redirect Flow
┌─────────────────────────────────────────────────────────────┐
│ OAUTH FLOW │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. User clicks "Sign in with Google" │
│ │ │
│ ▼ │
│ 2. signInWithOAuth({ provider: 'google' }) │
│ │ │
│ ▼ │
│ 3. Redirect to Google consent screen │
│ │ │
│ ▼ │
│ 4. User authorizes, Google redirects back │
│ │ │
│ ▼ │
│ 5. Supabase exchanges code for tokens │
│ │ │
│ ▼ │
│ 6. User created/logged in, redirect to app │
│ │
└─────────────────────────────────────────────────────────────┘
Supported Providers
Available OAuth Providers
┌─────────────────────────────────────────────────────────────┐
│ OAUTH PROVIDERS │
├─────────────────────────────────────────────────────────────┤
│ │
│ Popular: │
│ ├── Google (google) │
│ ├── GitHub (github) │
│ ├── Facebook (facebook) │
│ ├── Twitter/X (twitter) │
│ └── Apple (apple) │
│ │
│ Enterprise: │
│ ├── Azure AD (azure) │
│ ├── Okta (okta) │
│ ├── Keycloak (keycloak) │
│ └── SAML 2.0 (custom) │
│ │
│ Other: │
│ ├── Discord (discord) │
│ ├── Slack (slack) │
│ ├── Spotify (spotify) │
│ ├── Twitch (twitch) │
│ └── + many more... │
│ │
└─────────────────────────────────────────────────────────────┘
Setup Google OAuth
1. Google Cloud Console
1. Go to console.cloud.google.com
2. Create new project (or select existing)
3. APIs & Services → OAuth consent screen
- App name
- User support email
- Scopes: email, profile, openid
4. APIs & Services → Credentials → Create OAuth client ID
- Application type: Web application
- Authorized redirect URIs:
https://<your-project>.supabase.co/auth/v1/callback
2. Supabase Dashboard
Dashboard → Authentication → Providers → Google
- Enable: ON
- Client ID: (from Google)
- Client Secret: (from Google)
Setup GitHub OAuth
1. GitHub Developer Settings
1. GitHub → Settings → Developer settings
2. OAuth Apps → New OAuth App
- Application name: Your App
- Homepage URL: https://yourapp.com
- Authorization callback URL:
https://<your-project>.supabase.co/auth/v1/callback
3. Copy Client ID and generate Client Secret
2. Supabase Dashboard
Dashboard → Authentication → Providers → GitHub
- Enable: ON
- Client ID: (from GitHub)
- Client Secret: (from GitHub)
Implementation
Basic OAuth Sign In
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
});
// User is redirected to Google
// After authorization, redirected back to your app
With Options
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
// Redirect sau khi login thành công
redirectTo: 'https://myapp.com/dashboard',
// Request additional scopes
scopes: 'https://www.googleapis.com/auth/calendar.readonly',
// Additional query params
queryParams: {
access_type: 'offline', // Get refresh token
prompt: 'consent', // Always show consent
},
},
});
OAuth in Next.js
'use client';
import { createClient } from '@/lib/supabase/client';
export function OAuthButtons() {
const supabase = createClient();
const handleOAuthLogin = async (provider: 'google' | 'github') => {
const { error } = await supabase.auth.signInWithOAuth({
provider,
options: {
redirectTo: `${location.origin}/auth/callback`,
},
});
if (error) {
console.error('OAuth error:', error.message);
}
};
return (
<div className="oauth-buttons">
<button onClick={() => handleOAuthLogin('google')}>
<GoogleIcon />
Sign in with Google
</button>
<button onClick={() => handleOAuthLogin('github')}>
<GitHubIcon />
Sign in with GitHub
</button>
</div>
);
}
Callback Handler
// app/auth/callback/route.ts
import { createClient } from '@/lib/supabase/server';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url);
const code = searchParams.get('code');
const next = searchParams.get('next') ?? '/dashboard';
if (code) {
const supabase = await createClient();
// Exchange code for session
const { error } = await supabase.auth.exchangeCodeForSession(code);
if (!error) {
return NextResponse.redirect(`${origin}${next}`);
}
}
// OAuth error
return NextResponse.redirect(`${origin}/auth/error`);
}
Access Provider Data
const { data: { user } } = await supabase.auth.getUser();
if (user) {
// Identity info
const googleIdentity = user.identities?.find(
(i) => i.provider === 'google'
);
// User metadata from provider
console.log('Email:', user.email);
console.log('Name:', user.user_metadata.full_name);
console.log('Avatar:', user.user_metadata.avatar_url);
// Provider-specific data
console.log('Google ID:', googleIdentity?.id);
}
Store in Profile Table
-- Trigger to sync OAuth data to profiles
CREATE OR REPLACE FUNCTION sync_user_profile()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.profiles (id, email, full_name, avatar_url)
VALUES (
NEW.id,
NEW.email,
NEW.raw_user_meta_data->>'full_name',
NEW.raw_user_meta_data->>'avatar_url'
)
ON CONFLICT (id) DO UPDATE SET
full_name = EXCLUDED.full_name,
avatar_url = EXCLUDED.avatar_url,
updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
Linking Multiple Providers
Link thêm Provider
// User đã đăng nhập bằng email
// Muốn link thêm Google
const { data, error } = await supabase.auth.linkIdentity({
provider: 'google',
});
// User redirected to Google
// After auth, Google linked to existing account
Unlink Provider
// Get user's identities
const { data: { user } } = await supabase.auth.getUser();
const googleIdentity = user?.identities?.find(i => i.provider === 'google');
if (googleIdentity) {
const { error } = await supabase.auth.unlinkIdentity(googleIdentity);
}
Check Linked Providers
const { data: { user } } = await supabase.auth.getUser();
const providers = user?.identities?.map(i => i.provider) ?? [];
console.log('Linked providers:', providers);
// ['email', 'google', 'github']
Provider-specific Scopes
Google Scopes
// Request Google Calendar access
await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
scopes: [
'https://www.googleapis.com/auth/calendar.readonly',
'https://www.googleapis.com/auth/drive.readonly',
].join(' '),
},
});
GitHub Scopes
// Request repo access
await supabase.auth.signInWithOAuth({
provider: 'github',
options: {
scopes: 'repo read:user user:email',
},
});
Access Provider Token
// After OAuth, get provider token for API calls
const { data: { session } } = await supabase.auth.getSession();
const providerToken = session?.provider_token;
const providerRefreshToken = session?.provider_refresh_token;
// Use token to call provider APIs
const response = await fetch('https://api.github.com/user', {
headers: {
Authorization: `Bearer ${providerToken}`,
},
});
Error Handling
Common OAuth Errors
const { error } = await supabase.auth.signInWithOAuth({
provider: 'google',
});
if (error) {
switch (error.message) {
case 'Provider not enabled':
// Provider chưa được setup trong dashboard
break;
case 'Email already registered':
// Email đã dùng với provider khác
break;
case 'access_denied':
// User từ chối authorization
break;
default:
console.error('OAuth error:', error.message);
}
}
Best Practices
Security
✅ Always use HTTPS
✅ Validate redirect URLs
✅ Store tokens securely (Supabase handles this)
✅ Request minimum necessary scopes
✅ Handle token refresh properly
UX
✅ Show which providers are linked
✅ Allow unlinking (but keep at least one)
✅ Clear error messages
✅ Loading states during redirect
Tổng kết
OAuth Setup Checklist
- Create OAuth app with provider
- Add callback URL:
https://<project>.supabase.co/auth/v1/callback
- Copy Client ID & Secret to Supabase
- Enable provider in Dashboard
- Implement
signInWithOAuth in frontend
- Handle callback route
Common Providers
| Provider |
Scopes |
Common Use |
| Google |
email, profile |
Most apps |
| GitHub |
user, email |
Dev tools |
| Facebook |
email, public_profile |
Social apps |
| Apple |
email, name |
iOS apps |
Q&A
- Cần support những providers nào?
- Có cần link multiple providers?
- Provider-specific data cần access không?