Auth0
Logic-only authentication module for Next.js App Router, built on @auth0/nextjs-auth0 v4. No UI is included — email/password screens, "Forgot password?", and OAuth provider buttons are all rendered by Auth0's hosted Universal Login, per Auth0's own recommended integration pattern for Regular Web Applications.
Dependencies
@auth0/nextjs-auth0: official Auth0 SDK for Next.js — provides theAuth0Client, the middleware-mounted auth routes (/auth/login,/auth/logout,/auth/callback,/auth/profile), and theuseUser()client hook.server-only: build-time guard that throws ifservice.tsis ever imported into a Client Component bundle.
Folder structure
auth/auth0/
types.ts // shared types: AppUser, AppSession, Role, AuthError, mapToAppUser()
service.ts // internal — Auth0Client instance + raw SDK/Management API calls
actions.ts // "use server" — public entry point for Server Components/forms/buttons
client.tsx // AppAuthProvider + isomorphic login/signup/logout URL builders
hooks.ts // useAuth, useRequireAuth, and click-handler login/logout helpers
middleware.ts // PROJECT ROOT, not inside auth/auth0/ — session refresh + route protection
.env.local // from env.example — Auth0 domain, client credentials, session tuning
There is no route.ts and no index.ts. Every auth endpoint Auth0 itself needs is mounted automatically by the SDK from middleware.ts; the one operation that isn't (account deletion) is a Server Action, not an HTTP route, since nothing external calls it.
How the files fit together
service.tsis internal. It holds the singleAuth0Clientinstance and every raw call to Auth0's Authentication API and Management API — session reads, password-reset emails, verification-email resends, account deletion. Nothing here is meant to be imported from a component;actions.tsandmiddleware.tsare its only two callers.actions.tsis the public door. It's a"use server"module that thinly wrapsservice.ts, so Server Components, forms, and buttons only ever need one import path for server-side auth logic.client.tsxholds what a Client Component needs that isn't reactive: the optionalAppAuthProviderwrapper, and pure URL builders (getLoginUrl,getSignupUrl,getSocialLoginUrl,getLogoutUrl) safe to import from both server and client code, since they're just string construction with no secrets involved.hooks.tsholds what a Client Component needs that is reactive:useAuth()wraps the SDK'suseUser()and normalizes it to the sameAppUsershapeservice.tsuses server-side, plususeRequireAuth()as a client-side fallback guard, and click-handler versions of login/signup/logout for buttons that can't use a plain<a href>.middleware.tslives at the project root, not inside the module folder — Next.js only recognizes middleware there. It's self-contained: the protected/public route configuration is defined at the top of the same file, since it's the only file that needs it.
What actions.ts covers
Everything a Server Component, form, or button needs to drive auth without ever touching service.ts directly:
- Session and user reads —
getSession(),getCurrentUser(),getAccessToken() - Guards —
requireAuth()(redirects to login if absent),requireRole(),hasPermission()(placeholder until this module ships more than one role) - Password recovery —
sendPasswordResetEmail(), which also covers "update password" (Auth0 has no direct "change my password while logged in" endpoint without Management API and re-authentication, so both flows use the same hosted reset-email ticket) - Email verification —
resendVerificationEmail()(the initial verification email is sent automatically by Auth0 on signup) - Account deletion —
deleteAccountAction(), a Server Action usable directly as a<form action={...}>target. It resolves the caller's user id from their own session rather than accepting one as an argument, so it can never be pointed at a different account, and it redirects to/auth/logouton success to clear both the local session cookie and the Auth0 IdP session in one navigation.
What it deliberately does not cover: registration, login, and the social-provider consent screens. Those are Universal Login URLs (client.tsx), not server logic — Auth0 renders the actual forms.
Usage examples
Get the current user (Server Component)
import { getCurrentUser } from "@/auth/auth0/actions";
export default async function ProfilePage() {
const user = await getCurrentUser();
if (!user) return <p>Not signed in</p>;
return <p>Hello, {user.name} ({user.email})</p>;
}
Get the current user (Client Component, reactive)
"use client";
import { useAuth } from "@/auth/auth0/hooks";
export function UserBadge() {
const { user, isLoading } = useAuth();
if (isLoading) return <p>Loading...</p>;
if (!user) return <p>Not signed in</p>;
return <p>Hello, {user.name}</p>;
}
Protect a Server Component
import { requireAuth } from "@/auth/auth0/actions";
export default async function DashboardPage() {
const session = await requireAuth("/dashboard"); // redirects to login if absent
return <p>Welcome, {session.user.name}</p>;
}
Sign in with Google
import { getSocialLoginUrl } from "@/auth/auth0/client";
<a href={getSocialLoginUrl("google-oauth2")}>Continue with Google</a>
"use client";
import { loginWithProvider } from "@/auth/auth0/hooks";
<button onClick={() => loginWithProvider("google-oauth2")}>
Continue with Google
</button>
Sign up / log in / log out links
import { getSignupUrl, getLoginUrl, getLogoutUrl } from "@/auth/auth0/client";
<a href={getSignupUrl()}>Sign up</a>
<a href={getLoginUrl()}>Log in</a>
<a href={getLogoutUrl()}>Log out</a>
Password reset
"use server";
import { sendPasswordResetEmail } from "@/auth/auth0/actions";
export async function requestPasswordReset(formData: FormData) {
const email = formData.get("email") as string;
const result = await sendPasswordResetEmail(email);
if (!result.success) {
// handle result.error.code
}
}
Delete account
import { deleteAccountAction } from "@/auth/auth0/actions";
<form action={deleteAccountAction}>
<button type="submit">Delete my account</button>
</form>
Require a role
import { requireRole } from "@/auth/auth0/actions";
export default async function AdminPage() {
await requireRole("user"); // extend Role in types.ts as more roles are added
// ...
}
What you can build with this
This module covers the full account lifecycle for anything that needs gated access without building auth screens by hand: SaaS dashboards, member-only content sites, internal tools, or any app where "sign in, stay in, eventually leave" is the extent of what auth needs to do. Combined with a payments module, it's the login layer for a subscription product — getCurrentUser()/requireAuth() gate the paid area, and the role/permission extension points in types.ts are where a "free vs. paid" or "member vs. admin" distinction would plug in once that's needed. Combined with an email module, sendPasswordResetEmail() and resendVerificationEmail() are already the transactional triggers — no separate email-sending logic to write for those two flows.