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
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
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 migrateJWT 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()
]
});Cookie-based Account Storage
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
sendChangeEmailVerificationfrom thechangeEmailflow to useemailVerification.sendVerificationEmailinstead. -
Forgot Password Flow:
authClient.forgotPasswordis nowauthClient.requestPasswordReset. -
Account Info Endpoint: The
accountInfoendpoint has changed fromPOST /account-infotoGET /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
sendVerificationOTPandgenerateOTPnow usectxinstead ofrequestin its callback function. You can still access request viactx.request. -
Magic Link Plugin: Plugin options for
sendMagicLinknow usesctxinstead ofrequestfor one of its callback options. You can still access request viactx.request. -
Phone Number Plugin: Plugin options for
sendOTP,sendPasswordResetOTP&callbackOnVerificationhave now replaced request withctxin one of its callback options. You can still access request viactx.request. -
Organization Plugin: Plugin options for
teams.defaultTeam.customCreateDefaultTeam&teams.maximumTeamshave now replaced request withctxfor one of its callback options. You can still access request viactx.request. -
OIDC Plugin: The field
redirectURLshave been updated toredirectUrls, 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.generateIdremoved: The long-deprecatedadvanced.generateIdhas been removed. Please useadvanced.database.generateIdinstead.advanced.database.useNumberIdmoved:advanced.database.useNumberIdhas been moved toadvanced.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-networkCloudflare Runtime
Better Auth uses AsyncLocalStorage for async context tracking.
To enable this in Cloudflare Workers, add the nodejs_compat flag to your wrangler.toml:
compatibility_flags = ["nodejs_compat"]
compatibility_date = "2024-09-23"Alternatively, if you only need AsyncLocalStorage support:
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. AuthClientType 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!