listUsers returns status 401 UNAUTHORIZED even though the current user is an admin.
I am using the
admin()
and adminClient()
plugins while testing out better-auth. I am trying to list out the users using the auth client, but I am getting a 401 unauthorized. I've registered a user and manually changed the role to admin
in the database. I am using nextjs15
Here's the code:
Here are the console logs:
What am I missing? 😅8 Replies
Okay, so I realized I was using the
authClient
on the server side. I've changed it since to use the server side auth. However, the issue persists. This is what I changed it to:
I added the manual auth check, thinking that it might be the culprit. The session logs correctly and the role is still set to admin
, however I get the same error as before
It turns out you need to pass in the headers the same way you would with any other auth form, so this is the final solution to the conundrum
i have the same issue🥲
@belikebee Have you seen my latest message? It turns out you just need to pass in the
headers: await headers()
to the object because it is a server function and can't know if there's a session on the request time without including the headers.yeah. it helped me...thx😍
@invocation97 Thankyou soo much 🙏 Was facing the same issue. They should include this in the docs
Hi!
I did something like this:
"use client";
import { authClient } from "@/lib/auth-client";
import React, { useEffect, useState } from "react";
import {
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
interface User {
id: string;
name: string;
email: string;
emailVerified: boolean;
createdAt: string;
updatedAt: string;
role: string;
}
export default function ListAdm() {
const [users, setUsers] = useState<User[]>([]);
useEffect(() => {
async function fetchUsers() {
const response: any = await authClient.admin.listUsers({
query: { limit: 100 },
});
setUsers(response.data.users);
}
fetchUsers();
}, []);
if (!users || users.length === 0) {
return (
<div className="space-y-4">
<p>No users found.</p>
</div>
);
}
return (
<>
<Table>
<TableCaption>Lista de usuários</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Nome</TableHead>
<TableHead className="w-[100px]">E-mail</TableHead>
<TableHead className="text-right">Ação</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{users.map((user, index) => (
<TableRow key={index}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell className="font-medium">{user.email}</TableCell>
<TableCell className="text-right">BOTAO</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</>
);
}
I'm using authClient.admin.listUsers instead of using auth.api.listUsers.
Is this a good way to do it?
And I'm thinking how to a get a instant refresh when my auth component creates an user.
@invocation97Hey 😉
That's more of a "react" way to do it, which is completely fine, but you're loosing the benefits of nextjs server rendering. It works because the client already has the headers containing the information about the current users session (I'm assuming you're logged in as the admin).
The idea is that the HTML is rendered ahead of time in my example, and in yours the
<script>
tag containing your javaScript is in the initial HTML, then it runs and gets the data, and lastly it appends it to the DOM. That process is obviously a bit slower and has more requests. The way you can improve it, if you want to keep it client-side, is you could use a fetching library like (tanstack query)[https://tanstack.com/query/latest/docs/framework/react/overview]. There you can set a key for each query, which you can invalidate whenever you do a mutation, which in your case would be doing any CRUD operations with the users. Just make sure that the keys match, and the data would get re-fetched everywhere. However, I'd still recommend my approach for Nextjs specifically since it is inherently faster.Got it, thanks so much for sharing with me! 😃
Your approach fits much better, taking advantage of the speed of Next.js
I have an admin authentication form that creates the user and is displayed in that list. For the authentication, I'm using authClient.admin.createUser and router.fresh which refreshes the page.
And for the list, I'm using your approach with auth.api.listUsers.
I thought I could use authClient.admin for everything just for the actions and admin pages. But by using that, I'd have to use ‘use client’ and wouldn't take advantage of the power of Next.js, right?