135 lines
3.0 KiB
TypeScript
135 lines
3.0 KiB
TypeScript
import { getUserByUsername, writeAuthAudit } from "@/lib/store";
|
|
import { parseHy2Userpass, verifyNodeToken, evaluateUserForAuth } from "@/lib/hy2";
|
|
import { verifyPassword } from "@/lib/password";
|
|
|
|
type AuthRequest = {
|
|
addr?: string;
|
|
auth?: string;
|
|
tx?: number;
|
|
};
|
|
|
|
function deny(id = "") {
|
|
return Response.json({ ok: false, id });
|
|
}
|
|
|
|
export async function POST(
|
|
request: Request,
|
|
context: RouteContext<"/api/hy2/nodes/[slug]/auth">,
|
|
) {
|
|
const { slug } = await context.params;
|
|
const requestUrl = new URL(request.url);
|
|
const token =
|
|
requestUrl.searchParams.get("token") ||
|
|
request.headers.get("authorization")?.replace(/^Bearer\s+/i, "") ||
|
|
request.headers.get("x-node-token");
|
|
|
|
const nodeResult = await verifyNodeToken(slug, token);
|
|
if (!nodeResult.ok) {
|
|
await writeAuthAudit({
|
|
nodeId: nodeResult.node?.id ?? null,
|
|
ok: false,
|
|
reason: nodeResult.reason,
|
|
});
|
|
return deny();
|
|
}
|
|
|
|
const node = nodeResult.node;
|
|
if (!node) {
|
|
return deny();
|
|
}
|
|
|
|
let payload: AuthRequest;
|
|
|
|
try {
|
|
payload = (await request.json()) as AuthRequest;
|
|
} catch {
|
|
await writeAuthAudit({
|
|
nodeId: node.id,
|
|
addr: null,
|
|
requestedTx: null,
|
|
ok: false,
|
|
reason: "bad_json",
|
|
});
|
|
return deny();
|
|
}
|
|
|
|
if (!payload.auth) {
|
|
await writeAuthAudit({
|
|
nodeId: node.id,
|
|
addr: payload.addr ?? null,
|
|
requestedTx: payload.tx ?? null,
|
|
ok: false,
|
|
reason: "missing_auth",
|
|
});
|
|
return deny();
|
|
}
|
|
|
|
const parsed = parseHy2Userpass(payload.auth);
|
|
if (!parsed) {
|
|
await writeAuthAudit({
|
|
nodeId: node.id,
|
|
addr: payload.addr ?? null,
|
|
requestedTx: payload.tx ?? null,
|
|
ok: false,
|
|
reason: "bad_auth_format",
|
|
});
|
|
return deny();
|
|
}
|
|
|
|
const user = await getUserByUsername(parsed.username);
|
|
if (!user) {
|
|
await writeAuthAudit({
|
|
nodeId: node.id,
|
|
username: parsed.username,
|
|
addr: payload.addr ?? null,
|
|
requestedTx: payload.tx ?? null,
|
|
ok: false,
|
|
reason: "user_not_found",
|
|
});
|
|
return deny();
|
|
}
|
|
|
|
const passwordOk = await verifyPassword(parsed.password, user.password_hash);
|
|
if (!passwordOk) {
|
|
await writeAuthAudit({
|
|
nodeId: node.id,
|
|
userId: user.id,
|
|
authId: user.auth_id,
|
|
username: user.username,
|
|
addr: payload.addr ?? null,
|
|
requestedTx: payload.tx ?? null,
|
|
ok: false,
|
|
reason: "password_invalid",
|
|
});
|
|
return deny();
|
|
}
|
|
|
|
const access = evaluateUserForAuth(user);
|
|
if (!access.ok) {
|
|
await writeAuthAudit({
|
|
nodeId: node.id,
|
|
userId: user.id,
|
|
authId: user.auth_id,
|
|
username: user.username,
|
|
addr: payload.addr ?? null,
|
|
requestedTx: payload.tx ?? null,
|
|
ok: false,
|
|
reason: access.reason,
|
|
});
|
|
return deny();
|
|
}
|
|
|
|
await writeAuthAudit({
|
|
nodeId: node.id,
|
|
userId: user.id,
|
|
authId: user.auth_id,
|
|
username: user.username,
|
|
addr: payload.addr ?? null,
|
|
requestedTx: payload.tx ?? null,
|
|
ok: true,
|
|
reason: "ok",
|
|
});
|
|
|
|
return Response.json({ ok: true, id: user.auth_id });
|
|
}
|