WorkOS to Better Auth feature mapping
WorkOS features map to Better Auth as follows: Single Sign-On uses SSO Plugin; Email + Password is built-in; Passkeys uses Passkey Plugin; Social Login is built-in with more providers; Multi-Factor Auth uses Two Factor Plugin; Magic Auth uses Magic Link Plugin; CLI Auth uses Device Authorization Plugin; API Keys uses API Key Plugin; Custom Emails is fully customizable; Directory Provisioning uses SCIM Plugin; Domain Verification uses SSO Plugin; Email Verification is built-in; Identity Linking is built-in; Impersonation uses Admin Plugin; JWT Templates uses JWT Plugin; Metadata External IDs can be added by extending core schema; Roles and Permissions uses Organization Plugin.
WorkOS features partially supported in Better Auth
JIT Provisioning is partially supported via SSO Plugin. Invitations have no ready-to-use dashboard but can be implemented using Admin Plugin + Organization Plugin. Organization Policies are partially supported but can be fully implemented using SSO Plugin + Organization Plugin hooks.
WorkOS password hash migration limitation
WorkOS does not provide an export of password hashes. After migration to Better Auth, users will need to reset their passwords. Notify users of this change with sufficient lead time.
WorkOS webhook synchronization migration consideration
If previously using Webhooks with WorkOS for data synchronization, additional adjustments will be needed with Better Auth since you now fully own your authentication system and can manage data freely through the API.
Active sessions not migrated from WorkOS
Existing active sessions will not be migrated when moving from WorkOS to Better Auth. After migration, users will need to sign in again. Notify users in advance of this requirement.
Zero downtime migration from WorkOS is challenging
Due to constraints like inability to export password hashes, performing a zero downtime migration from WorkOS to Better Auth is challenging. Plan the migration carefully, allow enough buffer time, and communicate the expected impact to users.
Bundle size optimization with better-auth/minimal
When using database adapters (Drizzle, Prisma, MongoDB, or community adapters), import betterAuth from 'better-auth/minimal' instead of 'better-auth' to reduce bundle size. See the Bundle Size Optimization guide for more information.
Mount Better Auth handler on Encore with api.raw()
In Encore, mount Better Auth as a catch-all endpoint using 'api.raw()' with the path '/api/auth/*path' and method '*'. Use 'toNodeHandler' from 'better-auth/node' to bridge Encore's Node.js request/response types to Better Auth's Web API handler.
Encore Better Auth handler example code
In auth/handler.ts:
import { api } from "encore.dev/api";
import { toNodeHandler } from "better-auth/node";
import { auth } from "./auth";
export const authHandler = api.raw(
{ expose: true, path: "/api/auth/*path", method: "*" },
toNodeHandler(auth)
);
Configure CORS in encore.app for credentials
In encore.app, configure 'global_cors' with 'allow_origins_with_credentials' array to allow cookies to be sent with requests from frontend origins running on different origins.
CORS encore.app configuration example
{
"id": "your-app",
"global_cors": {
"allow_origins_with_credentials": ["http://localhost:3000"]
}
}
Protecting endpoints in Encore with Better Auth
Create an auth handler in Encore that validates Better Auth sessions by calling 'auth.api.getSession()' with headers containing Authorization and Cookie. Then protect endpoints by setting 'auth: true' in the api decorator.
Encore auth gateway handler example
In auth/gateway.ts:
import { APIError, Gateway, Header } from "encore.dev/api";
import { authHandler } from "encore.dev/auth";
import { auth } from "./auth";
interface AuthParams {
authorization: Header<"Authorization">;
cookie: Header<"Cookie">;
}
interface AuthData {
userID: string;
email: string;
name: string;
}
const handler = authHandler(async (params: AuthParams): Promise<AuthData> => {
const headers = new Headers();
if (params.authorization) {
headers.set("Authorization", params.authorization);
}
if (params.cookie) {
headers.set("Cookie", params.cookie);
}
const session = await auth.api.getSession({ headers });
if (!session?.user) {
throw APIError.unauthenticated("invalid session");
}
return {
userID: session.user.id,
email: session.user.email,
name: session.user.name,
};
});
export const gateway = new Gateway({ authHandler: handler });
Protect Encore endpoint with auth true
To protect an Encore endpoint with Better Auth, set 'auth: true' in the api decorator and retrieve auth data with 'getAuthData()' function inside the endpoint handler.
Local development with Encore and Better Auth
Start Encore apps with 'encore run' command. Docker must be running as Encore uses it to manage local infrastructure. The local dashboard at 'localhost:9400' shows traces for all requests including auth handler execution and session validation.
Auth.js support and migration recommendation
Existing Auth.js/NextAuth.js users can continue using the project without disruption as security patches and urgent issues will be addressed. However, new projects are strongly recommended to start with Better Auth unless there are specific feature gaps, notably stateless session management without a database, which is on Better Auth's roadmap.
Migration guides available
Better Auth provides a guide for teams considering migration from Auth.js/NextAuth.js, with additional guides and documentation planned. A NextAuth migration guide is available at /docs/guides/next-auth-migration-guide.
Supabase Auth to Better Auth migration overview
A migration guide exists for moving authentication from Supabase Auth to Better Auth with PlanetScale PostgreSQL as the database backend. The migration invalidates all active sessions and does not currently cover migrating two-factor (2FA) or Row Level Security (RLS) configurations, though both should be possible with additional steps.
User migration from Supabase Auth to Better Auth
User migration script is provided in the guide that handles: querying Supabase users and identities, creating Better Auth user records with mapped fields (id, email, name, role, emailVerified, image, createdAt, updatedAt, isAnonymous), and creating account records for both credential and social provider identities. The script uses ctx.adapter.create() for user and account model creation.