Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 83 additions & 48 deletions src/lib/middleware/requireAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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;
Expand Down Expand Up @@ -34,18 +35,23 @@

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

Check failure on line 41 in src/lib/middleware/requireAuth.ts

View workflow job for this annotation

GitHub Actions / build (22.x)

'rateLimit' is never reassigned. Use 'const' instead
? (
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({
Expand All @@ -68,6 +74,13 @@
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";

Expand Down Expand Up @@ -96,58 +109,80 @@

// 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);
Expand Down
21 changes: 8 additions & 13 deletions src/redisClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Awaited<typeof redis>["set"]> => {
return await (await redis).set(key, data);
const set: Awaited<typeof redis>["set"] = async (...args) => {
return await (await redis).set(...args) ;
};

const get = async (key: string): ReturnType<Awaited<typeof redis>["get"]> => {
return await (await redis).get(key);
const get: Awaited<typeof redis>["get"] = async (...args) => {
return await (await redis).get(...args) ;
};

const del = async (
key: string[] | string,
): ReturnType<Awaited<typeof redis>["del"]> => {
return await (await redis).del(key);
const del: Awaited<typeof redis>["del"] = async (...args) => {
return await (await redis).del(...args) ;
};

const flush = async (): ReturnType<Awaited<typeof redis>["flushDb"]> => {
return await (await redis).flushDb();
const flush: Awaited<typeof redis>["flushDb"] = async (...args) => {
return await (await redis).flushDb(...args) ;
};

export const kv = {
Expand Down
Loading