Skip to content

Quickstart

Create a realm and a client in the console, then sign a user in to a Next.js app with Auth.js — in about ten minutes.

This guide takes you from an empty account to a Next.js app (App Router) that signs people in through Authio’s hosted login and reads who they are on the server. It uses Auth.js v5 and authio-provider-nextauth, the same provider the Authio console itself signs in with.

Before you start

You need Node.js 20 or later and a Next.js app using the App Router. If you do not have an Authio account yet, sign up: the 14-day trial needs no card.

Set up Authio

  1. Create a realm

    In the console, open Realms and choose New realm. Give it a name; the realm ID derived from it becomes part of the realm’s issuer and cannot be changed later, so pick something stable such as acme.

    The realm’s overview shows its issuer, the address every client of the realm points at:

    IssuerPlain text
    https://authio.api.hyperio.tech/realms/acme
  2. Create a client for your app

    Open Clients and choose New client. Pick the realm you just created, give the client an ID (lowercase letters, numbers and hyphens, for example acme-web) and a display name — the name your users see on the sign-in screen.

    Under redirect URIs, add the address Auth.js receives the sign-in on:

    Redirect URIPlain text
    http://localhost:3000/api/auth/callback/authio
    The client secret is shown once, right after the client is created. Copy it now; if you lose it, rotate it from the client’s view.

Connect your app

  1. Install the packages

    TerminalBash
    npm install next-auth@beta authio-provider-nextauth
  2. Add your credentials

    Auth.js reads the provider’s settings from variables prefixed with AUTH_AUTHIO_. AUTH_SECRET is a different thing: the key that encrypts the session cookie. Generate it with npx auth secret and never reuse the client secret for it.

    .env.local.env
    AUTH_SECRET=generated-by-npx-auth-secret
    AUTH_AUTHIO_ISSUER=https://authio.api.hyperio.tech/realms/acme
    AUTH_AUTHIO_ID=acme-web
    AUTH_AUTHIO_SECRET=YOUR_CLIENT_SECRET
  3. Configure Auth.js

    Register the Authio provider and expose Auth.js’s route handlers. With the variables above in place, the provider needs no options.

    auth.tsTypeScript
    import NextAuth from 'next-auth'
    import Authio from 'authio-provider-nextauth/v5'
    
    export const { handlers, signIn, signOut, auth } = NextAuth({
      providers: [Authio],
    })
  4. Add sign-in and sign-out

    Read the session on the server with auth() and use server actions for the two buttons:

    app/page.tsxTypeScript
    import { auth, signIn, signOut } from '@/auth'
    
    export default async function Home() {
      const session = await auth()
    
      if (!session?.user) {
        return (
          <form
            action={async () => {
              'use server'
              await signIn('authio')
            }}
          >
            <button type="submit">Sign in</button>
          </form>
        )
      }
    
      return (
        <form
          action={async () => {
            'use server'
            await signOut()
          }}
        >
          <p>Signed in as {session.user.email}</p>
          <button type="submit">Sign out</button>
        </form>
      )
    }
  5. Try it

    Run npm run dev, open http://localhost:3000 and choose Sign in. You land on the realm’s hosted login. Sign in with a user you added to the realm in the console, and you are back in your app with a session.

Troubleshooting

What you seeWhyWhat to do
The hosted login refuses the request for its redirect URIThe address is compared in full with the ones registered on the client.Register exactly http://localhost:3000/api/auth/callback/authio — scheme, host, port and path.
invalid_scope — scope must include openidThe client requires the openid scope and the request did not carry it.If you override scope, write the whole list: openid profile email plus what you add.
UntrustedHost in productionAuth.js only trusts the host named in AUTH_URL (or AUTH_TRUST_HOST).Set AUTH_URL to your app’s public address.
An error page right after a long pause on the sign-in screenThe check Auth.js keeps for one sign-in expired or was replaced by a newer one in another tab.Start the sign-in again; Authio already holds the session, so it goes straight through.

Next steps