diff --git a/backend/src/middleware/refreshJWT.ts b/backend/src/middleware/refreshJWT.ts index bac4c21..9af7c30 100644 --- a/backend/src/middleware/refreshJWT.ts +++ b/backend/src/middleware/refreshJWT.ts @@ -57,9 +57,9 @@ export async function refreshJWT( }); } - const tempToExpire = 60 * 15; - const jwt = generateSignedJWT(user.id, user.permissions, tempToExpire) - res.json({ signed_jwt: jwt }); + const toExpire = 60 * 15; + const jwt = generateSignedJWT(user.id, user.permissions, toExpire) + res.set("X-New-Access-Token", jwt); // Have to overwrite with new jwt // Also I ain't try/catching cause I just made it diff --git a/backend/src/routes/auth.ts b/backend/src/routes/auth.ts index a7fd535..7a37e5f 100644 --- a/backend/src/routes/auth.ts +++ b/backend/src/routes/auth.ts @@ -4,6 +4,7 @@ import { eq } from 'drizzle-orm'; import { users } from '../db/schema.ts'; import express, { Request, Response } from "express"; import { generateSignedJWT, base64UrlDecode } from '../helpers/jwt.ts'; +import { loadJWT } from '../middleware/loadJWT.ts'; const db = drizzle(process.env.DATABASE_URL!); const hashSecret = process.env.PASSWORD_HASH_SECRET!; @@ -27,6 +28,43 @@ authRouter.get("/login", async (req: Request, res: Response) => { const [username, password] = decodedHeader.split(":"); + loginUser(username, password, res); +}); + +authRouter.put("/logout", loadJWT, async (req: Request, res: Response) => { + // since login is controlled by a jwt, all we can do here is wipe the refresh_token + // frontend will be responsible for clearing temp token from cache/cookies + // This endpoint will require a logged in user (valid jwt) + const resp = await db + .update(users) + .set({ refresh_token: null, logged_in: false }) + .where(eq(users.id, req.jwt.payload.uid)); + + res.json(resp); +}); + +authRouter.post("/signup", async (req: Request, res: Response) => { + const { username, password, display_name } = req.body + + const hashedPassword = crypto + .createHmac('sha256', hashSecret) + .update(`${presalt}${password}${postsalt}`) + .digest("base64url"); + + await db + .insert(users) + .values({ + display_name: display_name, + username: username, + hashed_password: hashedPassword, + permissions: 44 + }); + + loginUser(username, password, res); +}); + +// because it write the response, has to be last step +async function loginUser(username: string, password: string, res: Response) { const hashedPassword = crypto .createHmac('sha256', hashSecret) .update(`${presalt}${password}${postsalt}`) @@ -40,6 +78,12 @@ authRouter.get("/login", async (req: Request, res: Response) => { const user = potentialUsers[0]; + if (!user) { + return res.status(401).json({ + error: "401: Invalid Username", + }); + } + const calculatedBuff = Buffer.from(hashedPassword); const storedBuff = Buffer.from(user.hashed_password); @@ -47,20 +91,21 @@ authRouter.get("/login", async (req: Request, res: Response) => { crypto.timingSafeEqual(calculatedBuff, storedBuff); if (!match) - return res.status(401).json({ error: "401: failed to authenticate username/password" }); + return res.status(401).json({ error: "401: Failed to authenticate username/password" }); // fifteen minutes const tempToExpire = 60 * 15; const jwt = generateSignedJWT(user.id, user.permissions, tempToExpire) - // 7 days + // seven days const refreshToExpire = 60 * 60 * 24 * 7; const refreshjwt = generateSignedJWT(user.id, user.permissions, refreshToExpire); const resp = await db .update(users) .set({ - refresh_token: refreshjwt + refresh_token: refreshjwt, + logged_in: true }) .where(eq(users.id, user.id)); @@ -68,12 +113,4 @@ authRouter.get("/login", async (req: Request, res: Response) => { signed_jwt: jwt, refresh_resp: resp }); -}); - -authRouter.get("/logout", async (req: Request, res: Response) => { - -}); - -authRouter.get("/signup", async (req: Request, res: Response) => { - -}); +}