feat: enhance authentication flow by adding API key support and refactoring user session retrieval
This commit is contained in:
parent
7cd576911b
commit
f8a6893f01
1 changed files with 40 additions and 95 deletions
|
|
@ -5,7 +5,7 @@ import { AuditLog } from "@/models/audit";
|
|||
import { ApiKey } from "@/models/api-key";
|
||||
import { USER_ROLES, User } from "@/models/user";
|
||||
import { createAuditLog } from "@/lib/audit";
|
||||
import { createPlainApiKey } from "@/lib/api-key";
|
||||
import { authenticateApiKey, createPlainApiKey } from "@/lib/api-key";
|
||||
import { ulid } from "ulid";
|
||||
|
||||
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 }) {
|
||||
const rawToken = mizuki.value;
|
||||
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 });
|
||||
}
|
||||
|
||||
async function getAdminSessionUser(jwt: { verify: (token: string) => Promise<unknown> }, mizuki: { value?: unknown }) {
|
||||
const requester = await getSessionUser(jwt, mizuki);
|
||||
async function getAuthenticatedUser(
|
||||
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") {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -44,20 +61,10 @@ export default new Elysia({ prefix: "/auth" })
|
|||
secret: config.auth.jwt_secret,
|
||||
})
|
||||
)
|
||||
.get("/me", async ({ jwt, cookie: { mizuki }, status }) => {
|
||||
const rawToken = mizuki.value;
|
||||
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 });
|
||||
.get("/me", async ({ jwt, cookie: { mizuki }, headers, status }) => {
|
||||
const user = await getAuthenticatedUser(jwt, mizuki, headers);
|
||||
if (!user) {
|
||||
return status(404, "User not found");
|
||||
return status(401, "Unauthorized");
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -197,18 +204,8 @@ export default new Elysia({ prefix: "/auth" })
|
|||
})
|
||||
})
|
||||
|
||||
.post("/role", async ({ jwt, body, cookie: { mizuki }, status }) => {
|
||||
const rawToken = mizuki.value;
|
||||
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 });
|
||||
.post("/role", async ({ jwt, body, cookie: { mizuki }, headers, status }) => {
|
||||
const requester = await getAuthenticatedUser(jwt, mizuki, headers);
|
||||
if (!requester) {
|
||||
return status(401, "Unauthorized");
|
||||
}
|
||||
|
|
@ -288,19 +285,9 @@ export default new Elysia({ prefix: "/auth" })
|
|||
}),
|
||||
})
|
||||
|
||||
.delete("/user/:id", async ({ params, jwt, cookie: { mizuki }, status }) => {
|
||||
const rawToken = mizuki.value;
|
||||
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 || requester.role !== "admin") {
|
||||
.delete("/user/:id", async ({ params, jwt, cookie: { mizuki }, headers, status }) => {
|
||||
const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
|
||||
if (!requester) {
|
||||
return status(403, "Forbidden");
|
||||
}
|
||||
|
||||
|
|
@ -339,23 +326,9 @@ export default new Elysia({ prefix: "/auth" })
|
|||
})
|
||||
})
|
||||
|
||||
.get("/users", async ({ jwt, cookie: { mizuki }, status }) => {
|
||||
const rawToken = mizuki.value;
|
||||
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 });
|
||||
.get("/users", async ({ jwt, cookie: { mizuki }, headers, status }) => {
|
||||
const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
|
||||
if (!requester) {
|
||||
return status(401, "Unauthorized");
|
||||
}
|
||||
|
||||
if (requester.role !== "admin") {
|
||||
return status(403, "Forbidden");
|
||||
}
|
||||
|
||||
|
|
@ -370,23 +343,9 @@ export default new Elysia({ prefix: "/auth" })
|
|||
}));
|
||||
})
|
||||
|
||||
.post("/user", async ({ jwt, body, cookie: { mizuki }, status }) => {
|
||||
const rawToken = mizuki.value;
|
||||
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 });
|
||||
.post("/user", async ({ jwt, body, cookie: { mizuki }, headers, status }) => {
|
||||
const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
|
||||
if (!requester) {
|
||||
return status(401, "Unauthorized");
|
||||
}
|
||||
|
||||
if (requester.role !== "admin") {
|
||||
return status(403, "Forbidden");
|
||||
}
|
||||
|
||||
|
|
@ -443,8 +402,8 @@ export default new Elysia({ prefix: "/auth" })
|
|||
}),
|
||||
})
|
||||
|
||||
.get("/api-keys", async ({ jwt, cookie: { mizuki }, status }) => {
|
||||
const requester = await getAdminSessionUser(jwt, mizuki);
|
||||
.get("/api-keys", async ({ jwt, cookie: { mizuki }, headers, status }) => {
|
||||
const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
|
||||
if (!requester) {
|
||||
return status(403, "Forbidden");
|
||||
}
|
||||
|
|
@ -461,8 +420,8 @@ export default new Elysia({ prefix: "/auth" })
|
|||
}));
|
||||
})
|
||||
|
||||
.post("/api-keys", async ({ jwt, body, cookie: { mizuki }, status }) => {
|
||||
const requester = await getAdminSessionUser(jwt, mizuki);
|
||||
.post("/api-keys", async ({ jwt, body, cookie: { mizuki }, headers, status }) => {
|
||||
const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
|
||||
if (!requester) {
|
||||
return status(403, "Forbidden");
|
||||
}
|
||||
|
|
@ -512,8 +471,8 @@ export default new Elysia({ prefix: "/auth" })
|
|||
}),
|
||||
})
|
||||
|
||||
.delete("/api-keys/:id", async ({ jwt, params, cookie: { mizuki }, status }) => {
|
||||
const requester = await getAdminSessionUser(jwt, mizuki);
|
||||
.delete("/api-keys/:id", async ({ jwt, params, cookie: { mizuki }, headers, status }) => {
|
||||
const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
|
||||
if (!requester) {
|
||||
return status(403, "Forbidden");
|
||||
}
|
||||
|
|
@ -552,23 +511,9 @@ export default new Elysia({ prefix: "/auth" })
|
|||
}),
|
||||
})
|
||||
|
||||
.get("/audit-logs", async ({ jwt, cookie: { mizuki }, query, status }) => {
|
||||
const rawToken = mizuki.value;
|
||||
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 });
|
||||
.get("/audit-logs", async ({ jwt, cookie: { mizuki }, headers, query, status }) => {
|
||||
const requester = await getAdminAuthenticatedUser(jwt, mizuki, headers);
|
||||
if (!requester) {
|
||||
return status(401, "Unauthorized");
|
||||
}
|
||||
|
||||
if (requester.role !== "admin") {
|
||||
return status(403, "Forbidden");
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue