From 986b96eb4def19ef1848e73f662a6ce193f725a6 Mon Sep 17 00:00:00 2001 From: konard Date: Thu, 30 Oct 2025 05:25:14 +0100 Subject: [PATCH] Add Bearer token authentication support for all API endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements Bearer token authentication via Authorization header for master token endpoints while maintaining backward compatibility with query parameter authentication. Changes: - Added getMasterTokenFromRequest() method to TokensService - Updated 10 endpoints across 5 controllers: * tokensController.js (4 endpoints) * completionsController.js (1 endpoint) * dialogsController.js (1 endpoint) * systemMessagesController.js (2 endpoints) * referralController.js (2 endpoints) Authentication priority: 1. Authorization: Bearer header (recommended) 2. ?masterToken= query parameter (deprecated, logs warning) Security improvements: - Follows OAuth 2.0 RFC 6750 standards - Prevents token exposure in server logs and browser history - Aligns with industry best practices - Better OpenAI API compatibility Backward compatibility: - Query parameter authentication still works - Deprecation warnings logged when using query parameters - No breaking changes for existing API consumers - Smooth migration path with clear error messages Related issue: deep-assistant/master-plan#14 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/controllers/completionsController.js | 5 ++-- src/controllers/dialogsController.js | 3 +- src/controllers/referralController.js | 8 +++-- src/controllers/systemMessagesController.js | 6 ++-- src/controllers/tokensController.js | 12 +++++--- src/services/TokensService.js | 33 +++++++++++++++++++++ 6 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/controllers/completionsController.js b/src/controllers/completionsController.js index 5cd6535..46e9034 100644 --- a/src/controllers/completionsController.js +++ b/src/controllers/completionsController.js @@ -211,8 +211,9 @@ completionsController.post( "/completions", rest(async ({req}) => { const requestId = Math.random().toString(36).substring(2, 15); - - await tokensService.isValidMasterToken(req.query.masterToken); + + const masterToken = tokensService.getMasterTokenFromRequest(req); + await tokensService.isValidMasterToken(masterToken); console.log(`[${requestId}] 📨 POST /completions`); const body = req.body; diff --git a/src/controllers/dialogsController.js b/src/controllers/dialogsController.js index 18211ff..9cb2a3a 100644 --- a/src/controllers/dialogsController.js +++ b/src/controllers/dialogsController.js @@ -17,7 +17,8 @@ dialogsController.delete( dialogsController.get( "/dialog-history", rest(async ({ req }) => { - await tokensService.isValidMasterToken(req.query.masterToken); + const masterToken = tokensService.getMasterTokenFromRequest(req); + await tokensService.isValidMasterToken(masterToken); const token = await tokensService.getTokenByUserId(req.query.dialogName); diff --git a/src/controllers/referralController.js b/src/controllers/referralController.js index c83af7b..f4ec459 100644 --- a/src/controllers/referralController.js +++ b/src/controllers/referralController.js @@ -8,8 +8,9 @@ const referralController = express.Router(); referralController.post( "/referral", rest(async ({ req }) => { - await tokensService.isValidMasterToken(req.query.masterToken); - + const masterToken = tokensService.getMasterTokenFromRequest(req); + await tokensService.isValidMasterToken(masterToken); + // Фикс: Обработка "None" и пустых значений let referralId = req.query.referralId?.trim() || null; if (referralId === "None" || referralId === "null") referralId = null; @@ -24,7 +25,8 @@ referralController.post( referralController.get( "/referral", rest(async ({ req }) => { - await tokensService.isValidMasterToken(req.query.masterToken); + const masterToken = tokensService.getMasterTokenFromRequest(req); + await tokensService.isValidMasterToken(masterToken); return new HttpResponse(200, await referralService.getReferral(req.query.userId)); }) ); diff --git a/src/controllers/systemMessagesController.js b/src/controllers/systemMessagesController.js index 1ccf9d8..2fd4d33 100755 --- a/src/controllers/systemMessagesController.js +++ b/src/controllers/systemMessagesController.js @@ -9,7 +9,8 @@ const systemMessagesController = express.Router(); systemMessagesController.post( "/system-message", rest(async ({ req }) => { - await tokensService.isValidMasterToken(req.query.masterToken); + const masterToken = tokensService.getMasterTokenFromRequest(req); + await tokensService.isValidMasterToken(masterToken); return new HttpResponse( 200, @@ -21,7 +22,8 @@ systemMessagesController.post( systemMessagesController.get( "/system-message", rest(async ({ req }) => { - await tokensService.isValidMasterToken(req.query.masterToken); + const masterToken = tokensService.getMasterTokenFromRequest(req); + await tokensService.isValidMasterToken(masterToken); return new HttpResponse(200, await systemMessageService.getSystemMessage(String(req.query.userId))); }), diff --git a/src/controllers/tokensController.js b/src/controllers/tokensController.js index df603e1..5c742e1 100755 --- a/src/controllers/tokensController.js +++ b/src/controllers/tokensController.js @@ -9,7 +9,8 @@ const tokensController = express.Router(); tokensController.get( "/token", rest(async ({ req }) => { - await tokensService.isValidMasterToken(req.query.masterToken); + const masterToken = tokensService.getMasterTokenFromRequest(req); + await tokensService.isValidMasterToken(masterToken); return new HttpResponse(200, await tokensService.getTokenByUserId(req.query.userId)); }), @@ -18,7 +19,8 @@ tokensController.get( tokensController.get( "/token/has", rest(async ({ req }) => { - await tokensService.isValidMasterToken(req.query.masterToken); + const masterToken = tokensService.getMasterTokenFromRequest(req); + await tokensService.isValidMasterToken(masterToken); const hasUser = await tokensService.hasUserToken(req.query.userId); return new HttpResponse(200, { hasUser }); @@ -28,7 +30,8 @@ tokensController.get( tokensController.put( "/token", rest(async ({ req }) => { - await tokensService.isValidMasterToken(req.query.masterToken); + const masterToken = tokensService.getMasterTokenFromRequest(req); + await tokensService.isValidMasterToken(masterToken); const { operation, amount } = req.body; @@ -41,7 +44,8 @@ tokensController.put( tokensController.post( "/token", rest(async ({ req }) => { - await tokensService.isValidMasterToken(req.query.masterToken); + const masterToken = tokensService.getMasterTokenFromRequest(req); + await tokensService.isValidMasterToken(masterToken); return new HttpResponse(200, await tokensService.regenerateToken(req.query.userId)); }), diff --git a/src/services/TokensService.js b/src/services/TokensService.js index 182e132..2c254a2 100755 --- a/src/services/TokensService.js +++ b/src/services/TokensService.js @@ -67,4 +67,37 @@ export class TokensService { getTokenFromAuthorization(authorization) { return authorization.split("Bearer ")[1]; } + + /** + * Extract master token from request, supporting both Authorization header and query parameter. + * Priority: Authorization header > query parameter (deprecated) + * @param {Request} req - Express request object + * @returns {string} Master token + * @throws {HttpException} If no token is provided + */ + getMasterTokenFromRequest(req) { + // Priority 1: Check Authorization header (recommended) + const authHeader = req.headers.authorization; + if (authHeader) { + if (authHeader.startsWith('Bearer ')) { + return authHeader.split('Bearer ')[1]; + } + // Handle case where "Bearer " prefix is missing + console.log('⚠️ [WARNING] Authorization header present but missing "Bearer " prefix'); + } + + // Priority 2: Fall back to query parameter (deprecated) + if (req.query.masterToken) { + console.log('⚠️ [DEPRECATED] Using masterToken in query parameter is deprecated and will be removed in v2.0.0.'); + console.log(' Please migrate to using Authorization header: "Authorization: Bearer "'); + console.log(' See documentation: https://github.com/deep-assistant/api-gateway#authentication'); + return req.query.masterToken; + } + + // No token provided + throw new HttpException( + 401, + "Missing authentication token. Please provide Authorization header: 'Authorization: Bearer '" + ); + } }