SuperTokens NextJS13教程:如何在新版NextJS13中使用SuperTokens?
NextJS 13 App Router 下的 SuperTokens 实现方案与模板示例
核心依赖安装
首先安装必要的包:
npm install supertokens-node supertokens-web-js nextjs-cors
目录结构示例
适配 App Router 的典型项目结构:
./app ├── api │ └── auth │ └── [...supertokens] │ └── route.ts ├── layout.tsx ├── login │ └── page.tsx ├── dashboard │ └── page.tsx └── lib └── supertokens.ts
关键文件实现
1. SuperTokens 核心配置 (lib/supertokens.ts)
import SuperTokens from "supertokens-node"; import EmailPassword from "supertokens-node/recipe/emailpassword"; import Session from "supertokens-node/recipe/session"; SuperTokens.init({ framework: "next", supertokens: { connectionURI: "https://try.supertokens.io", // 替换为你的 SuperTokens 实例地址 }, appInfo: { appName: "Your App Name", apiDomain: process.env.NEXT_PUBLIC_API_DOMAIN || "http://localhost:3000", websiteDomain: process.env.NEXT_PUBLIC_WEBSITE_DOMAIN || "http://localhost:3000", }, recipeList: [ EmailPassword.init(), Session.init(), ], });
2. Auth API 路由 (app/api/auth/[...supertokens]/route.ts)
处理 SuperTokens 的所有 auth 相关请求:
import { superTokensNextWrapper } from 'supertokens-node/nextjs'; import { middleware } from 'supertokens-node/framework/express'; import NextCors from 'nextjs-cors'; import type { NextRequest, NextResponse } from 'next/server'; export async function GET(request: NextRequest) { await NextCors(request, { origin: process.env.NEXT_PUBLIC_WEBSITE_DOMAIN || "http://localhost:3000", methods: ["GET", "POST", "OPTIONS"], credentials: true, }); const response = new NextResponse(); await superTokensNextWrapper( async (next) => { await middleware()(request as any, response as any, next); }, request, response ); return response; } export async function POST(request: NextRequest) { await NextCors(request, { origin: process.env.NEXT_PUBLIC_WEBSITE_DOMAIN || "http://localhost:3000", methods: ["GET", "POST", "OPTIONS"], credentials: true, }); const response = new NextResponse(); await superTokensNextWrapper( async (next) => { await middleware()(request as any, response as any, next); }, request, response ); return response; } export async function OPTIONS(request: NextRequest) { await NextCors(request, { origin: process.env.NEXT_PUBLIC_WEBSITE_DOMAIN || "http://localhost:3000", methods: ["GET", "POST", "OPTIONS"], credentials: true, }); return new NextResponse(null, { status: 200 }); }
3. 客户端初始化 (app/layout.tsx)
在根布局中初始化 SuperTokens 客户端:
'use client'; import React, { useEffect } from 'react'; import SuperTokens from 'supertokens-web-js'; import EmailPassword from 'supertokens-web-js/recipe/emailpassword'; import Session from 'supertokens-web-js/recipe/session'; export default function RootLayout({ children }: { children: React.ReactNode }) { useEffect(() => { SuperTokens.init({ appInfo: { appName: "Your App Name", apiDomain: process.env.NEXT_PUBLIC_API_DOMAIN || "http://localhost:3000", websiteDomain: process.env.NEXT_PUBLIC_WEBSITE_DOMAIN || "http://localhost:3000", }, recipeList: [ EmailPassword.init(), Session.init(), ], }); }, []); return ( <html lang="en"> <body>{children}</body> </html> ); }
4. 登录页面 (app/login/page.tsx)
实现邮箱密码登录逻辑:
'use client'; import React, { useState } from 'react'; import { signIn } from 'supertokens-web-js/recipe/emailpassword'; import { useRouter } from 'next/navigation'; export default function LoginPage() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); const router = useRouter(); const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); setError(''); try { await signIn({ formFields: [ { id: 'email', value: email }, { id: 'password', value: password }, ], }); router.push('/dashboard'); } catch (err: any) { setError(err.message || '登录失败'); } }; return ( <div style={{ maxWidth: '400px', margin: '2rem auto', padding: '0 1rem' }}> <h1>登录</h1> <form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}> <div> <label htmlFor="email">邮箱:</label> <input id="email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} required style={{ width: '100%', padding: '0.5rem', marginTop: '0.25rem' }} /> </div> <div> <label htmlFor="password">密码:</label> <input id="password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} required style={{ width: '100%', padding: '0.5rem', marginTop: '0.25rem' }} /> </div> {error && <p style={{ color: 'red', margin: 0 }}>{error}</p>} <button type="submit" style={{ padding: '0.5rem', cursor: 'pointer' }}>登录</button> </form> </div> ); }
5. 受保护的仪表盘页面 (app/dashboard/page.tsx)
验证用户会话,未登录则重定向到登录页:
import { getSession } from 'supertokens-node/recipe/session'; import { redirect } from 'next/navigation'; export default async function DashboardPage() { try { const session = await getSession(); const userId = session.getUserId(); return ( <div style={{ maxWidth: '800px', margin: '2rem auto', padding: '0 1rem' }}> <h1>仪表盘</h1> <p>欢迎回来,用户ID: {userId}</p> </div> ); } catch (err) { redirect('/login'); } }
注意事项
- 确保环境变量中配置正确的
NEXT_PUBLIC_API_DOMAIN和NEXT_PUBLIC_WEBSITE_DOMAIN - 生产环境中替换
connectionURI为你自己部署的 SuperTokens 实例地址 - 可根据需求扩展其他认证方式(如第三方登录、无密码登录等)
内容的提问来源于stack exchange,提问作者ssDev
相关产品推荐
相关产品推荐

