Call Admin routes with API key

So, I created a System user of role admin and then created an API key like this:
const apiKey = await auth.api.createApiKey({
body: {
name: 'System',
userId: '01991763-29cc-7409-9136-98d127723df9',
rateLimitEnabled: false,
permissions: {
user: ['create','list','set-role','ban','impersonate','delete','set-password'],
session: ['list', 'revoke', 'delete'],
},
},
});
const apiKey = await auth.api.createApiKey({
body: {
name: 'System',
userId: '01991763-29cc-7409-9136-98d127723df9',
rateLimitEnabled: false,
permissions: {
user: ['create','list','set-role','ban','impersonate','delete','set-password'],
session: ['list', 'revoke', 'delete'],
},
},
});
When trying to use the returned API Key to call the setUserPassword or listUsers I get this error:
{
"type": "Error",
"message": "",
"stack": "APIError",
"status": "UNAUTHORIZED",
"headers": {},
"statusCode": 401,
"name": "APIError"
}
{
"type": "Error",
"message": "",
"stack": "APIError",
"status": "UNAUTHORIZED",
"headers": {},
"statusCode": 401,
"name": "APIError"
}
Here is the example of how its being called:
const data = await auth.api
.setUserPassword({
body: {
newPassword,
userId: user.userId,
},
headers: new Headers({
'x-api-key': env.BETTER_AUTH_SYSTEM_KEY,
}),
});

const list = await auth.api
.listUsers({
headers: new Headers({
'x-api-key': <apiKey>,
}),
query: {},
});
const data = await auth.api
.setUserPassword({
body: {
newPassword,
userId: user.userId,
},
headers: new Headers({
'x-api-key': env.BETTER_AUTH_SYSTEM_KEY,
}),
});

const list = await auth.api
.listUsers({
headers: new Headers({
'x-api-key': <apiKey>,
}),
query: {},
});
I've tried getting a session from the API Key and it does work
const session = await auth.api
.getSession({
headers: new Headers({
'x-api-key': <apiKey>,
}),
})
{
"user": {
"username": "system",
"role": "admin",
"id": "01991763-29cc-7409-9136-98d127723df9"
...
},
"session": {
"userId": "01991763-29cc-7409-9136-98d127723df9",
...
}
}
const session = await auth.api
.getSession({
headers: new Headers({
'x-api-key': <apiKey>,
}),
})
{
"user": {
"username": "system",
"role": "admin",
"id": "01991763-29cc-7409-9136-98d127723df9"
...
},
"session": {
"userId": "01991763-29cc-7409-9136-98d127723df9",
...
}
}
But this does fail
const permissionsCheck = await auth.api
.userHasPermission({
body: {
permission: {
user: ['set-password', 'list'],
},
},
headers: new Headers({
'x-api-key': <apiKey>,
}),
})
const permissionsCheck = await auth.api
.userHasPermission({
body: {
permission: {
user: ['set-password', 'list'],
},
},
headers: new Headers({
'x-api-key': <apiKey>,
}),
})
4 Replies
Ping
Ping•2mo ago
in the createApiKey call, you need to pass user headers
Monopolo11
Monopolo11OP•2mo ago
Thanks! Just tested ths but I'm still getting the same error, any clues on what else could it be? @Max any clues? 😬
Ping
Ping•2mo ago
Can you show me your auth config?
Monopolo11
Monopolo11OP•2mo ago
import { db } from '@tms/db/client';
import { betterAuth } from 'better-auth';
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
import { admin, apiKey, customSession, username } from 'better-auth/plugins';
import { env } from './env';
import { uuid } from './utils/utils';

export const auth = betterAuth({
emailAndPassword: { enabled: true },
database: drizzleAdapter(db, {
provider: 'pg',
}),
logger: {
disabled: false,
level: 'debug',
},
trustedOrigins: [...env.CORS.split(',')],
plugins: [
username(),
admin({
adminUserIds: ['01991763-29cc-7409-9136-98d127723df9'],
adminRoles: ['admin', 'user'],
}),
apiKey({
apiKeyHeaders: ['x-api-key'],
}),
customSession(async ({ user, session }) => {
const profile = await db.query.profile.findFirst({
with: {
roles: {
columns: { name: true },
with: {
feature: {
columns: {
name: true,
},
},
},
},
},
where: {
userId: user.id,
},
});

if (!profile) {
throw new Error('Profile not found');
}

const roles = profile.roles.map(
(role) =>
`${role.feature?.name?.toLowerCase()}:${role.name.toLowerCase()}`
);

return {
...session,
user,
roles,
profileId: profile.id,
userType: profile.userType,
};
}),
],
advanced: {
database: {
generateId: () => uuid(),
},
},
});
import { db } from '@tms/db/client';
import { betterAuth } from 'better-auth';
import { drizzleAdapter } from 'better-auth/adapters/drizzle';
import { admin, apiKey, customSession, username } from 'better-auth/plugins';
import { env } from './env';
import { uuid } from './utils/utils';

export const auth = betterAuth({
emailAndPassword: { enabled: true },
database: drizzleAdapter(db, {
provider: 'pg',
}),
logger: {
disabled: false,
level: 'debug',
},
trustedOrigins: [...env.CORS.split(',')],
plugins: [
username(),
admin({
adminUserIds: ['01991763-29cc-7409-9136-98d127723df9'],
adminRoles: ['admin', 'user'],
}),
apiKey({
apiKeyHeaders: ['x-api-key'],
}),
customSession(async ({ user, session }) => {
const profile = await db.query.profile.findFirst({
with: {
roles: {
columns: { name: true },
with: {
feature: {
columns: {
name: true,
},
},
},
},
},
where: {
userId: user.id,
},
});

if (!profile) {
throw new Error('Profile not found');
}

const roles = profile.roles.map(
(role) =>
`${role.feature?.name?.toLowerCase()}:${role.name.toLowerCase()}`
);

return {
...session,
user,
roles,
profileId: profile.id,
userType: profile.userType,
};
}),
],
advanced: {
database: {
generateId: () => uuid(),
},
},
});

Did you find this page helpful?