diff --git a/api/src/core/api.js b/api/src/core/api.js index 71c182f0..4ee64508 100644 --- a/api/src/core/api.js +++ b/api/src/core/api.js @@ -17,6 +17,7 @@ import { verifyTurnstileToken } from "../security/turnstile.js"; import { friendlyServiceName } from "../processing/service-alias.js"; import { verifyStream, getInternalStream } from "../stream/manage.js"; import { createResponse, normalizeRequest, getIP } from "../processing/request.js"; +import * as APIKeys from "../security/api-keys.js"; const git = { branch: await getBranch(), @@ -78,7 +79,7 @@ export const runAPI = (express, app, __dirname) => { const apiLimiter = rateLimit({ windowMs: env.rateLimitWindow * 1000, - max: env.rateLimitMax, + max: (req) => req.rateLimitMax || env.rateLimitMax, standardHeaders: true, legacyHeaders: false, keyGenerator: req => req.rateLimitKey || generateHmac(getIP(req), ipSalt), @@ -87,10 +88,10 @@ export const runAPI = (express, app, __dirname) => { const apiTunnelLimiter = rateLimit({ windowMs: env.rateLimitWindow * 1000, - max: env.rateLimitMax, + max: (req) => req.rateLimitMax || env.rateLimitMax, standardHeaders: true, legacyHeaders: false, - keyGenerator: req => generateHmac(getIP(req), ipSalt), + keyGenerator: req => req.rateLimitKey || generateHmac(getIP(req), ipSalt), handler: (req, res) => { return res.sendStatus(429) } @@ -119,6 +120,33 @@ export const runAPI = (express, app, __dirname) => { next(); }); + app.post('/', (req, res, next) => { + if (!env.apiKeyURL) { + return next(); + } + + const { success, error } = APIKeys.validateAuthorization(req); + if (!success) { + // We call next() here if either if: + // a) we have user sessions enabled, meaning the request + // will still need a Bearer token to not be rejected, or + // b) we do not require the user to be authenticated, and + // so they can just make the request with the regular + // rate limit configuration; + // otherwise, we reject the request. + if ( + (env.sessionEnabled || !env.authRequired) + && ['missing', 'not_api_key'].includes(error) + ) { + return next(); + } + + return fail(res, `error.api.auth.key.${error}`); + } + + return next(); + }); + app.post('/', (req, res, next) => { if (!env.sessionEnabled) { return next(); @@ -315,6 +343,10 @@ export const runAPI = (express, app, __dirname) => { setGlobalDispatcher(new ProxyAgent(env.externalProxy)) } + if (env.apiKeyURL) { + APIKeys.setup(env.apiKeyURL); + } + app.listen(env.apiPort, env.listenAddress, () => { console.log(`\n` + Bright(Cyan("cobalt ")) + Bright("API ^ω⁠^") + "\n" +