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
Set up Authio
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 texthttps://authio.api.hyperio.tech/realms/acmeCreate 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 texthttp://localhost:3000/api/auth/callback/authioThe 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
Install the packages
TerminalBashnpm install next-auth@beta authio-provider-nextauthAdd your credentials
Auth.js reads the provider’s settings from variables prefixed with
AUTH_AUTHIO_.AUTH_SECRETis a different thing: the key that encrypts the session cookie. Generate it withnpx auth secretand never reuse the client secret for it..env.local.envAUTH_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_SECRETConfigure 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.tsTypeScriptimport NextAuth from 'next-auth' import Authio from 'authio-provider-nextauth/v5' export const { handlers, signIn, signOut, auth } = NextAuth({ providers: [Authio], })Add sign-in and sign-out
Read the session on the server with
auth()and use server actions for the two buttons:app/page.tsxTypeScriptimport { 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> ) }Try it
Run
npm run dev, openhttp://localhost:3000and 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 see | Why | What to do |
|---|---|---|
| The hosted login refuses the request for its redirect URI | The 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 openid | The 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 production | Auth.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 screen | The 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. |