How to Create Custom Login Pages with NextAuth
I’ve been considering adding authentication into my Next.js application. While OAuth integration is fairly simple, I noticed a massive community backing for next-auth, which supports many built-in OAuth providers and even passwordless authentication.
Getting it up and running with protected pages only took a few hours.
The only thing that took a little more effort was creating a custom login page (I just wanted nice, colorful buttons for each provider).
Disclaimer: I’m going to assume you’ve gone through the documentation for setting up basic OAuth using
next-auth
’s built-in login page. Also, the code snippets in this article require NextAuth.js v4. Check out how to upgrade to version 4.
Solution from Documentation
The next-auth configuration documentation states to use the pages
option in pages/api/auth/[...nextauth].js
to define a custom login page.
// pages/api/auth/[...nextauth].js
pages: {
signIn: '/signin',
...
}
We can then have our custom, branded login page that pulls the supported providers from [...nextauth].js
.
// pages/signin.jsx
import { getProviders, signIn } from "next-auth/react";
export default function SignIn({ providers }) {
return (
<>
{Object.values(providers).map((provider) => (
<div key={provider.name}>
<button onClick={() => signIn(provider.id)}>
Sign in with {provider.name}
</button>
</div>
))}
</>
);
}
export async function getServerSideProps(context) {
return { props: { providers: await getProviders() } };
}
Finally, next-auth
will redirect to our custom page when we make a call to /api/auth/signin
.
// path/to/someComponent.jsx
<Link legacyBehavior href="/api/auth/signin">Sign in</Link>
This works as expected, but the URL ends up looking something like this:
http://localhost:3000/signin?callbackUrl=http://localhost:3000
The callbackUrl
is explicitly defined in the query string, which might not be desired in many use cases.
Alternative Solution
I found that we don’t need to route through next-auth
for our custom /signin
page.
We can fully remove our default /signin
page from [...nextauth].js
.
// pages/api/auth/[...nextauth].js
pages: {
// signIn: '/signin',
...
}
And then redirect directly to this page from another component.
// path/to/someComponent.jsx
<Link legacyBehavior href="/signin">Sign in</Link>
The signIn()
and even signOut()
functions will still work as expected.
I would recommended explicitly setting the callbackUrl
for these function calls.