Next.js and Auth.js
Working sign-in in three files. Verified against Next.js 16 and Auth.js 5.
1. Start with an organization
Applications belong to an organization, not to you personally. If you do not have one yet: Console → Organization → "Create an organization". Type a name and confirm — Applications shows up in the menu right away.
Already have an organization? Skip straight to the next step.
2. Register a client
Console → Applications → "Add an application". Give it a name — users see it on the consent screen — and a redirect URI:
https://your-site/api/auth/callback/authmy
The same form has a URI builder: type your site address, pick Auth.js, and the path is filled in; a checkbox adds a localhost URI for development.
You then get a client_id and a client_secret.
The secret is shown once.
3. Install the library
npm install next-auth
4. Three files
// auth.ts
import NextAuth from 'next-auth'
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
{
id: 'authmy',
name: 'auth.my',
type: 'oidc',
issuer: 'https://auth.my',
clientId: process.env.AUTHMY_CLIENT_ID,
clientSecret: process.env.AUTHMY_CLIENT_SECRET,
},
],
})
// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth'
export const { GET, POST } = handlers
// app/page.tsx
import { auth, signIn } from '@/auth'
export default async function Home() {
const session = await auth()
if (session?.user) return <p>Signed in as {session.user.email}</p>
return (
<form action={async () => { 'use server'; await signIn('authmy') }}>
<button>Sign in with auth.my</button>
</form>
)
}
Handlers come out of a handlers object. Writing
export { GET, POST } from '@/auth' does not work — the site returns 500
on the very first request.
5. Environment
AUTHMY_CLIENT_ID=…
AUTHMY_CLIENT_SECRET=…
AUTH_SECRET=…
AUTH_SECRET is your own secret; Auth.js signs its own session cookie with
it. It has nothing to do with auth.my. Generate one:
openssl rand -base64 32
Shorter: the wrapper package
The same defaults are gathered in a package, @authmy/authjs. It is a
shortcut, not the way in: auth.my is a plain OpenID Connect provider, and the
fifteen lines above do exactly the same thing.
npm install @authmy/authjs
// auth.ts
import NextAuth from 'next-auth'
import AuthMy from '@authmy/authjs'
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [AuthMy()],
})
clientId and clientSecret are read from AUTHMY_CLIENT_ID and
AUTHMY_CLIENT_SECRET — the same variables as above.
What it pins down: the issuer, the scope openid profile email, PKCE, and
the provider id authmy, which the redirect URI is built from. It adds
nothing to the protocol and will not become required: a free choice of
library is a promise made on the front page.
What you do not configure
No signing algorithm, no endpoint list, no PKCE setup, no refresh handling. The library reads all of it from discovery and does it itself.
Who signed in
const session = await auth()
session?.user?.email
The permanent identifier is sub, not the email. To carry it into the
session:
callbacks: {
jwt({ token, profile }) {
if (profile?.sub) token.sub = profile.sub
return token
},
session({ session, token }) {
if (session.user) session.user.id = token.sub as string
return session
},
}
The whole thing
A ready-to-run example lives in the repository: examples/nextjs-authjs.