Seed admin acccount and generalize login/auth routes
This commit is contained in:
+20
-15
@@ -20,7 +20,7 @@ import { CalculatorService, CalculatorUnavailableError } from "./services/calcul
|
||||
import { StandardsParamsSchema } from "@blade-and-brawn/calculator";
|
||||
import { StandardsService } from "./services/standards";
|
||||
import { EventsService, EventStatusSchema } from "./services/events";
|
||||
import { AccountsService } from "./services/accounts";
|
||||
import { AccountsService, type AccountRole } from "./services/accounts";
|
||||
import { Value } from "@sinclair/typebox/value";
|
||||
|
||||
// CONSTANTS
|
||||
@@ -56,15 +56,15 @@ const authPlugin = new Elysia({ name: "auth" })
|
||||
authAdmin: {
|
||||
async resolve({ jwt, cookie: { auth } }) {
|
||||
const token = auth.value && await jwt.verify(auth.value);
|
||||
if (!token || token.role !== "admin" || !token.sessionId) throw status(401, "Unauthorized");
|
||||
return { sessionId: token.sessionId.toString() };
|
||||
if (!token || token.role !== "admin" || !token.accountId || !token.sessionId) throw status(401, "Unauthorized");
|
||||
return { role: token.role.toString(), sessionId: token.sessionId.toString(), accountId: token.accountId.toString() };
|
||||
}
|
||||
},
|
||||
authUser: {
|
||||
auth: {
|
||||
async resolve({ jwt, cookie: { auth } }) {
|
||||
const token = auth.value && await jwt.verify(auth.value);
|
||||
if (!token || token.role !== "user" || !token.accountId || !token.sessionId) throw status(401, "Unauthorized");
|
||||
return { sessionId: token.sessionId.toString(), accountId: token.accountId.toString() };
|
||||
if (!token || !token.role || !token.accountId || !token.sessionId) throw status(401, "Unauthorized");
|
||||
return { role: token.role.toString(), sessionId: token.sessionId.toString(), accountId: token.accountId.toString() };
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -137,22 +137,27 @@ export const app = new Elysia()
|
||||
.get("/health", () => ({ status: "ok" }))
|
||||
|
||||
// AUTHENTICATION
|
||||
.post("/auth/admin/login", async ({ jwt, body, cookie: { auth } }) => {
|
||||
const match = crypto.timingSafeEqual(sha256Sum(body.password), Buffer.from(env.ADMIN_PASSWORD, "hex"));
|
||||
if (!match) throw status(401, "Invalid credentials");
|
||||
.post("/auth/login", async ({ jwt, body: { password, email }, cookie: { auth } }) => {
|
||||
const account = await s.Accounts.getByEmail(email);
|
||||
const password_match = crypto.timingSafeEqual(
|
||||
sha256Sum(password),
|
||||
account?.password_hash ? Buffer.from(account?.password_hash, "hex") : sha256Sum("Dummy")
|
||||
);
|
||||
|
||||
if (!account?.password_hash || !password_match) throw status(401, "Invalid credentials");
|
||||
|
||||
auth.set({
|
||||
value: await jwt.sign({ role: "admin", sessionId: randomUUIDv7(), exp: JWT_EXP }),
|
||||
value: await jwt.sign({ role: account.role, sessionId: randomUUIDv7(), accountId: account.id, exp: JWT_EXP }),
|
||||
path: "/",
|
||||
maxAge: JWT_EXP_SECONDS,
|
||||
sameSite: "lax",
|
||||
sameSite: env.NODE_ENV === "production" ? "lax" : "none",
|
||||
httpOnly: true,
|
||||
secure: env.NODE_ENV === "production",
|
||||
secure: true,
|
||||
domain: env.NODE_ENV === "production" ?
|
||||
".bladeandbrawn.com" :
|
||||
undefined
|
||||
});
|
||||
}, { body: t.Object({ password: t.String() }) })
|
||||
}, { body: t.Object({ email: t.String(), password: t.String() }) })
|
||||
|
||||
.group("/auth/discord", (app) => app
|
||||
.get("/login", async ({ cookie: { authDiscord } }) => {
|
||||
@@ -226,7 +231,7 @@ export const app = new Elysia()
|
||||
|
||||
// Authenticate
|
||||
auth.set({
|
||||
value: await jwt.sign({ role: "user", sessionId: randomUUIDv7(), accountId, exp: JWT_EXP }),
|
||||
value: await jwt.sign({ role: "user" satisfies AccountRole, sessionId: randomUUIDv7(), accountId, exp: JWT_EXP }),
|
||||
path: "/",
|
||||
maxAge: JWT_EXP_SECONDS,
|
||||
sameSite: env.NODE_ENV === "production" ? "lax" : "none",
|
||||
@@ -457,7 +462,7 @@ export const app = new Elysia()
|
||||
}, {
|
||||
params: t.Object({ id: t.String() })
|
||||
})
|
||||
.guard({ authUser: true }, (app) => app
|
||||
.guard({ auth: true }, (app) => app
|
||||
.get("/me/stats", async ({ accountId }) => {
|
||||
const stats = await s.Accounts.stats(accountId);
|
||||
if (!stats) throw new NotFoundError("Account stats not found");
|
||||
|
||||
Reference in New Issue
Block a user