我对刷新令牌在 NextAuth 中的工作原理有点困惑。
我遵循了他们的文档,但在回调中,他们在与客户端共享的会话中包含刷新令牌。这意味着会话对象(可在浏览器中访问)包含刷新令牌,这似乎不安全。
情况是这样的:使用NextAuth时,登录发生在服务器端。我使用凭据提供程序登录 Node.js 后端,该后端生成两个安全的 httpOnly cookie:
refreshToken
和 accessToken
。
现在,我的问题是:我应该如何在 NextAuth 回调中处理这个问题?具体来说,如何检查访问令牌是否已过期并请求新的服务器端而不在与客户端共享的会话中包含刷新令牌?
对于上下文,所有其他 API 调用都是在客户端进行的,并且我的 Next.js 受保护的路由在服务器端进行验证。我确实将访问令牌保存在客户端会话中,因为 API 调用需要它,但我想避免将刷新令牌暴露给客户端。
要解决在 NextAuth 中安全管理刷新令牌的问题,您可以使用 next-refresh-token(https://www.npmjs.com/package/next-refresh-token) 包。该包提供了一种安全有效的方法来处理刷新令牌,确保它们存储在服务器端的 httpOnly cookie 中,而不是将它们暴露给客户端。
首先,使用以下命令安装软件包:
npm install next-refresh-token
下面是如何将
next-refresh-token
集成到 Next.js 项目中以安全管理刷新令牌的分步实施。
next-refresh-token
next-refresh-token
包增强了 NextAuth,允许您安全地处理刷新令牌,而无需在客户端公开它们。按如下方式更新您的 NextAuth API 路由:
// pages/api/auth/[...nextauth].js
import NextAuth from "next-auth";
import { withRefreshToken } from "next-refresh-token";
export default withRefreshToken(
NextAuth({
providers: [
{
id: "credentials",
name: "Credentials",
authorize: async (credentials) => {
const user = await loginToYourBackend(credentials); // Custom login logic
if (user) {
return user;
} else {
return null;
}
},
},
],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.accessToken = user.accessToken;
token.refreshToken = user.refreshToken;
}
return token;
},
async session({ session, token }) {
session.accessToken = token.accessToken;
return session;
},
},
secret: process.env.NEXTAUTH_SECRET, // Set a secure secret
}),
{
refreshTokenEndpoint: "/api/auth/refresh", // Endpoint to handle token refreshes
accessTokenExpiry: 60 * 15, // Access token expiry time in seconds (e.g., 15 minutes)
}
);
刷新端点用于使用存储在 httpOnly cookie 中的刷新令牌安全地生成新的访问令牌。
// pages/api/auth/refresh.js
import { refreshTokenHandler } from "next-refresh-token";
export default refreshTokenHandler({
async refresh(refreshToken) {
// Replace this with your backend API call to get a new access token
const response = await fetch("https://your-backend.com/api/refresh-token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ refreshToken }),
});
if (response.ok) {
const data = await response.json();
return {
accessToken: data.accessToken, // Return the new access token
refreshToken: data.refreshToken, // Return a new refresh token if applicable
};
} else {
throw new Error("Failed to refresh token");
}
},
});
为了确保您的客户端 API 调用始终具有有效的访问令牌,您可以使用
getSession
中的 next-auth/react
检查会话并在需要时刷新令牌。
import { getSession } from "next-auth/react";
async function fetchData() {
const session = await getSession(); // Get the session object with the access token
const accessToken = session?.accessToken;
const response = await fetch("/api/protected-endpoint", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
if (!response.ok) {
throw new Error("Failed to fetch data");
}
return response.json();
}
如果您的应用严重依赖受保护的路由,您可以创建中间件来确保所有 API 请求都经过身份验证并且访问令牌有效。
// middleware.js
import { withAuthMiddleware } from "next-refresh-token";
export default withAuthMiddleware({
tokenRefreshEndpoint: "/api/auth/refresh",
});
next-refresh-token
next-auth
的即插即用解决方案。通过使用
next-refresh-token
包,您可以安全地处理刷新令牌,而无需将它们暴露给客户端。这种方法增强了安全性并简化了令牌生命周期管理,使其成为生产就绪应用程序的绝佳选择。
更多详情请参考官方文档:next-refresh-token。