Horizon

Test Okta sign-in locally with an OIDC redirect URI

Test Okta OIDC sign-in on localhost with a Horizon tunnel, and set the sign-in redirect URI on your app integration once.

Sign in with Okta on your laptop, with a sign-in redirect URI that stays the same across restarts.

Okta checks the redirect_uri of each sign-in request against the Sign-in redirect URIs of your app integration. 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.
  • An Okta org where you can create app integrations
  • 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 Okta 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 Okta won't surprise you later.

Create the app integration in Okta

This page uses the Okta provider in Auth.js (next-auth).

  1. In the Okta Admin Console, open Applications and Resources, then Applications.
  2. Select Create App Integration.
  3. Select OIDC - OpenID Connect as the sign-in method and Web Application as the application type. Okta warns that the wrong app type can break sign-in and sign-out.
  4. Set Sign-in redirect URIs to https://my-app.hrzn.run/api/auth/callback/okta. This is the callback path Auth.js uses with Next.js.
  5. Set Sign-out redirect URIs to https://my-app.hrzn.run.
  6. Under Controlled access, choose who can sign in, then save.
  7. On the General tab, under Client Credentials, copy the Client ID and Client Secret.

Okta says sign-in redirect URIs must be absolute URIs. The Okta docs don't allow wildcards for sign-out redirect URIs.

Set the app environment variables

Install Auth.js and generate a secret. npx auth secret adds AUTH_SECRET to .env.local.

npm install next-auth@beta
npx auth secret

Add the Okta values. AUTH_TRUST_HOST=true tells Auth.js to trust the X-Forwarded-Host header. Horizon sets Host to localhost:3000 and sends the public host in that header, so without it the callback can point at localhost.

.env.local
AUTH_OKTA_ID=your_client_id
AUTH_OKTA_SECRET=your_client_secret
AUTH_OKTA_ISSUER=your_okta_issuer_url
AUTH_TRUST_HOST=true

Create the Auth.js config with the Okta provider.

auth.ts
import NextAuth from "next-auth";
import Okta from "next-auth/providers/okta";

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [Okta],
});

Create the route handler.

app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth";

export const { GET, POST } = handlers;

Add a sign-in button.

app/page.tsx
import { signIn } from "@/auth";

export default function Home() {
  return (
    <form
      action={async () => {
        "use server";
        await signIn("okta");
      }}
    >
      <button type="submit">Sign in with Okta</button>
    </form>
  );
}

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

Check it works

Okta shows its sign-in page. After you sign in, Okta redirects the browser to https://my-app.hrzn.run/api/auth/callback/okta, and Auth.js sends you back to your app.

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

Troubleshooting

The redirect_uri must be a login redirect URI

Okta returns a 400 Bad Request when the redirect_uri of the sign-in request isn't one of the Sign-in redirect URIs on the app integration.

  • Open the app integration, and check that https://my-app.hrzn.run/api/auth/callback/okta is in Sign-in redirect URIs.
  • Check that AUTH_OKTA_ID belongs to the same app integration.
  • Check the scheme and the path. http and https are different URIs.

The callback goes to localhost

Horizon sets the Host header to localhost:3000 and sends the public host in X-Forwarded-Host. Auth.js trusts that header only when AUTH_TRUST_HOST is true. Set it, then 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