introduced auth strategy actions to allow user creation in UI

This commit is contained in:
dswbx
2025-01-17 10:19:26 +01:00
parent d4f647c0db
commit b61634e261
23 changed files with 464 additions and 108 deletions
+29 -4
View File
@@ -1,3 +1,4 @@
import type { AuthActionResponse } from "auth/api/AuthController";
import type { AppAuthSchema, AppAuthStrategies } from "auth/auth-schema";
import type { AuthResponse, SafeUser, Strategy } from "auth/authenticate/Authenticator";
import { type BaseModuleApiOptions, ModuleApi } from "modules/ModuleApi";
@@ -13,22 +14,46 @@ export class AuthApi extends ModuleApi<AuthApiOptions> {
};
}
async loginWithPassword(input: any) {
const res = await this.post<AuthResponse>(["password", "login"], input);
async login(strategy: string, input: any) {
const res = await this.post<AuthResponse>([strategy, "login"], input);
if (res.ok && res.body.token) {
await this.options.onTokenUpdate?.(res.body.token);
}
return res;
}
async registerWithPassword(input: any) {
const res = await this.post<AuthResponse>(["password", "register"], input);
async register(strategy: string, input: any) {
const res = await this.post<AuthResponse>([strategy, "register"], input);
if (res.ok && res.body.token) {
await this.options.onTokenUpdate?.(res.body.token);
}
return res;
}
async actionSchema(strategy: string, action: string) {
return this.get<Strategy>([strategy, "actions", action, "schema.json"]);
}
async action(strategy: string, action: string, input: any) {
return this.post<AuthActionResponse>([strategy, "actions", action], input);
}
/**
* @deprecated use login("password", ...) instead
* @param input
*/
async loginWithPassword(input: any) {
return this.login("password", input);
}
/**
* @deprecated use register("password", ...) instead
* @param input
*/
async registerWithPassword(input: any) {
return this.register("password", input);
}
me() {
return this.get<{ user: SafeUser | null }>(["me"]);
}
+77 -1
View File
@@ -1,5 +1,16 @@
import type { AppAuth } from "auth";
import { type AppAuth, AuthPermissions, type SafeUser, type Strategy } from "auth";
import { TypeInvalidError, parse } from "core/utils";
import { DataPermissions } from "data";
import type { Hono } from "hono";
import { Controller } from "modules/Controller";
import type { ServerEnv } from "modules/Module";
export type AuthActionResponse = {
success: boolean;
action: string;
data?: SafeUser;
errors?: any;
};
export class AuthController extends Controller {
constructor(private auth: AppAuth) {
@@ -10,6 +21,70 @@ export class AuthController extends Controller {
return this.auth.ctx.guard;
}
private registerStrategyActions(strategy: Strategy, mainHono: Hono<ServerEnv>) {
const actions = strategy.getActions?.();
if (!actions) {
return;
}
const { auth, permission } = this.middlewares;
const hono = this.create().use(auth());
const name = strategy.getName();
const { create, change } = actions;
const em = this.auth.em;
const mutator = em.mutator(this.auth.config.entity_name as "users");
if (create) {
hono.post(
"/create",
permission([AuthPermissions.createUser, DataPermissions.entityCreate]),
async (c) => {
try {
const body = await this.auth.authenticator.getBody(c);
const valid = parse(create.schema, body, {
skipMark: true
});
const processed = (await create.preprocess?.(valid)) ?? valid;
console.log("processed", processed);
// @todo: check processed for "role" and check permissions
mutator.__unstable_toggleSystemEntityCreation(false);
const { data: created } = await mutator.insertOne({
...processed,
strategy: name
});
mutator.__unstable_toggleSystemEntityCreation(true);
return c.json({
success: true,
action: "create",
strategy: name,
data: created as unknown as SafeUser
} as AuthActionResponse);
} catch (e) {
if (e instanceof TypeInvalidError) {
return c.json(
{
success: false,
errors: e.errors
},
400
);
}
throw e;
}
}
);
hono.get("create/schema.json", async (c) => {
return c.json(create.schema);
});
}
mainHono.route(`/${name}/actions`, hono);
}
override getController() {
const { auth } = this.middlewares;
const hono = this.create();
@@ -18,6 +93,7 @@ export class AuthController extends Controller {
for (const [name, strategy] of Object.entries(strategies)) {
//console.log("registering", name, "at", `/${name}`);
hono.route(`/${name}`, strategy.getController(this.auth.authenticator));
this.registerStrategyActions(strategy, hono);
}
hono.get("/me", auth(), async (c) => {