Supabase OAuth社交登录 #
一、OAuth概述 #
1.1 OAuth流程 #
text
OAuth登录流程
├── 1. 用户点击第三方登录按钮
├── 2. 跳转到OAuth提供商授权页面
├── 3. 用户授权应用访问
├── 4. OAuth提供商返回授权码
├── 5. Supabase交换访问令牌
├── 6. 创建/登录用户
└── 7. 返回会话信息
1.2 支持的OAuth提供商 #
| 提供商 | Provider ID | 说明 |
|---|---|---|
| GitHub | github | 开发者常用 |
| 通用社交登录 | ||
| Apple | apple | iOS应用必需 |
| 社交应用 | ||
| 社交应用 | ||
| Discord | discord | 游戏社区 |
| Spotify | spotify | 音乐应用 |
| Slack | slack | 企业应用 |
| 职业社交 | ||
| Twitch | twitch | 直播平台 |
二、配置OAuth #
2.1 Dashboard配置 #
text
Dashboard > Authentication > Providers
配置步骤
├── 1. 选择要启用的提供商
├── 2. 获取OAuth应用凭证
├── 3. 填写Client ID和Client Secret
├── 4. 配置回调URL
└── 5. 保存配置
2.2 GitHub OAuth配置 #
text
1. 访问 GitHub Settings > Developer settings > OAuth Apps
2. 创建新的OAuth App
├── Application name: Your App Name
├── Homepage URL: https://your-app.com
└── Authorization callback URL:
https://<project-ref>.supabase.co/auth/v1/callback
3. 获取Client ID
4. 生成Client Secret
5. 在Supabase Dashboard中配置
2.3 Google OAuth配置 #
text
1. 访问 Google Cloud Console
2. 创建OAuth 2.0客户端ID
├── 应用类型: Web应用
├── 已授权的JavaScript来源: https://your-app.com
└── 已授权的重定向URI:
https://<project-ref>.supabase.co/auth/v1/callback
3. 获取客户端ID和密钥
4. 在Supabase Dashboard中配置
三、实现OAuth登录 #
3.1 基础OAuth登录 #
typescript
// GitHub登录
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'github',
})
// 用户将被重定向到GitHub授权页面
3.2 带配置的OAuth登录 #
typescript
// 带回调URL的登录
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'github',
options: {
redirectTo: 'https://example.com/auth/callback',
scopes: 'repo gist', // 请求的权限范围
},
})
3.3 获取用户信息 #
typescript
// OAuth登录后获取用户信息
const { data: { user } } = await supabase.auth.getUser()
// 用户元数据包含OAuth信息
console.log(user?.user_metadata)
// {
// avatar_url: 'https://avatars.githubusercontent.com/u/...',
// full_name: 'John Doe',
// user_name: 'johndoe',
// provider_id: '12345678',
// ...
// }
四、处理OAuth回调 #
4.1 回调页面 #
typescript
// auth/callback.tsx
import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { supabase } from '../lib/supabase'
export function AuthCallback() {
const navigate = useNavigate()
useEffect(() => {
// Supabase自动处理OAuth回调
supabase.auth.getSession().then(({ data: { session } }) => {
if (session) {
navigate('/dashboard')
} else {
navigate('/login')
}
})
}, [navigate])
return <div>Processing login...</div>
}
4.2 Next.js App Router处理 #
typescript
// app/auth/callback/route.ts
import { createClient } from '@/lib/supabase/server'
import { NextResponse } from 'next/server'
export async function GET(request: Request) {
const requestUrl = new URL(request.url)
const code = requestUrl.searchParams.get('code')
const next = requestUrl.searchParams.get('next') || '/'
if (code) {
const supabase = await createClient()
await supabase.auth.exchangeCodeForSession(code)
}
return NextResponse.redirect(requestUrl.origin + next)
}
五、OAuth登录组件 #
5.1 通用OAuth按钮组件 #
tsx
import { supabase } from '../lib/supabase'
interface OAuthButtonProps {
provider: 'github' | 'google' | 'apple' | 'facebook' | 'twitter'
redirectTo?: string
children: React.ReactNode
}
export function OAuthButton({
provider,
redirectTo,
children
}: OAuthButtonProps) {
const handleLogin = async () => {
const { error } = await supabase.auth.signInWithOAuth({
provider,
options: {
redirectTo: redirectTo || window.location.origin + '/auth/callback',
},
})
if (error) {
console.error('OAuth error:', error)
}
}
return (
<button onClick={handleLogin} className="oauth-button">
{children}
</button>
)
}
5.2 使用示例 #
tsx
export function LoginPage() {
return (
<div className="login-page">
<h1>Sign in with</h1>
<OAuthButton provider="github">
<GitHubIcon /> Continue with GitHub
</OAuthButton>
<OAuthButton provider="google">
<GoogleIcon /> Continue with Google
</OAuthButton>
<OAuthButton provider="apple">
<AppleIcon /> Continue with Apple
</OAuthButton>
</div>
)
}
六、链接OAuth账号 #
6.1 链接新提供商 #
typescript
// 将OAuth账号链接到当前用户
const { data, error } = await supabase.auth.linkIdentity({
provider: 'github',
})
// 用户可以同时使用多种方式登录
6.2 解除链接 #
typescript
// 获取用户的身份
const { data: { identities } } = await supabase.auth.getUserIdentities()
// 解除链接
const { error } = await supabase.auth.unlinkIdentity(identities[0])
6.3 检查已链接账号 #
typescript
// 获取用户所有身份
const { data: { identities }, error } = await supabase.auth.getUserIdentities()
identities?.forEach(identity => {
console.log('Provider:', identity.provider)
console.log('Email:', identity.identity_data?.email)
})
七、自定义Scopes #
7.1 GitHub Scopes #
typescript
// 请求GitHub特定权限
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'github',
options: {
scopes: 'repo user email',
},
})
7.2 Google Scopes #
typescript
// 请求Google特定权限
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
scopes: 'https://www.googleapis.com/auth/calendar',
},
})
八、PKCE流程 #
8.1 启用PKCE #
typescript
// 使用PKCE增强安全性
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'github',
options: {
skipBrowserRedirect: true, // 手动处理重定向
},
})
if (data?.url) {
// 在新窗口打开
window.open(data.url, '_blank')
}
8.2 处理PKCE回调 #
typescript
// PKCE回调处理
useEffect(() => {
const hash = window.location.hash
if (hash) {
supabase.auth.getSession().then(({ data: { session } }) => {
if (session) {
// 登录成功
}
})
}
}, [])
九、移动端OAuth #
9.1 React Native #
typescript
import * as WebBrowser from 'expo-web-browser'
import * as Linking from 'expo-linking'
import { makeRedirectUri } from 'expo-auth-session'
// 完成OAuth会话
WebBrowser.maybeCompleteAuthSession()
// 生成重定向URI
const redirectUrl = makeRedirectUri({
scheme: 'myapp',
path: 'auth/callback',
})
// 发起OAuth登录
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'github',
options: {
redirectTo: redirectUrl,
skipBrowserRedirect: true,
},
})
if (data?.url) {
const result = await WebBrowser.openAuthSessionAsync(
data.url,
redirectUrl
)
if (result.type === 'success') {
// 处理回调
}
}
9.2 iOS配置 #
xml
<!-- Info.plist -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
9.3 Android配置 #
xml
<!-- AndroidManifest.xml -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
十、服务端验证 #
10.1 验证OAuth用户 #
typescript
// 服务端获取用户信息
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(url, serviceRoleKey, {
auth: {
autoRefreshToken: false,
persistSession: false,
},
})
// 通过access token获取用户
const { data: { user }, error } = await supabase.auth.getUser(accessToken)
if (user) {
// 验证OAuth提供商
const identities = user.identities || []
const hasGitHub = identities.some(i => i.provider === 'github')
}
十一、错误处理 #
11.1 常见错误 #
typescript
// 错误处理
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'github',
})
if (error) {
switch (error.message) {
case 'provider is not enabled':
console.error('该OAuth提供商未启用')
break
case 'invalid_callback_url':
console.error('回调URL配置错误')
break
case 'access_denied':
console.error('用户拒绝授权')
break
default:
console.error('OAuth错误:', error.message)
}
}
十二、最佳实践 #
12.1 安全建议 #
text
OAuth安全建议
├── 使用HTTPS
├── 配置正确的回调URL
├── 使用PKCE流程
├── 验证state参数
├── 最小权限原则
└── 定期审查授权
12.2 用户体验优化 #
typescript
// 保存登录前的页面
const handleOAuthLogin = async (provider: string) => {
// 保存当前路径
localStorage.setItem('redirectAfterLogin', window.location.pathname)
await supabase.auth.signInWithOAuth({
provider,
options: {
redirectTo: window.location.origin + '/auth/callback',
},
})
}
// 登录后跳转
const afterLogin = () => {
const redirect = localStorage.getItem('redirectAfterLogin') || '/dashboard'
localStorage.removeItem('redirectAfterLogin')
navigate(redirect)
}
十三、总结 #
OAuth登录要点:
| 操作 | 方法 |
|---|---|
| 登录 | signInWithOAuth({ provider }) |
| 链接 | linkIdentity({ provider }) |
| 解除 | unlinkIdentity(identity) |
| 获取身份 | getUserIdentities() |
下一步,让我们学习魔法链接登录!
最后更新:2026-03-28