mirror of
https://github.com/better-auth/better-auth.git
synced 2026-07-31 20:05:38 -05:00
548 lines
17 KiB
Plaintext
548 lines
17 KiB
Plaintext
---
|
|
title: "Better Auth 1.4"
|
|
description: "Stateless Auth, SCIM, Optimized database queries, additional state in OAuth flows, performance improvements, SSO domain verification, and more!"
|
|
date: 2025-11-21
|
|
author:
|
|
name: "Bereket Engida"
|
|
avatar: "/avatars/beka.jpg"
|
|
twitter: "bekacru"
|
|
image: "https://docs.better-auth.com/release-og/1-4.png"
|
|
tags: ["1.4", "stateless sessions", "SCIM", "database indexing", "database join", "SSO domain verification", "uuid", "device authorization"]
|
|
---
|
|
|
|
## 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:
|
|
|
|
```package-install
|
|
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](/docs/concepts/session-management#stateless-session-management)
|
|
|
|
```ts
|
|
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.
|
|
|
|
```ts
|
|
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](/docs/plugins/scim)
|
|
|
|
```ts
|
|
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](/docs/concepts/oauth#passing-additional-data-through-oauth-flow)
|
|
|
|
```ts
|
|
import { authClient } from "@/lib/auth-client";
|
|
|
|
await authClient.signIn.social({
|
|
provider: "google",
|
|
additionalData: { // [!code highlight]
|
|
referralCode: "ABC123", // [!code highlight]
|
|
source: "landing-page", // [!code highlight]
|
|
}, // [!code highlight]
|
|
});
|
|
```
|
|
|
|
Access additional data in any hook, middleware, or endpoints
|
|
|
|
```ts title="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](/docs/concepts/database#experimental-joins)
|
|
|
|
```ts
|
|
import { betterAuth } from "better-auth";
|
|
|
|
export const auth = betterAuth({
|
|
experimental: {
|
|
joins: true,
|
|
},
|
|
});
|
|
```
|
|
|
|
<Callout type="info">
|
|
Although joins are experimental, they are supported by all adapters and will be enabled by default in the next release.
|
|
</Callout>
|
|
|
|
---
|
|
|
|
### 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](/docs/plugins/api-key#storage-modes)
|
|
|
|
```ts
|
|
import { apiKey } from "@better-auth/api-key";
|
|
import { betterAuth } from "better-auth";
|
|
|
|
export const auth = betterAuth({
|
|
secondaryStorage: {
|
|
//...
|
|
},
|
|
plugins: [apiKey({ storage: "secondary-storage" })], // [!code highlight]
|
|
});
|
|
```
|
|
|
|
<Accordions type="single">
|
|
<Accordion title="How To Migrate?">
|
|
1. Start with `fallbackToDatabase` enabled and `storage` set to `"secondary-storage"`:
|
|
|
|
```ts
|
|
import { apiKey } from "@better-auth/api-key";
|
|
import { betterAuth } from "better-auth";
|
|
|
|
export const auth = betterAuth({
|
|
secondaryStorage: {
|
|
//...
|
|
},
|
|
plugins: [apiKey({ storage: "secondary-storage", fallbackToDatabase: true })],
|
|
});
|
|
```
|
|
|
|
This allows existing keys in the database to be accessible while new keys are stored in the secondary storage.
|
|
|
|
2. **Migrate existing keys** (optional): For faster initial cache population, you can write a migration script
|
|
to copy all keys from the database to the secondary storage. However, this is optional since the cache will warm up automatically as keys are accessed.
|
|
|
|
3. **Disable fallback**: Once all keys are migrated or when you're confident the cache is sufficiently populated:
|
|
|
|
```ts
|
|
import { apiKey } from "@better-auth/api-key";
|
|
import { betterAuth } from "better-auth";
|
|
|
|
export const auth = betterAuth({
|
|
plugins: [
|
|
apiKey({
|
|
storage: "secondary-storage",
|
|
fallbackToDatabase: false // omit, false or undefined // [!code highlight]
|
|
})
|
|
],
|
|
});
|
|
```
|
|
|
|
</Accordion>
|
|
</Accordions>
|
|
|
|
|
|
---
|
|
|
|
### 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](/docs/reference/options#onapierror)
|
|
|
|
<img src="/images/blogs/1-4_error.png" alt="Error Page" className="w-full max-w-2xl mx-auto rounded-lg border border-border" />
|
|
|
|
---
|
|
|
|
### 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](/docs/plugins/sso#domain-verification)
|
|
|
|
```ts
|
|
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.
|
|
|
|
```bash
|
|
npx auth generate
|
|
|
|
npx auth migrate
|
|
```
|
|
|
|
---
|
|
|
|
### JWT Key Rotation Support
|
|
|
|
The JWT plugin now supports key rotation, allowing you to rotate your JWT keys without downtime.
|
|
|
|
```ts
|
|
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 } // [!code highlight]
|
|
})
|
|
],
|
|
});
|
|
```
|
|
👉 [Read more about JWT key rotation](/docs/plugins/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.
|
|
|
|
```ts
|
|
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](/docs/guides/optimizing-for-performance#bundle-size-optimization)
|
|
|
|
```ts
|
|
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
import { betterAuth } from "better-auth/minimal"; // [!code highlight]
|
|
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](/docs/concepts/database#uuids)
|
|
|
|
```ts
|
|
import { betterAuth } from "better-auth";
|
|
import { db } from "./database";
|
|
|
|
export const auth = betterAuth({
|
|
database: db,
|
|
advanced: {
|
|
database: { generateId: "uuid" }, // [!code highlight]
|
|
},
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
### 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](/docs/concepts/users-accounts#confirming-with-current-email)
|
|
|
|
```ts
|
|
import { betterAuth } from "better-auth";
|
|
|
|
export const auth = betterAuth({
|
|
user: {
|
|
changeEmail: {
|
|
enabled: true,
|
|
sendChangeEmailConfirmation: async ({ user, newEmail, url, token }, request) => {
|
|
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](/docs/plugins/device-authorization)
|
|
|
|
```ts
|
|
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.
|
|
|
|
```ts
|
|
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:
|
|
|
|
```ts
|
|
auth.api.accountInfo({
|
|
body: {
|
|
// ...
|
|
}
|
|
});
|
|
```
|
|
|
|
to:
|
|
|
|
```ts
|
|
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:
|
|
|
|
```package-install
|
|
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.
|
|
|
|
```diff
|
|
- 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`:
|
|
|
|
```package-install
|
|
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`:
|
|
|
|
```toml title="wrangler.toml"
|
|
compatibility_flags = ["nodejs_compat"]
|
|
compatibility_date = "2024-09-23"
|
|
```
|
|
|
|
Alternatively, if you only need AsyncLocalStorage support:
|
|
```toml title="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](/docs/reference/errors).
|
|
- **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](https://github.com/better-auth/better-auth/releases/tag/v1.4.0)
|
|
|
|
## ❤️ Contributors
|
|
|
|
Thanks to all the contributors for making this release possible!
|
|
|
|
<Contributors usernames={[
|
|
"zain", "dandamian", "hartbit", "rmarscher", "kaandok", "acusti", "rbayliss", "kevcube", "bsklaroff", "jonathansamines",
|
|
"mburumaxwell", "erquhart", "dmmulroy", "iRoachie", "mohebifar", "thomasmol", "borgoat", "XiNiHa", "Berndwl", "xiaoyu2er",
|
|
"ebalo55", "udnes99", "himself65", "dvanmali", "AlexStrNik", "ThibautCuchet", "zbeyens", "xuchenhao001", "tnkuehne", "Tobix99",
|
|
"hieudien14310", "3ddelano", "bdkopen", "jslno", "chhoumann", "mrl5", "tgrassl", "hyoban", "redoh", "gregtjack",
|
|
"ouwargui", "kanarian", "natetewelde", "Badbird5907", "max-programming", "eni4sure", "frectonz", "ephraimduncan", "Eazash", "ahmedriad1",
|
|
"Velka-DEV", "Kinfe123", "zy1p", "okisdev", "surafel58", "DevDuki", "TheUntraceable", "nakasyou", "JagritGumber", "HoshangDEV",
|
|
"vagxrth", "Paola3stefania", "Bekacru", "kira-1011", "almadoro", "ocherry341", "QuintenStr", "Ridhim-RR", "ahmed-abdat", "mohit4bug",
|
|
"Pankaj3112", "ShobhitPatra", "ping-maxwell", "nbifrye", "rovertrack", "GautamBytes", "AntonVishal", "bytaesu", "ic4l4s9c", "jcajuab",
|
|
"yutaka5"
|
|
]} />
|
|
|
|
---
|