发布于 2026-01-06 0 阅读
0

将 SuperTokens 无密码身份验证与 Next.js 集成 DEV 的全球展示挑战赛(由 Mux 呈现):展示你的项目!

将 SuperTokens 无密码身份验证与 Next.js 集成

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

简要介绍

Authentication身份验证是应用程序验证用户身份的机制。使用用户 ID 和密码进行身份验证是许多应用程序中最常用的机制。然而,还有其他一些常用的方法。我们可以使用社交账号登录、一次性密码 (OTP) 或直接验证用户身份的链接。

SuperTokens是一个开源的、高度可定制的用户身份验证提供框架,可帮助您的 Web 应用程序实现各种身份验证机制。它可以轻松地与后端框架(例如 NodeJS、GoLang、Python)以及前端框架(例如 ReactJS、React Native、JavaScript)集成。

Next.js是一个流行的 React 框架,用于构建快速的 Web 应用程序。本文将介绍如何将 SuperTokens 身份验证机制集成到 Next.js 应用程序中。我们将重点介绍身份验证passwordless,但其他方法(例如使用社交账号登录或用户名/密码登录)也类似。

我们今天在建造什么?

我们将从一个简单但高效的 Next.js 应用入手,并把 SuperTokens 的强大功能集成到其中。这个应用名为“名人名言Retell”,它会展示世界各地名人的名言。请打开你常用的代码编辑器,并跟随本文的步骤进行操作。祝你开发愉快!

如果您想随时查阅源代码,可以在这里找到:

GitHub 标志 atapas /引言

一个旨在演示 SuperTokens 无密码认证与 Nextjs 集成的项目


如果您喜欢我的作品,请给仓库点个赞⭐。这会激励我继续创作。

设置Retell项目用户界面

本文假设您已具备 React.js 的基本知识,并且能够阅读 Next.js 源代码。请按照以下步骤在Retell本地运行项目用户界面:

  npm install
  # or
  yarn
Enter fullscreen mode Exit fullscreen mode
  • 然后,运行开发服务器。
  npm run dev
  # or
  yarn dev
Enter fullscreen mode Exit fullscreen mode

现在使用 URL http://localhost:3000访问该应用程序,即可看到成功渲染的报价。

报价用户界面

您也可以quotes使用以下 URL 访问 API:http://localhost:3000/api/quotes

恭喜!!!用户界面已经运行起来了。现在让我们把 SuperTokens 无密码认证功能集成到其中。

设置 SuperTokens 身份验证

SuperTokens无密码认证方案可帮助您使用一次性密码 (OTP) 和魔法链接进行身份验证。我们将通过几个步骤在 Next.js 应用程序中配置此认证类型。

  • 创建前端和后端配置。
  • 显示登录界面
  • 添加身份验证 API
  • 保护网站路由
  • 对 API 调用执行会话验证

上述步骤需要安装一些依赖项。请使用以下命令安装依赖项:

yarn add supertokens-auth-react supertokens-node dotenv  nodemailer
Enter fullscreen mode Exit fullscreen mode

前端和后端配置

.env.local在项目根目录下创建一个文件,内容如下:

NEXT_PUBLIC_NODEMAILER_USER=<YOUR_GMAIL_ID>
NEXT_PUBLIC_NODEMAILER_PASSWORD=<YOUR_GMAIL_PASSWORD>

NEXT_PUBLIC_APP_URL=http://localhost:3000
Enter fullscreen mode Exit fullscreen mode

重要提示:我们需要您的 Gmail 账号和密码才能通过电子邮件发送验证码和魔法链接。请勿使用您的个人 Gmail 账号。您可以创建一个虚拟 Gmail 账号并降低安全设置,用于测试目的。

config在项目根目录下创建一个文件夹。然后appInfo.js在该config文件夹内创建一个文件,并添加以下内容:

const port = process.env.APP_PORT || 3000

const apiBasePath = '/api/auth/'

export const websiteDomain =
  process.env.APP_URL ||
  process.env.NEXT_PUBLIC_APP_URL ||
  `http://localhost:${port}`

export const appInfo = {
  appName: 'Retell',
  websiteDomain,
  apiDomain: websiteDomain,
  apiBasePath,
}
Enter fullscreen mode Exit fullscreen mode

appInfo.js文件包含一个特殊对象,用于指定前端和后端配置的变量。请点击此处appInfo了解更多关于该对象的信息

接下来,在文件夹内创建一个frontendConfig.js文件config,内容如下:

import PasswordlessReact from "supertokens-auth-react/recipe/passwordless";
import SessionReact from "supertokens-auth-react/recipe/session";
import { appInfo } from "./appInfo";

export const frontendConfig = () => {
  return {
    appInfo,
    recipeList: [
      PasswordlessReact.init({
        contactMethod: "EMAIL_OR_PHONE",
      }),
      SessionReact.init(),
    ],
  };
};
Enter fullscreen mode Exit fullscreen mode

这里我们创建一个函数,用于为 SuperTokens 的前端 SDK 提供配置。稍后我们将介绍如何使用此函数。请注意,联系方式为“无密码验证” EMAIL_OR_PHONE。这意味着将通过向用户指定的邮箱或手机号码发送验证码 (OTP) 和 Magic Link 来进行无密码身份验证。

现在我们来创建后端配置。请在文件夹backendConfig.js内创建一个名为 `.bashrc` 的文件config,并添加以下内容:

require("dotenv").config();

import Session from "supertokens-auth-react/recipe/session";
import PasswordlessNode from "supertokens-node/recipe/passwordless";
import SessionNode from "supertokens-node/recipe/session";
import { appInfo } from "./appInfo";
let { getEmailBody } = require("../util/mailer");

export const backendConfig = () => {
  const nodemailer = require('nodemailer');

  const mailTransporter = nodemailer.createTransport({
    host: "smtp.gmail.com",
    port: 465,
    secure: true,
    auth: {
        user: process.env.NEXT_PUBLIC_NODEMAILER_USER,
        pass: process.env.NEXT_PUBLIC_NODEMAILER_PASSWORD,
    },
  });

  return {
    framework: "express",
    supertokens: {
      connectionURI: "https://try.supertokens.com",
      // apiKey: "IF YOU HAVE AN API KEY FOR THE CORE, ADD IT HERE",
    },
    appInfo,
    recipeList: [
      PasswordlessNode.init({
        flowType: "USER_INPUT_CODE_AND_MAGIC_LINK",
        contactMethod: "EMAIL_OR_PHONE",
        createAndSendCustomEmail: async (input, context) => {
          try{
            let htmlBody = getEmailBody(
              appInfo.appName,
              Math.ceil(input.codeLifetime / 1000),
              input.urlWithLinkCode,
              input.userInputCode,
              input.email
            );
            await mailTransporter.sendMail({
              html: htmlBody,
              to: input.email,
              from: `Team Supertokens <${appInfo.appName}>`,
              sender: process.env.NEXT_PUBLIC_NODEMAILER_USER,
              subject: `Login to ${appInfo.appName}`,
            });
          } catch (err) {
            console.log(err);
          }
        },
        createAndSendCustomTextMessage: async (input, context) => {
          // Creating a Twilio account and set it up.
        },
      }),
      SessionNode.init(),
    ],
    isInServerlessEnv: true,
  };
};
Enter fullscreen mode Exit fullscreen mode

我们已将上述值用作示例try.supertokens.comconnectionURI请注意,URI try.supertokens.com仅用于演示目的。您需要设置自己的核心并替换上述核心地址。

您可以通过两种方式设置自己的核心:

  • By self-hosting SuperTokens您可以自行托管 SuperTokens 核心程序,并使用您自己的数据库(MySQLPostgreSQL )。您可以手动安装 SuperTokens,也可以通过Docker进行托管
  • By using the managed service option要在托管服务上设置 SuperTokens Core,请先创建一个免费帐户并登录。然后,从控制面板创建一个新应用。完成后,您可以在应用详情页面找到连接 URI 和 API 密钥。

好了,现在我们来谈谈上面代码中指定的两种交付方式:

  • createAndSendCustomEmail这是一种基于电子邮件的发送方式,使用环境变量中的 Gmail 凭据。请注意,我们使用这种getEmailBody()方法来格式化电子邮件。

util请在项目根目录下创建一个名为 `mailer.js` 的文件夹。然后在该文件夹下创建一个名为 `mailer.js` 的文件mailer.js,该文件导出 `mailer.js`getEmailBody()方法。以下是 `mailer.js` 文件的示例实现,您可以复制粘贴其全部内容。

请点击此处查看更多关于配送方式属性的信息

现在我们将从文件中调用前端初始化函数_app.js。请打开文件夹_app.js下的文件pages,并将内容替换为以下内容:

import React from 'react'
import SuperTokensReact from 'supertokens-auth-react'
import { frontendConfig } from '../config/frontendConfig'
import '../styles/globals.css'


if (typeof window !== 'undefined') {
  // we only want to call this init function on the frontend, so 
  // we check typeof window !== 'undefined'
  SuperTokensReact.init(frontendConfig())
}

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp
Enter fullscreen mode Exit fullscreen mode

现在所有配置都已完成。接下来我们来展示登录用户界面。

显示登录界面

auth在该pages文件夹内创建一个文件夹。然后[[...path]].js在该auth/pages文件夹内创建一个文件,内容如下:

import dynamic from 'next/dynamic'
import React, { useEffect } from 'react'
import SuperTokens from 'supertokens-auth-react'
import { redirectToAuth } from 'supertokens-auth-react/recipe/passwordless'

const SuperTokensComponentNoSSR = dynamic(
  new Promise((res) => res(SuperTokens.getRoutingComponent)),
  { ssr: false }
)

export default function Auth() {

  // if the user visits a page that is not handled by us (like /auth/random), 
  // then we redirect them back to the auth page.
  useEffect(() => {
    if (SuperTokens.canHandleRoute() === false) {
      redirectToAuth()
    }
  }, [])

  return (
      <SuperTokensComponentNoSSR />
  )
}
Enter fullscreen mode Exit fullscreen mode

这是我们的身份验证组件。好的,我们来测试一下。打开浏览器标签页,访问应用的/authURL:http://localhost:3000/auth。此时,您应该会看到登录用户界面。

身份验证登录

主应用程序页面(localhost:3000)和报价 API(localhost:3000/api/quotes)目前尚未启用身份验证。让我们逐一进行配置。

添加身份验证 API

现在我们将添加所有后端身份验证 API /api/auth。请authpages/api/文件夹中创建文件夹。现在创建一个文件[[...path]].js,内容如下:

require("dotenv").config();
import supertokens from 'supertokens-node';
import { middleware } from 'supertokens-node/framework/express';
import { superTokensNextWrapper } from 'supertokens-node/nextjs';
import { backendConfig } from '../../../config/backendConfig';

supertokens.init(backendConfig())

export default async function superTokens(req, res) {

  await superTokensNextWrapper(
    async (next) => {
      await middleware()(req, res, next)
    },
    req,
    res
  )
  if (!res.writableEnded) {
    res.status(404).send('Not found')
  }
}
Enter fullscreen mode Exit fullscreen mode

[[...path]].js文件使用了由 公开的中间件supertokens-node,该中间件公开了所有 API,如登录、注册等。

请更新quotesAPI,使其仅允许授权用户访问。请将文件内容更新quotes.js为以下内容:

import supertokens from 'supertokens-node';
import { superTokensNextWrapper } from 'supertokens-node/nextjs';
import { verifySession } from 'supertokens-node/recipe/session/framework/express';
import { backendConfig } from '../../config/backendConfig';
import quoteList from '../../data/quotes.json';

supertokens.init(backendConfig())

export default async function quotes(req, res) {
  await superTokensNextWrapper(
    async (next) => {
      return await verifySession()(req, res, next)
    },
    req,
    res
  )

  return res.json(quoteList.quotes)
}
Enter fullscreen mode Exit fullscreen mode

现在访问报价 API,http://localhost:3000/api/quotes。你会看到未授权错误。

未经授权的错误

别担心,我们现在就修复整个工作流程。

保护路线

为了确保一切正常运行,我们需要保护路由。请打开文件夹index.js下的文件pages,并将内容替换为以下内容:

import dynamic from 'next/dynamic';
import Head from 'next/head';
import React from 'react';
import Passwordless from "supertokens-auth-react/recipe/passwordless";
import supertokensNode from 'supertokens-node';
import Session from 'supertokens-node/recipe/session';
import Footer from '../components/Footer';
import Header from '../components/Header';
import QuoteList from '../components/QuoteList';
import styles from '../styles/Home.module.css';

const PasswordlessAuthNoSSR = dynamic(
  new Promise((res) =>
    res(Passwordless.PasswordlessAuth)
  ),
  { ssr: false }
)

export default function Home(props) {
  return (
    <PasswordlessAuthNoSSR>
      <ProtectedPage />
    </PasswordlessAuthNoSSR>
  )
}

function ProtectedPage({ userId }) {
  async function logoutClicked() {
    await Passwordless.signOut()
    Passwordless.redirectToAuth()
  }

  return (
    <div className={styles.container}>
      <Head>
        <title>Retell</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <Header />
      <main className={styles.main}>
        <QuoteList />
      </main>
      <Footer />
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

就是这样。您可以通过登录页面访问应用程序或报价API,两者都受到登录页面的保护。您必须提供手机号码或邮箱地址以获取验证码并进行身份验证,然后才能访问报价。

你的任务

好的,给你一个小任务。你能实现注销功能吗?页面上index.js有一个名为 `logout` 的函数logoutClicked()。当有人点击Sign Out页面顶部的链接时,如何调用这个函数呢?请尝试一下!

再次提醒,完整的源代码已上传至我的 GitHub:https://github.com/atapas/quotes 。如果您在阅读本文时遇到任何问题,或在实施工作流程时遇到错误,请通过Discord联系 SuperTokens

在我们结束之前……

今天就到这里。希望这篇文章对您有所帮助。

我分享我的知识,

  • 🌐 Web 开发(JavaScript、ReactJS、Next.js、Node.js 等……)
  • 🛡️ 网络安全
  • 💼 职业发展
  • 🌱 开源
  • ✍️ 内容创作

让我们联系吧,

文章来源:https://dev.to/atapas/integrate-supertokens-passwordless-authentication-with-nextjs-3j7j