api/core: implement authentication with api keys

This commit is contained in:
dumbmoron 2024-10-04 16:50:55 +00:00
parent dcd33803c1
commit 81818f8741
No known key found for this signature in database

View file

@ -17,6 +17,7 @@ import { verifyTurnstileToken } from "../security/turnstile.js";
import { friendlyServiceName } from "../processing/service-alias.js"; import { friendlyServiceName } from "../processing/service-alias.js";
import { verifyStream, getInternalStream } from "../stream/manage.js"; import { verifyStream, getInternalStream } from "../stream/manage.js";
import { createResponse, normalizeRequest, getIP } from "../processing/request.js"; import { createResponse, normalizeRequest, getIP } from "../processing/request.js";
import * as APIKeys from "../security/api-keys.js";
const git = { const git = {
branch: await getBranch(), branch: await getBranch(),
@ -78,7 +79,7 @@ export const runAPI = (express, app, __dirname) => {
const apiLimiter = rateLimit({ const apiLimiter = rateLimit({
windowMs: env.rateLimitWindow * 1000, windowMs: env.rateLimitWindow * 1000,
max: env.rateLimitMax, max: (req) => req.rateLimitMax || env.rateLimitMax,
standardHeaders: true, standardHeaders: true,
legacyHeaders: false, legacyHeaders: false,
keyGenerator: req => req.rateLimitKey || generateHmac(getIP(req), ipSalt), keyGenerator: req => req.rateLimitKey || generateHmac(getIP(req), ipSalt),
@ -87,10 +88,10 @@ export const runAPI = (express, app, __dirname) => {
const apiTunnelLimiter = rateLimit({ const apiTunnelLimiter = rateLimit({
windowMs: env.rateLimitWindow * 1000, windowMs: env.rateLimitWindow * 1000,
max: env.rateLimitMax, max: (req) => req.rateLimitMax || env.rateLimitMax,
standardHeaders: true, standardHeaders: true,
legacyHeaders: false, legacyHeaders: false,
keyGenerator: req => generateHmac(getIP(req), ipSalt), keyGenerator: req => req.rateLimitKey || generateHmac(getIP(req), ipSalt),
handler: (req, res) => { handler: (req, res) => {
return res.sendStatus(429) return res.sendStatus(429)
} }
@ -119,6 +120,33 @@ export const runAPI = (express, app, __dirname) => {
next(); 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) => { app.post('/', (req, res, next) => {
if (!env.sessionEnabled) { if (!env.sessionEnabled) {
return next(); return next();
@ -315,6 +343,10 @@ export const runAPI = (express, app, __dirname) => {
setGlobalDispatcher(new ProxyAgent(env.externalProxy)) setGlobalDispatcher(new ProxyAgent(env.externalProxy))
} }
if (env.apiKeyURL) {
APIKeys.setup(env.apiKeyURL);
}
app.listen(env.apiPort, env.listenAddress, () => { app.listen(env.apiPort, env.listenAddress, () => {
console.log(`\n` + console.log(`\n` +
Bright(Cyan("cobalt ")) + Bright("API ^ω⁠^") + "\n" + Bright(Cyan("cobalt ")) + Bright("API ^ω⁠^") + "\n" +