fix(settings-proxy): send SMS shared secret as Bearer for CDN

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-11 19:53:30 +03:30
co-authored by Cursor
parent 8fe7d59c1b
commit aec5e08a1e
+17 -10
View File
@@ -116,9 +116,10 @@ export class SettingsProxyService {
}
/**
* Prefer per-tenant Bearer; else same shared HMAC used by SMS remote.
* Canonical path is always /api/super/settings so product middleware can verify
* regardless of /server prefix on nodeServer.
* Auth to product:
* 1) Tenant remoteApiToken as Bearer (per-tenant)
* 2) Else SMS shared secret as Bearer (CDN-safe; same secret as SMS)
* + optional HMAC headers for non-CDN installs
*/
private buildAuthHeaders(
tenant: {
@@ -137,22 +138,28 @@ export class SettingsProxyService {
const shared = String(
this.config.get<string>('SMS_REMOTE_SHARED_SECRET') || '',
).trim();
const domain = this.resolveTenantDomain(tenant);
if (!shared || !domain) {
if (!shared) {
return null;
}
// Bearer with shared secret survives CDNs that strip custom X-* headers.
const headers: Record<string, string> = {
Authorization: `Bearer ${shared}`,
};
const domain = this.resolveTenantDomain(tenant);
if (domain) {
const timestamp = String(Math.floor(Date.now() / 1000));
const payload = `${timestamp}.${method}.${SUPER_SETTINGS_CANONICAL_PATH}.${domain}`;
const signature = createHmac('sha256', shared)
.update(payload, 'utf8')
.digest('hex');
headers['X-Abshode-Domain'] = domain;
headers['X-Abshode-Timestamp'] = timestamp;
headers['X-Abshode-Signature'] = signature;
}
return {
'X-Abshode-Domain': domain,
'X-Abshode-Timestamp': timestamp,
'X-Abshode-Signature': signature,
};
return headers;
}
private resolveTenantDomain(tenant: {