docs/content/blogs/1-4.mdx
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 [email protected]
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 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()]
});
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: { // [!code highlight]
referralCode: "ABC123", // [!code highlight]
source: "landing-page", // [!code highlight]
}, // [!code highlight]
});
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);
})
}
});
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,
},
});
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" })], // [!code highlight]
});
```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]
})
],
});
```
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
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 } })],
});
The Better-Auth CLI now supports indexing your database out of the box, which greatly improves the performance of your application.
npx auth generate
npx auth migrate
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 } // [!code highlight]
})
],
});
š Read more about JWT key rotation
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,
})
]
})
]
});
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"; // [!code highlight]
import { db } from "./database";
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: "pg" }),
});
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" }, // [!code highlight]
},
});
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) => {
sendEmail({
to: user.email, // Sent to the CURRENT email
subject: 'Approve email change',
text: `Click the link to approve the change to ${newEmail}: ${url}`
})
}
}
}
});
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
}
});
We recommend going through each breaking change to ensure a smooth upgrade.
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: {
// ...
}
});
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.
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.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()],
});
With the new pooling features for useSession on Expo, you're required to install expo-network:
npx expo install expo-network
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.
disableSignal: Option to disable default abort signal behavior in the client.AuthClient Type Helper: New type helper for better client-side type inference.This release includes over 250+ bug fixes addressing issues across all areas:
A lot of refinements to make everything smoother, faster, and more reliable. š Check the full changelog
Thanks to all the contributors for making this release possible!
export const releaseContributorUsernames = [ // cspell:disable "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", // cspell:enable ];
<Contributors usernames={releaseContributorUsernames} />