Guide · Auth0 + Next.js App Router

Bots never reach Auth0.
Verify humans first.

Auth0's Universal Login is hosted on Auth0's domain — you can't run your own verification there. So don't: verify the human on your page, where behavioral signals live, and only then reveal the signup redirect. Unverified sessions never even learn the route. Every snippet below was live-tested on Next.js 16 + @auth0/nextjs-auth0 v4 + usehuma 1.2.1.

01Install & environment

npm install @auth0/nextjs-auth0 usehuma

Create a Regular Web Application in your Auth0 dashboard and copy its values from the Settings tab. You need usehuma@1.2.1+.

# .env.local — values from your Auth0 application's Settings tab
AUTH0_DOMAIN=dev-YOUR-TENANT.us.auth0.com
AUTH0_CLIENT_ID=••••••••
AUTH0_CLIENT_SECRET=••••••••
AUTH0_SECRET=$(openssl rand -hex 32)
APP_BASE_URL=http://localhost:3000
NEXT_PUBLIC_HUMA_KEY=huma_live_••••••••

02Auth0 client + proxy

// lib/auth0.ts
import { Auth0Client } from "@auth0/nextjs-auth0/server";

export const auth0 = new Auth0Client(); // reads the env vars above
// proxy.ts  (Next.js 16 — replaces middleware.ts)
import type { NextRequest } from "next/server";
import { auth0 } from "./lib/auth0";

export async function proxy(request: NextRequest) {
  return await auth0.middleware(request); // mounts /auth/login, /auth/callback…
}

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)"],
};
Two gotchas we hit live: (1) Next.js 16 uses proxy.tsmiddleware.ts is deprecated for the Node runtime. (2) The Auth0 middleware intercepts GET requests — if you smoke-test with curl -I (a HEAD request) you'll get a confusing 404. Use a plain GET and you'll see the 307 to your tenant.

03Configure callback URLs

In your Auth0 application → Settings → Application URIs:

Allowed Callback URLs:  http://localhost:3000/auth/callback
Allowed Logout URLs:    http://localhost:3000

(Add your production URLs alongside when you deploy.)

04Gate the signup redirect

// app/page.tsx — gate the signup redirect
"use client";

import { HumaGate } from "usehuma/react";

export default function Home() {
  return (
    <main style={{ padding: 48, textAlign: "center" }}>
      <h1>Create your account</h1>
      <HumaGate
        apiKey={process.env.NEXT_PUBLIC_HUMA_KEY!}
        userId="auth0_signup_visitor"
        fallback={<p>Verifying you&apos;re human…</p>}
      >
        <a href="/auth/login?screen_hint=signup">Sign up →</a>
      </HumaGate>
    </main>
  );
}
How it behaves: ~4 seconds of invisible signal collection while the visitor reads your page, then the "Sign up" link appears for verified humans and routes through /auth/login?screen_hint=signup straight to Auth0's signup screen. Bots get a retry wall — and the redirect path is never in their DOM.

Auth0 for identity, useHUMA for humanity.

14-day free trial · one API key · zero PII.

Get your API key →