feat: enhance authentication flow by adding API key support and refactoring user session retrieval

This commit is contained in:
암냥 2026-06-25 21:13:29 +09:00
commit f8a6893f01
No known key found for this signature in database

View file

@ -5,7 +5,7 @@ import { AuditLog } from "@/models/audit";
import { ApiKey } from "@/models/api-key"; import { ApiKey } from "@/models/api-key";
import { USER_ROLES, User } from "@/models/user"; import { USER_ROLES, User } from "@/models/user";
import { createAuditLog } from "@/lib/audit"; import { createAuditLog } from "@/lib/audit";
import { createPlainApiKey } from "@/lib/api-key"; import { authenticateApiKey, createPlainApiKey } from "@/lib/api-key";
import { ulid } from "ulid"; import { ulid } from "ulid";
const hardcodedOwners = new Set<string>( const hardcodedOwners = new Set<string>(
@ -14,6 +14,8 @@ const hardcodedOwners = new Set<string>(
: [], : [],
); );
type HeaderBag = Record<string, string | undefined>;
async function getSessionUser(jwt: { verify: (token: string) => Promise<unknown> }, mizuki: { value?: unknown }) { async function getSessionUser(jwt: { verify: (token: string) => Promise<unknown> }, mizuki: { value?: unknown }) {
const rawToken = mizuki.value; const rawToken = mizuki.value;
if (typeof rawToken !== "string" || rawToken.length === 0) { if (typeof rawToken !== "string" || rawToken.length === 0) {
@ -28,8 +30,23 @@ async function getSessionUser(jwt: { verify: (token: string) => Promise<unknown>
return await User.findOne({ userId: payload.id }); return await User.findOne({ userId: payload.id });
} }
async function getAdminSessionUser(jwt: { verify: (token: string) => Promise<unknown> }, mizuki: { value?: unknown }) { async function getAuthenticatedUser(
const requester = await getSessionUser(jwt, mizuki); jwt: { verify: (token: string) => Promise<unknown> },
mizuki: { value?: unknown },
headers: HeaderBag,
) {
const sessionUser = await getSessionUser(jwt, mizuki);
if (sessionUser) return sessionUser;
return await authenticateApiKey(headers);
}
async function getAdminAuthenticatedUser(
jwt: { verify: (token: string) => Promise<unknown> },
mizuki: { value?: unknown },
headers: HeaderBag,
) {
const requester = await getAuthenticatedUser(jwt, mizuki, headers);
if (!requester || requester.role !== "admin") { if (!requester || requester.role !== "admin") {
return null; return null;
} }
@ -44,20 +61,10 @@ export default new Elysia({ prefix: "/auth" })
secret: config.auth.jwt_secret, secret: config.auth.jwt_secret,
}) })
) )
.get("/me", async ({ jwt, cookie: { mizuki }, status }) => { .get("/me", async ({ jwt, cookie: { mizuki }, headers, status }) => {
const rawToken = mizuki.value; const user = await getAuthenticatedUser(jwt, mizuki, headers);
if (typeof rawToken !== "string" || rawToken.length === 0) {
return status(401, "Unauthorized");
}
const payload = await jwt.verify(rawToken);
if (!payload || typeof payload !== "object" || !("id" in payload) || typeof payload.id !== "string") {
return status(401, "Unauthorized");
}
const user = await User.findOne({ userId: payload.id });
if (!user) { if (!user) {
return status(404, "User not found"); return status(401, "Unauthorized");
} }
return { return {
@ -197,18 +204,8 @@ export default new Elysia({ prefix: "/auth" })
}) })
}) })
.post("/role", async ({ jwt, body, cookie: { mizuki }, status }) => { .post("/role", async ({ jwt, body, cookie: { mizuki }, headers, status }) => {
const rawToken = mizuki.value; const requester = await getAuthenticatedUser(jwt, mizuki, headers);
if (typeof rawToken !== "string" || rawToken.length === 0) {
return status(401, "Unauthorized");
}
const payload = await jwt.verify(rawToken);
if (!payload || typeof payload !== "object" || !("id" in payload) || typeof payload.id !== "string") {
return status(401, "Unauthorized");
}
const requester = await User.findOne({ userId: payload.id });
if (!requester) { if (!requester) {
return status(401, "Unauthorized"); return status(401, "Unauthorized");
} }
@ -288,19 +285,9 @@ export default new Elysia({ prefix: "/auth" })
}), }),
}) })
.delete("/user/:id", async ({ params, jwt, cookie: { mizuki }, status }) => { .delete("/user/:id", async ({ params, jwt, cookie: { mizuki }, headers, status }) => {
const rawToken = mizuki.value; const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
if (typeof rawToken !== "string" || rawToken.length === 0) { if (!requester) {
return status(401, "Unauthorized");
}
const payload = await jwt.verify(rawToken);
if (!payload || typeof payload !== "object" || !("id" in payload) || typeof payload.id !== "string") {
return status(401, "Unauthorized");
}
const requester = await User.findOne({ userId: payload.id });
if (!requester || requester.role !== "admin") {
return status(403, "Forbidden"); return status(403, "Forbidden");
} }
@ -339,23 +326,9 @@ export default new Elysia({ prefix: "/auth" })
}) })
}) })
.get("/users", async ({ jwt, cookie: { mizuki }, status }) => { .get("/users", async ({ jwt, cookie: { mizuki }, headers, status }) => {
const rawToken = mizuki.value; const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
if (typeof rawToken !== "string" || rawToken.length === 0) {
return status(401, "Unauthorized");
}
const payload = await jwt.verify(rawToken);
if (!payload || typeof payload !== "object" || !("id" in payload) || typeof payload.id !== "string") {
return status(401, "Unauthorized");
}
const requester = await User.findOne({ userId: payload.id });
if (!requester) { if (!requester) {
return status(401, "Unauthorized");
}
if (requester.role !== "admin") {
return status(403, "Forbidden"); return status(403, "Forbidden");
} }
@ -370,23 +343,9 @@ export default new Elysia({ prefix: "/auth" })
})); }));
}) })
.post("/user", async ({ jwt, body, cookie: { mizuki }, status }) => { .post("/user", async ({ jwt, body, cookie: { mizuki }, headers, status }) => {
const rawToken = mizuki.value; const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
if (typeof rawToken !== "string" || rawToken.length === 0) {
return status(401, "Unauthorized");
}
const payload = await jwt.verify(rawToken);
if (!payload || typeof payload !== "object" || !("id" in payload) || typeof payload.id !== "string") {
return status(401, "Unauthorized");
}
const requester = await User.findOne({ userId: payload.id });
if (!requester) { if (!requester) {
return status(401, "Unauthorized");
}
if (requester.role !== "admin") {
return status(403, "Forbidden"); return status(403, "Forbidden");
} }
@ -443,8 +402,8 @@ export default new Elysia({ prefix: "/auth" })
}), }),
}) })
.get("/api-keys", async ({ jwt, cookie: { mizuki }, status }) => { .get("/api-keys", async ({ jwt, cookie: { mizuki }, headers, status }) => {
const requester = await getAdminSessionUser(jwt, mizuki); const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
if (!requester) { if (!requester) {
return status(403, "Forbidden"); return status(403, "Forbidden");
} }
@ -461,8 +420,8 @@ export default new Elysia({ prefix: "/auth" })
})); }));
}) })
.post("/api-keys", async ({ jwt, body, cookie: { mizuki }, status }) => { .post("/api-keys", async ({ jwt, body, cookie: { mizuki }, headers, status }) => {
const requester = await getAdminSessionUser(jwt, mizuki); const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
if (!requester) { if (!requester) {
return status(403, "Forbidden"); return status(403, "Forbidden");
} }
@ -512,8 +471,8 @@ export default new Elysia({ prefix: "/auth" })
}), }),
}) })
.delete("/api-keys/:id", async ({ jwt, params, cookie: { mizuki }, status }) => { .delete("/api-keys/:id", async ({ jwt, params, cookie: { mizuki }, headers, status }) => {
const requester = await getAdminSessionUser(jwt, mizuki); const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
if (!requester) { if (!requester) {
return status(403, "Forbidden"); return status(403, "Forbidden");
} }
@ -552,23 +511,9 @@ export default new Elysia({ prefix: "/auth" })
}), }),
}) })
.get("/audit-logs", async ({ jwt, cookie: { mizuki }, query, status }) => { .get("/audit-logs", async ({ jwt, cookie: { mizuki }, headers, query, status }) => {
const rawToken = mizuki.value; const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
if (typeof rawToken !== "string" || rawToken.length === 0) {
return status(401, "Unauthorized");
}
const payload = await jwt.verify(rawToken);
if (!payload || typeof payload !== "object" || !("id" in payload) || typeof payload.id !== "string") {
return status(401, "Unauthorized");
}
const requester = await User.findOne({ userId: payload.id });
if (!requester) { if (!requester) {
return status(401, "Unauthorized");
}
if (requester.role !== "admin") {
return status(403, "Forbidden"); return status(403, "Forbidden");
} }