【问题标题】:Problem whit NextAuth.js token length and CognitoNext Auth.js 令牌长度和 Cognito 的问题
【发布时间】:2021-09-10 12:24:50
【问题描述】:

我正在尝试使用 NextAuth.js 和 Cognito 在我的 Next.js 应用程序中实现身份验证。 这是我的问题:当调用 jwt 回调时,我想在会话 3 中存储令牌和其他内容,但令牌最大长度为 4096 字节。我该如何实施? 提前致谢!

  providers: [
    Providers.Cognito({
      clientId: process.env.COGNITO_CLIENT_ID,
      domain: process.env.COGNITO_DOMAIN,
      idToken: true,
      scope: 'openid profile email aws.cognito.signin.user.admin',
    }),
  ],
  callbacks: {
    async jwt(token, user, account, profile) {
      // Initial sign in
      if (account && user) {
        // Max 4096 bytes
        return {
          accessToken: account.accessToken,
          idToken: account.idToken, // Too long
          accessTokenExpires: Date.now() + account.expires_in! * 1000,
          refreshToken: account.refresh_token,
          user, // Too long
          profile, // Too long
        };
      }

      // Return previous token if the access token has not expired yet
      if (Date.now() < (token.accessTokenExpires as number)) {
        return token;
      }

      // Access token has expired, try to update it
      return refreshAccessToken(token);
    },
    async session(session, token) {
      const sessionToken = session;
      sessionToken.accessToken = token.accessToken;
      sessionToken.idToken = token.idToken;
      sessionToken.profile = token.profile;
      return sessionToken;
    },
  },
});

【问题讨论】:

    标签: typescript jwt next.js amazon-cognito next-auth


    【解决方案1】:

    我不知道这是否会有所帮助,但也许会有所帮助。我正在构建的应用程序上遇到同样的问题。我在 NextAuth Github 站点上发现了一个关于此的问题讨论,也许这会对您有所帮助:https://github.com/nextauthjs/next-auth/issues/2213 我真的不知道这对我有什么帮助,因为我仍然需要将 idToken 添加到标题中需要授权的请求。我是 Next 的新手,所以也许我只是很密集,所以我也探索了使用数据库选项,只有没有 JWT,但是 idToken 没有保存在数据库中,所以你没有可用于身份验证的标头调用。

    最后,我认为可能有用的方法(至少对我而言)是同时使用数据库和 JWT,我将 idToken 添加到我的会话数据中,这样我就可以立即开始查询,但数据库中有 acccessToken 和 refreshToken .

    我在 /lib 中添加了一个 db.js 文件(安装 serverless-mysql 之后)

    import mysql from 'serverless-mysql';
    
    const db = mysql({
      config: {
        host: process.env.DB_HOST,
        port: process.env.DB_PORT,
        database: process.env.DB_NAME,
        user: process.env.DB_USER,
        password: process.env.DB_PASS
      }
    });
    export default async function executeQuery({query, values}) {
      try {
        const results = await db.query(query, values);
        await db.end();
        return results;
      } catch (error) {
        return {error};
      }
    }

    然后,我在 /pages/api 中创建了一个名为 get-tokens.js 的文件:

    import {getSession} from "next-auth/client";
    import executeQuery from "../../lib/db";
    
    
    const getTokenData = async (session) => {
      try {
        const result = await executeQuery({
          query: 'SELECT users.id, users.email, accounts.refresh_token, accounts.access_token FROM users LEFT JOIN accounts ON accounts.user_id = users.id WHERE users.email = ?',
          values: session.userData.email,
        });
        return result;
      } catch (error) {
        return error;
      }
    }
    
    export default async (req, res) => {
      try {
        const session = await getSession({req});
        let tokenData = await getTokenData(session);
    
        res.setHeader('Content-Type', 'application/json');
        res.end(JSON.stringify(tokenData));
    
      } catch (error) {
        res.json(error);
        res.status(405).end;
      }
    
    }

    这将返回一个包含电子邮件、refresh_token 和 access_token 的 JSON 对象。您可以轻松地将其调整为仅通过 SQL 查询返回您想要的内容,或根据需要/想要添加其他元素。

    这是我在 [...nextauth].js 文件中的 jwt 和会话回调:

    async jwt(token, user, account, profile, isNewUser) {
    
      if (account?.idToken) {
        token.idToken = account.idToken;
      }
    
      return token
    },
    async session(session, token) {
    
      if (token?.idToken) {
        session.idToken = token.idToken;
      }
    
      return session;
    },

    【讨论】:

    • 感谢您的帮助!
    • @SimoneUrbani 没问题。感谢您在下面发布您的解决方案。我没有意识到您可以在 API NextAuth 文件中设置 cookie。您的解决方案更加优雅!您介意发布您的解决方案以刷新访问令牌吗?我有一个可行的解决方案,但它有点像弗兰肯斯坦的怪物。我在互联网上搜索了使用 Cognito 和 idToken 与 NextAuth 的综合解决方案,但找不到任何东西。您可以考虑创建一篇关于您如何解决它的 Medium 帖子。
    • 我只是从谷歌复制/过去,我还没有测试它。当我确定我的解决方案有效时,我会发布它!
    【解决方案2】:

    我通过导出一个需要两个参数的函数来解决:req(请求)和res(响应)。这些参数允许我设置和获取我想要的所有 cookie。 这是我的代码:

    const getOptions = (req, res) => ({
      providers: [
        Providers.Cognito({
          clientId: process.env.COGNITO_CLIENT_ID,
          domain: process.env.COGNITO_DOMAIN,
          idToken: true,
          scope: 'openid profile email aws.cognito.signin.user.admin',
        }),
      ],
      callbacks: {
        async jwt(token, user, account) {
          // Initial sign in
          if (account && user) {
            const cookies = new Cookies(req, res);
            cookies.set('userId', user.id);
            cookies.set('idToken', account.idToken);
            // Max 4096 bytes
            return {
              accessToken: account.accessToken,
              accessTokenExpires: Date.now() + account.expires_in! * 1000,
              refreshToken: account.refresh_token,
            };
          }
    
          // Return previous token if the access token has not expired yet
          if (Date.now() < (token.accessTokenExpires as number)) {
            return token;
          }
    
          // Access token has expired, try to update it
          return refreshAccessToken(token);
        },
        async session(session, token) {
          const cookies = new Cookies(req, res);
          const userId = cookies.get('userId');
          const idToken = cookies.get('idToken');
    
          const sessionToken = session;
          sessionToken.accessToken = token.accessToken;
          sessionToken.idToken = idToken;
          sessionToken.userId = userId;
          return sessionToken;
        },
      },
    });
    
    export default (req, res) => NextAuth(req, res, getOptions(req, res));
    
    

    【讨论】:

      【解决方案3】:

      @SimoneUrbani,我能够使用 NextAuth Google 示例让 Cognito 刷新令牌正常工作。这是我这样做的代码,以防对其他人有帮助:

      async function refreshAccessToken(token) {
        try {
          const url =
            "https://" + process.env.COGNITO_DOMAIN + "/oauth2/token?" +
            new URLSearchParams({
              grant_type: "refresh_token",
              client_id: process.env.COGNITO_CLIENT_ID,
              client_secret: process.env.COGNITO_CLIENT_SECRET,
              refresh_token: token.refreshToken,
            });
      
      
          // Base 64 encode authentication string
          const headerString = process.env.COGNITO_CLIENT_ID + ':' + process.env.COGNITO_CLIENT_SECRET;
          const buff = Buffer.from(headerString, 'utf-8');
          const authHeader = buff.toString('base64');
      
          const refreshedTokensResponse = await fetch(url, {
            headers: {
              "Content-Type": "application/x-www-form-urlencoded",
              "Authorization": "Basic " + authHeader
            },
            method: "POST",
          })
      
          const refreshedTokens = await refreshedTokensResponse.json();
      
          if (!refreshedTokensResponse.ok) {
            throw refreshedTokens;
          }
      
          const cookies = new Cookies(req, res);
      
          cookies.set('accessToken', refreshedTokens.access_token, {sameSite: true});
          cookies.set('idToken', refreshedTokens.id_token, {sameSite: true});
      
          return {
            ...token,
            accessToken: refreshedTokens.access_token,
            accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000,
            refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, // Fall back to old refresh token
          };
      
        } catch (error) {
      
          return {
            ...token,
            error: "RefreshAccessTokenError",
          };
        }
      }
      

      【讨论】:

        猜你喜欢
        • 2022-07-07
        • 2021-10-21
        • 2020-09-28
        • 2020-10-27
        • 1970-01-01
        • 2016-08-18
        • 2018-03-23
        • 2021-07-10
        • 2021-12-21
        相关资源
        最近更新 更多