在 NextJS 中使用 next-auth 添加基于角色的身份验证

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

我正在使用 next-auth v4 使用 CognitoProvider 实现基于角色的身份验证,我修改了它以添加角色,但角色属性未在最终会话 json 中传递

import NextAuth from "next-auth/next";

function CognitoProvider(options) {
  return {
    id: "cognito",
    name: "Cognito",
    type: "oauth",
    wellKnown: `${options.issuer}/.well-known/openid-configuration`,
    idToken: true,
    profile(profile) {
      console.log(profile);
      return {
        id: profile.sub,
        name: profile.name,
        email: profile.email,
        image: profile.picture,
        role: profile["cognito:groups"],
      };
    },
    options,
  };
}

export default NextAuth({
  providers: [
    CognitoProvider({
      clientId: process.env.COGNITO_CLIENT_ID,
      clientSecret: process.env.COGNITO_CLIENT_SECRET,
      issuer: process.env.COGNITO_DOAMIN,
    }),
  ],
  callbacks: {
    session: (props) => {
      console.log(props);
      return props.session;
    },
  },
});

以下是配置文件对象的控制台日志

enter image description here

role: profile["cognito:groups"]

实物

enter image description here

我已将一个用户添加到管理组,并希望他访问我的 NextJS 应用程序中的特定路由。

如有任何帮助,我们将不胜感激。

amazon-web-services next.js oauth-2.0 amazon-cognito next-auth
2个回答
2
投票

您需要配置

jwt
session
回调以在会话中包含更多数据。

来自 Next-Auth 文档:

如果您想让通过

jwt()
回调添加到令牌 [...] 的某些内容可用,您必须在此处显式转发它 [
session()
回调],以使其可供客户端使用。

添加用户角色:

export default NextAuth({
  // ...
  callbacks: {
    jwt({ token, account, profile }) {
        if (account) {
            // modify token
            token.role = profile.role;
        }

        return token;
    },

    session({ session, token }) {
      // Send properties to the client

      if (session.user) {
        // modify session
        session.user.roles = token.role;
      }

      return session;
    },
  },
});

然后在您的路线中,您将从 session

session.user.role

获取用户的角色

0
投票

当您不使用任何适配器时,请参阅@koolskateguy89 的答案以获取基于 cookie 的用户信息。

但是为了在数据库上持久保存用户信息和角色,我们使用适配器,在这种情况下我们不需要 jwt 回调。所以这里是该用例的简单实现。

我使用的是Firestore适配器,您可以根据您的适配器和数据库替换代码。

api/auth/[...nextauth]/route.js

...
import { FirestoreAdapter } from "@auth/firebase-adapter"
import { db } from '@/lib/firebase/config'

const ROLES = {
  USER: 'user',
  MANAGER: 'manager',
  ADMIN: 'admin',
  OWNER: 'owner'
}

export const authOptions = {
  adapter: FirestoreAdapter(db),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    }),
    GithubProvider({
      clientId: process.env.GITHUB_ID,
      clientSecret: process.env.GITHUB_SECRET,
    }),
    // add more providers
  ],
  callbacks: {
    async session({ session, user }) {
      // Safely add role to the session
      try {
        session.user.role = user.role ?? ROLES.USER; 
        return session;
      } catch (error) {
        console.error("Error in session callback:", error.message)
        session.user.role = ROLES.USER; // Ensure we always return a valid session
        return session;
      }
    }
  },
  events: {
    async createUser({ user }) {
      try {
        if (user?.id) {
          // store role on database when user sign up
          await db.collection('users').doc(user.id).update({ role: ROLES.USER })
        }
      } catch (error) {
        console.error("Error in createUser event:", error)
      }
    }
  },
  debug: process.env.NODE_ENV === 'development',
}

const handler = NextAuth(authOptions)

export { handler as GET, handler as POST }

关于代码:

  • 假设您已经配置了所需的适配器,您可以使用它而不是 firestore。我已使用 firestore 实例作为 db (db = getFirestore())。
  • 我将用户角色存储在 firestore 集合中。如果您使用其他数据库,请用您自己的逻辑替换代码行。
  • next-auth 本身创建用户、帐户、会话等集合,这就是为什么我使用“用户”集合来存储特定用户的角色

版本:

    "firebase-admin": "^12.7.0",
    "next": "^14.2.5",
    "next-auth": "^4.24.10"
    "@auth/firebase-adapter": "^2.7.3"
© www.soinside.com 2019 - 2024. All rights reserved.