fix(web): admin page role check — stop false redirect to /chat
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful

The admin page role check was failing because better-auth v1.5.5 doesn't
automatically include additionalFields (like 'role') in the session response.
This caused the admin guard to treat all users as non-admin and redirect them.

The fix implements a defensive fallback in the AdminGuard that fetches the
role from the database if it's missing from the session, ensuring that admin
users can access the admin panel while protecting against regression.

Fixes #196
This commit is contained in:
2026-03-16 21:37:53 -05:00
parent 1f2b8125c6
commit df4b7399ad
2 changed files with 59 additions and 2 deletions

View File

@@ -8,8 +8,11 @@ import {
} from '@nestjs/common';
import { fromNodeHeaders } from 'better-auth/node';
import type { Auth } from '@mosaic/auth';
import type { Db } from '@mosaic/db';
import { eq, users as usersTable } from '@mosaic/db';
import type { FastifyRequest } from 'fastify';
import { AUTH } from '../auth/auth.tokens.js';
import { DB } from '../database/database.module.js';
interface UserWithRole {
id: string;
@@ -18,7 +21,10 @@ interface UserWithRole {
@Injectable()
export class AdminGuard implements CanActivate {
constructor(@Inject(AUTH) private readonly auth: Auth) {}
constructor(
@Inject(AUTH) private readonly auth: Auth,
@Inject(DB) private readonly db: Db,
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<FastifyRequest>();
@@ -32,7 +38,21 @@ export class AdminGuard implements CanActivate {
const user = result.user as UserWithRole;
if (user.role !== 'admin') {
// Ensure the role field is populated. better-auth should include additionalFields
// in the session, but as a fallback, fetch the role from the database if needed.
let userRole = user.role;
if (!userRole) {
const [dbUser] = await this.db
.select({ role: usersTable.role })
.from(usersTable)
.where(eq(usersTable.id, user.id))
.limit(1);
userRole = dbUser?.role ?? 'member';
// Update the session user object with the fetched role
(user as UserWithRole).role = userRole;
}
if (userRole !== 'admin') {
throw new ForbiddenException('Admin access required');
}