Skip to content
This repository was archived by the owner on Apr 19, 2023. It is now read-only.

Commit 2297994

Browse files
✨ Add auth support for API key pair
1 parent 7a7b1a2 commit 2297994

File tree

3 files changed

+72
-6
lines changed

3 files changed

+72
-6
lines changed

src/config.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@ export const RATE_LIMIT_TIME = process.env.RATE_LIMIT_TIME
1717
: 60000; // 1 minute
1818
export const RATE_LIMIT_MAX = process.env.RATE_LIMIT_MAX
1919
? parseInt(process.env.RATE_LIMIT_MAX)
20-
: 200; // Max 200 requests/minute from an IP
20+
: 300; // Max 300 requests/minute from an IP
21+
export const PUBLIC_RATE_LIMIT_TIME = process.env.PUBLIC_RATE_LIMIT_TIME
22+
? parseInt(process.env.PUBLIC_RATE_LIMIT_TIME)
23+
: 60000; // 1 minute
24+
export const PUBLIC_RATE_LIMIT_MAX = process.env.PUBLIC_RATE_LIMIT_MAX
25+
? parseInt(process.env.PUBLIC_RATE_LIMIT_MAX)
26+
: 60; // Max 60 requests/minute from an IP
2127
export const SPEED_LIMIT_TIME = process.env.SPEED_LIMIT_TIME
2228
? parseInt(process.env.SPEED_LIMIT_TIME)
2329
: 600000; // 10 minutes

src/crud/user.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,19 @@ export const getApiKey = async (apiKey: string) => {
211211
))[0];
212212
};
213213

214+
/**
215+
* Get an API key/secret
216+
*/
217+
export const getApiKeySecret = async (apiKey: string, secretKey: string) => {
218+
deleteItemFromCache(CacheCategories.API_KEY, apiKey);
219+
return (<ApiKey[]>(
220+
await query(
221+
"SELECT * FROM `api-keys` WHERE apiKey = ? AND secretKey = ? LIMIT 1",
222+
[apiKey, secretKey]
223+
)
224+
))[0];
225+
};
226+
214227
/**
215228
* Create an API key
216229
*/

src/helpers/middleware.ts

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@ import {
1212
RATE_LIMIT_TIME,
1313
SPEED_LIMIT_DELAY,
1414
SPEED_LIMIT_COUNT,
15-
SPEED_LIMIT_TIME
15+
SPEED_LIMIT_TIME,
16+
PUBLIC_RATE_LIMIT_TIME,
17+
PUBLIC_RATE_LIMIT_MAX
1618
} from "../config";
19+
import { getApiKey, getApiKeySecret } from "../crud/user";
1720
const store = new Brute.MemoryStore();
1821
const bruteForce = new Brute(store, {
1922
freeRetries: BRUTE_FREE_RETRIES,
@@ -23,6 +26,10 @@ const rateLimiter = RateLimit({
2326
windowMs: RATE_LIMIT_TIME,
2427
max: RATE_LIMIT_MAX
2528
});
29+
const publicRateLimiter = RateLimit({
30+
windowMs: PUBLIC_RATE_LIMIT_TIME,
31+
max: PUBLIC_RATE_LIMIT_MAX
32+
});
2633
const speedLimiter = slowDown({
2734
windowMs: SPEED_LIMIT_TIME,
2835
delayAfter: SPEED_LIMIT_COUNT,
@@ -80,10 +87,23 @@ export const authHandler = async (
8087
return res.json(error);
8188
}
8289
if (token.startsWith("Bearer ")) token = token.replace("Bearer ", "");
90+
let localsToken;
91+
try {
92+
localsToken = await verifyToken(token, Tokens.LOGIN);
93+
} catch (e) {}
94+
const secretKey = req.get("X-Api-Secret");
8395
try {
84-
res.locals.token = await verifyToken(token, Tokens.LOGIN);
96+
if (secretKey) {
97+
const apiKey = await getApiKeySecret(token, secretKey);
98+
if (apiKey.userId) {
99+
localsToken = { id: apiKey.userId };
100+
}
101+
}
102+
} catch (e) {}
103+
if (localsToken) {
104+
res.locals.token = localsToken;
85105
next();
86-
} catch (e) {
106+
} else {
87107
const error = safeError(ErrorCode.INVALID_TOKEN);
88108
res.status(error.status);
89109
return res.json(error);
@@ -98,9 +118,36 @@ export const bruteForceHandler = bruteForce.prevent;
98118
/**
99119
* Rate limiting middleware
100120
*/
101-
export const rateLimitHandler = rateLimiter;
121+
export const rateLimitHandler = async (
122+
req: Request,
123+
res: Response,
124+
next: NextFunction
125+
) => {
126+
const apiKey = req.get("X-Api-Key");
127+
if (apiKey) {
128+
const apiKeyDetails = await getApiKey(apiKey);
129+
if (apiKeyDetails.userId) {
130+
return rateLimiter(req, res, next);
131+
}
132+
}
133+
return publicRateLimiter(req, res, next);
134+
};
102135

103136
/**
104137
* Speed limiting middleware
105138
*/
106-
export const speedLimitHandler = speedLimiter;
139+
export const speedLimitHandler = async (
140+
req: Request,
141+
res: Response,
142+
next: NextFunction
143+
) => {
144+
const apiKey = req.get("X-Api-Key");
145+
if (apiKey) {
146+
const apiKeyDetails = await getApiKey(apiKey);
147+
if (apiKeyDetails.userId) {
148+
// Don't slow down requests if an API key is used
149+
return next();
150+
}
151+
}
152+
return speedLimiter(req, res, next);
153+
};

0 commit comments

Comments
 (0)