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.
This commit is contained in:
bear
2026-08-11 16:17:28 +08:00
committed by GitHub
parent b56dcec118
commit 1edb531549
2 changed files with 26 additions and 0 deletions
@@ -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',
@@ -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<string, string>;
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', () => {