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
Now we need to set up our pages/api/[..nextauth].js
to something like this
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
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).
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
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
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
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
Hopefully this article helped you in your custom authentication journey!
Shoutout to @iainCollins, @balazsorban and all the contributors to NextAuth.js for the awesome work!
Top comments (35)
you can also set
redirect = false
and catch response fromsignIn
function :signIn
return a Promise which has following structure :you can check more on next-auth.js.org/getting-started/c...
Cool! Happy to see it has been added as a feature. Thanks for the update :)
Thank you so much! I hated that the function reloaded the page every time I submit the form
How does this work?
I have followed your example and am not getting any response in login page
You are not getting a response because the user isn't being set properly. The following code is able to handle Credentials (user & pass) and Providers auth, while setting jwt and session correctly.
Hi guys, if you need the token in an axios request, do it like this!
Hi guys, the article has gotten me as far as a successful authentication, for which am grateful. However, I am unable to get the session data using
getSession()
oruseSession()
client-side for which they both return null. What are the possible causes of the bug/issue?Have you added the Provider in your _app.js ?
for me it returns undefined.
Did you solve your problem? If yes, can you explain me how?
I would be interested in the HTML parts as well! do you have a zip of the shell project? I'm new to building React and Next applications from the ground up and find that most tutorials omit important details, making it very difficult to figure out what they are
Sure thing. I'll add the HTML part soon
Hello Twisha, I came here after one full days googling. I wanted to create a social media sign up button for a nextjs app with with what you call a custom laravel backend. The usage of ' pages/api/[..nextauth].js ' made me asking, where are they even using the custom backend .
So Just to confirm, even you have a custom backend you have to use pages/api feature in the app and define your auth providers there and call the custom backend api's from there right ?
Hey Bharath,
If you are using a third party provider for authentication i.e Google, Facebook, Apple, Twitter etc. you don't need a custom backend as everything will be handled by nextauth.js.
If instead you want to use email-passsword login where the credentials are sent to your custom backend (in this case I'm assuming your Laravel app) then you can use the Credentials provider and configure it as it is explained in the article.
For third party provider logins (google, facebook, twitter etc.) nextauth has good documentation. For example this one is for Google -> next-auth.js.org/providers/google
Overall all the configurations for next-auth is in the pages/api/[..nextauth].js file.
Hope it answers your question :)
What I actually wanted was to save the username, email ... fields after the google / signup to my own database. I think I figured it out. I am using the callbacks options inside the [...nextauth].js to call my api to pass username and email to my backend to store in my db.
Ah okay I see what you mean, thanks for sharing your solution :)
Hi there! Thank you for this very helpful article. I understand the login process, but how would you go about creating a custom registration page? Would you have to use the same 'authorize' callback that would have to hit different API endpoints (/login, /signup)? I would love to hear your thoughts on this :)
Please find this link.
dev.to/ekrresa/authenticating-with...
Thanks for sharing this.
Glad it helped! :)
Hi Twisha! Great article, thanks for the tips!
I'm wondering if there is a possibility to implement a OTP Login with NextAuth. I can't find anything on the internet 🤔 Do you have some advices?
Thank you! 🎩
Thanks Vlăduț Ilie !
Unfortunately I have not explored that option with NextAuth yet :(
Hope you find a useful solution soon :)
Muy útil ... mucha gracias!
Glad it's helpful :)
Hi Twisha, nice article. However the file to configure the providers should be pages/api/auth/[...nextauth].js NOT pages/api/auth/[...next-auth].js
That's correct! Thanks for catching that. Will update the article
Should we use session table in database for post our session?
this does not handle CSRF token. Could you please share how you would go about handling cross site request forgery?
THANK YOU SO MUCH !
Glad it helped! :)
You just absolutely saved me! First article I found which is dealing with own backend. 1000x thanks @twisha !
I'm so glad! :)
In the JWT callback you access the token inside user.data, isn't that not secure?