自动刷新访问令牌?

问题描述 投票:0回答:1

我正在使用 Next、Auth js 和 Spotify API 构建一个仪表板。我已经实现了 OAuth 流程,并且用户正确连接了 Spotify 帐户。 身份验证过程后,Spotify 会为您提供访问令牌和刷新令牌,因为访问令牌会在 1 小时后过期。 您可以使用刷新令牌来获取另一个访问令牌,我希望自动执行此操作,但由于某些原因它不起作用。

这就是我在 auth.ts 中所做的,基本上我遵循文档。

import NextAuth, { DefaultSession } from "next-auth";
import Spotify from "next-auth/providers/spotify";

declare module "next-auth" {
  interface Session extends DefaultSession {
    access_token?: string;
    expires_at?: number;
    refresh_token?: string;
    error?: "RefreshAccessTokenError";
  }
}

declare module "@auth/core/jwt" {
  interface JWT {
    access_token: string;
    expires_at: number;
    refresh_token: string;
    error?: "RefreshAccessTokenError";
    user?: DefaultSession["user"];
  }
}

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    Spotify({
      clientId: process.env.AUTH_SPOTIFY_ID,
      clientSecret: process.env.AUTH_SPOTIFY_SECRET,
      authorization: `https://accounts.spotify.com/authorize?scope=user-read-private user-read-email user-top-read user-read-recently-played user-library-read`,
    }),
  ],
  callbacks: {
    authorized({ auth, request: { nextUrl } }) {
      const isLoggedIn = !!auth?.user; // !! is used here to convert the potentially truthy/falsy value of auth?.user to a strict boolean representation
      const isOnDashboard = nextUrl.pathname.startsWith("/dashboard");
      if (isOnDashboard) {
        if (isLoggedIn) return true;
        return false; // redirect authenticated users to login page
      } else if (isLoggedIn) {
        return Response.redirect(new URL("/dashboard", nextUrl));
      }
      return true;
    },

    async jwt({ token, account, user }) {
      if (account) {
        console.log("New authentication, creating new token");
        return {
          ...token,
          access_token: account.access_token,
          expires_at: Math.floor(
            Date.now() / 1000 + (account.expires_in || 3600)
          ),
          refresh_token: account.refresh_token,
          user,
        };
      } else if (Date.now() < token.expires_at * 1000) {
        console.log("token still valid, returning existing token");
        return token;
      } else {
        console.log("token expired, trying to refresh");
        if (!token.refresh_token) throw new Error("Missing refresh token");

        try {
          const response = await fetch(
            "https://accounts.spotify.com/api/token",
            {
              headers: { "Content-Type": "application/x-www-form-urlencoded" },
              body: new URLSearchParams({
                client_id: process.env.AUTH_SPOTIFY_ID!,
                client_secret: process.env.AUTH_SPOTIFY_SECRET!,
                grant_type: "refresh_token",
                refresh_token: token.refresh_token,
              }),
              method: "POST",
            }
          );

          const tokens = await response.json();
          if (!response.ok) throw tokens;

          console.log("token refreshed successfully, returning updated token");

          return {
            ...token,
            access_token: tokens.access_token,
            expires_at: Math.floor(
              Date.now() / 1000 + (tokens.expires_in || 3600)
            ),
            refresh_token: tokens.refresh_token || token.refresh_token,
          };
        } catch (error) {
          console.error("Error refreshing access token", error);
          return { ...token, error: "RefreshAccessTokenError" as const };
        }
      }
    },
    session({ session, token }) {
      if (session.user) {
        session.user.id = token.id as string;
        session.access_token = token.access_token as string;
      }

      return session;
    },
  },
});

现在,当我登录时,一小时后,如果我刷新页面,它会给我一个获取错误(我正在获取一些数据以显示在仪表板的主页中)。发生这种情况是因为访问令牌未正确刷新。如果我再次刷新错误页面,它将正常工作并正确显示数据。

我想要实现的是自动刷新令牌,并且不会出现错误,这样用户就可以在主页上停留多久,并随时刷新数据。

这就是我在页面中获取访问令牌并使用它来获取的方式:

export default async function Page() {
  const session = await auth();

  if (!session?.user) return null;

  console.log("Session:", session); // Log the session object
  console.log("Access Token:", session.access_token); // Log the access token

  const accessToken = session.access_token ?? "";

谢谢!

next.js oauth-2.0 jwt next-auth refresh-token
1个回答
0
投票

澄清一下: 获取新令牌是否存在问题,或者在获取数据时访问它是否存在问题?

你明白了吗

console.log("token refreshed successfully, returning updated token")

或者你得到

console.error("Error refreshing access token", error);

--

我刚刚检查了我的代码,因为我目前也在使用 Spotify api,我注意到的第一件事是你的计算方式

expires_at

我跳过 account.expires_at 的计算步骤,直接执行

expires_at: account.expires_at

要检查令牌是否仍然有效,我只需将其与当前时间进行比较:

async jwt({token, account}) {
  if(account) {
    return {
      ...token,
      accessToken: account.access_token
      refreshToken: account.refresh_token,
      expires_at: account.expires_at
    };
  }

  if (token.expires_at && Date.now() < token.expires_at) {
    console.log("USING_OLD_TOKEN";  
    return token;
  }

  const refreshedToken = await refreshAccessToken(token);
  return refreshedToken;
}

我只对引用的令牌进行计算,因为它不包括

expires_at
,而只包括
expires_in

async function refreshAccessToken(token) {
  //removed try catch for readability
  const res = await fetch(...)
  const refreshedToken = await res.json();

  return {
    ...token,
    accessToken: refreshedToken.access_token,
    expires_at: Date.now() + refreshedToken.expires_in * 1000,
    refreshToken: refreshedToken.refresh_token
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.