Supabase邮箱密码认证 #
一、用户注册 #
1.1 基础注册 #
typescript
// 用户注册
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'secure-password',
})
// 注册结果
// data.user: 用户信息
// data.session: 会话信息(如果自动登录)
// error: 错误信息
1.2 带元数据注册 #
typescript
// 注册时添加用户信息
const { data, error } = await supabase.auth.signUp({
email: 'user@example.com',
password: 'secure-password',
options: {
data: {
full_name: 'John Doe',
avatar_url: 'https://example.com/avatar.jpg',
},
emailRedirectTo: 'https://example.com/welcome',
},
})
1.3 注册流程 #
text
注册流程
├── 1. 用户提交邮箱和密码
├── 2. Supabase验证格式
├── 3. 检查邮箱是否已注册
├── 4. 创建用户记录
├── 5. 发送验证邮件(可选)
├── 6. 返回用户信息
└── 7. 自动登录(可选)
1.4 注册表单示例 #
tsx
import { useState } from 'react'
import { supabase } from '../lib/supabase'
export function SignUpForm() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const [message, setMessage] = useState('')
const handleSignUp = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
setMessage('')
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
full_name: email.split('@')[0],
},
},
})
setLoading(false)
if (error) {
setMessage(error.message)
} else if (data.user && !data.session) {
setMessage('请查收验证邮件完成注册')
} else {
setMessage('注册成功!')
}
}
return (
<form onSubmit={handleSignUp}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
minLength={6}
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Loading...' : 'Sign Up'}
</button>
{message && <p>{message}</p>}
</form>
)
}
二、用户登录 #
2.1 基础登录 #
typescript
// 用户登录
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'secure-password',
})
// 登录结果
// data.user: 用户信息
// data.session: 会话信息
2.2 登录表单示例 #
tsx
import { useState } from 'react'
import { supabase } from '../lib/supabase'
export function SignInForm() {
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const handleSignIn = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
setError('')
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
})
setLoading(false)
if (error) {
setError(error.message)
} else {
// 登录成功,跳转
window.location.href = '/dashboard'
}
}
return (
<form onSubmit={handleSignIn}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Loading...' : 'Sign In'}
</button>
{error && <p className="error">{error}</p>}
</form>
)
}
三、邮箱验证 #
3.1 配置邮箱验证 #
text
Dashboard > Authentication > Providers > Email
配置选项
├── Enable email confirmations
│ └── 开启后注册需要验证邮箱
│
├── Secure email change
│ └── 更改邮箱时需要验证
│
└── Secure password change
└── 更改密码时需要验证
3.2 验证邮件回调 #
typescript
// 处理验证邮件回调
// URL: https://example.com/auth/callback?token=xxx&type=signup
import { useEffect } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { supabase } from '../lib/supabase'
export function AuthCallback() {
const [searchParams] = useSearchParams()
const navigate = useNavigate()
useEffect(() => {
const token = searchParams.get('token')
const type = searchParams.get('type')
if (token && type) {
supabase.auth.verifyOtp({
token_hash: token,
type: type as any,
}).then(({ error }) => {
if (error) {
console.error('Verification failed:', error)
navigate('/auth/error')
} else {
navigate('/dashboard')
}
})
}
}, [searchParams, navigate])
return <div>Verifying...</div>
}
3.3 重新发送验证邮件 #
typescript
// 重新发送验证邮件
const { error } = await supabase.auth.resend({
type: 'signup',
email: 'user@example.com',
options: {
emailRedirectTo: 'https://example.com/auth/callback',
},
})
四、密码重置 #
4.1 发送重置邮件 #
typescript
// 发送密码重置邮件
const { data, error } = await supabase.auth.resetPasswordForEmail(
'user@example.com',
{
redirectTo: 'https://example.com/reset-password',
}
)
4.2 重置密码页面 #
tsx
import { useState } from 'react'
import { supabase } from '../lib/supabase'
export function ResetPasswordForm() {
const [password, setPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
const [loading, setLoading] = useState(false)
const [message, setMessage] = useState('')
const handleResetPassword = async (e: React.FormEvent) => {
e.preventDefault()
if (password !== confirmPassword) {
setMessage('Passwords do not match')
return
}
setLoading(true)
const { error } = await supabase.auth.updateUser({
password: password,
})
setLoading(false)
if (error) {
setMessage(error.message)
} else {
setMessage('Password updated successfully!')
}
}
return (
<form onSubmit={handleResetPassword}>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="New Password"
minLength={6}
required
/>
<input
type="password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
placeholder="Confirm Password"
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Updating...' : 'Reset Password'}
</button>
{message && <p>{message}</p>}
</form>
)
}
4.3 处理重置回调 #
typescript
// 处理密码重置回调
// URL: https://example.com/reset-password?token=xxx&type=recovery
useEffect(() => {
const hash = window.location.hash
if (hash && hash.includes('type=recovery')) {
// 用户点击了重置链接,显示重置表单
setShowResetForm(true)
}
}, [])
五、用户登出 #
5.1 登出当前设备 #
typescript
// 登出当前会话
const { error } = await supabase.auth.signOut()
5.2 登出所有设备 #
typescript
// 登出所有设备
const { error } = await supabase.auth.signOut({ scope: 'global' })
5.3 登出示例 #
tsx
import { supabase } from '../lib/supabase'
export function SignOutButton() {
const handleSignOut = async () => {
const { error } = await supabase.auth.signOut()
if (error) {
console.error('Sign out error:', error)
} else {
window.location.href = '/login'
}
}
return (
<button onClick={handleSignOut}>
Sign Out
</button>
)
}
六、获取用户信息 #
6.1 获取当前用户 #
typescript
// 获取当前用户
const { data: { user }, error } = await supabase.auth.getUser()
if (user) {
console.log('User ID:', user.id)
console.log('Email:', user.email)
console.log('Metadata:', user.user_metadata)
}
6.2 获取会话 #
typescript
// 获取当前会话
const { data: { session }, error } = await supabase.auth.getSession()
if (session) {
console.log('Access Token:', session.access_token)
console.log('Refresh Token:', session.refresh_token)
console.log('Expires At:', session.expires_at)
}
6.3 监听认证状态 #
typescript
// 监听认证状态变化
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
if (event === 'SIGNED_IN') {
console.log('User signed in:', session?.user)
}
if (event === 'SIGNED_OUT') {
console.log('User signed out')
}
}
)
// 清理监听
subscription.unsubscribe()
七、更新用户信息 #
7.1 更新用户元数据 #
typescript
// 更新用户元数据
const { data, error } = await supabase.auth.updateUser({
data: {
full_name: 'John Doe',
avatar_url: 'https://example.com/new-avatar.jpg',
},
})
7.2 更新邮箱 #
typescript
// 更新邮箱
const { data, error } = await supabase.auth.updateUser({
email: 'new-email@example.com',
})
7.3 更新密码 #
typescript
// 更新密码(需要当前会话)
const { data, error } = await supabase.auth.updateUser({
password: 'new-secure-password',
})
八、服务端操作 #
8.1 管理员创建用户 #
typescript
import { createClient } from '@supabase/supabase-js'
const supabaseAdmin = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
{
auth: {
autoRefreshToken: false,
persistSession: false,
},
}
)
// 管理员创建用户
const { data, error } = await supabaseAdmin.auth.admin.createUser({
email: 'admin@example.com',
password: 'secure-password',
email_confirm: true, // 自动确认邮箱
user_metadata: {
role: 'admin',
},
})
8.2 管理员列出用户 #
typescript
// 列出所有用户
const { data, error } = await supabaseAdmin.auth.admin.listUsers()
// 分页列出
const { data, error } = await supabaseAdmin.auth.admin.listUsers({
page: 1,
perPage: 50,
})
8.3 管理员删除用户 #
typescript
// 删除用户
const { error } = await supabaseAdmin.auth.admin.deleteUser(userId)
九、错误处理 #
9.1 常见错误码 #
typescript
const errorMessages: Record<string, string> = {
'invalid_credentials': '邮箱或密码错误',
'email_not_confirmed': '请先验证邮箱',
'user_already_exists': '该邮箱已注册',
'weak_password': '密码强度不足',
'invalid_email': '邮箱格式不正确',
'user_not_found': '用户不存在',
'session_not_found': '会话已过期,请重新登录',
}
function getErrorMessage(error: any): string {
return errorMessages[error.message] || error.message
}
9.2 错误处理示例 #
typescript
async function handleAuth() {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
})
if (error) {
switch (error.message) {
case 'invalid_credentials':
return '邮箱或密码错误'
case 'email_not_confirmed':
return '请先验证邮箱'
default:
return error.message
}
}
return data
}
十、安全建议 #
10.1 密码策略 #
typescript
// 密码验证函数
function validatePassword(password: string): { valid: boolean; message: string } {
if (password.length < 8) {
return { valid: false, message: '密码至少8个字符' }
}
if (!/[A-Z]/.test(password)) {
return { valid: false, message: '密码需要包含大写字母' }
}
if (!/[a-z]/.test(password)) {
return { valid: false, message: '密码需要包含小写字母' }
}
if (!/[0-9]/.test(password)) {
return { valid: false, message: '密码需要包含数字' }
}
return { valid: true, message: '' }
}
10.2 防暴力破解 #
text
Supabase内置保护
├── 登录失败次数限制
├── IP限制
├── 验证码保护
└── 速率限制
十一、总结 #
邮箱密码认证要点:
| 操作 | 方法 |
|---|---|
| 注册 | signUp({ email, password }) |
| 登录 | signInWithPassword({ email, password }) |
| 登出 | signOut() |
| 获取用户 | getUser() |
| 更新用户 | updateUser({ … }) |
| 重置密码 | resetPasswordForEmail(email) |
下一步,让我们学习OAuth社交登录!
最后更新:2026-03-28