将 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”,它会展示世界各地名人的名言。请打开你常用的代码编辑器,并跟随本文的步骤进行操作。祝你开发愉快!
如果您想随时查阅源代码,可以在这里找到:
如果您喜欢我的作品,请给仓库点个赞⭐。这会激励我继续创作。
设置Retell项目用户界面
本文假设您已具备 React.js 的基本知识,并且能够阅读 Next.js 源代码。请按照以下步骤在Retell本地运行项目用户界面:
- fork 或 clone 此仓库:https://github.com/atapas/quotes
- 切换到仅限分支的用户界面
- 使用终端中的以下命令安装依赖项:
npm install
# or
yarn
- 然后,运行开发服务器。
npm run dev
# or
yarn dev
现在使用 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
前端和后端配置
.env.local在项目根目录下创建一个文件,内容如下:
NEXT_PUBLIC_NODEMAILER_USER=<YOUR_GMAIL_ID>
NEXT_PUBLIC_NODEMAILER_PASSWORD=<YOUR_GMAIL_PASSWORD>
NEXT_PUBLIC_APP_URL=http://localhost:3000
重要提示:我们需要您的 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,
}
该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(),
],
};
};
这里我们创建一个函数,用于为 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,
};
};
我们已将上述值用作示例try.supertokens.com。connectionURI请注意,URI try.supertokens.com仅用于演示目的。您需要设置自己的核心并替换上述核心地址。
您可以通过两种方式设置自己的核心:
By self-hosting SuperTokens您可以自行托管 SuperTokens 核心程序,并使用您自己的数据库(MySQL或PostgreSQL )。您可以手动安装 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` 文件的示例实现,您可以复制粘贴其全部内容。
createAndSendCustomTextMessage这是基于短信的推送方式。您可以创建一个Twilio帐户,并通过几个简单的步骤进行设置。您可以点击此处查看示例。
请点击此处查看更多关于配送方式属性的信息。
现在我们将从文件中调用前端初始化函数_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
现在所有配置都已完成。接下来我们来展示登录用户界面。
显示登录界面
请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 />
)
}
这是我们的身份验证组件。好的,我们来测试一下。打开浏览器标签页,访问应用的/authURL:http://localhost:3000/auth。此时,您应该会看到登录用户界面。
主应用程序页面(localhost:3000)和报价 API(localhost:3000/api/quotes)目前尚未启用身份验证。让我们逐一进行配置。
添加身份验证 API
现在我们将添加所有后端身份验证 API /api/auth。请auth在pages/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')
}
}
该[[...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)
}
现在访问报价 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>
)
}
就是这样。您可以通过登录页面访问应用程序或报价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


