From 1edb531549f5c6aa21723f9ce3bd4d7079e36a22 Mon Sep 17 00:00:00 2001 From: bear Date: Tue, 11 Aug 2026 16:17:28 +0800 Subject: [PATCH] fix(auth): send Referer on the Google Maps key validation request (#1862) The admin panel's test button reported a key with an HTTP referrer restriction as invalid, because the validation call was the one Google request that did not send a Referer. It sends the same header googleFetch already sets for every real request. --- server/src/nest/auth/user-profile.service.ts | 5 +++++ .../unit/nest/user-profile.service.test.ts | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/server/src/nest/auth/user-profile.service.ts b/server/src/nest/auth/user-profile.service.ts index fd5247c48..b36a6ae07 100644 --- a/server/src/nest/auth/user-profile.service.ts +++ b/server/src/nest/auth/user-profile.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import path from 'path'; import fs from 'fs'; +import { readEnv, getAppUrl } from '../../app-config'; import { DatabaseService } from '../database/database.service'; import { decrypt_api_key, maybe_encrypt_api_key } from '../common/crypto/apiKeyCrypto'; import { avatarUrl } from '../common/avatarUrl'; @@ -201,11 +202,15 @@ export class UserProfileService { const maps_api_key = decrypt_api_key(user.maps_api_key); if (maps_api_key) { try { + // Same Referer as maps.service googleFetch — without it, keys with an + // HTTP-referrer restriction fail validation while real requests succeed. + const referer = readEnv().app.appUrl ? getAppUrl() : undefined; const mapsRes = await fetch( `https://places.googleapis.com/v1/places:searchText`, { method: 'POST', headers: { + ...(referer ? { Referer: referer } : {}), 'Content-Type': 'application/json', 'X-Goog-Api-Key': maps_api_key, 'X-Goog-FieldMask': 'places.displayName', diff --git a/server/tests/unit/nest/user-profile.service.test.ts b/server/tests/unit/nest/user-profile.service.test.ts index 45288129f..01d3d58fa 100644 --- a/server/tests/unit/nest/user-profile.service.test.ts +++ b/server/tests/unit/nest/user-profile.service.test.ts @@ -232,6 +232,27 @@ describe('validateKeys', () => { fetchSpy.mockRestore(); }); + + it('AUTH-DB-098: sends Referer from APP_URL so referrer-restricted keys validate like real requests', async () => { + const { user } = createAdmin(testDb); + testDb.prepare('UPDATE users SET maps_api_key = ? WHERE id = ?').run('test-key', user.id); + + const prevAppUrl = process.env.APP_URL; + process.env.APP_URL = 'https://trek.example.com'; + const fetchSpy = vi.spyOn(global, 'fetch').mockResolvedValueOnce({ + status: 200, + statusText: 'OK', + text: async () => '', + } as Response); + + await profile.validateKeys(user.id); + const headers = (fetchSpy.mock.calls[0]?.[1]?.headers ?? {}) as Record; + expect(headers.Referer).toBe('https://trek.example.com'); + + fetchSpy.mockRestore(); + if (prevAppUrl === undefined) delete process.env.APP_URL; + else process.env.APP_URL = prevAppUrl; + }); }); describe('updateMapsKey / avatar', () => {