Better Auth 1.4

Stateless Auth, SCIM, Optimized database queries, additional state in OAuth flows, performance improvements, SSO domain verification, and more!

B
Bereket Engidaยท@iambereketยท

Better Auth 1.4 Release

We're excited to announce the release of Better Auth 1.4. This release includes several new features, performance and security improvements.

To upgrade, run:

npm install better-auth@1.4

๐Ÿš€ Highlights

Stateless Auth

You can now enable stateless session management without any database by omitting the database option in your auth config.

๐Ÿ‘‰ Read more about stateless auth

import { betterAuth } from "better-auth";

export const auth = betterAuth({
    // No database configuration
    socialProviders: {
        google: {
            clientId: process.env.GOOGLE_CLIENT_ID,
            clientSecret: process.env.GOOGLE_CLIENT_SECRET,
        },
    },
});

It also supports getting accessToken, accountInfo and refreshToken endpoints without any database.

import { authClient } from "@/lib/auth-client";

const accessToken = await authClient.getAccessToken();

// get account info
const accountInfo = await authClient.accountInfo();

SCIM Provisioning

SCIM makes managing identities in multi-domain scenarios easier to support via a standardized protocol.

๐Ÿ‘‰ Read more about SCIM provisioning

import { scim } from "@better-auth/scim";
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    plugins: [scim()]
});

Custom State for OAuth Flows

You can now provide any additional state through the OAuth flow.

๐Ÿ‘‰ Read more about custom state for OAuth flows

import { authClient } from "@/lib/auth-client";

await authClient.signIn.social({
    provider: "google",
    additionalData: { 
        referralCode: "ABC123", 
        source: "landing-page", 
    }, 
});

Access additional data in any hook, middleware, or endpoints

auth.ts
import { betterAuth } from "better-auth";
import { getOAuthState, createAuthMiddleware } from "better-auth/api";

const auth = betterAuth({
    hooks: {
      before: createAuthMiddleware(async (ctx) => {
        const additionalData = await getOAuthState<{ referralCode: string, source: string }>();
        console.log(additionalData);
      })
    }
});

Database Joins Improvements (experimental)

Better-Auth now supports database joins, improving over 50 endpoints by 2 to 3x in latency. This is achieved by using database joins under the hood.

To enable, simply set the joins flag to true under experimental in your auth config. Then, re-run migrations or schema generation to get the updated schema which supports joins!

๐Ÿ‘‰ Read more about database joins

import { betterAuth } from "better-auth";

export const auth = betterAuth({
    experimental: {
        joins: true,
    },
});

Although joins are experimental, they are supported by all adapters and will be enabled by default in the next release.


Secondary Storage Support for API Keys

The API-Key plugin now supports secondary storage for faster key lookups!

๐Ÿ‘‰ Read more about secondary storage for API keys

import { apiKey } from "@better-auth/api-key";
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    secondaryStorage: {
        //...
    },
    plugins: [apiKey({ storage: "secondary-storage" })], 
});


New Default Error Page

We've revamped our error page to look much better and more informative! You can also customize the stylings of our default error page or simply provide your own path to a custom error page.

๐Ÿ‘‰ Read more about error page improvements

Error Page

SSO Domain Verification

Domain verification allows your application to automatically trust a new SSO provider by automatically validating ownership via the associated domain:

๐Ÿ‘‰ Read more about domain verification

import { sso } from "@better-auth/sso";
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    plugins: [sso({ domainVerification: { enabled: true } })],
});

CLI Database Index Support

The Better-Auth CLI now supports indexing your database out of the box, which greatly improves the performance of your application.

npx @better-auth/cli generate

npx @better-auth/cli migrate

JWT Key Rotation Support

The JWT plugin now supports key rotation, allowing you to rotate your JWT keys without downtime.

import { jwt } from "@better-auth/jwt";
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    plugins: [
      jwt({ 
        jwks: { rotationInterval: 60 * 60 * 24 * 30, gracePeriod: 60 * 60 * 24 * 30 } 
      })
    ],
});

๐Ÿ‘‰ Read more about JWT key rotation

Improved Generic OAuth Plugin

OAuth providers not yet supported natively will be available as pre-configured options for the generic OAuth plugin, with plans to eventually support all providers that Auth.js does.

import { betterAuth } from "better-auth";
import { genericOAuth } from "better-auth/plugins";
import { keycloak } from "better-auth/plugins/generic-oauth";

export const auth = betterAuth({
    plugins: [genericOAuth({
        config: [
            keycloak({
                clientId: process.env.KEYCLOAK_CLIENT_ID,
                clientSecret: process.env.KEYCLOAK_CLIENT_SECRET,
                issuer: process.env.KEYCLOAK_ISSUER,
            })
        ]
    })
    ]
});

Bundle Size Optimizations

If you're using custom adapters (like Prisma, Drizzle, or MongoDB), you can reduce your bundle size by using better-auth/minimal instead of better-auth. This version excludes Kysely, which is only needed when using direct database connections.

๐Ÿ‘‰ Read more about bundle size optimizations

import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { betterAuth } from "better-auth/minimal"; 
import { db } from "./database";

export const auth = betterAuth({
    database: drizzleAdapter(db, { provider: "pg" }),
});

UUID Support

We now support having UUIDs as the primary ID field type out of the box.

๐Ÿ‘‰ Read more about UUID support

import { betterAuth } from "better-auth";
import { db } from "./database";

export const auth = betterAuth({
    database: db,
    advanced: {
        database: { generateId: "uuid" }, 
    },
});

Improved Email Change Flow

For added security, you can now require users to confirm the change via their current email before the verification email is sent to the new address.

๐Ÿ‘‰ Read more about improved email change flow

import { betterAuth } from "better-auth";

export const auth = betterAuth({
    user: {
        changeEmail: {
            enabled: true,
            sendChangeEmailConfirmation: async ({ user, newEmail, url, token }, request) => {
                await sendEmail({
                    to: user.email, // Sent to the CURRENT email
                    subject: 'Approve email change',
                    text: `Click the link to approve the change to ${newEmail}: ${url}`
                })
            }
        }
    }
});

Device Authorization Plugin

Full support for the OAuth 2.0 Device Authorization Grant (RFC 8628), allowing users to sign in on input-constrained devices like Smart TVs and gaming consoles.

๐Ÿ‘‰ Read more about Device Authorization

import { betterAuth } from "better-auth";
import { deviceAuthorization } from "better-auth/plugins";

export const auth = betterAuth({
    plugins: [
        deviceAuthorization()
    ]
});

You can now store user account data in a signed cookie, which is perfect for stateless applications or when you want to defer database persistence.

export const auth = betterAuth({
    account: {
        storeAccountCookie: true
    } 
});

โš ๏ธ Breaking Changes

We recommend going through each breaking change to ensure a smooth upgrade.

Existing Auth Flows

Minor auth flow changes to make everything smoother, faster, and more reliable.

  • Change-Email Flow: We've removed sendChangeEmailVerification from the changeEmail flow to use emailVerification.sendVerificationEmail instead.

  • Forgot Password Flow: authClient.forgotPassword is now authClient.requestPasswordReset.

  • Account Info Endpoint: The accountInfo endpoint has changed from POST /account-info to GET /account-info.

    This means any auth.api calls must change from:

    auth.api.accountInfo({
        body: {
            // ...
        }
    });

    to:

    auth.api.accountInfo({
        query: {
            // ...
        }
    });

Plugin Options Changes

Multiple plugin options that accept request as an argument for callback functions have now replaced request with ctx instead. Passkey plugin is now moved to its own package, and API Key mock-sessions are now disabled by default.

  • Passkey Plugin: The passkey plugin is now moved to its own package.

    Install it today:

    npm install @better-auth/passkey
  • Email OTP Plugin: Plugin options for sendVerificationOTP and generateOTP now use ctx instead of request in its callback function. You can still access request via ctx.request.

  • Magic Link Plugin: Plugin options for sendMagicLink now uses ctx instead of request for one of its callback options. You can still access request via ctx.request.

  • Phone Number Plugin: Plugin options for sendOTP, sendPasswordResetOTP & callbackOnVerification have now replaced request with ctx in one of its callback options. You can still access request via ctx.request.

  • Organization Plugin: Plugin options for teams.defaultTeam.customCreateDefaultTeam & teams.maximumTeams have now replaced request with ctx for one of its callback options. You can still access request via ctx.request.

  • OIDC Plugin: The field redirectURLs have been updated to redirectUrls, this requires database migration.

  • API Key Plugin: Mock-sessions by api-keys are now disabled by default, and should be enabled through the auth-config first.

Auth Config Changes

Database configuration options changes.

  • advanced.generateId removed: The long-deprecated advanced.generateId has been removed. Please use advanced.database.generateId instead.
  • advanced.database.useNumberId moved: advanced.database.useNumberId has been moved to advanced.database.generateId: "serial" instead.

Tanstack Start

The Better-Auth offered reactStartCookies plugin is now moved to tanstackStartCookies plugin.

- import { reactStartCookies } from "better-auth/react-start";
+ import { tanstackStartCookies } from "better-auth/tanstack-start";

export const auth = betterAuth({
-    plugins: [reactStartCookies()],
+    plugins: [tanstackStartCookies()],
});

Expo

With the new pooling features for useSession on Expo, you're required to install expo-network:

npx expo install expo-network

Cloudflare Runtime

Better Auth uses AsyncLocalStorage for async context tracking. To enable this in Cloudflare Workers, add the nodejs_compat flag to your wrangler.toml:

wrangler.toml
compatibility_flags = ["nodejs_compat"]
compatibility_date = "2024-09-23"

Alternatively, if you only need AsyncLocalStorage support:

wrangler.toml
compatibility_flags = ["nodejs_als"]

In the next major release, we will assume AsyncLocalStorage support by default, so this configuration will be necessary.


โœจ More Features

  • Cookie Chunking: No more cookie size exceeding errors.
  • MongoDB String IDs: Support for using String IDs instead of ObjectIDs in MongoDB.
  • JWE Cookie Cache: Session store cookie cache now uses JWE by default for enhanced security.
  • OIDC RP-Initiated Logout: Trigger logout at the OIDC provider.
  • Server-side IP Detection: Automatic client IP detection utility.
  • Custom OTP Verification: Allow custom logic for OTP verification in the Phone Number plugin.
  • Client disableSignal: Option to disable default abort signal behavior in the client.
  • AuthClient Type Helper: New type helper for better client-side type inference.
  • New Providers: Polar and Paybin OAuth providers added.
  • ESM Only: Better-Auth is now 100% ESM.
  • Session Refetching: Support for polling & on-window-focus refetching.
  • Cloudflare Workers: CLI support for virtual module imports.
  • Error Documentation: Common OAuth flow errors are now documented.
  • Last-Login-Method: Support for tracking SIWE & Passkey.

๐Ÿ› Bug fixes & Improvements

This release includes over 250+ bug fixes addressing issues across all areas:

  • Adapter Fixes: Improved query handling, better type transformations, UUID column types, and resolved issues with nullable foreign keys.
  • Session Management: Fixed session refresh triggers, improved cookie handling, and better cache management.
  • OAuth: Fixed state management, improved callback handling, and resolved cross-origin issues.
  • Organization: Fixed member deletion, team management, permission checks, and inference issues.
  • Two-Factor: Improved TOTP error messages, fixed backup code encryption, and better device trust handling.
  • Email OTP: Prevented duplicate verification emails and improved session signal triggers.
  • SSO: Fixed SAML callback handling, improved OIDC scope fallback, and better consent enforcement.
  • API-Keys: Fixed usage tracking and improved request validation.
  • Phone Number: Prevent unauthorized phone number updates and improved verification flow.
  • And many more!

A lot of refinements to make everything smoother, faster, and more reliable. ๐Ÿ‘‰ Check the full changelog

โค๏ธ Contributors

Thanks to all the contributors for making this release possible!