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 { 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue