Skip to main content
Auth.js (the project that grew out of NextAuth.js) is a good fit when all you need is “sign in with Google and read the session in Next.js”. People usually leave it for one of three reasons: they need passwords with a real reset flow, two-factor or organizations that Auth.js does not ship, or AI agents and an MCP OAuth server that need their own identities. If none of those apply, staying put is a perfectly reasonable choice. This guide assumes Auth.js v5 (the AUTH_* environment variables, NextAuth() returning { handlers, auth, signIn, signOut }). The v4 getServerSession API maps the same way, only the names on the left differ.

Concepts map

Server setup

Two differences trip people up. theAuth does not read provider credentials from AUTH_* variables on its own, you pass them in. And the oauth() plugin refuses to start without auth.session, because it needs somewhere to put the session it issues after the callback.

Route handler

The adapter refuses to start if it has no way to authenticate its management routes. That is a safety net, not an Auth.js feature you need to port. See adapters.

OAuth callback URL

Auth.js callback URLs look like /api/auth/callback/google. theAuth’s look like {baseUrl}/auth/oauth/callback/google, so with the setup above it is:
Add that URL in the Google and GitHub consoles before you cut over. You can keep the old URL registered too, so both stacks work during the overlap.

Reading the session

validate returns a Session or null and does not throw. The session carries a userId, not an email, so look the user up if you need more.

Middleware

Auth.js lets you export auth as middleware and it validates the JWT on the edge. theAuth sessions are database rows, and the edge runtime usually cannot open your database connection. Keep middleware to a cheap cookie check and do the real validation in requireUser() above.
A forged cookie passes this check, which is fine because the page still calls requireUser(). If you want stateless verification on the edge, use JWT sessions instead of the cookie session manager.

Client side

Note that useSession() returns { session, isLoading, refresh }, not { data, status }. See React hooks for the provider modes.

Database

If you used an Auth.js database adapter, you have User, Account, Session and VerificationToken tables (names depend on the adapter). Copy users and OAuth links, and leave sessions and verification tokens behind. Run createTheAuth once first so the theauth_* tables exist.
The names above are the Prisma adapter defaults and the SQL is for Postgres. Check yours with SELECT column_name FROM information_schema.columns WHERE table_name = 'User' before running anything, and run the whole thing against a copy first. Check the column type of theauth_oauth_accounts.expires_at too: Auth.js stores seconds since the epoch, hence to_timestamp. What does not move:
  • Sessions. With the database strategy the session token is a random string, with the JWT strategy it is an encrypted token. Neither is a theAuth token. Users sign in again, which for OAuth users is one click.
  • Passwords. Auth.js has no built-in password storage. If you wrote a Credentials provider, you own the hash format. theAuth’s username module only verifies pbkdf2:<iterations>:<saltHex>:<hashHex>, so bcrypt or argon2 hashes need a forced reset or a lazy rehash. The approach is spelled out under Migrate from Auth0 and applies unchanged.
  • Email sign-in tokens. Verification tokens are short lived, let them expire.

Run both during the cutover

authJsAdapter lets theAuth treat an existing Auth.js session as the signed-in user, so agent routes and plugin endpoints keep working while pages still use auth().
getSession must return { user: { id, email?, name?, image? } } or null. The adapter only resolves identity, it does not create theAuth sessions. Use it to move the agent and MCP side first, then switch the human sign-in over when you are ready. Mixing it with auth.session for the same routes is possible but auth.adapter takes one adapter, so the overlap period needs a small customAuth wrapper, as described in Migrate from better-auth.

Things that behave differently

  • Cookie names. Auth.js uses authjs.session-token (and the __Secure- prefixed variant on HTTPS). theAuth uses theauth_session. Both can coexist on one domain.
  • Session lifetime. Auth.js JWT sessions last 30 days by default. The theAuth cookie session manager defaults to 7 days with a sliding window, see Cookie options.
  • Where the secret lives. Rotating AUTH_SECRET logs everyone out in Auth.js. Changing auth.session.secret does the same here, and theAuth does not take a list of secrets.
  • After the OAuth callback the user lands on {baseUrl}/ with an auth_user query parameter, not on a callbackUrl you pass at sign-in. Keep that in mind if you rely on deep links, and see Redirect chains.

Add to an existing app

Keep your current auth and add agents on top.

Next.js adapter

Route handler options and authenticating the management routes.

Troubleshooting

Cookie, redirect and proxy problems after a cutover.

Production checklist

What to verify before you point real traffic at theAuth.
Last modified on October 9, 2026