You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 13:20:41