mrkeyoor.com_
Wed 23 Sept 00:33 UTC
npmSecurityupdated 22 Sept 2026

@auth/core review

@auth/core 0.41.3 is the low-level Auth.js package used by framework adapters. Its Auth function accepts a Web Request and returns a Web Response for provider redirects, OAuth callbacks, session reads, sign-out, CSRF checks, and the supplied auth pages. It can keep sessions in encrypted JWT cookies or persist them through an adapter. Email links, credentials, OAuth, OIDC, and experimental WebAuthn are available, but authorization rules, password storage, recovery flows, and account-management screens remain application work. Our install also shows that this server-side protocol engine is a substantial browser import at 45.8 KB gzipped.

Verdict

@auth/core 0.41.3 installed in 3.5 seconds with 0 audit findings, but our browser build was 45.8 KB gzipped and direct use leaves routing, proxy trust, adapters, and failure handling to you. Install it for adapter work or encrypted stateless sessions; for an ordinary new app, the maintainers themselves point first to Better Auth.

We installed it

Lab card: what happened when we installed @auth/coreScreenshot of @auth/core documentation
Install✓ · 3.5s12 packages on disk · 7 MB
ImportESM import works · require() works · ESM package with exports map
Browser45.8 KBgzipped (140.5 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does @auth/core install cleanly?

Yes. In a fresh container with an empty cache, npm install @auth/core finished in 4 seconds, leaving 12 packages and 7 MB on disk. npm audit reported no known vulnerabilities.

How much does @auth/core add to a browser bundle?

45.8 KB gzipped (140.5 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does @auth/core work with both ESM and CommonJS?

Yes. Both import '@auth/core' and require('@auth/core') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does @auth/core include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

@auth/core or better-auth: which should you use?

better-auth: Pick it for a new application when you do not depend on Auth.js's encrypted database-free session behavior. @auth/core 0.41.3 installed in 3.5 seconds with 0 audit findings, but our browser build was 45.8 KB gzipped and direct use leaves routing, proxy trust, adapters, and failure handling to you.

When should you not use @auth/core?

This is a new application with no Auth.js-specific requirement. The repository recommends Better Auth for new work after Auth.js joined that project.

API stability2/5Version 0.41.3 remains below 1.0, and the reference labels the primary core module experimental. Request in and Response out is a clear boundary, yet callers also depend on provider options, mutable environment defaults, callback contracts, and adapter methods. WebAuthn remains behind an experimental flag. Auth.js joining Better Auth introduces another roadmap variable for teams choosing this API directly.
Docs4/5The official reference returned HTTP 200 on August 26, 2026 and documents AuthConfig, callbacks, error classes, JWT helpers, adapters, and individual providers. It states the secret, host-trust, session, and Credentials constraints close to the relevant options. Framework examples often assume /api/auth while bare core defaults to /auth, so an adapter author still has to separate framework conventions from the core contract.
Maintenance4/5npm published 0.41.3 on July 20, 2026, and GitHub records a repository push two days later. The monorepo is unarchived and continues to update the protocol and provider stack. GitHub's current combined counter shows 598 issues and pull requests across all NextAuth and Auth.js packages. That activity is real, although the README now directs greenfield adoption toward Better Auth.
Ecosystem5/5The npm endpoint counted 4,292,805 downloads for August 18 through 24, 2026, and GitHub reports 28,342 stars. The monorepo includes many OAuth and OIDC provider definitions, while separate adapters cover common SQL, document, and hosted databases. Standard Request and Response objects make framework bridges possible, but the project's own README now sends most new adopters to Better Auth.

Use it if

  • You are building an Auth.js adapter around standard Request and Response objects for a framework that has no official integration.
  • Encrypted cookie sessions without a database are a firm requirement, which the project calls out as the main gap that can still justify choosing Auth.js for a new build.
  • You need the packaged OAuth and OIDC provider definitions while retaining control of user and session data.
  • One authentication layer must support either JWT cookies or an application-owned database adapter.
Skip it if

Setup reality

We installed @auth/core 0.41.3 in 3.5 seconds in a clean Node 22 Bookworm container. The install left 12 packages using 7 MB. npm audit reported 0 known vulnerabilities. The package itself has 5 direct dependencies, 3 peer dependencies, 3260 KB unpacked, an ISC license, and bundled TypeScript declarations. It is ESM with an exports map; both require() and ESM import succeeded in our checks. An all-exports browser build reached 140.5 KB minified and 45.8 KB gzipped.

Direct use requires GET and POST handlers below the default /auth path, plus an exact callback URL registered at every identity provider. Auth() does not populate its own configuration from process.env. Pass the secret, provider credentials, basePath, and trustHost yourself, or call setEnvDefaults(process.env, config), which mutates the object and reads AUTH_SECRET, AUTH_URL, AUTH_TRUST_HOST, and provider-prefixed variables. A reverse proxy must sanitize forwarded host headers before trustHost can be enabled safely.

Without an adapter, encrypted JWT cookies are the session store. Adding an adapter switches the default to database sessions and brings schema, migration, and adapter-method obligations. Credentials data arrives as unknown input and needs validation, hashing checks, throttling, and safe error messages in application code. Email sign-in adds the Nodemailer peer plus SMTP and adapter setup. Experimental WebAuthn adds both SimpleWebAuthn peers and more adapter methods.

HTTPS affects secure-cookie defaults, while custom cookie options can undo them. The session callback decides which token values reach client code. For secret rotation, supply an array with the newest value first and retain old values only for decrypting existing sessions. Test proxy headers, redirects, CSRF failure, cookie scope, account linking, and rejected provider callbacks before shipping.

Patterns

Handle a request with the core function handle-auth-request

import { Auth, type AuthConfig } from '@auth/core';
import GitHub from '@auth/core/providers/github';

const config = {
  providers: [
    GitHub({
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    }),
  ],
  secret: process.env.AUTH_SECRET!,
  trustHost: true,
} satisfies AuthConfig;

export function handleAuth(request: Request) {
  return Auth(request, config);
}

The default base path is /auth, and both GET and POST requests must reach this handler. Enable trustHost only behind a server or proxy that validates host headers.

Read Auth.js environment variables load-auth-environment

import { Auth, setEnvDefaults, type AuthConfig } from '@auth/core';
import GitHub from '@auth/core/providers/github';

const config: AuthConfig = { providers: [GitHub] };
setEnvDefaults(process.env, config);

export const handleAuth = (request: Request) => Auth(request, config);

Auth() never calls this helper for you. setEnvDefaults changes the config object and expects AUTH_SECRET plus provider variables such as AUTH_GITHUB_ID and AUTH_GITHUB_SECRET.

Register two OAuth providers configure-multiple-providers

import GitHub from '@auth/core/providers/github';
import Google from '@auth/core/providers/google';

const config = {
  providers: [
    GitHub({ clientId: githubId, clientSecret: githubSecret }),
    Google({ clientId: googleId, clientSecret: googleSecret }),
  ],
  secret,
  trustHost: true,
};

Register a distinct redirect URI for each provider. With the default path, these callbacks end in /auth/callback/github and /auth/callback/google.

Use GitHub Enterprise use-github-enterprise

GitHub({
  clientId: process.env.GHE_CLIENT_ID!,
  clientSecret: process.env.GHE_CLIENT_SECRET!,
  enterprise: {
    baseUrl: 'https://github.company.example',
  },
})

baseUrl determines the OAuth host and the /api/v3 endpoint. The callback URL belongs in the OAuth application on that Enterprise installation.

Limit sign-in by email domain restrict-sign-in

const config = {
  // providers, secret, trustHost...
  callbacks: {
    async signIn({ profile }) {
      const email = profile?.email;
      return typeof email === 'string' && email.endsWith('@company.example');
    },
  },
};

A matching suffix alone does not prove ownership. Check the provider's verified-email property as well when the profile includes one.

Expose one JWT claim to the client expose-jwt-claim

const config = {
  session: { strategy: 'jwt' as const },
  callbacks: {
    async jwt({ token, user }) {
      if (user) token.userId = user.id;
      return token;
    },
    async session({ session, token }) {
      session.user.id = token.userId as string;
      return session;
    },
  },
};

Anything returned from session can reach browser code. Keep provider tokens and other secrets out unless a client feature has a documented need for them.

Store sessions through Prisma use-database-sessions

import { PrismaAdapter } from '@auth/prisma-adapter';
import { prisma } from './db.js';

const config = {
  providers,
  adapter: PrismaAdapter(prisma),
  session: { strategy: 'database' as const },
  secret,
  trustHost: true,
};

@auth/prisma-adapter is a separate install, and its models must be migrated first. Once an adapter is present, database sessions become the default strategy.

Check password credentials authorize-credentials

import Credentials from '@auth/core/providers/credentials';

Credentials({
  credentials: {
    email: { label: 'Email', type: 'email' },
    password: { label: 'Password', type: 'password' },
  },
  async authorize(credentials) {
    if (typeof credentials.email !== 'string') return null;
    if (typeof credentials.password !== 'string') return null;
    return verifyPassword(credentials.email, credentials.password);
  },
})

Credentials accepts untrusted input and works only with JWT sessions. Your application still needs validation, password hashing, throttling, and failure messages that do not reveal account existence.

Send a sign-in link by email send-email-link

import Nodemailer from '@auth/core/providers/nodemailer';

const config = {
  adapter,
  providers: [
    Nodemailer({
      server: process.env.EMAIL_SERVER!,
      from: 'Sign in <login@example.com>',
    }),
  ],
  secret,
  trustHost: true,
};

Email login needs a compatible Nodemailer peer and an adapter. Verification tokens use database methods even when normal sessions remain JWT based.

Keep old sessions through secret rotation rotate-auth-secret

const config = {
  providers,
  secret: [
    process.env.AUTH_SECRET_NEW!,
    process.env.AUTH_SECRET_OLD!,
  ],
  trustHost: true,
};

The first array entry encrypts new sessions. Retain previous values only for the lifetime needed to decrypt cookies issued before the rotation.

Route to application-owned auth pages set-custom-pages

const config = {
  providers,
  secret,
  trustHost: true,
  basePath: '/auth',
  pages: {
    signIn: '/login',
    error: '/auth-error',
    verifyRequest: '/check-your-email',
  },
};

These paths must exist in your application. Keep the error route outside auth protection or a failed sign-in can loop back into the same guard.

Connect authentication logs capture-auth-logs

const config = {
  providers,
  secret,
  trustHost: true,
  logger: {
    error(error: Error) { appLogger.error({ error }, 'auth error'); },
    warn(code) { appLogger.warn({ code }, 'auth warning'); },
    debug(message, metadata) { appLogger.debug({ metadata }, message); },
  },
};

A custom logger overrides the debug option. Strip cookies, credentials, provider tokens, and sensitive response fields before metadata leaves the process.

Alternatives

PackageRegistryPick it when
better-authnpmPick it for a new application when you do not depend on Auth.js's encrypted database-free session behavior.
passportnpmPick it for an Express or Connect codebase that already models authentication as strategies and owns its session middleware.
supertokens-nodenpmPick it when password login, social providers, recovery, and session recipes should come as one maintained system with an optional hosted service.

More security guides

cryptography · pyjwt · jose · requests-oauthlib · oauthlib · dompurify · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.