-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Typescript error in auth export #5637
Description
Is this suited for github?
- Yes, this is suited for github
To Reproduce
after updating to the latest version of better auth ie "better-auth": "^1.3.33", i am facing type error in the auth export which was not before.
export const auth = betterAuth({
...
i also type caste into
export const auth: Auth = betterAuth({
...
alos tried type infer of betterAuth but didn't work.
Current vs. Expected behavior
current:
auth word shows red type error
expected:
no type error
What version of Better Auth are you using?
^1.3.33
System info
{
"system": {
"platform": "linux",
"arch": "x64",
"version": "#33~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Fri Sep 19 17:02:30 UTC 2",
"release": "6.14.0-33-generic",
"cpuCount": 16,
"cpuModel": "12th Gen Intel(R) Core(TM) i5-1240P",
"totalMemory": "15.25 GB",
"freeMemory": "4.68 GB"
},
"node": {
"version": "v22.17.1",
"env": "development"
},
"packageManager": {
"name": "npm",
"version": "11.5.2"
},
"frameworks": [
{
"name": "express",
"version": "^5.1.0"
}
],
"databases": null,
"betterAuth": {
"version": "^1.3.33",
"config": {
"secret": "[REDACTED]",
"baseURL": "http://localhost:3000",
"trustedOrigins": [
"http://localhost:5173"
],
"errorURL": "http://localhost:5173",
"user": {
"additionalFields": {
"isOnboardingComplete": {
"type": "boolean",
"defaultValue": false,
"required": false
},
"timezone": {
"type": "string",
"defaultValue": "Europe/London",
"required": false
}
}
},
"account": {
"accountLinking": {
"enabled": true,
"allowDifferentEmails": false,
"updateUserInfoOnLink": true
}
},
"plugins": [
{
"name": "admin",
"config": {
"id": "admin",
"hooks": {
"after": [
{}
]
},
"endpoints": {},
"$ERROR_CODES": {
"FAILED_TO_CREATE_USER": "Failed to create user",
"USER_ALREADY_EXISTS": "User already exists.",
"USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": "User already exists. Use another email.",
"YOU_CANNOT_BAN_YOURSELF": "You cannot ban yourself",
"YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": "You are not allowed to change users role",
"YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": "You are not allowed to create users",
"YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": "You are not allowed to list users",
"YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": "You are not allowed to list users sessions",
"YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": "You are not allowed to ban users",
"YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": "You are not allowed to impersonate users",
"YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": "You are not allowed to revoke users sessions",
"YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": "You are not allowed to delete users",
"YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": "[REDACTED]",
"BANNED_USER": "You have been banned from this application",
"YOU_ARE_NOT_ALLOWED_TO_GET_USER": "You are not allowed to get user",
"NO_DATA_TO_UPDATE": "No data to update",
"YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users",
"YOU_CANNOT_REMOVE_YOURSELF": "You cannot remove yourself",
"YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE": "You are not allowed to set a non-existent role value"
},
"schema": {
"user": {
"fields": {
"role": {
"type": "string",
"required": false,
"input": false
},
"banned": {
"type": "boolean",
"defaultValue": false,
"required": false,
"input": false
},
"banReason": {
"type": "string",
"required": false,
"input": false
},
"banExpires": {
"type": "date",
"required": false,
"input": false
}
}
},
"session": {
"fields": {
"impersonatedBy": {
"type": "string",
"required": false
}
}
}
},
"options": {
"defaultRole": "user",
"adminRoles": [
"admin"
]
}
}
},
{
"name": "stripe",
"config": {
"id": "stripe",
"endpoints": {},
"schema": {
"subscription": {
"fields": {
"plan": {
"type": "string",
"required": true
},
"referenceId": {
"type": "string",
"required": true
},
"stripeCustomerId": {
"type": "string",
"required": false
},
"stripeSubscriptionId": {
"type": "string",
"required": false
},
"status": {
"type": "string",
"defaultValue": "incomplete"
},
"periodStart": {
"type": "date",
"required": false
},
"periodEnd": {
"type": "date",
"required": false
},
"trialStart": {
"type": "date",
"required": false
},
"trialEnd": {
"type": "date",
"required": false
},
"cancelAtPeriodEnd": {
"type": "boolean",
"required": false,
"defaultValue": false
},
"seats": {
"type": "number",
"required": false
}
}
},
"user": {
"fields": {
"stripeCustomerId": {
"type": "string",
"required": false
}
}
}
}
}
}
],
"emailAndPassword": {
"enabled": true,
"requireEmailVerification": true
},
"emailVerification": {
"sendOnSignUp": true,
"autoSignInAfterVerification": true
},
"session": {
"expiresIn": 604800,
"updateAge": 86400
},
"advanced": {
"defaultCookieAttributes": {
"sameSite": "lax",
"secure": false,
"httpOnly": true
}
},
"socialProviders": {
"google": {
"prompt": "select_account consent",
"clientId": "[REDACTED]",
"clientSecret": "[REDACTED]",
"scope": [
"openid",
"email",
"profile",
"https://www.googleapis.com/auth/gmail.send"
],
"accessType": "offline"
}
},
"databaseHooks": {
"account": {
"create": {}
}
},
"hooks": {}
}
}
}Which area(s) are affected? (Select all that apply)
Types
Auth config (if applicable)
import {
BETTER_AUTH_SECRET,
BETTER_AUTH_URL,
CORS_ORIGIN,
GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET,
IS_PROD,
REQUIRE_EMAIL_VERIFICATION,
STRIPE_SECRET_KEY,
STRIPE_WEBHOOK_SECRET,
} from "@/config/constants";
import {
addProviderEmail,
ProviderId,
} from "@/features/user/services/addProviderEmail.service";
import { MailClient } from "@/lib/mail-client";
import { client } from "@/lib/mongo";
import { stripe } from "@better-auth/stripe";
import { userRoleEnum } from "@packages/shared/schemas/user";
import { logger } from "@packages/shared/utils";
import { betterAuth } from "better-auth";
import { mongodbAdapter } from "better-auth/adapters/mongodb";
import { admin, createAuthMiddleware } from "better-auth/plugins";
import Stripe from "stripe";
import { STRIPE_PLANS } from "./stripe";
// Using MailClient object-based approach
const stripeClient = new Stripe(STRIPE_SECRET_KEY, {
apiVersion: "2025-09-30.clover", // Latest API version as of Stripe SDK v19
});
export const auth = betterAuth({
database: mongodbAdapter(client),
secret: BETTER_AUTH_SECRET,
baseURL: BETTER_AUTH_URL,
trustedOrigins: [CORS_ORIGIN],
errorURL: CORS_ORIGIN,
user: {
additionalFields: {
isOnboardingComplete: {
type: "boolean",
defaultValue: false,
required: false,
},
timezone: {
type: "string",
defaultValue: "Europe/London",
required: false,
},
},
},
account: {
accountLinking: {
enabled: true,
allowDifferentEmails: false,
updateUserInfoOnLink: true,
},
},
plugins: [
admin({
defaultRole: userRoleEnum.USER,
adminRoles: [userRoleEnum.ADMIN],
}),
stripe({
stripeClient,
stripeWebhookSecret: STRIPE_WEBHOOK_SECRET,
createCustomerOnSignUp: true,
subscription: {
enabled: true,
plans: STRIPE_PLANS,
},
}),
],
emailAndPassword: {
enabled: true,
requireEmailVerification: REQUIRE_EMAIL_VERIFICATION,
sendResetPassword: async ({ user, url, token }, request) => {
await MailClient.sendPasswordResetEmail(user.email, user.name, url);
},
},
emailVerification: {
sendOnSignUp: true,
autoSignInAfterVerification: true,
sendVerificationEmail: async ({ user, url, token }, request) => {
await MailClient.sendVerificationEmail(user.email, user.name, url);
},
},
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // 1 day
},
advanced: {
defaultCookieAttributes: {
sameSite: "lax",
secure: IS_PROD,
httpOnly: true,
},
},
socialProviders: {
google: {
prompt: "select_account consent",
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
scope: [
"openid",
"email",
"profile",
"https://www.googleapis.com/auth/gmail.send",
],
accessType: "offline", // Required to get refresh tokens
},
// microsoft: {
// clientId: MICROSOFT_CLIENT_ID,
// clientSecret: MICROSOFT_CLIENT_SECRET,
// tenantId: "common",
// prompt: "select_account",
// },
},
databaseHooks: {
account: {
create: {
after: async (account) => {
// skip if providerId is credentials
if (account.providerId === "credentials") {
return;
}
// Use service to add provider email
await addProviderEmail(
account.id,
account.providerId as ProviderId,
account.accessToken || undefined
);
},
},
},
},
hooks: {
before: createAuthMiddleware(async (ctx) => {
if (ctx.path === "/error") {
const { error } = ctx.query as { error?: string };
logger.error(new Error("Auth error"), "AUTH");
let redirectUrl = `${CORS_ORIGIN}/?error=${encodeURIComponent(
"Authentication Error"
)}&error_description=${encodeURIComponent("Failed to authenticate")}`;
if (error === "banned") {
redirectUrl = `${CORS_ORIGIN}/?error=${encodeURIComponent(
"You have been banned"
)}&error_description=${encodeURIComponent("Contact support team")}`;
}
throw ctx.redirect(redirectUrl);
}
}),
},
});
// Use the shared types for consistency
export type Session = typeof auth.$Infer.Session.session;
export type User = typeof auth.$Infer.Session.user;Additional context
The inferred type of 'auth' cannot be named without a reference to '.pnpm/@better-auth+core@1.3.33_@better-auth+utils@0.3.0_@better-fetch+fetch@1.1.18_better-cal_7d993e08fee368dcdde874190a187847/node_modules/@better-auth/core/db/adapter'. This is likely not portable. A type annotation is necessary.ts(2742)
now:
type User = {
id: string;
createdAt: Date;
updatedAt: Date;
email: string;
emailVerified: boolean;
name: string;
image?: string | null | undefined;
isOnboardingComplete: boolean | null | undefined;
timezone: string | null | undefined;
banned: boolean | null | undefined;
role?: string | null | undefined;
banReason?: string | null | undefined;
banExpires?: Date | null | undefined;
}
when used:
export const auth :Auth = ...
type User = {
id: string;
createdAt: Date;
updatedAt: Date;
email: string;
emailVerified: boolean;
name: string;
image?: string | null | undefined;
}