Using Credentials provider with a custom backend in NextAuth.js!
Tags: nextjs, authentication, nextauth, credentailslogin
Published: 2021-01-22 12:51:33

Hello! NextAuth is a great choice when it comes to adding authentication to your next.js app. And it's easy to see why, with it's vast coverage of providers ranging from Google, Github, Facebook, Apple, Slack, Twitter and more (!) it can help you set up you authentication within a few minutes! 

However sometimes you might need to use your own custom backend with an email/password login for various reasons. That's where you would want to use the credentials provider linked with your API server. I was in a similar situation and couldn't find a detailed description with examples so it took me a while piece together the ins and out (especially handling errors from the custom backend and handling them on your own custom login page). Hope this helps you out if you're in the same boat!

First, we need to setup next-auth for the app. It's a straightforward process and the instructions can be found [here](https://next-auth.js.org/getting-started/example)

Now we need to set up our `pages/api/[..nextauth].js` to something like this

```javascript
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import axios from 'axios'

const providers = [
  Providers.Credentials({
    name: 'Credentials',
    authorize: async (credentials) => {
      const user = await axios.post('https://myapi.com/login',
        {
          user: {
            password: credentials.password,
            email: credentials.email
          }
        },
        {
          headers: {
            accept: '*/*',
            'Content-Type': 'application/json'
          }
        })

      if (user) {
        return user
      } else {
        return null
      }
    }
  })
]

const callbacks = {
  // Getting the JWT token from API response
  async jwt(token, user) {
    if (user) {
      token.accessToken = user.token
    }

    return token
  },

  async session(session, token) {
    session.accessToken = token.accessToken
    return session
  }
}

const options = {
  providers,
  callbacks
}

export default (req, res) => NextAuth(req, res, options)

```

And your custom login page which in my case is `pages/login.js` will have a form submit handler which will use nextauth's signIn function to log the user in

```javascript
import { signIn } from 'next-auth/client'

const handleLogin = () => {
    signIn('credentials',
      {
        email,
        password,
        // The page where you want to redirect to after a 
        // successful login
        callbackUrl: `${window.location.origin}/account_page` 
      }
    )
  }

```

At this point if you have entered the correct credentials and your API endpoint is working as you'd hope it to work, you should be able to log in just fine.

But if there is any issue such as the server being down, invalid credentials etc. you will be redirected to the default error page (like the image below).

![NextAuth Error](https://drive.google.com/uc?export=view&id=16hJXOzSMHfhZjvCN2Jp2oGvNs3OTlG3-)

Instead what I want is to redirect it to my custom login page and explain the situation in a bit more detail to the user. So here's what we do, let's tweak the `pages/api/[..nextauth].js` a bit

```javascript
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import axios from 'axios'

const providers = [
  Providers.Credentials({
    name: 'Credentials',
    authorize: async (credentials) => {
      try {
        const user = await axios.post('https://myapi.com/login',
        {
          user: {
            password: credentials.password,
            email: credentials.email
          }
        },
        {
          headers: {
            accept: '*/*',
            'Content-Type': 'application/json'
          }
        })

        if (user) {
          return {status: 'success', data: user}
        } 
      } catch (e) {
        const errorMessage = e.response.data.message
        // Redirecting to the login page with error message          in the URL
        throw new Error(errorMessage + '&email=' + credentials.email)
      }
      
    }
  })
]

const callbacks = {
  async jwt(token, user) {
    if (user) {
      token.accessToken = user.data.token
    }

    return token
  },

  async session(session, token) {
    session.accessToken = token.accessToken
    return session
  }
}

const options = {
  providers,
  callbacks,
  pages: {
    error: '/login' // Changing the error redirect page to our custom login page
  }
}

export default (req, res) => NextAuth(req, res, options)

```

We will also update our login page `pages/login.js` to look for URL changes and any error messages

```javascript
import { useRouter } from 'next/router'

export default function Login () {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [loginError, setLoginError] = useState('')
  const router = useRouter()

  useEffect(() => {
    // Getting the error details from URL
    if (router.query.error) {
      setLoginError(router.query.error) // Shown below the input field in my example
      setEmail(router.query.email) // To prefill the email after redirect
    }
  }, [router])
}

```

And with this setup we can display the error message in whichever format we prefer on our custom login page. For example, I'm displaying the error message I receive from server as below

![User friendly error message](https://drive.google.com//uc?export=view&id=1_Pruvrln1B5-596xswopm6-KZ09pTKWP)

~~P.S I haven't added the HTML part in the code snippets as it is self-explanatory. But if you want it, please let me know in the comments and I will add it :)~~

You can find the code [here](https://github.com/twilink/nextauth-example-with-credentials)

Hopefully this article helped you in your custom authentication journey!

Shoutout to [@iainCollins](https://dev.to/iaincollins), [@balazsorban](https://dev.to/balazsorban) and all the contributors to [NextAuth.js](https://next-auth.js.org/) for the awesome work!