Next.js 身份验证策略
Next.js Authentication Strategies
我一直在尝试为 Next.js 项目实施可靠的身份验证流程,但我现在完全迷路了。我已经看过 Next.js 的示例回购。但是我有很多问题想要一个完整的解决方案。
我有一个 express.js API 和一个单独的 Next.js 前端项目。所有数据和身份验证都由 API 处理。前端只是用 SSR 渲染页面。如果我只想创建一个整体项目,其中呈现页面和所有数据都由单个服务器处理(我的意思是使用 Next.js 的自定义服务器选项),我将只使用 express-session 和 csurf。这将是管理会话和创建针对 CSRF 的安全性的传统方式。
Express.js API 不是必需的。这只是一个例子。它可以是 Django API,或 .Net Core API。重点是,它是一个独立的服务器,一个独立的项目。
我怎样才能拥有一个简单而可靠的结构?我检查了一些我最喜欢的网站(netlify、zeit.co、heroku、spectrum.chat 等)。其中一些使用本地存储来存储访问和刷新令牌(易受 XSS 攻击)。其中一些使用 cookie,它们甚至不是 HTTPOnly(XSS 和 CSRF 都容易受到攻击)。像 spectrum.chat 这样的例子使用我上面提到的方式(cookie-session + 防止 csrf)。
我知道围绕 JWT 代币的炒作很厉害。但我觉得它们太复杂了。大多数教程只是跳过所有的过期、令牌刷新、令牌撤销、黑名单、白名单等
Next.js 的许多会话 cookie 示例几乎从未提及 CSRF。老实说,身份验证对我来说一直是个大问题。有一天我读到应该使用 HTTPOnly cookies,第二天我看到一个巨大的流行网站甚至没有使用它们。或者他们说 "never store your tokens to localStorage",并且一些大型项目正在使用这种方法。
任何人都可以为我指明方向吗?
对于我当前的项目,我也不得不考虑这个问题。我使用相同的技术:ExpressJS API 和 NextJS 服务器端呈现的前端。
我选择做的是在 ExpressJS API 中使用 passport.js。 YouTube 上的 TheNetNinja 有一个非常好的播放列表,共有 21 集。他向您展示了如何在您的 API 中实现 Google OAuth 2.0,但此逻辑转移到任何其他策略(JWT、电子邮件 + 密码、Facebook 身份验证等)。
在前端,我会直接将用户重定向到 Express API 中的 url。 url 会向用户显示 Google OAuth 屏幕,用户点击 "Allow",API 会做一些更多的事情,为特定用户制作 cookie,然后重定向回到前端的 url 。现在,用户已通过身份验证。
关于HTTPOnly cookies:我选择关闭这个功能,因为我在cookie中存储了前端需要的信息。如果启用此功能,则前端 (javascript) 无法访问这些 cookie,因为它们是 HTTPOnly。
这是我正在谈论的播放列表的 link:
https://www.youtube.com/watch?v=sakQbeRjgwg&list=PL4cUxeGkcC9jdm7QX143aMLAqyM-jTZ2x
希望我已经为您指明了方向。
编辑:
我没有回答你关于CSURF的问题,那是因为我不熟悉它。
我终于找到了解决办法!
现在我使用的是 csrf npm 包,而不是 csurf。 csurf 只是把 csrf 变成了一个 express 中间件。
所以,我在_app的getInitialProps中创建了一个csrfSecret。它创建秘密,将其设置为 httpOnly cookie。稍后,它会创建一个 csrfToken 并使用 pageProps returns 它。因此,我可以使用 window.NEXT_DATA.props.csrfToken 访问它。如果用户刷新页面,csrfSecret 保持不变,但 csrfToken 会更新。
当我向代理“/api/graphql API 路由发出请求时,它首先从 x-xsrf-token header 获取 csrf 令牌并使用 csrfSecret cookie 对其进行验证价值。
之后,它提取 authToken cookie 的值并将其传递给实际的 GraphQL API.
API 都是基于令牌的。它只需要一个 non-expiring 访问令牌。 (顺便说一句,它不需要是 JWT。可以使用任何加密强度高的随机令牌。这意味着 reference/opaque 令牌。)
实际 API 不需要 CSRF 检查,因为它不依赖 cookie 进行身份验证。它只检查授权 header。
authToken 和 csrfSecret 都是 httpOnly cookies。我什至从未将它们存储在 client-side 内存中。
我认为这是我所能得到的最安全的。现在我对这个解决方案很满意。
免责声明:我是下面免费开源包的维护者,但我认为这里是合适的,因为这是一个常见问题,没有很好的答案,因为许多流行的解决方案在问题中提出了特定的安全漏洞(例如不在适当的地方使用 CSRF 并将会话令牌或 Web 令牌暴露给客户端 JavaScript)。
软件包 NextAuth.js 试图通过免费的开源软件解决上述问题。
- 它使用
httpOnly
个 cookie 和 secure
。
- 它具有 CSRF 保护(双重提交 cookie 方法,带有签名 cookie)。
- Cookies 有适当的前缀(例如
__HOST-
或 __Secure
)。
- 它支持 email/passwordless 登录和 OAuth 提供程序(包括许多)。
- 它同时支持 JSON Web 令牌(签名+加密)和会话数据库。
- 您可以在没有数据库的情况下使用它(例如任何 ANSI SQL、MongoDB)。
- 有 live demo (view source).
- 它是 100% FOSS,它不是商业软件或 SaaS 解决方案(不卖任何东西)。
示例API路线
例如page/api/auth/[...nextauth.js]
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
const options = {
providers: [
// OAuth authentication providers
Providers.Apple({
clientId: process.env.APPLE_ID,
clientSecret: process.env.APPLE_SECRET
}),
Providers.Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET
}),
// Sign in with email (passwordless)
Providers.Email({
server: process.env.MAIL_SERVER,
from: '<no-reply@example.com>'
}),
],
// MySQL, Postgres or MongoDB database (or leave empty)
database: process.env.DATABASE_URL
}
export default (req, res) => NextAuth(req, res, options)
示例 React 组件
例如pages/index.js
import React from 'react'
import {
useSession,
signin,
signout
} from 'next-auth/client'
export default () => {
const [ session, loading ] = useSession()
return <p>
{!session && <>
Not signed in <br/>
<button onClick={signin}>Sign in</button>
</>}
{session && <>
Signed in as {session.user.email} <br/>
<button onClick={signout}>Sign out</button>
</>}
</p>
}
即使您不选择使用它,您也可能会发现该代码可作为参考(例如 JSON Web Tokens are handled and how they are rotated in sessions.
我一直在尝试为 Next.js 项目实施可靠的身份验证流程,但我现在完全迷路了。我已经看过 Next.js 的示例回购。但是我有很多问题想要一个完整的解决方案。
我有一个 express.js API 和一个单独的 Next.js 前端项目。所有数据和身份验证都由 API 处理。前端只是用 SSR 渲染页面。如果我只想创建一个整体项目,其中呈现页面和所有数据都由单个服务器处理(我的意思是使用 Next.js 的自定义服务器选项),我将只使用 express-session 和 csurf。这将是管理会话和创建针对 CSRF 的安全性的传统方式。
Express.js API 不是必需的。这只是一个例子。它可以是 Django API,或 .Net Core API。重点是,它是一个独立的服务器,一个独立的项目。
我怎样才能拥有一个简单而可靠的结构?我检查了一些我最喜欢的网站(netlify、zeit.co、heroku、spectrum.chat 等)。其中一些使用本地存储来存储访问和刷新令牌(易受 XSS 攻击)。其中一些使用 cookie,它们甚至不是 HTTPOnly(XSS 和 CSRF 都容易受到攻击)。像 spectrum.chat 这样的例子使用我上面提到的方式(cookie-session + 防止 csrf)。
我知道围绕 JWT 代币的炒作很厉害。但我觉得它们太复杂了。大多数教程只是跳过所有的过期、令牌刷新、令牌撤销、黑名单、白名单等
Next.js 的许多会话 cookie 示例几乎从未提及 CSRF。老实说,身份验证对我来说一直是个大问题。有一天我读到应该使用 HTTPOnly cookies,第二天我看到一个巨大的流行网站甚至没有使用它们。或者他们说 "never store your tokens to localStorage",并且一些大型项目正在使用这种方法。
任何人都可以为我指明方向吗?
对于我当前的项目,我也不得不考虑这个问题。我使用相同的技术:ExpressJS API 和 NextJS 服务器端呈现的前端。
我选择做的是在 ExpressJS API 中使用 passport.js。 YouTube 上的 TheNetNinja 有一个非常好的播放列表,共有 21 集。他向您展示了如何在您的 API 中实现 Google OAuth 2.0,但此逻辑转移到任何其他策略(JWT、电子邮件 + 密码、Facebook 身份验证等)。
在前端,我会直接将用户重定向到 Express API 中的 url。 url 会向用户显示 Google OAuth 屏幕,用户点击 "Allow",API 会做一些更多的事情,为特定用户制作 cookie,然后重定向回到前端的 url 。现在,用户已通过身份验证。
关于HTTPOnly cookies:我选择关闭这个功能,因为我在cookie中存储了前端需要的信息。如果启用此功能,则前端 (javascript) 无法访问这些 cookie,因为它们是 HTTPOnly。
这是我正在谈论的播放列表的 link: https://www.youtube.com/watch?v=sakQbeRjgwg&list=PL4cUxeGkcC9jdm7QX143aMLAqyM-jTZ2x
希望我已经为您指明了方向。
编辑: 我没有回答你关于CSURF的问题,那是因为我不熟悉它。
我终于找到了解决办法!
现在我使用的是 csrf npm 包,而不是 csurf。 csurf 只是把 csrf 变成了一个 express 中间件。
所以,我在_app的getInitialProps中创建了一个csrfSecret。它创建秘密,将其设置为 httpOnly cookie。稍后,它会创建一个 csrfToken 并使用 pageProps returns 它。因此,我可以使用 window.NEXT_DATA.props.csrfToken 访问它。如果用户刷新页面,csrfSecret 保持不变,但 csrfToken 会更新。
当我向代理“/api/graphql API 路由发出请求时,它首先从 x-xsrf-token header 获取 csrf 令牌并使用 csrfSecret cookie 对其进行验证价值。 之后,它提取 authToken cookie 的值并将其传递给实际的 GraphQL API.
API 都是基于令牌的。它只需要一个 non-expiring 访问令牌。 (顺便说一句,它不需要是 JWT。可以使用任何加密强度高的随机令牌。这意味着 reference/opaque 令牌。)
实际 API 不需要 CSRF 检查,因为它不依赖 cookie 进行身份验证。它只检查授权 header。 authToken 和 csrfSecret 都是 httpOnly cookies。我什至从未将它们存储在 client-side 内存中。
我认为这是我所能得到的最安全的。现在我对这个解决方案很满意。
免责声明:我是下面免费开源包的维护者,但我认为这里是合适的,因为这是一个常见问题,没有很好的答案,因为许多流行的解决方案在问题中提出了特定的安全漏洞(例如不在适当的地方使用 CSRF 并将会话令牌或 Web 令牌暴露给客户端 JavaScript)。
软件包 NextAuth.js 试图通过免费的开源软件解决上述问题。
- 它使用
httpOnly
个 cookie 和secure
。 - 它具有 CSRF 保护(双重提交 cookie 方法,带有签名 cookie)。
- Cookies 有适当的前缀(例如
__HOST-
或__Secure
)。 - 它支持 email/passwordless 登录和 OAuth 提供程序(包括许多)。
- 它同时支持 JSON Web 令牌(签名+加密)和会话数据库。
- 您可以在没有数据库的情况下使用它(例如任何 ANSI SQL、MongoDB)。
- 有 live demo (view source).
- 它是 100% FOSS,它不是商业软件或 SaaS 解决方案(不卖任何东西)。
示例API路线
例如page/api/auth/[...nextauth.js]
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
const options = {
providers: [
// OAuth authentication providers
Providers.Apple({
clientId: process.env.APPLE_ID,
clientSecret: process.env.APPLE_SECRET
}),
Providers.Google({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET
}),
// Sign in with email (passwordless)
Providers.Email({
server: process.env.MAIL_SERVER,
from: '<no-reply@example.com>'
}),
],
// MySQL, Postgres or MongoDB database (or leave empty)
database: process.env.DATABASE_URL
}
export default (req, res) => NextAuth(req, res, options)
示例 React 组件
例如pages/index.js
import React from 'react'
import {
useSession,
signin,
signout
} from 'next-auth/client'
export default () => {
const [ session, loading ] = useSession()
return <p>
{!session && <>
Not signed in <br/>
<button onClick={signin}>Sign in</button>
</>}
{session && <>
Signed in as {session.user.email} <br/>
<button onClick={signout}>Sign out</button>
</>}
</p>
}
即使您不选择使用它,您也可能会发现该代码可作为参考(例如 JSON Web Tokens are handled and how they are rotated in sessions.