Maqed
Maqed
BABetter Auth
Created by Maqed on 5/9/2025 in #bug-reports
CRITICAL BUG IN PRODUCTION: authClient.useSession returns the session of a random user
I used authClient.useSession in the development environment, everything is working perfectly fine. When I tried to use it in production, it gets the session of a random user all of the time even if I log out & sign in again, it gets that random user. I "solved" this issue by getting the session on the server for now....
18 replies
BABetter Auth
Created by Maqed on 5/4/2025 in #help
making a custom id with advanced.database.useNumberId = true not working
I'm trying to create a custom int id for the user from 1 to 999999. But the problem it goes to the default and creates a user with autoIncremented id
const auth = betterAuth({
database: prismaAdapter(db, {
provider: "postgresql",
}),
advanced: {
database: { useNumberId: true },
},
databaseHooks: {
user: {
create: {
before: async (user) => {
let randomId = generateRandomInt();
// To check if there's a user with this ID
while (await db.user.findUnique({ where: { id: randomId } }))
randomId = generateRandomInt();
return {
data: {
...user,
id: randomId as unknown as string,
},
};
},
},
},
},
})
const auth = betterAuth({
database: prismaAdapter(db, {
provider: "postgresql",
}),
advanced: {
database: { useNumberId: true },
},
databaseHooks: {
user: {
create: {
before: async (user) => {
let randomId = generateRandomInt();
// To check if there's a user with this ID
while (await db.user.findUnique({ where: { id: randomId } }))
randomId = generateRandomInt();
return {
data: {
...user,
id: randomId as unknown as string,
},
};
},
},
},
},
})
5 replies
BABetter Auth
Created by Maqed on 5/2/2025 in #bug-reports
authClient.admin.removeUser doesn't trigger beforeDelete
I'm using the admin plugin. It has authClient.admin.removeUser functionality. the problem is that I set up user beforeDelete to delete all of the images from my s3 bucket before removing the user and it doesn't work. It works if and only if the user deletes himself manually. user deletion from the admin side:
async function handleDelete() {
await authClient.admin.removeUser({
//@ts-expect-error
userId,
});
router.push("/admin/dashboard");
router.refresh();
setOpen(false);
}
async function handleDelete() {
await authClient.admin.removeUser({
//@ts-expect-error
userId,
});
router.push("/admin/dashboard");
router.refresh();
setOpen(false);
}
better-auth instance:
const auth = betterAuth({
user: {
deleteUser: {
beforeDelete: async (user) => {
// images deletion logic
const userChatRoomImages = (
await db.chatRoom.findUnique({
where: {
userId: Number(user.id),
},
select: { images: true },
})
)?.images;
userChatRoomImages?.map(async (image) => await deleteImage(image.key));
},
enabled: true,
},
}
})
const auth = betterAuth({
user: {
deleteUser: {
beforeDelete: async (user) => {
// images deletion logic
const userChatRoomImages = (
await db.chatRoom.findUnique({
where: {
userId: Number(user.id),
},
select: { images: true },
})
)?.images;
userChatRoomImages?.map(async (image) => await deleteImage(image.key));
},
enabled: true,
},
}
})
7 replies
BABetter Auth
Created by Maqed on 4/19/2025 in #help
Trying to make my plugin work as npm package with bun
Hello everyone, I built a better-auth plugin. It works perfectly when I use it directly in my app. But it doesn't work when I try to put it as an npm package. I tried to bun link it with
bun link my-plugin
bun link my-plugin
and copied the way @better-auth/stripe handles it. But it still gives me this error: Module not found: Can't resolve 'my-plugin' 1 | import { betterAuth} from "better-auth";
2 | import { myPlugin } from "my-plugin";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Module not found: Can't resolve 'my-plugin/client' 1 | import { createAuthClient } from "better-auth/react";
2 | import { myPluginClient} from "my-plugin/client";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14 replies
BABetter Auth
Created by Maqed on 4/15/2025 in #help
updating schema field of type: string[] doesn't work.
No description
11 replies
BABetter Auth
Created by Maqed on 4/12/2025 in #help
How can I get member with user in adapter
No description
5 replies
BABetter Auth
Created by Maqed on 4/12/2025 in #help
Error generating schema
No description
11 replies
BABetter Auth
Created by Maqed on 4/12/2025 in #help
How can I get other plugin's option in my plugin?
I'm building a plugin to extend organization plugin. I want to get organizationOptions so I can handle other stuff in the plugin. How can I achieve that? I tried
ctx.context.options.plugins?.find((plugin) => plugin.id === "organization")?.options
ctx.context.options.plugins?.find((plugin) => plugin.id === "organization")?.options
but that didn't work even though organization plugin is setup correctly...
7 replies
BABetter Auth
Created by Maqed on 4/10/2025 in #help
How to make two unique fields in a schema
No description
2 replies
BABetter Auth
Created by Maqed on 4/7/2025 in #bug-reports
Reference naming doesn't fit all use-cases
No description
10 replies
BABetter Auth
Created by Maqed on 4/3/2025 in #help
Plugin to extend organization plugin
Hello everyone 👋 I'm building a plugin to extend organizations by making sub-organizations. The idea is that I want to extend organization schema to include sub-organizations to make it one-to-many (one organization to many sub-organization) how can I achieve such thing? I'm thinking of auth.ts:
import { betterAuth } from "better-auth"
import { organization } from "better-auth/plugins"
import {subOrganization} from "./subOrganization"

const auth = betterAuth({
plugins: [
organization({
schema: {
organization: {
fields: {
name: "subOrganizationId"
}
}
}
}),
subOrganization({
// ...
})
]
})
import { betterAuth } from "better-auth"
import { organization } from "better-auth/plugins"
import {subOrganization} from "./subOrganization"

const auth = betterAuth({
plugins: [
organization({
schema: {
organization: {
fields: {
name: "subOrganizationId"
}
}
}
}),
subOrganization({
// ...
})
]
})
subOrganization.ts
import type { BetterAuthPlugin } from "better-auth";

export const subOrganization= ()=>{
return {
id: "subOrganization",
schema: {
organization: {
fields: {
name: "organizationId"
}
}
}
// rest of the subOrganization logic
} satisfies BetterAuthPlugin
}
import type { BetterAuthPlugin } from "better-auth";

export const subOrganization= ()=>{
return {
id: "subOrganization",
schema: {
organization: {
fields: {
name: "organizationId"
}
}
}
// rest of the subOrganization logic
} satisfies BetterAuthPlugin
}
I don't really like this implementation as I think it would be better If I could handle all of the schema logic inside my subOrganization plugin. Is there any way better than this to handle it? thank you in advance
2 replies
BABetter Auth
Created by Maqed on 2/11/2025 in #help
Link phone number to existing users
As title says. Is there a way to link phone number to existing users? And after linking the phone number the user can sign in with this phone number.
2 replies
BABetter Auth
Created by Maqed on 2/5/2025 in #help
Can I use Phone Number plugin to just add phone number to existing users?
Basically I just wanna add phone number to existing users. Can I use Phone number plugin to do so or should I implement it myself? because Everytime I verify the phone number. callbackOnVerification return null for the user. Here is my code: client:
await authClient.phoneNumber.verify({
phoneNumber,
code,
disableSession: true,
fetchOptions: {
onError: (ctx) => {
form.setError("code", {
message: `phone-number.OTP.${ctx.error.code}`,
});
},
onSuccess: () => {
setIsDialogOpen(false);
setDialogState("InputNumber");
},
},
});
});
await authClient.phoneNumber.verify({
phoneNumber,
code,
disableSession: true,
fetchOptions: {
onError: (ctx) => {
form.setError("code", {
message: `phone-number.OTP.${ctx.error.code}`,
});
},
onSuccess: () => {
setIsDialogOpen(false);
setDialogState("InputNumber");
},
},
});
});
/lib/auth.ts:
export const auth = betterAuth({
database: prismaAdapter(db, {
provider: "postgresql",
}),
plugins: [
phoneNumber({
sendOTP: ({ phoneNumber, code }, request) => {
// TODO: Implement sending OTP code via Whatsapp
console.log({
phoneNumber,
code,
});
},
callbackOnVerification: async ({ phoneNumber, user }, request) => {
console.log({
user,
});
// await db.user.update({
// where: {
// id: user?.id,
// },
// data: {
// phoneNumber,
// phoneNumberVerified: true,
// },
// });
},
}),
],
});
export const auth = betterAuth({
database: prismaAdapter(db, {
provider: "postgresql",
}),
plugins: [
phoneNumber({
sendOTP: ({ phoneNumber, code }, request) => {
// TODO: Implement sending OTP code via Whatsapp
console.log({
phoneNumber,
code,
});
},
callbackOnVerification: async ({ phoneNumber, user }, request) => {
console.log({
user,
});
// await db.user.update({
// where: {
// id: user?.id,
// },
// data: {
// phoneNumber,
// phoneNumberVerified: true,
// },
// });
},
}),
],
});
11 replies
BABetter Auth
Created by Maqed on 2/2/2025 in #help
Session fetched in the middleware doesn't update on every request.
As shown in the video, After signing in. I can go back to /login page even though I protected it using middleware.ts. I even tried no-storing the session but in vain. Here is middleware.ts:
import createMiddleware from "next-intl/middleware";
import { locales } from "./config";
import { NextRequest, NextResponse } from "next/server";
import {
ONLY_UNAUTHENTICATED_ROUTES,
DEFAULT_UNAUTHENTICATED_REDIRECT,
PROTECTED_ROUTES,
DEFAULT_LOGIN_REDIRECT,
} from "./consts/routes";
import { absoluteURL } from "./lib/utils";

const intlMiddleware = createMiddleware({
// A list of all locales that are supported
locales,

// Used when no locale matches
defaultLocale: "ar",
});

export default async function middleware(req: NextRequest) {
const data = await fetch(absoluteURL("/api/auth/get-session"), {
headers: {
cookie: req.headers.get("cookie") || "",
},
cache: "no-cache",
});
const session = await data.json();

// Remove locale and clean pathname
const localeMatch = req.nextUrl.pathname.match(/^\/(ar|en)(\/|$)/);
const locale = localeMatch ? localeMatch[1] : null;
const pathnameWithoutLocale = locale
? req.nextUrl.pathname.replace(`/${locale}`, "") || "/"
: req.nextUrl.pathname;

const isProtectedRoute = PROTECTED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
);

if (isProtectedRoute && !session) {
return NextResponse.redirect(
new URL(DEFAULT_UNAUTHENTICATED_REDIRECT, req.url),
);
}

if (
session &&
ONLY_UNAUTHENTICATED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
)
) {
return NextResponse.redirect(new URL(DEFAULT_LOGIN_REDIRECT, req.url));
}

return intlMiddleware(req);
}
import createMiddleware from "next-intl/middleware";
import { locales } from "./config";
import { NextRequest, NextResponse } from "next/server";
import {
ONLY_UNAUTHENTICATED_ROUTES,
DEFAULT_UNAUTHENTICATED_REDIRECT,
PROTECTED_ROUTES,
DEFAULT_LOGIN_REDIRECT,
} from "./consts/routes";
import { absoluteURL } from "./lib/utils";

const intlMiddleware = createMiddleware({
// A list of all locales that are supported
locales,

// Used when no locale matches
defaultLocale: "ar",
});

export default async function middleware(req: NextRequest) {
const data = await fetch(absoluteURL("/api/auth/get-session"), {
headers: {
cookie: req.headers.get("cookie") || "",
},
cache: "no-cache",
});
const session = await data.json();

// Remove locale and clean pathname
const localeMatch = req.nextUrl.pathname.match(/^\/(ar|en)(\/|$)/);
const locale = localeMatch ? localeMatch[1] : null;
const pathnameWithoutLocale = locale
? req.nextUrl.pathname.replace(`/${locale}`, "") || "/"
: req.nextUrl.pathname;

const isProtectedRoute = PROTECTED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
);

if (isProtectedRoute && !session) {
return NextResponse.redirect(
new URL(DEFAULT_UNAUTHENTICATED_REDIRECT, req.url),
);
}

if (
session &&
ONLY_UNAUTHENTICATED_ROUTES.some((route) =>
pathnameWithoutLocale.startsWith(route),
)
) {
return NextResponse.redirect(new URL(DEFAULT_LOGIN_REDIRECT, req.url));
}

return intlMiddleware(req);
}
NOTE: same issue occurs when I sign out and I'm in a protected route. I don't get routed to /login for some reason.
45 replies
BABetter Auth
Created by Maqed on 2/2/2025 in #help
Client side session isn't removed after deleting user
No description
4 replies