使用NextAuth.js在Next.js中的身份验证
#javascript #网络开发人员 #typescript #nextjs

如果您是前端开发人员,那么考虑身份验证可能会非常具有挑战性。在身份验证中,从将JWT存储在本地存储中到管理cookie,我们都会考虑这一切。 NextAuth.js当前是在您的下一个项目中处理身份验证的最受欢迎的库。

我们将使用什么?

在本指南中,我们将使用 nextAuth.js 凭证提供商,以便我们可以设置自定义字段以进行身份​​验证。这些字段将是用户名和密码。在下一个侧面,我们将使用API​​路由(页面路由器),因为当前大多数代码库都使用这些路由。我们还将使用我们的自定义登录页面,而不是依赖NextAuth.js的默认页面。

设置凭据提供商

  • 应用程序设置 - 希望您已经有了一个下一个应用

  • 首先安装NextAuth.js:

    npm install next-auth
    
  • 创建[... nextauth] .ts文件在目录/pages/api/auth/ [.....nextauth]并添加以下代码:

    import NextAuth from 'next-auth';
    import CredentialsProvider from 'next-auth/providers/credentials';
    import dbConnect from '../../../lib/dbConnect'; //import your dbConnect, if you have one.
    import User from '../../../models/userModel'; //ignore this
    import { Provider } from 'next-auth/providers';
    
    export const authOptions = {
      // Configure one or more authentication providers
      providers: [
        CredentialsProvider({
          id: 'credentials',
          type: 'credentials',
          name: 'Credentials',
          credentials: {
            username: { label: 'Username', type: 'text' },
            password: { label: 'Password', type: 'password' },
          },
          async authorize(credentials, req) {
            //Add logic here to look up the user from the credentials supplied
            await dbConnect(); // connect to your db, to check the user
            if (credentials?.password?.length && credentials?.username?.length) {
              //I am using a mongoose model in this example.
              const existingUser = await User.findOne({
                username: credentials.username,
                password: credentials.password,
              });
              if (existingUser) {
                return existingUser;
              } else return null;
            } else {
              return null;
            }
          },
        }),
      ] as Provider[],
      secret: process.env.NEXTAUTH_SECRET,
      session: {
        strategy: 'jwt',
        maxAge: 30 * 24 * 60 * 60,
      },
      jwt: {
        encryption: true,
      },
      pages: {
        signIn: '/login',
      },
      // callbacks in the next section
    };
    
    export default NextAuth(authOptions);
    

让我们按一件片段打破此代码片段:

  • 当我们使用凭据提供商时,我们首先将提供商添加到提供商列表中。我们在使用自定义登录页面时,我们将ID作为 访问此提供商的“凭据”。我们还将类型称为“凭据”,因为我们使用的是相同类型。

  • 在下一部分中,我们定义了一个凭证对象,该对象告诉NextAuth我们正在处理哪种字段。在这种情况下,这些字段为用户名和密码

  • 接下来,我们定义一个称为“授权”的异步函数,NextAuth在身份验证时使用该函数,当我们单击登录/登录按钮时,该函数被调用。在此功能中,我们编写逻辑以验证用户凭据,如果存在用户,我们返回用户,否则我们返回null或可以丢弃错误。

  • 接下来,我们定义一个秘密变量,这将是我们的JWT Secret(将其存储在项目的根部中)。

  • 在下一部分中,我们定义了会话对象,该对象将我们的策略声明为JWT,Maxage为30天(您可以使用自己喜欢的任何价值)。这定义了我们的用户会话将保持活动的持续时间,一旦登录。我们还将JWT加密设置为True,该加密加密我们的令牌。

  • 对于自定义登录页面,我们将页面路由定义为自定义登录页面中的页面。

回调(JWT和会话)

  • 通过在下面添加回调设置来扩展上述凭据提供商

    export const authOptions:any = {
      providers: [
        CredentialsProvider({
            // above mentioned code,
        callbacks: {
          jwt: async ({ token, user }) => {
            if (user) {
              token.username= user.username;
              token.id=user._id
            }
    
            return token;
          },
          session: ({ session, token }) => {
            if (token) {
              session.user.username = token.username;
              session.user.id=token.id
            }
            return session;
          },
        },
    }
    
  • JWT回调每当NextAuth创建JWT令牌时都会调用(登录)。在这种情况下,我们将令牌和用户作为参数。参数用户是授权函数返回的用户,令牌是我们可以在会话对象中使用用户详细信息的用户。

  • 每当我们尝试访问客户端或服务器端的会话时,都会调用

    会话回调。我们可以使用从JWT回调中收到的令牌扩展会话中的用户对象。

    每当我们尝试访问会话时,此回调设置对于获取用户名和用户ID是必要的。

登录页面设置

登录页面结构和样式取决于您的项目,因此我们将专注于核心功能。每当用户在输入凭据后单击登录时,应调用Next-aTH的signin函数。示例:

import { signIn } from 'next-auth/react';
// on button click this clickhandler should be invoked
const clickHandler: React.MouseEventHandler<HTMLButtonElement> = async (e) => {
  e.preventDefault();
  try {
    const res = await signIn('credentials', {
      username, // entered by the user
      password, // entered by the user
      redirect: false,
      callbackUrl: '/',
    });
    if (res?.status === 200) {
      addSuccess('Successfully logged in'); //custom function for toast notification, you can just use console.log()
    } else {
      addError('error signing in');
    }
    // Here, you can also redirect based on successfull authentication using useRouter
  } catch (error) {
    if (isAxiosError(error)) {
      if (error.response) {
        addError(error.response.data);
      }
    }
  }
};

我们将以下参数传递给nextAuth的signin()函数:

  • 第一个参数是您的提供者ID,在我们的情况下是“凭据”。

  • 第二个参数是一个对象,该对象包含我们从用户那里获得的凭据以及其他一些参数(例如重定向和callbackurl)。在我们的情况下,我们不需要重定向,因此我们将重定向设置为:false。

最后,让我们谈谈访问会话

我们有这些访问会话的方式:

  • 使用()钩:这是NextAuth提供的钩子,用于访问会话和客户端的身份验证状态:

    import { useSession } from 'next-auth/react';
    const { data: session, status } = useSession()
    
    //session contains our user object
    //status can be authentication | unauthenticated | loading
    
  • getsession():这是NextAuth调用API并获得会话的功能。当您自己管理加载状态时,可以使用此方法。但是,在大多数情况下,通常使用使用()。

  • getServersession():顾名思义,此功能用于将会话放在服务器端。此功能接受3个参数。示例:

    //API route example :
    export default async function handler(
      req: NextApiRequest,
      res: NextApiResponse
    ) {
      try {
        const session: Session | null = await getServerSession(
          req,
          res,
          authOptions //we defined the authOptions in our first snippet
        );
        await dbConnect();
        if (session) {
          //if session exists proceed to the backend logic
        } else {
          res.status(401).json({ message: 'Unauthorized' });
        }
      } catch (error) {
        console.log(error);
      }
    }
    

NextAuth提供的这些功能使我们的生活变得容易。

确保页面和组件

  • 需要包装:

    您可以使用此包装器来确保需要在客户端进行身份验证状态的页面和组件。

    import React from 'react';
    import { signIn, useSession } from 'next-auth/react';
    
    function RequireAuth({ children }: { children: React.JSX.Element | string }) {
      const {status}=useSession() 
      if(status==="unauthenticated"){
        signIn()
      }
      if(status==="loading"){
        return <Loader/>
      }
      if(status==="authenticated"){
        return <>{children}</>
      }
    }
    export default RequireAuth;
    
  • 中间件: NextAuth还为我们提供了中间件以保护页面:在与页面目录相同的级别上创建一个Middleware.ts文件并使用以下代码:

    export { default } from 'next-auth/middleware';
    export const config = { matcher: [...] }; // add routes to secure pages
    

其他建议:尽可能使用开发人员工具(应用程序部分),查看您的cookie并尝试通过更改/删除cookie来播放不同的方案。这将帮助您避免与身份验证有关的潜在错误。

Twitter上与我联系| Linkedin | Github

愉快的编码! ðð