Horizon

Test Auth0 sign-in locally with a public callback URL

Test Auth0 sign-in on localhost with a Horizon tunnel, and set Allowed Callback URLs, Allowed Logout URLs and Allowed Web Origins once.

Sign in with Auth0 on your laptop, with callback, logout and origin URLs that stay the same across restarts.

Auth0 only redirects to URLs you list on the application. A stable Horizon URL means you set that list once.

Before you begin

  • Node.js 20 or later, which the Auth0 Next.js SDK requires
  • A Horizon account and the CLI (see Getting started)
  • A reserved subdomain for -s. Reserve one on the Subdomains page.
  • An Auth0 tenant with a Regular Web Application, and a Next.js app

Start a tunnel

Use -s with a subdomain you reserved. Without it, the subdomain is random and changes every run, so the URLs you save in Auth0 would point at a dead URL after a restart. Reserved subdomains are a paid feature, see Pricing.

hrzn tunnel http://localhost:3000 -s my-app
Output
HORIZON: Tunnel connected
  URL          https://my-app.hrzn.run (reserved)
  Forwarding   http://localhost:3000
  Request log  https://hrzn.run/dashboard/tunnels/my-app

Your public URL is https://my-app.hrzn.run. Keep this terminal open.

The first time you open the URL in a browser, Horizon shows a Before you continue page. Select Continue to site. You see it once every 7 days per IP address, so the redirect back from Auth0 won't surprise you later.

Add the URLs in Auth0

  1. In the Auth0 Dashboard, open Applications and select your application.
  2. On the Settings tab, scroll to the Application URIs section.
  3. Set Allowed Callback URLs to https://my-app.hrzn.run/auth/callback.
  4. Set Allowed Logout URLs to https://my-app.hrzn.run.
  5. Set Allowed Web Origins to https://my-app.hrzn.run.
  6. Save your changes.

Set the app environment variables

Install the Auth0 SDK for Next.js.

npm install @auth0/nextjs-auth0

Set APP_BASE_URL to the public URL. If you leave it out, the SDK infers the base URL from the request host, and Horizon sets the Host header to localhost:3000. Generate AUTH0_SECRET with openssl rand -hex 32.

.env.local
AUTH0_DOMAIN=your-tenant.us.auth0.com
AUTH0_CLIENT_ID=your_client_id
AUTH0_CLIENT_SECRET=your_client_secret
AUTH0_SECRET=your_generated_secret
APP_BASE_URL=https://my-app.hrzn.run

Create the Auth0 client.

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

export const auth0 = new Auth0Client();

The SDK mounts /auth/login, /auth/logout and /auth/callback through a proxy. On Next.js 16, create proxy.ts.

proxy.ts
import { auth0 } from "./lib/auth0";

export async function proxy(request: Request) {
  return await auth0.middleware(request);
}

export const config = {
  matcher: [
    "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
  ],
};

On Next.js 15, create middleware.ts instead.

middleware.ts
import type { NextRequest } from "next/server";
import { auth0 } from "./lib/auth0";

export async function middleware(request: NextRequest) {
  return await auth0.middleware(request);
}

export const config = {
  matcher: [
    "/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)",
  ],
};

Add a sign-in link. Use a plain <a> tag, because Auth0 advises against <Link prefetch={true}> for login links.

app/page.tsx
export default function Home() {
  return <a href="/auth/login">Log in</a>;
}

Start the app on port 3000. Restart it after you edit .env.local.

npm run dev

Sign in through the public URL

Open https://my-app.hrzn.run and select Log in. Start from the public URL so the browser, Auth0 and your app agree on the host.

Check it works

Auth0 shows its Universal Login page. After you sign in, Auth0 redirects the browser to https://my-app.hrzn.run/auth/callback, and the SDK sends you back to your app.

Your Horizon terminal prints a GET line for /auth/callback. A 3xx status means your app redirected you onward. If the line shows a 4xx or 5xx, see Troubleshooting.

Troubleshooting

Callback URL mismatch

Auth0 shows "The provided redirect_uri is not in the list of allowed callback URLs". The redirect_uri your app sent doesn't match a value in Allowed Callback URLs.

  • Check for a trailing slash, a different path or http instead of https.
  • Check that APP_BASE_URL is https://my-app.hrzn.run.
  • Check that you saved the change on the application your AUTH0_CLIENT_ID belongs to.

The callback goes to localhost

APP_BASE_URL is missing or still holds http://localhost:3000. Horizon sets the Host header to localhost:3000 and sends the public host in X-Forwarded-Host, so a framework that builds URLs from Host gets localhost. Set APP_BASE_URL to https://my-app.hrzn.run and restart npm run dev.

/auth/login returns 404

The proxy file is missing or in the wrong place. Auth0's quickstart says /auth/login returns a 404 without it. Check that proxy.ts (Next.js 16) or middleware.ts (Next.js 15) exists in the project root, or in src if your app uses it.

The tunnel URL changed

You started the tunnel without -s, so Horizon gave you a new random subdomain. Restart with -s my-app and the URL stays the same. -s needs a subdomain you reserved, see Pricing.

Nothing reaches your app

  • Check that the Horizon terminal is still running. If its last line is Connection lost. Reconnecting…, wait for Reconnected.
  • Check that your app listens on port 3000.

Next steps

On this page