糖哥l
使用AI开发WEB网站,必须使用一套AI友好的技术架构,使用NextJS及其相关生态技术栈成为很多人的选择,我近期正在使用开发一个网站
🏗️ 整体架构原理
认证流程图
用户点击登录 → GitHub OAuth → NextAuth.js 处理 → SupabaseAdapter → 数据存储到 Supabase
↓ ↓ ↓ ↓ ↓
前端页面 GitHub授权页 NextAuth回调处理 数据库适配器 PostgreSQL数据库
核心组件
- NextAuth.js: 认证框架,处理 OAuth 流程
- GitHub OAuth App: 提供第三方登录服务
- SupabaseAdapter: 将认证数据存储到 Supabase
- Supabase PostgreSQL: 云数据库存储用户信息
🔧 关键配置步骤
1. Supabase 数据库准备
首先在 Supabase 中创建相关表:
-- 创建
CREATE SCHEMA IF NOT EXISTS next_auth;
-- 创建用户表
CREATE TABLE IF NOT EXISTS next_auth.users (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
name text,
email text UNIQUE,
"emailVerified" timestamptz,
image text
);
-- 创建账户关联表
CREATE TABLE IF NOT EXISTS next_auth.accounts (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
"userId" uuid NOT NULL REFERENCES next_auth.users(id) ON DELETE CASCADE,
type text NOT NULL,
provider text NOT NULL,
"providerAccountId" text NOT NULL,
refresh_token text,
access_token text,
expires_at bigint,
token_type text,
scope text,
id_token text,
session_state text,
UNIQUE(provider, "providerAccountId")
);
-- 创建会话表
CREATE TABLE IF NOT EXISTS next_auth.sessions (
id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
"sessionToken" text NOT NULL UNIQUE,
"userId" uuid NOT NULL REFERENCES next_auth.users(id) ON DELETE CASCADE,
expires timestamptz NOT NULL
);
-- 创建验证令牌表
CREATE TABLE IF NOT EXISTS next_auth.verification_tokens (
identifier text,
token text UNIQUE,
expires timestamptz NOT NULL,
PRIMARY KEY (identifier, token)
);
2. 环境变量配置
# NextAuth.js 配置
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key
# Supabase 配置
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
# GitHub OAuth 配置
AUTH_GITHUB_ID=your-github-client-id
AUTH_GITHUB_SECRET=your-github-client-secret
NEXT_PUBLIC_AUTH_GITHUB_ENABLED=true
💻 核心代码实现
1. NextAuth.js 配置 (auth/config.ts
)
import GitHubProvider from "next-auth/providers/github";
import { AuthOptions } from "next-auth";
import { SupabaseAdapter } from "@auth/supabase-adapter";
export const authOptions: AuthOptions = {
// 🔑 关键:使用 SupabaseAdapter 连接数据库
adapter: SupabaseAdapter({
url: process.env.NEXT_PUBLIC_SUPABASE_URL!,
secret: process.env.SUPABASE_SERVICE_ROLE_KEY!,
}),
// 🔐 配置 GitHub OAuth 提供商
providers: [
GitHubProvider({
clientId: process.env.AUTH_GITHUB_ID!,
clientSecret: process.env.AUTH_GITHUB_SECRET!,
})
],
// 🛡️ 安全密钥
secret: process.env.NEXTAUTH_SECRET,
// 📄 自定义页面路径
pages: {
signIn: "/auth/signin",
error: "/auth/error",
},
// 🔄 回调函数配置
callbacks: {
async signIn({ user, account, profile }) {
// 登录验证逻辑
return true;
},
async redirect({ url, baseUrl }) {
// 登录后重定向逻辑
if (url.startsWith("/")) return `${baseUrl}${url}`;
return baseUrl;
},
async session({ session, token }) {
// 会话数据处理
if (session?.user && token) {
session.user.id = token.uid as string;
}
return session;
},
async jwt({ token, user }) {
// JWT 令牌处理
if (user) {
token.uid = user.id;
}
return token;
},
},
// 📊 会话策略
session: {
strategy: "jwt",
},
// 🐛 开发环境调试
debug: process.env.NODE_ENV === 'development',
};
2. NextAuth.js 初始化 (auth/index.ts
)
import NextAuth from "next-auth";
import { authOptions } from "./config";
const nextAuth = NextAuth(authOptions);
export default nextAuth;
export const { auth, signIn, signOut } = nextAuth;
3. API 路由配置 (app/api/auth/[...nextauth]/route.ts
)
import NextAuth from "next-auth";
import { authOptions } from "@/auth/config";
const handler = NextAuth(authOptions);
// 🚀 导出 GET 和 POST 处理器
export { handler as GET, handler as POST };
🔄 完整数据流程解析
第一步:用户点击登录
// 前端触发登录
import { signIn } from "next-auth/react";
<button onClick={() => signIn('github')}>
Continue with GitHub
</button>
第二步:GitHub OAuth 授权
- 用户被重定向到 GitHub 授权页面
- GitHub 返回授权码到回调 URL
- NextAuth.js 接收授权码
第三步:NextAuth.js 处理认证
从日志可以看到详细过程:
# 1. 创建授权 URL
[next-auth][debug][GET_AUTHORIZATION_URL] {
url: 'https://github.com/login/oauth/authorize?client_id=...'
}
# 2. 获取用户信息
[next-auth][debug][PROFILE_DATA] {
OAuthProfile: {
login: 'bytevirts',
email: 'bytevirt@gmail.com',
// ... 其他用户信息
}
}
# 3. 检查用户是否存在
[next-auth][debug][adapter_getUserByEmail] { args: [ 'XXX@gmail.com' ] }
# 4. 创建新用户(如果不存在)
[next-auth][debug][adapter_createUser] {
args: [{
name: 'XXX',
email: 'XXX@gmail.com',
image: 'https://avatars.githubusercontent.com/u/XXX?v=4'
}]
}
# 5. 关联 GitHub 账户
[next-auth][debug][adapter_linkAccount] {
args: [{
provider: 'github',
providerAccountId: '204438200',
userId: 'XXX-c325-4968-9b46-7a85ed96982b'
}]
}
第四步:SupabaseAdapter 存储数据
SupabaseAdapter 自动执行以下操作:
- 检查用户是否存在:查询
users
表 - 创建用户记录:插入新用户到
users
表 - 关联账户信息:插入 GitHub 账户信息到
accounts
表 - 生成会话:创建用户会话记录
第五步:登录成功
NextAuth signIn event: {
user: {
id: '1481a099-c325-4968-9b46-7a85ed96982b',
name: 'xxx',
email: 'xxx@gmail.com'
},
isNewUser: true
}
📊 Supabase 数据库结构
登录成功后,Supabase 中的数据结构:
users
表
id: 1481a099-c325-4968-9b46-7a85ed96982b
name: bytevirts
email: XXX@gmail.com
image: https://avatars.githubusercontent.com/u/204438200?v=4
emailVerified: null
accounts
表
userId: 1481a099-c325-4968-9b46-7a85ed96982b
provider: github
providerAccountId: 204438200
access_token: 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
token_type: bearer
scope: read:user,user:email
🛠️ 遇到的问题和解决方案
1. Schema 权限问题
问题:nextjs操作supabase时提示没有权限
解决方案:在 Supabase Dashboard 中将schema如 next_auth
添加到 Exposed schemas
🎯 核心优势
- 安全性:OAuth 2.0 标准,无需存储密码
- 可扩展性:支持多种登录方式(GitHub、Google 等)
- 数据持久化:用户信息安全存储在 Supabase 云数据库
- 开发友好:NextAuth.js 提供完整的认证解决方案
- 类型安全:TypeScript 支持,减少运行时错误
这个完整的认证系统现在已经可以:
- ✅ 安全地处理 GitHub OAuth 登录
- ✅ 自动创建和管理用户账户
- ✅ 将所有数据持久化存储到 Supabase
- ✅ 提供完整的会话管理功能
跟着操作一遍,就能基本理解登录的流程!