0

我正在尝试为 Hasura 身份验证和授权设置 NextAuth。由于 Hasura 需要自定义 jwt 声明,我无法使用 OAuth 提供者提供的默认访问令牌。所以我在 [...nextauth].js 中使用编码块来编码自定义 jwt 令牌,一切正常。但我不知道如何为我的自定义令牌实现刷新令牌。下面是我的“pages/api/auth/[...nextauth].js”

import * as jwt from "jsonwebtoken";
import NextAuth from "next-auth";
import Providers from "next-auth/providers";

export default NextAuth({
  providers: [
    Providers.Google({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      authorizationUrl:
        "https://accounts.google.com/o/oauth2/v2/auth?prompt=consent&access_type=offline&response_type=code",
    }),
  ],
  
  secret: process.env.SECRET,

  session: {
    jwt: true,
  },

  jwt: {
    secret: process.env.SECRET,
    encode: async ({ secret, token, maxAge }) => {
      const jwtClaims = {
        sub: token.id,
        name: token.name,
        email: token.email,
        picture: token.picture,
        iat: Date.now() / 1000,
        exp: Math.floor(Date.now() / 1000) + 60,
        "https://hasura.io/jwt/claims": {
          "x-hasura-allowed-roles": ["user"],
          "x-hasura-default-role": "user",
          "x-hasura-role": "user",
          "x-hasura-user-id": token.id,
        },
      };
      const encodedToken = jwt.sign(jwtClaims, secret, { algorithm: "HS256" });
      return encodedToken;
    },
    decode: async ({ secret, token, maxAge }) => {
      const decodedToken = jwt.verify(token, secret, { algorithms: ["HS256"] });
      return decodedToken;
    },
  },

  pages: {
    // signIn: '/auth/signin',  // Displays signin buttons
    // signOut: '/auth/signout', // Displays form with sign out button
    // error: '/auth/error', // Error code passed in query string as ?error=
    // verifyRequest: '/auth/verify-request', // Used for check email page
    // newUser: null // If set, new users will be directed here on first sign in
  },

  // Callbacks are asynchronous functions you can use to control what happens
  // when an action is performed.
  // https://next-auth.js.org/configuration/callbacks
  callbacks: {
    // async signIn(user, account, profile) { return true },
    // async redirect(url, baseUrl) { return baseUrl },
    async session(session, token) {
      const encodedToken = jwt.sign(token, process.env.SECRET, {
        algorithm: "HS256",
      });
      session.token = encodedToken;
      session.id = token.id;
      return Promise.resolve(session);
    },
    async jwt(token, user, account, profile, isNewUser) {
      const isUserSignedIn = user ? true : false;
      // make a http call to our graphql api
      // store this in postgres

      if (isUserSignedIn) {
        token.id = profile.id.toString();
      }
      return Promise.resolve(token);
    },
  },

  // Events are useful for logging
  // https://next-auth.js.org/configuration/events
  events: {},

  // Enable debug messages in the console if you are having problems
  debug: true,
});

有人可以告诉我在使用自定义 jwt 令牌时如何使用 next-auth 处理刷新令牌吗?

4

1 回答 1

0

我目前正在使用我在问题页面上的帖子中找到next-auth的方法。

本质上,我们正在使用clientMaxAge您可以传递给提供程序的选项的组合来重新获取会话,从而重新运行 jwt 回调。我不确定我keepAlive是否正确使用了该属性,但目前这似乎可以正确轮询,尽管您可能需要对此进行试验。

在您的 JWT 回调中,您可以使用您的逻辑来检查您现有的到期时间,并从您的服务器获取新令牌以分配给会话。

//_app.tsx 
const sessionOptions = {
    clientMaxAge: 60 * 30, // Re-fetch session if cache is older than 30 minutes 
    keepAlive: 60 * 30, // Send keepAlive message every hour
};
<Provider options={sessionOptions} session={pageProps.session}>
  ..
</Provider>

// [...nextauth].ts

const callbacks: CallbacksOptions = {
    async jwt(token: any, user: any) {
        if (user) {
            token.accessToken = user.token;
            token.expires = Date.now() + user.config.USER_SESSION_LENGTH_IN_SECONDS * 1000;
        }

        // Don't access user as it's only available once, access token.accessToken instead
        if (token?.accessToken) {
            const tokenExpiry = token.expires;
            const almostNow = Date.now() + 60 * 1000;

            if (tokenExpiry !== undefined && tokenExpiry < almostNow) {
                // Token almost expired, refresh
                try {
                    const newToken = await api.renewToken(token.accessToken); // calling external endpoint to get a new token
                    // re-assign to the token obj that will be passed into the session callback
                    token.accessToken = newToken.token;
                    token.expires = Date.now() + user.config.USER_SESSION_LENGTH_IN_SECONDS * 1000;
                } catch (error) {
                    console.error(error, 'Error refreshing access token');
                }
            }
        }

        return token;
    },

    async session(session: any, user: any) {
        session.accessToken = user.accessToken;
        session.expires = user.expires;
        return session;
    }
}
于 2021-05-04T23:54:34.967 回答