mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 04:27:21 +00:00
added authentication, $schema, fixed media adapter mcp
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { DB } from "bknd";
|
||||
import type { DB, PrimaryFieldType } from "bknd";
|
||||
import * as AuthPermissions from "auth/auth-permissions";
|
||||
import type { AuthStrategy } from "auth/authenticate/strategies/Strategy";
|
||||
import type { PasswordStrategy } from "auth/authenticate/strategies/PasswordStrategy";
|
||||
@@ -87,6 +87,7 @@ export class AppAuth extends Module<AppAuthSchema> {
|
||||
super.setBuilt();
|
||||
|
||||
this._controller = new AuthController(this);
|
||||
this._controller.registerMcp();
|
||||
this.ctx.server.route(this.config.basepath, this._controller.getController());
|
||||
this.ctx.guard.registerPermissions(AuthPermissions);
|
||||
}
|
||||
@@ -176,6 +177,32 @@ export class AppAuth extends Module<AppAuthSchema> {
|
||||
return created;
|
||||
}
|
||||
|
||||
async changePassword(userId: PrimaryFieldType, newPassword: string) {
|
||||
const users_entity = this.config.entity_name as "users";
|
||||
const { data: user } = await this.em.repository(users_entity).findId(userId);
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
} else if (user.strategy !== "password") {
|
||||
throw new Error("User is not using password strategy");
|
||||
}
|
||||
|
||||
const togglePw = (visible: boolean) => {
|
||||
const field = this.em.entity(users_entity).field("strategy_value")!;
|
||||
|
||||
field.config.hidden = !visible;
|
||||
field.config.fillable = visible;
|
||||
};
|
||||
|
||||
const pw = this.authenticator.strategy("password" as const) as PasswordStrategy;
|
||||
togglePw(true);
|
||||
await this.em.mutator(users_entity).updateOne(user.id, {
|
||||
strategy_value: await pw.hash(newPassword),
|
||||
});
|
||||
togglePw(false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
override toJSON(secrets?: boolean): AppAuthSchema {
|
||||
if (!this.config.enabled) {
|
||||
return this.configDefault;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SafeUser } from "bknd";
|
||||
import type { DB, SafeUser } from "bknd";
|
||||
import type { AuthStrategy } from "auth/authenticate/strategies/Strategy";
|
||||
import type { AppAuth } from "auth/AppAuth";
|
||||
import * as AuthPermissions from "auth/auth-permissions";
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
transformObject,
|
||||
mcpTool,
|
||||
} from "bknd/utils";
|
||||
import type { PasswordStrategy } from "auth/authenticate/strategies";
|
||||
|
||||
export type AuthActionResponse = {
|
||||
success: boolean;
|
||||
@@ -200,4 +201,110 @@ export class AuthController extends Controller {
|
||||
|
||||
return hono;
|
||||
}
|
||||
|
||||
override registerMcp(): void {
|
||||
const { mcp } = this.auth.ctx;
|
||||
|
||||
const getUser = async (params: { id?: string | number; email?: string }) => {
|
||||
let user: DB["users"] | undefined = undefined;
|
||||
if (params.id) {
|
||||
const { data } = await this.userRepo.findId(params.id);
|
||||
user = data;
|
||||
} else if (params.email) {
|
||||
const { data } = await this.userRepo.findOne({ email: params.email });
|
||||
user = data;
|
||||
}
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
return user;
|
||||
};
|
||||
|
||||
mcp.tool(
|
||||
// @todo: needs permission
|
||||
"auth_user_create",
|
||||
{
|
||||
description: "Create a new user",
|
||||
inputSchema: s.object({
|
||||
email: s.string({ format: "email" }),
|
||||
password: s.string({ minLength: 8 }),
|
||||
role: s
|
||||
.string({
|
||||
enum: Object.keys(this.auth.config.roles ?? {}),
|
||||
})
|
||||
.optional(),
|
||||
}),
|
||||
},
|
||||
async (params, c) => {
|
||||
return c.json(await this.auth.createUser(params));
|
||||
},
|
||||
);
|
||||
|
||||
mcp.tool(
|
||||
// @todo: needs permission
|
||||
"auth_user_token",
|
||||
{
|
||||
description: "Get a user token",
|
||||
inputSchema: s.object({
|
||||
id: s.anyOf([s.string(), s.number()]).optional(),
|
||||
email: s.string({ format: "email" }).optional(),
|
||||
}),
|
||||
},
|
||||
async (params, c) => {
|
||||
const user = await getUser(params);
|
||||
return c.json({ user, token: await this.auth.authenticator.jwt(user) });
|
||||
},
|
||||
);
|
||||
|
||||
mcp.tool(
|
||||
// @todo: needs permission
|
||||
"auth_user_password_change",
|
||||
{
|
||||
description: "Change a user's password",
|
||||
inputSchema: s.object({
|
||||
id: s.anyOf([s.string(), s.number()]).optional(),
|
||||
email: s.string({ format: "email" }).optional(),
|
||||
password: s.string({ minLength: 8 }),
|
||||
}),
|
||||
},
|
||||
async (params, c) => {
|
||||
const user = await getUser(params);
|
||||
if (!(await this.auth.changePassword(user.id, params.password))) {
|
||||
throw new Error("Failed to change password");
|
||||
}
|
||||
return c.json({ changed: true });
|
||||
},
|
||||
);
|
||||
|
||||
mcp.tool(
|
||||
// @todo: needs permission
|
||||
"auth_user_password_test",
|
||||
{
|
||||
description: "Test a user's password",
|
||||
inputSchema: s.object({
|
||||
email: s.string({ format: "email" }),
|
||||
password: s.string({ minLength: 8 }),
|
||||
}),
|
||||
},
|
||||
async (params, c) => {
|
||||
const pw = this.auth.authenticator.strategy("password") as PasswordStrategy;
|
||||
const controller = pw.getController(this.auth.authenticator);
|
||||
|
||||
const res = await controller.request(
|
||||
new Request("https://localhost/login", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
email: params.email,
|
||||
password: params.password,
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
return c.json({ valid: res.ok });
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user