From de7fc7d03cf089b7d2200ec75b0ff9a677c73281 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 06:25:47 +0000 Subject: [PATCH 1/2] fix(openrouter): trim API key and guard against empty key An OpenRouter key with trailing whitespace/newline (common from copy-paste) produces "Missing Authentication header" from their API, which looked identical to a missing key. Trim apiKey on save (schema level) and on use (defense in depth), fail fast with a clear message if the stored key is empty, and log a masked key preview + model on each generation call to make future auth failures diagnosable from container logs. --- server/services/openrouter.ts | 11 +++++++++-- shared/schema.ts | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/server/services/openrouter.ts b/server/services/openrouter.ts index c5b5c8e..073e7d8 100644 --- a/server/services/openrouter.ts +++ b/server/services/openrouter.ts @@ -26,16 +26,23 @@ export class OpenRouterService { throw new Error('Configuration OpenRouter non trouvée. Veuillez demander à un administrateur de configurer OpenRouter dans les Paramètres.'); } + if (!config.apiKey || !config.apiKey.trim()) { + throw new Error('Clé API OpenRouter manquante ou vide en base de données. Veuillez la ressaisir dans les Paramètres.'); + } + const prompt = this.buildPrompt(productInfo, config.systemPrompt); - + // Use provided model or fall back to config model const modelToUse = modelOverride || config.model; + const keyPreview = `${config.apiKey.slice(0, 10)}...(len=${config.apiKey.length})`; + console.log(`[OpenRouter] Generating with model="${modelToUse}" key=${keyPreview} configUserId=${config.userId}`); + try { const response = await fetch(this.baseUrl, { method: "POST", headers: { - "Authorization": `Bearer ${config.apiKey}`, + "Authorization": `Bearer ${config.apiKey.trim()}`, "Content-Type": "application/json", "HTTP-Referer": process.env.APP_URL || "http://localhost:5555", "X-Title": "Social Flow" diff --git a/shared/schema.ts b/shared/schema.ts index 5797ead..e8ff5ad 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -394,7 +394,7 @@ export const insertOpenrouterConfigSchema = createInsertSchema(openrouterConfig) createdAt: true, updatedAt: true, }).extend({ - apiKey: z.string().min(1, "La clé API ne peut pas être vide"), + apiKey: z.string().trim().min(1, "La clé API ne peut pas être vide"), }); export const updateOpenrouterConfigSchema = insertOpenrouterConfigSchema.partial({ From 8155b30c63564cb2b389b22905810e5f3a82977c Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 06:31:18 +0000 Subject: [PATCH 2/2] fix(openrouter): stop logging API key material, use a hash fingerprint Address Codex review: the debug log previously included the key's first 10 characters. Replace with a non-reversible SHA-256 fingerprint so logs remain useful for correlating support reports without ever exposing key material. --- server/services/openrouter.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/services/openrouter.ts b/server/services/openrouter.ts index 073e7d8..2137458 100644 --- a/server/services/openrouter.ts +++ b/server/services/openrouter.ts @@ -12,6 +12,7 @@ interface GeneratedText { characterCount: number; } +import crypto from 'crypto'; import { storage } from '../storage'; export class OpenRouterService { @@ -35,8 +36,8 @@ export class OpenRouterService { // Use provided model or fall back to config model const modelToUse = modelOverride || config.model; - const keyPreview = `${config.apiKey.slice(0, 10)}...(len=${config.apiKey.length})`; - console.log(`[OpenRouter] Generating with model="${modelToUse}" key=${keyPreview} configUserId=${config.userId}`); + const keyFingerprint = crypto.createHash('sha256').update(config.apiKey).digest('hex').slice(0, 8); + console.log(`[OpenRouter] Generating with model="${modelToUse}" keyFingerprint=${keyFingerprint} keyLength=${config.apiKey.length} configUserId=${config.userId}`); try { const response = await fetch(this.baseUrl, {