mirror of
https://github.com/shishantbiswas/bknd.git
synced 2026-03-16 12:37:20 +00:00
introduced auth strategy actions to allow user creation in UI
This commit is contained in:
@@ -1,6 +1,14 @@
|
||||
import { Exception } from "core";
|
||||
import { type DB, Exception } from "core";
|
||||
import { addFlashMessage } from "core/server/flash";
|
||||
import { type Static, StringEnum, Type, parse, runtimeSupports, transformObject } from "core/utils";
|
||||
import {
|
||||
type Static,
|
||||
StringEnum,
|
||||
type TObject,
|
||||
Type,
|
||||
parse,
|
||||
runtimeSupports,
|
||||
transformObject
|
||||
} from "core/utils";
|
||||
import type { Context, Hono } from "hono";
|
||||
import { deleteCookie, getSignedCookie, setSignedCookie } from "hono/cookie";
|
||||
import { sign, verify } from "hono/jwt";
|
||||
@@ -10,6 +18,14 @@ import type { ServerEnv } from "modules/Module";
|
||||
type Input = any; // workaround
|
||||
export type JWTPayload = Parameters<typeof sign>[0];
|
||||
|
||||
export const strategyActions = ["create", "change"] as const;
|
||||
export type StrategyActionName = (typeof strategyActions)[number];
|
||||
export type StrategyAction<S extends TObject = TObject> = {
|
||||
schema: S;
|
||||
preprocess: (input: unknown) => Promise<Omit<DB["users"], "id" | "strategy">>;
|
||||
};
|
||||
export type StrategyActions = Partial<Record<StrategyActionName, StrategyAction>>;
|
||||
|
||||
// @todo: add schema to interface to ensure proper inference
|
||||
export interface Strategy {
|
||||
getController: (auth: Authenticator) => Hono<any>;
|
||||
@@ -17,6 +33,7 @@ export interface Strategy {
|
||||
getMode: () => "form" | "external";
|
||||
getName: () => string;
|
||||
toJSON: (secrets?: boolean) => any;
|
||||
getActions?: () => StrategyActions;
|
||||
}
|
||||
|
||||
export type User = {
|
||||
@@ -274,6 +291,14 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
|
||||
return c.req.header("Content-Type") === "application/json";
|
||||
}
|
||||
|
||||
async getBody(c: Context) {
|
||||
if (this.isJsonRequest(c)) {
|
||||
return await c.req.json();
|
||||
} else {
|
||||
return Object.fromEntries((await c.req.formData()).entries());
|
||||
}
|
||||
}
|
||||
|
||||
private getSuccessPath(c: Context) {
|
||||
const p = (this.config.cookie.pathSuccess ?? "/").replace(/\/+$/, "/");
|
||||
|
||||
@@ -338,3 +363,13 @@ export class Authenticator<Strategies extends Record<string, Strategy> = Record<
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function createStrategyAction<S extends TObject>(
|
||||
schema: S,
|
||||
preprocess: (input: Static<S>) => Promise<Partial<DB["users"]>>
|
||||
) {
|
||||
return {
|
||||
schema,
|
||||
preprocess
|
||||
} as StrategyAction<S>;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { Authenticator, Strategy } from "auth";
|
||||
import { type Static, StringEnum, Type, parse } from "core/utils";
|
||||
import { hash } from "core/utils";
|
||||
import { type Context, Hono } from "hono";
|
||||
import { type StrategyAction, type StrategyActions, createStrategyAction } from "../Authenticator";
|
||||
|
||||
type LoginSchema = { username: string; password: string } | { email: string; password: string };
|
||||
type RegisterSchema = { email: string; password: string; [key: string]: any };
|
||||
@@ -54,17 +55,9 @@ export class PasswordStrategy implements Strategy {
|
||||
getController(authenticator: Authenticator): Hono<any> {
|
||||
const hono = new Hono();
|
||||
|
||||
async function getBody(c: Context) {
|
||||
if (authenticator.isJsonRequest(c)) {
|
||||
return await c.req.json();
|
||||
} else {
|
||||
return Object.fromEntries((await c.req.formData()).entries());
|
||||
}
|
||||
}
|
||||
|
||||
return hono
|
||||
.post("/login", async (c) => {
|
||||
const body = await getBody(c);
|
||||
const body = await authenticator.getBody(c);
|
||||
|
||||
try {
|
||||
const payload = await this.login(body);
|
||||
@@ -76,7 +69,7 @@ export class PasswordStrategy implements Strategy {
|
||||
}
|
||||
})
|
||||
.post("/register", async (c) => {
|
||||
const body = await getBody(c);
|
||||
const body = await authenticator.getBody(c);
|
||||
|
||||
const payload = await this.register(body);
|
||||
const data = await authenticator.resolve("register", this, payload.password, payload);
|
||||
@@ -85,6 +78,27 @@ export class PasswordStrategy implements Strategy {
|
||||
});
|
||||
}
|
||||
|
||||
getActions(): StrategyActions {
|
||||
return {
|
||||
create: createStrategyAction(
|
||||
Type.Object({
|
||||
email: Type.String({
|
||||
pattern: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$"
|
||||
}),
|
||||
password: Type.String({
|
||||
minLength: 8 // @todo: this should be configurable
|
||||
})
|
||||
}),
|
||||
async ({ password, ...input }) => {
|
||||
return {
|
||||
...input,
|
||||
strategy_value: await this.hash(password)
|
||||
};
|
||||
}
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
getSchema() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user