From 190442a8cf999333319cb36aadf625c9f6fa2fda Mon Sep 17 00:00:00 2001 From: Stefano Date: Mon, 30 Jun 2025 12:32:44 -0500 Subject: [PATCH] feat(auth): add api key authentication Implement API key authentication by introducing a new auth module. Update configuration and .env.example to support API key setup, and add authorization checks in the server endpoints. --- .env.example | 3 ++- src/auth.ts | 38 ++++++++++++++++++++++++++++++++++++++ src/config.ts | 6 ++++++ src/server.ts | 5 +++++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/auth.ts diff --git a/.env.example b/.env.example index 7791e53..5361eab 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ PORT=11434 -VERBOSE=false \ No newline at end of file +VERBOSE=false +API_KEY=MY0P3NA1K3Y \ No newline at end of file diff --git a/src/auth.ts b/src/auth.ts new file mode 100644 index 0000000..0a111b9 --- /dev/null +++ b/src/auth.ts @@ -0,0 +1,38 @@ +/** + * @fileoverview This file contains the authentication logic for the server. + */ +import http from 'http'; +import { config } from './config'; + +/** + * Checks for API key authentication. + * @param req - The HTTP incoming message object. + * @param res - The HTTP server response object. + * @returns True if the request is authorized, false otherwise. + */ +export function isAuthorized( + req: http.IncomingMessage, + res: http.ServerResponse, +): boolean { + if (!config.API_KEY) { + return true; // No key configured, public access. + } + + const authHeader = req.headers.authorization; + if (!authHeader) { + res.writeHead(401, { 'Content-Type': 'application/json' }); + res.end( + JSON.stringify({ error: { message: 'Missing Authorization header' } }), + ); + return false; + } + + const token = authHeader.split(' ')[1]; + if (token !== config.API_KEY) { + res.writeHead(401, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ error: { message: 'Invalid API key' } })); + return false; + } + + return true; +} \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index d5ef890..87e7bdd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -23,4 +23,10 @@ export const config = { * @type {boolean} */ VERBOSE: Boolean(process.env.VERBOSE ?? true), + /** + * The API key for securing the server. + * If not set, the server will be public. + * @type {string | undefined} + */ + API_KEY: process.env.API_KEY, }; \ No newline at end of file diff --git a/src/server.ts b/src/server.ts index ab66426..0c290d4 100644 --- a/src/server.ts +++ b/src/server.ts @@ -8,6 +8,7 @@ import { listModels, sendChat, sendChatStream } from './chatwrapper'; import { mapRequest, mapResponse, mapStreamChunk } from './mapper.js'; import { RequestBody, GeminiResponse, GeminiStreamChunk, Part } from './types'; import { config } from './config'; +import { isAuthorized } from './auth'; // ================================================================== // Server Configuration @@ -97,6 +98,10 @@ http return; } + if (!isAuthorized(req, res)) { + return; + } + // Route for listing available models. if (pathname === '/v1/models' || pathname === '/models') { res.writeHead(200, { 'Content-Type': 'application/json' });