开发者问题收集

next-auth 不断因凭证提供程序错误而重定向

2022-09-13
11528

我正在尝试使用 Credentials 提供程序在我的下一个 js 应用程序中实现 next-auth。但是,每次我登录失败时,它都会继续尝试访问 /api/auth/error 路由。我尝试通过停留在登录页面并在网址末尾添加一个参数(如 /login?status=Failed )来自定义处理错误。我做错了什么,它一直尝试进入 /api/auth/error 路由而不是遵循我的流程?

我的代码:

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

import { authenticate } from "api/login";
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";

export default NextAuth({
  session: {
    strategy: 'jwt'
  },
  providers: [
    CredentialsProvider({
      id: 'credentials',
      name: 'Credentials',
      credentials: {
        username: { type: 'text', label: 'Username'},
        password: { type: 'text', label: 'Password' }
      },
      async authorize(credentials, req) {
        const res = await authenticate(credentials); // custom function that returns an object with a jwt if auth is successful

        if(res.status !== 200) {
          return null;
        }

        return res.user;
      }
    })
  ],
  pages: {
    signIn: '/login'  
  },
  callbacks: {
    async signIn(user) {
      console.log('user: ' + user);
      
      if (!user) return '/login?status=fail';

      return '/'
    }
  }
});

登录页面(我知道这不是最佳实践) /pages/login.js

import { useState } from 'react';
import { signIn } from 'next-auth/react';

import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';

export default function Login() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  
  const onSubmit = async () => {
    signIn('credentials', {
      username: username,
      password: password,
      redirect: false,
    })
    .then(res => {
      console.log(res);
    })
    .catch(err => {
      console.error(err);
    });
  };

  return (
    <Container>
      <Row>
        <Col lg={6} className="offset-lg-3">
          <Form>
            <Row>
              <Col>
                <Form.Group controlId="loginUsername">
                  <Form.Label>Username</Form.Label>
                  <Form.Control 
                    type="text"
                    onChange={(e) => setUsername(e.target.value)}
                    value={username} />
                </Form.Group>
              </Col>
            </Row>
            <Row>
              <Col>
                <Form.Group controlId="loginPassword">
                  <Form.Label>Password</Form.Label>
                  <Form.Control 
                    type="text"
                    onChange={(e) => setPassword(e.target.value)}
                    value={password} />
                </Form.Group>
              </Col>
            </Row>
            <Row className="pt-3">
              <Col lg={4}>
                <Button onClick={onSubmit}>Login</Button>
              </Col>
            </Row>
            <Row>
              <Col>
              </Col>
            </Row>
          </Form>
        </Col>
      </Row>
    </Container>
  )
}

_app.js

import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { SessionProvider } from 'next-auth/react';


function MyApp({
  Component,
  pageProps: { session, ...pageProps } }) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps} />
    </SessionProvider>
  );
}

export default MyApp;

此外,这是我设置的受保护页面

import React from 'react';
import { getSession } from 'next-auth/react';

import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';

export default function Home() {
  return (
    <Container fluid>
      <Row>
        <Col>
          <h1>Hello World</h1>
        </Col>
      </Row>
    </Container>
  );
}

export async function getServerSideProps(context) {
  const session = await getSession({ req: context.req });

  if (!session) {
    return {
      redirect: {
        destination: '/login',
        permanent: false
      }
    }
  }

  return {
    props: { session }
  }
}
3个回答

您还需要指定错误页面,因为您想在登录页面上显示错误,它应该是这样的:

pages: {
    signIn: '/login',
    error: '/login'
  },

您可以尝试我从此 博客文章 中获得的这种方法来自定义登录页面以处理不同的登录错误:

// pages/login.js
import { useState } from 'react';
import { signIn } from 'next-auth/react';
import { useRouter } from 'next/router';

import Container from 'react-bootstrap/Container';
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';

export default function Login() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const { error } = useRouter().query;

  const onSubmit = async () => {
    signIn('credentials', {
      username: username,
      password: password,
      redirect: false,
    })
      .then((res) => {
        console.log(res);
      })
      .catch((err) => {
        console.error(err);
      });
  };

  return (
    <Container>
      <Row>
        <Col lg={6} className="offset-lg-3">
          {/* Error message */}
          <Row>
            <Col>{error && <SignInError error={error} />}</Col>
          </Row>
          <Form>
            <Row>
              <Col>
                <Form.Group controlId="loginUsername">
                  <Form.Label>Username</Form.Label>
                  <Form.Control
                    type="text"
                    onChange={(e) => setUsername(e.target.value)}
                    value={username}
                  />
                </Form.Group>
              </Col>
            </Row>
            <Row>
              <Col>
                <Form.Group controlId="loginPassword">
                  <Form.Label>Password</Form.Label>
                  <Form.Control
                    type="text"
                    onChange={(e) => setPassword(e.target.value)}
                    value={password}
                  />
                </Form.Group>
              </Col>
            </Row>
            <Row className="pt-3">
              <Col lg={4}>
                <Button onClick={onSubmit}>Login</Button>
              </Col>
            </Row>
            <Row>
              <Col></Col>
            </Row>
          </Form>
        </Col>
      </Row>
    </Container>
  );
}

const errors = {
  Signin: 'Try signing with a different account.',
  OAuthSignin: 'Try signing with a different account.',
  OAuthCallback: 'Try signing with a different account.',
  OAuthCreateAccount: 'Try signing with a different account.',
  EmailCreateAccount: 'Try signing with a different account.',
  Callback: 'Try signing with a different account.',
  OAuthAccountNotLinked:
    'To confirm your identity, sign in with the same account you used originally.',
  EmailSignin: 'Check your email address.',
  CredentialsSignin:
    'Sign in failed. Check the details you provided are correct.',
  default: 'Unable to sign in.',
};

const SignInError = ({ error }) => {
  const errorMessage = error && (errors[error] ?? errors.default);
  return <div>{errorMessage}</div>;
};

您可以根据需要自定义错误的显示方式

mocherfaoui
2022-09-13

对我来说,问题在于我在开发和生产环境中使用了相同的 MongoDB 实例。意识到这一点后,我删除了 MongoDB 中的用户帐户和会话信息,一切正常。

kiefx
2023-07-03

就我而言,问题出在 middleware.js 文件上,我输入该文件是为了保护 /signin 页面,而那是我的 login 页面,因此它会进行无限回调,我们想要登录,但登录页面受到保护,无限登录回调下一个身份验证

infinite signin call next-auth

muhammad wahyu ramadhan
2024-02-06