Horizon

Test Twitch OAuth login locally

Sign in with Twitch on localhost by registering a stable HTTPS OAuth Redirect URL from a Horizon tunnel, with Auth.js in Next.js.

Test "Sign in with Twitch" on your laptop, with an OAuth Redirect URL Twitch can reach.

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 Twitch account
  • A Next.js App Router app with Auth.js set up. The Auth.js guide covers AUTH_SECRET and the route handler.

Start a tunnel

Use -s with a subdomain you reserved. Without it, the subdomain is random and changes every run, so the redirect URI you register 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

Keep this terminal open.

Register the app in Twitch

  1. Log in to the Twitch developer console.
  2. Select the Applications tab, then Register Your Application.
  3. Set Name. It must be unique.
  4. Set OAuth Redirect URLs to https://my-app.hrzn.run/api/auth/callback/twitch.
  5. Select a Category.
  6. Complete the CAPTCHA and select Create.
  7. Open your app and select Manage. Copy the Client ID, then select New Secret and copy the secret.

Configure Auth.js

Auth.js reads the Twitch credentials from AUTH_TWITCH_ID and AUTH_TWITCH_SECRET. AUTH_TRUST_HOST=true makes Auth.js trust X-Forwarded-Host, so it builds the callback from the public host. Horizon sets Host to localhost:3000.

.env.local
AUTH_SECRET=replace-with-the-generated-secret
AUTH_TRUST_HOST=true
AUTH_TWITCH_ID=replace-with-the-client-id
AUTH_TWITCH_SECRET=replace-with-the-client-secret
auth.ts
import NextAuth from "next-auth";
import Twitch from "next-auth/providers/twitch";

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [Twitch],
});
app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth";

export const { GET, POST } = handlers;

Add a sign-in button, then start the app on port 3000.

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

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

Sign in

Open https://my-app.hrzn.run. On the first visit, select Continue to site on the Before you continue page. Then select Sign in with Twitch.

Check it works

Twitch shows its authorization screen. After you approve, your browser lands back on https://my-app.hrzn.run.

Your Horizon terminal prints a line for the callback:

Output
  GET     302  /api/auth/callback/twitch

Troubleshooting

Twitch doesn't accept the redirect URL

Twitch expects the redirect_uri your app sends to be one of the registered OAuth Redirect URLs.

  • Check that the URL is exactly https://my-app.hrzn.run/api/auth/callback/twitch.
  • Check that AUTH_TRUST_HOST=true is set and that you restarted npm run dev. Without it, Auth.js can build a localhost callback.
  • Check that the subdomain matches the one you pass to -s.

Twitch sends you to the wrong URL

With several OAuth Redirect URLs, Twitch redirects to the first one. Move the tunnel URL to the top of the list, or remove the others while you test.

The URL changed after a restart

You started the tunnel without -s, so Horizon gave you a new random subdomain. Restart with -s my-app and the registered URL stays valid. -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 the app listens on port 3000, the port in your tunnel command.

Next steps

On this page