Magic Link with Auth0 and NextJS
In this blog we implement Auth0 with nextjs and use magin link how login method. We use NextJS 14 with app router and nextjs-auth SDK
☝🏾 Magic Links are not compatible with the New Universal Login. Magic link uses the Classic Universal Login, the problem is that the initial request and response works if done in the same browser. So this is a problem for iOS users.
Another clarification:
💡 You are using the Auth0 Email Provider which is only intended for development/trial use. Any customizations made to this email template will NOT take effect until a Custom Email Provider is configured. All emails sent will use the default Auth0 templates, even if customized. Please enable a Custom Email Provider for production use of custom templates.
With this in mind, let's get started.
Install and setting nextjs-auth0 and Auth0
First, we have to configure the Auth0 application as a Regular Web Application
, then we configure the callback URLs so that Auth0 redirects our users when they try to login or logout. Go to settings on application in auth0 and configurate http://localhost:3000/api/auth/callback
in Allowed Callbacks URLs
and http://localhost:3000/
in Allowed Logout URLs
.
The we need instal npm install @auth0/nextjs-auth0
and setup envs in .env.local
AUTH0_SECRET='use [openssl rand -hex 32] to generate a 32 bytes value'
AUTH0_BASE_URL='[http://localhost:3000](http://localhost:3000/)'
AUTH0_ISSUER_BASE_URL='https://{yourDomain}'
AUTH0_CLIENT_ID='{yourClientId}'
AUTH0_CLIENT_SECRET='{yourClientSecret}'
Then we will have to configure Auth0 to use magic link, you can follow this documentation.
Settings in NextJS
Create dynamic API route:
// app/api/auth/[auth0]/route.js
import { handleAuth } from '@auth0/nextjs-auth0';
export const GET = handleAuth();
And then configurate UserProvider
// app/layout.jsx
import { UserProvider } from '@auth0/nextjs-auth0/client';
export default function RootLayout({ children }) {
return (
<html lang="en">
<UserProvider>
<body>{children}</body>
</UserProvider>
</html>
);
}
Create a SessionButton
// components/session-button.tsx
import {getSession} from "@auth0/nextjs-auth0";
import {Button} from "./ui/button";
export default async function SessionButton() {
const user = await getSession();
return user ? (
<Button asChild variant={"destructive"}>
<a href="/api/auth/logout">Logout</a>
</Button>
) : (
<Button asChild>
<a href="/api/auth/login">Login</a>
</Button>
);
}
☝ We use the <a> tag because the <Link> component of NextJS is for client-side navigation and we are calling an API route.
And a page for view user Info
// app/me/page.tsx
import {getSession} from "@auth0/nextjs-auth0";
import Image from "next/image";
import {Card, CardContent, CardFooter, CardHeader, CardTitle} from "@/components/ui/card";
import SessionButton from "@/components/session-button";
export default async function Page() {
const dataSession = await getSession();
const user = dataSession?.user;
return (
<section className="flex gap-5 min-h-screen flex-col items-center p-24">
{user ? (
<Card className="max-w-[300px] grid justify-center items-center">
<CardHeader>
<CardTitle>My User</CardTitle>
<Image
alt={user.name}
className="rounded-full"
height={80}
src={user.picture}
width={80}
/>
</CardHeader>
<CardContent>
<h2 className="text-lg font-semibold">Name: {user.nickname}</h2>
<p>Email: {user.email}</p>
</CardContent>
<CardFooter>
<SessionButton />
</CardFooter>
</Card>
) : (
<>
<h3 className="text-2xl font-semibold">Login for see your user</h3>
<SessionButton />
</>
)}
</section>
);
}
Create middleware
We will create a middleware because the expiration is not updated with the user's visits, we solve this withMiddlewareAuthRequired
.
// middleware.ts
import {withMiddlewareAuthRequired} from "@auth0/nextjs-auth0/edge";
export default withMiddlewareAuthRequired();
// define the protected routes
export const config = {
matcher: "/protected-route",
};
The end
Thank you for making it to the end. If you like you can review a sample app. This is the code of the app and this is the deployed version.