Better Auth 1.3 Release
We're excited to announce the release of Better Auth 1.3. This release includes a lot of new features and improvements.
To upgrade, run:
npm install [email protected]
🚀 Highlights
SSO Plugin
The SSO plugin has been moved to its own package and now supports both OIDC and SAML 2.0.
import { betterAuth } from "better-auth";
import { sso } from "@better-auth/sso";
export const auth = betterAuth({
plugins: [
sso({
oidc: {
clientId: process.env.OIDC_CLIENT_ID!,
clientSecret: process.env.OIDC_CLIENT_SECRET!,
},
saml: {
entryPoint: "https://example.com/saml",
issuer: "better-auth-example",
certificate: "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
},
providersLimit: async (user) => {
const plan = await getUserPlan(user);
return plan.name === "pro" ? 10 : 1;
},
}),
],
});
OIDC & MCP Plugins – Now Stable
Both OIDC and MCP plugins are production‑ready.
✅ Features:
- Refresh token support in discovery & token endpoints
- JWKs and PKCE for public clients
- Trusted clients
- Encrypted & hashed client secrets
👉 Read OIDC docs 👉 Read MCP docs
import { mcp } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
mcp({
loginPage: "/login",
}),
],
});
Stripe Plugin is now production ready
The Stripe plugin is now stable and usage based pricing is coming very soon.
import { betterAuth } from "better-auth";
import { stripe } from "@better-auth/stripe";
export const auth = betterAuth({
plugins: [
stripe({
// ...
}),
],
});
SIWE Plugin
Native support for Sign‑In with Ethereum.
import { siwe } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
siwe(),
],
});
New Social Providers
We’ve added providers for Notion, Slack, Linear, and Faceit.
import { betterAuth } from "better-auth";
export const auth = betterAuth({
socialProviders: {
notion: { /* ... */ },
slack: { /* ... */ },
linear: { /* ... */ },
faceit: { /* ... */ },
},
});
SvelteKit Cookie Helper Plugin
Utilities for handling cookies in SvelteKit server actions.
Breaking change: building
and getRequestEvent
must now be passed in as props.
import { betterAuth } from "better-auth";
import { sveltekitCookies } from "better-auth/svelte-kit";
import { getRequestEvent } from "$app/server";
export const auth = betterAuth({
plugins: [sveltekitCookies(getRequestEvent)],
});
Email Verification on Sign‑In
export const auth = betterAuth({
emailVerification: {
sendOnSignIn: true, // sends a verification email on sign‑in if the user isn’t verified
},
});
Multi‑Team Support
The organization plugin now supports members belonging to multiple teams.
Breaking change:
teamId
has been removed from the member
table. A new teamMembers
table is required.
export const auth = betterAuth({
plugins: [
organization({
// ...
}),
],
});
import { createAuthClient } from "better-auth/client";
import { organizationClient } from "better-auth/client/plugins";
import { auth } from "./auth";
export const authClient = createAuthClient({
// pass your auth instance to infer additional fields
plugins: [organizationClient({ $inferAuth: {} as typeof auth })],
});
Additional Organization Fields
Add custom fields to organization
, member
, and invitation
models.
export const auth = betterAuth({
plugins: [
organization({
schema: {
organization: { additionalFields: { /* ... */ } },
member: { additionalFields: { /* ... */ } },
invitation: { additionalFields: { /* ... */ } },
},
}),
],
});
Other new options:
maximumMembersPerTeam
– set team member limitslistUserInvitations
– list all invitations for a user
Generic OAuth Improvements
- Added support for extra token URL params
- OAuth token encryption options
export const auth = betterAuth({
plugins: [
genericOAuth({
// ...
}),
],
});
API Keys
requireName
option for key creationverifyKey
now supports async functions
Username
- Availability checks
- Custom normalization
✨ More Features
- Migrated to Zod 4 for better type safety and performance
- CLI supports custom adapter
createSchema
inferAuth
utility to infer types from the client- Improved docs with
auth
andauthClient
examples rememberMe
support insignUp
afterEmailVerification
hookfreshAge
and customerrorURL
respected properly- OAuth2 tokens now include
refresh_token_expires_in
🐛 Bug Fixes & Improvements
Plugins
-
Expo: Fixed type path import
-
SSO: Fixed SAML redirection & type checks
-
Dropbox: Token access type support
-
Stripe:
- Prevent duplicate customers
- Allow upgrading incomplete subscriptions
-
Admin:
- Fixed missing
ctx
in hooks - Proper error when removing invalid user IDs
- Fixed missing
OAuth & Providers
- Fixed duplicate OAuth registration
- Improved Google/Microsoft scope handling
- Fixed malformed error URLs in generic OAuth
- Facebook: Better detection for limited token JWT
- Twitter: Improved email verification logic
Core Authentication
- Exclude current user from username uniqueness check
- Support
callbackURL
insignInUsername
- Allow account linking without email
- Fixed missing
null
type in/get-session
response - Global
onSuccess
hook now works - JWT: Alternate algorithms supported in JWKS
origin-check
: Wildcard trusted origins supported
CLI, DB, and Adapters
- CLI: Improved Drizzle schema formatting
- MongoAdapter: Works with
create-adapter
- Schema generation respects
useNumberId
- Postgres: Better varchar normalization and type comparison
- Drizzle CLI: Uses
serial
as PK ifuseNumberId
is enabled
Email & OTP
- OTPs now encrypted
- Fixed
onEmailVerification
not firing - Proper error when sign‑up is disabled
- Phone number: Reset clears verification values
Two-Factor Auth
- Default OTP period fix
- URI generation doesn’t require enabling 2FA
- Fixed OTP URI separator mismatch
Miscellaneous
- Delete organization if member not found
- Correct error codes for API key rate limits
- Additional fields now show in OpenAPI
- Fixed FK constraint generation for MySQL
- Various improvements to account linking
- OIDC
offline_access
no longer requiresprompt=consent
- Fixed malformed base64 encoding for token validation
A lot of refinements to make everything smoother, faster, and more reliable. 👉 Check the full changelog