使用 NextAuth.js 立即向现有的无服务器 Next.js 应用程序添加身份验证!
更新于 2021 年 5 月 25 日,包含最新的 NextAuth.js 示例/最佳实践/链接
您是否有一个 Next.js 应用程序,并且想要添加用户/身份验证支持?不用再犹豫了!您只需要从NextAuth.js
您喜欢的提供商(Google、Github 等)获取一些 OAuth 密钥,以及大约 5 分钟的时间!
在开始之前,让我们总结一下我们要做的事情。
在本教程中,我们将执行以下操作:
- 在 Google 开发者门户中创建 OAuth 密钥
- 在我们的 Next.js 应用程序中安装并配置 NextAuth.js
- 保护一些页面路由和api路由
让我们开始吧!
先决条件
我假设你已经拥有以下内容:
- Google 帐户
- 现有的 Next.js 应用
Google OAuth 设置
因此,首先我们必须从 Google 获取 OAuth 客户端 ID 和密钥。NextAuth.js 内置支持大量 OAuth 提供商,例如 Apple、Discord、Facebook、Github、Google、LinkedIn、Okta、Slack、Twitch、Twitter 等等,您可以在它们的文档 - 提供商中找到更多内容。那里有关于如何检索所有提供商所需 OAuth 密钥的说明,但我在这里只详细介绍如何设置 Google。
首先,访问https://console.developers.google.com/apis/credentials。
如果您尚未在 Google 开发者控制台中设置项目,或者想要为该应用程序创建一个新项目,请继续选择左上角的项目概览,然后点击“创建项目”
创建项目后,请确保您位于左侧主菜单的“API 和服务”->“凭据”部分。在那里,选择“创建凭据”,然后选择“OAuth 客户端 ID”。
如果您尚未为您的域名设置 OAuth 同意屏幕,则必须先进行设置 - 只需输入应用名称和您将用于该应用的域名即可。对于内部应用,无需 Google 验证流程,但该流程也只允许您 G Suite 域内的成员使用该应用。公共应用必须先通过 Google 验证域名,然后才允许大规模登录 - OAuth 限制为 100 次“敏感范围登录”,即在 Google 验证同意屏幕之前进行测试登录。
接下来,选择“Web 应用程序”作为我们要创建的 OAuth 客户端 ID 类型,并为其命名。您还需要在此处输入“授权 JavaScript 来源”和“授权重定向 URI”的 URI 。
例如,授权的 Javascript 来源只是您的应用程序的域myapp.domain.com
。
授权重定向 URI将根据您设置的提供程序而有所不同,但对于使用 Google 作为 OAuth 提供程序的 NextAuth.js 的默认设置,重定向 URI 将类似于:https://myapp.domain.com/api/auth/callback/google
。显然,请确保将域调整为您在其下运行应用程序的任何域,重要的部分是/api/auth/callback/google
路径。
按下“创建”后,您将获得您的OAuth 客户端 ID和OAuth 密钥!请务必保存它们,因为我们稍后会需要它们!
NextAuth.js 安装
接下来,我们需要将 NextAuth.js 安装到我们的 Next.js 应用程序中。为此,请导航到您的项目目录并安装next-auth
。
cd /opt/dev/my-nextjs-app
npm i -s next-auth
现在我们已准备好设置 NextAuth.js!
初始设置
现在我们已经解决了所有先决条件和依赖项,让我们获取 NextAuth.js 设置的配置文件。首先,.env
如果您还没有配置文件,我们需要在项目根目录下创建一个。
您的.env
文件应包含以下内容:
NEXTAUTH_URL=https://myapp.domain.com
GOOGLE_ID=abc123
GOOGLE_SECRET=abc123
是
NEXTAUTH_URL
您的应用程序将运行的 URL。您可以通过设置多个.env
文件(例如.env.production
、.env.development
等)为不同的环境设置多个 URL。几乎所有托管服务提供商的环境变量设置部分(例如 Netlify 或 Vercel 等)都可以做到这一点。GOOGLE_ID
和GOOGLE_SECRET
是我们之前从 Google 开发者控制台收到的 OAuth 密钥!
接下来,在 处创建用于身份验证的 Next.js API 路由pages/api/auth/[...nextauth].js
。此文件至少应包含以下内容:
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
const options = {
providers: [
Providers.Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET
}),
],
debug: false
}
export default (req, res) => NextAuth(req, res, options)
这里还有很多配置项可以配置,但这是基于 JWT 的 OAuth 身份验证的最低要求。更多配置选项可以在docs - configuration中找到,也可以在这里找到此文件的完整注释版本。
眼尖的朋友们可能会注意到,我们这里没有指定任何类型的数据库。这意味着以下几点:
- 会话处理默认启用 JWT 支持
- 我们没有永久的用户数据库
如果您想将用户/帐户永久存储在数据库中,NextAuth.js 可以轻松添加 SQLite 进行测试,或添加其他各种适配器,例如 MySQL、Postgres、MongoDB(通过 TypeORM)、FaunaDB、Firebase、DynamoDB 和 Prisma。有关数据库详细信息,请参阅其文档 - 适配器。
接下来,您还需要将您的应用程序包装在 NextAuth 提供程序中,因此pages/_app.js
如果您还没有,请打开或创建一个。
它最终看起来应该是这样的:
import { Provider } from 'next-auth/client'
export default function App ({ Component, pageProps }) {
return (
<Provider session={pageProps.session}>
<Component {...pageProps} />
</Provider>
)
}
通过将整个应用程序包装在此提供程序中,我们可以使用 React ContextuseSession
在应用程序中的任何页面/组件中提供钩子并共享会话状态。
不管你信不信,这就是使用 NextAuth.js 进行基本 Google OAuth 设置所需要的全部内容!
该库默认包含一些基本的登录/注销页面,因此无需另行编写。如果您想自定义这些页面,我们当然支持。更多详情,请点击此处查看。
在本教程中,我们要做的最后一件事是展示如何实际限制只有经过身份验证的用户才能访问页面。
目前,用户仍然可以导航到我们应用程序中的任何路线,并且无论他们的登录状态如何,都会获得所有内容的渲染,因为我们还没有有条件地渲染任何内容。
限制页面访问
首先,让我们构建一个小组AccessDenied
件,当用户进入受保护的路径但尚未登录时,我们会向用户显示该组件。这将告知用户他们没有访问权限,并让他们有机会登录。
让我们将其命名为,components/accessDenied.js
import { signIn } from 'next-auth/client'
export default function AccessDenied () {
return (
<>
<h1>Access Denied</h1>
<p>
<a href="/api/auth/signin"
onClick={(e) => {
e.preventDefault()
signIn()
}}>You must be signed in to view this page</a>
</p>
</>
)
}
最后,在我们想要保护的页面上,我们只需要导入useSession
钩子next-auth
并在返回页面内容之前检查会话,accessDenied.js
如果没有可用的会话则显示组件。
客户端页面
受保护的客户端呈现页面的示例(即pages/index.js
)可能如下所示:
import React from 'react'
import { useSession } from 'next-auth/client'
import Layout from '../components/layout'
import AccessDenied from '../components/access-denied'
export default function Page () {
const [ session, loading ] = useSession()
// When rendering client side don't display anything until loading is complete
if (typeof window !== 'undefined' && loading) return null
// If no session exists, display access denied message
if (!session) { return <Layout><AccessDenied/></Layout> }
// If session exists, display content
return (
<Layout>
<h1>Protected Page</h1>
<p><strong>Welcome {session.user.name}</strong></p>
</Layout>
)
}
SSR 页面
示例 SSR 页面可能如下所示:
import { getSession } from 'next-auth/client'
import Layout from '../components/layout'
import AccessDenied from '../components/access-denied'
export default function Page ({ session }) {
// If no session exists, display access denied message
if (!session) { return <Layout><AccessDenied/></Layout> }
// If session exists, display content
return (
<Layout>
<h1>Protected Page</h1>
<p><strong>Welcome {session.user.name}</strong></p>
</Layout>
)
}
export async function getServerSideProps(context) {
const session = await getSession(context)
return {
props: {
session
}
}
}
API 路由
最后,受保护的 API 路由示例可能如下所示:
// pages/api/protected.js
import { getSession } from 'next-auth/client'
export default async (req, res) => {
const session = await getSession({ req })
if (session) {
res.send({ content: 'This is protected content. You can access this content because you are signed in.' })
} else {
res.send({ error: 'You must be signed in to access this api route' })
}
}
结论
好了各位,我希望这不会比承诺的 5 分钟花费更多时间!
例如,此示例可以部署到 Vercel,因为 NextAuth.js 与它们的无服务器 api 功能兼容。
如果我没有提到,除了我在介绍中提到的许多 OAuth 提供商的内置支持之外,还有基于电子邮件的“魔术链接”登录的内置支持以及添加自定义 OAuth 提供商和其他自定义身份验证提供商的选项,包括详细介绍如何使用 LDAP 的精彩教程,那我也是失职了。
我要感谢@iaincollins、@balazsorban以及所有为NextAuth.js做出贡献的优秀开发者们!这篇文章主要基于示例应用,以及我在过去几个月里在2-3个Next.js应用中使用NextAuth.js的经验,以及我为该项目做出的贡献。
您可以在此处的官方 NextAuth.js 示例应用程序中找到更多信息。