diff --git a/src/lib/middleware/requireAuth.ts b/src/lib/middleware/requireAuth.ts index 22a07730..bc82c760 100644 --- a/src/lib/middleware/requireAuth.ts +++ b/src/lib/middleware/requireAuth.ts @@ -4,6 +4,7 @@ import { User } from "@prisma/client"; import { Request as ExpressRequest, Response, NextFunction } from "express"; import * as jose from "jose"; import { createHash } from "crypto"; +import { kv } from "../../redisClient.js"; export interface AuthenticatedRequest extends ExpressRequest { user: User; @@ -34,18 +35,23 @@ export const requireAuth = async ( const keyHash = createHash("sha256").update(tokenString).digest("hex"); - const rateLimit = await prisma.apiKey.findUnique({ - where: { - keyHash: keyHash, - }, - }); + const keyRowValue = await kv.get(`auth:apikey:${keyHash}:lastused`); + + let rateLimit = + keyRowValue !== null + ? ( + await prisma.apiKey.findUnique({ where: { keyHash: keyHash } }) + ).lastUsed.getTime() + : parseInt(keyRowValue as string); - if (Date.now() - rateLimit.lastUsed.getTime() <= 3 * 1000) { + if (Date.now() - rateLimit <= 3 * 1000) { res.status(429).json({ message: "You have exceeded the rate limit for an API Key. Please wait before making more requests.", retryAfterSeconds: 3, }); + + return; } const apiKey = await prisma.apiKey.update({ @@ -68,6 +74,13 @@ export const requireAuth = async ( return; } + await kv.set( + `auth:apikey:${keyHash}:lastused`, + apiKey.lastUsed.getTime().toString(), { + expiration: {type: "EX", value: 60 * 15}, + } + ); + req.user = apiKey.user; req.tokenType = "apiKey"; @@ -96,58 +109,80 @@ export const requireAuth = async ( // Get user info from Auth0 try { - const authResponse = await axios.get( - `https://${process.env.AUTH0_DOMAIN}/userinfo`, - { - headers: { - Authorization: `Bearer ${tokenString}`, - "Content-Type": "application/json", + const userRow = await kv.get(`auth:user:${userId}`); + if (userRow !== null) { + req.user = JSON.parse((await userRow) as string) as User; + + next(); + } else { + const authResponse = await axios.get( + `https://${process.env.AUTH0_DOMAIN}/userinfo`, + { + headers: { + Authorization: `Bearer ${tokenString}`, + "Content-Type": "application/json", + }, }, - }, - ); + ); - console.log("Updating"); - // Get JSON - const authData = authResponse.data; + console.log("Updating"); + // Get JSON + const authData = authResponse.data; - // Update database - const user = await prisma.user.upsert({ - where: { - id: userId, - }, - update: { - email: authData.email, - emailVerified: authData.email_verified, - }, - create: { - id: userId, - email: authData.email, - emailVerified: authData.email_verified, - role: "ANALYST", - }, - }); + // Update database + const user = await prisma.user.upsert({ + where: { + id: userId, + }, + update: { + email: authData.email, + emailVerified: authData.email_verified, + }, + create: { + id: userId, + email: authData.email, + emailVerified: authData.email_verified, + role: "ANALYST", + } + }); - // Add user to request - req.user = user; - req.tokenType = "jwt"; + kv.set(`auth:user:${user.id}`, JSON.stringify(user), { + expiration: {type: "EX", value: 60 * 15}, + }); - next(); + req.user = user; + + next(); + } } catch (error) { console.log("Using existing"); - const user = await prisma.user.findUnique({ - where: { - id: userId, - }, - }); + console.error(error); - if (!user) { - res.status(500).send("Internal server error"); - return; - } + const userRow = await kv.get(`auth:user:${userId}`); + if (userRow !== null) { + req.user = JSON.parse((await userRow) as string) as User; - req.user = user; + next(); + } else { + const user = await prisma.user.findUnique({ + where: { + id: userId, + }, + }); - next(); + if (!user) { + res.status(401).send("User not found"); + return; + } + + kv.set(`auth:user:${user.id}`, JSON.stringify(user), { + expiration: {type: "EX", value: 60 * 15}, + }); + + req.user = user; + + next(); + } } } catch (error) { console.error(error); diff --git a/src/redisClient.ts b/src/redisClient.ts index 6220288f..9e2e6e40 100644 --- a/src/redisClient.ts +++ b/src/redisClient.ts @@ -4,25 +4,20 @@ const redis = createClient({ url: process.env.REDIS_URL }) .on("error", (err) => console.log("Redis Client Error", err)) .connect(); -const set = async ( - key: string, - data: string, -): ReturnType["set"]> => { - return await (await redis).set(key, data); +const set: Awaited["set"] = async (...args) => { + return await (await redis).set(...args) ; }; -const get = async (key: string): ReturnType["get"]> => { - return await (await redis).get(key); +const get: Awaited["get"] = async (...args) => { + return await (await redis).get(...args) ; }; -const del = async ( - key: string[] | string, -): ReturnType["del"]> => { - return await (await redis).del(key); +const del: Awaited["del"] = async (...args) => { + return await (await redis).del(...args) ; }; -const flush = async (): ReturnType["flushDb"]> => { - return await (await redis).flushDb(); +const flush: Awaited["flushDb"] = async (...args) => { + return await (await redis).flushDb(...args) ; }; export const kv = {