Horizon

Test Kinde sign-in locally with a public callback URL

Test Kinde sign-in on localhost with a Horizon tunnel, using a stable HTTPS URL for the allowed callback and logout redirect URLs.

Sign in with Kinde on your laptop, with a callback URL that stays the same across restarts.

Kinde checks the callback URL against the list you set for your application. A stable Horizon URL means you set that list once.

Before you begin

  • Node.js 18 or later
  • A Horizon account and the CLI (see Getting started)
  • A reserved subdomain for -s. Reserve one on the Subdomains page.
  • A Kinde account with an application, and a Next.js App Router app

Start a tunnel

Use -s with a subdomain you reserved. Without it, the subdomain is random and changes every run, so the callback URL you save in Kinde 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 Kinde won't surprise you later.

Add the URLs in Kinde

  1. In Kinde, open Settings, then Applications.
  2. Open your application's details.
  3. In Allowed callback URLs, enter https://my-app.hrzn.run/api/auth/callback.
  4. In Allowed logout redirect URLs, enter https://my-app.hrzn.run.
  5. Save your changes.

Check both fields for spaces before or after the URL. Kinde lists stray spaces as a cause of an invalid callback URL.

Set the app environment variables

Install the Kinde SDK for Next.js.

npm install @kinde-oss/kinde-auth-nextjs

Set the site URL and the post-logout URL to the public address. If you leave them at http://localhost:3000, the SDK builds localhost URLs.

.env.local
KINDE_CLIENT_ID=your_client_id
KINDE_CLIENT_SECRET=your_client_secret
KINDE_ISSUER_URL=your_issuer_url
KINDE_SITE_URL=https://my-app.hrzn.run
KINDE_POST_LOGIN_REDIRECT_URL=/dashboard
KINDE_POST_LOGOUT_REDIRECT_URL=https://my-app.hrzn.run

Create the route handler that serves the SDK's auth routes.

app/api/auth/[kindeAuth]/route.ts
import { handleAuth } from "@kinde-oss/kinde-auth-nextjs/server";

export const GET = handleAuth();

Add a sign-in link.

app/page.tsx
import { LoginLink } from "@kinde-oss/kinde-auth-nextjs/components";

export default function Home() {
  return <LoginLink>Sign in</LoginLink>;
}

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 Sign in. Start from the public URL so the browser, Kinde and your app agree on the host.

Check it works

Kinde shows its sign-in page. After you sign in, Kinde redirects the browser to https://my-app.hrzn.run/api/auth/callback, and the SDK sends you to /dashboard.

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

Troubleshooting

Invalid callback URL

  • Check that the value in Allowed callback URLs matches the callback your app sends, character for character.
  • Remove spaces before and after the URL.
  • Check that KINDE_CLIENT_ID belongs to the application you edited.

The callback goes to localhost

KINDE_SITE_URL still holds http://localhost:3000, or the dev server didn't pick up the change. 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 KINDE_SITE_URL to https://my-app.hrzn.run and restart npm run dev.

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