updated auth routes to use sessions and "httpOnly" cookies

This commit is contained in:
Oliver Bryan
2026-01-09 05:33:36 +00:00
parent 89b38a4aa3
commit f90ddc2e4c
6 changed files with 104 additions and 30 deletions

View File

@@ -1,6 +1,6 @@
import type { BunRequest } from "bun";
import { generateToken, verifyPassword } from "../../auth/utils";
import { getUserByUsername } from "../../db/queries";
import { buildAuthCookie, generateToken, verifyPassword } from "../../auth/utils";
import { createSession, getUserByUsername } from "../../db/queries";
const isNonEmptyString = (value: unknown): value is string =>
typeof value === "string" && value.trim().length > 0;
@@ -37,10 +37,24 @@ export default async function login(req: BunRequest) {
return new Response("invalid credentials", { status: 401 });
}
const token = generateToken(user.id);
const session = await createSession(user.id);
if (!session) {
return new Response("failed to create session", { status: 500 });
}
return Response.json({
user: { id: user.id, name: user.name, username: user.username },
token,
});
const token = generateToken(session.id, user.id);
return new Response(
JSON.stringify({
user: { id: user.id, name: user.name, username: user.username, avatarURL: user.avatarURL },
csrfToken: session.csrfToken,
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
"Set-Cookie": buildAuthCookie(token),
},
},
);
}

View File

@@ -0,0 +1,19 @@
import type { AuthedRequest } from "../../auth/middleware";
import { buildClearAuthCookie } from "../../auth/utils";
import { deleteSession } from "../../db/queries";
export default async function logout(req: AuthedRequest) {
if (req.method !== "POST") {
return new Response("method not allowed", { status: 405 });
}
await deleteSession(req.sessionId);
return new Response(JSON.stringify({ success: true }), {
status: 200,
headers: {
"Content-Type": "application/json",
"Set-Cookie": buildClearAuthCookie(),
},
});
}

View File

@@ -8,5 +8,10 @@ export default async function me(req: AuthedRequest) {
return new Response("user not found", { status: 404 });
}
return Response.json(user as UserRecord);
const { passwordHash: _, ...safeUser } = user;
return Response.json({
user: safeUser as Omit<UserRecord, "passwordHash">,
csrfToken: req.csrfToken,
});
}

View File

@@ -1,6 +1,6 @@
import type { BunRequest } from "bun";
import { generateToken, hashPassword } from "../../auth/utils";
import { createUser, getUserByUsername } from "../../db/queries";
import { buildAuthCookie, generateToken, hashPassword } from "../../auth/utils";
import { createSession, createUser, getUserByUsername } from "../../db/queries";
const isNonEmptyString = (value: unknown): value is string =>
typeof value === "string" && value.trim().length > 0;
@@ -54,10 +54,24 @@ export default async function register(req: BunRequest) {
return new Response("failed to create user", { status: 500 });
}
const token = generateToken(user.id);
const session = await createSession(user.id);
if (!session) {
return new Response("failed to create session", { status: 500 });
}
return Response.json({
token,
user,
});
const token = generateToken(session.id, user.id);
return new Response(
JSON.stringify({
user: { id: user.id, name: user.name, username: user.username, avatarURL: user.avatarURL },
csrfToken: session.csrfToken,
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
"Set-Cookie": buildAuthCookie(token),
},
},
);
}

View File

@@ -1,4 +1,5 @@
import authLogin from "./auth/login";
import authLogout from "./auth/logout";
import authMe from "./auth/me";
import authRegister from "./auth/register";
import issueCreate from "./issue/create";
@@ -30,6 +31,7 @@ import userUploadAvatar from "./user/upload-avatar";
export const routes = {
authRegister,
authLogin,
authLogout,
authMe,
userByUsername,