Use X-Signal-Timestamp header
Co-authored-by: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com>
This commit is contained in:
parent
cdcd7bb02f
commit
0b60be8431
5 changed files with 27 additions and 18 deletions
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -126,7 +126,7 @@
|
||||||
"@indutny/rezip-electron": "2.0.1",
|
"@indutny/rezip-electron": "2.0.1",
|
||||||
"@indutny/symbolicate-mac": "2.3.0",
|
"@indutny/symbolicate-mac": "2.3.0",
|
||||||
"@napi-rs/canvas": "0.1.61",
|
"@napi-rs/canvas": "0.1.61",
|
||||||
"@signalapp/mock-server": "10.2.0",
|
"@signalapp/mock-server": "10.4.0",
|
||||||
"@storybook/addon-a11y": "8.4.4",
|
"@storybook/addon-a11y": "8.4.4",
|
||||||
"@storybook/addon-actions": "8.4.4",
|
"@storybook/addon-actions": "8.4.4",
|
||||||
"@storybook/addon-controls": "8.4.4",
|
"@storybook/addon-controls": "8.4.4",
|
||||||
|
@ -6491,9 +6491,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@signalapp/mock-server": {
|
"node_modules/@signalapp/mock-server": {
|
||||||
"version": "10.2.0",
|
"version": "10.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@signalapp/mock-server/-/mock-server-10.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@signalapp/mock-server/-/mock-server-10.4.0.tgz",
|
||||||
"integrity": "sha512-5OL/ajXrEpBmwEpZlZiZbcJSJb9RRy19rMyeYiwIxkPSH3xnyobw510y4p4WkcSfeiV/uif4Tmr99Fk/3oAfxg==",
|
"integrity": "sha512-Y2Fj2rP8sI/Z8JBjXgJoHO+6VqfORopeFixka11CrxxDBXQWr4u3+P3hsS5wIwwtgcsaXIultRW+kYhbkttRLw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -217,7 +217,7 @@
|
||||||
"@indutny/rezip-electron": "2.0.1",
|
"@indutny/rezip-electron": "2.0.1",
|
||||||
"@indutny/symbolicate-mac": "2.3.0",
|
"@indutny/symbolicate-mac": "2.3.0",
|
||||||
"@napi-rs/canvas": "0.1.61",
|
"@napi-rs/canvas": "0.1.61",
|
||||||
"@signalapp/mock-server": "10.2.0",
|
"@signalapp/mock-server": "10.4.0",
|
||||||
"@storybook/addon-a11y": "8.4.4",
|
"@storybook/addon-a11y": "8.4.4",
|
||||||
"@storybook/addon-actions": "8.4.4",
|
"@storybook/addon-actions": "8.4.4",
|
||||||
"@storybook/addon-controls": "8.4.4",
|
"@storybook/addon-controls": "8.4.4",
|
||||||
|
|
|
@ -7,7 +7,7 @@ import type { WebAPIType } from './textsecure/WebAPI';
|
||||||
import * as log from './logging/log';
|
import * as log from './logging/log';
|
||||||
import type { AciString } from './types/ServiceId';
|
import type { AciString } from './types/ServiceId';
|
||||||
import { parseIntOrThrow } from './util/parseIntOrThrow';
|
import { parseIntOrThrow } from './util/parseIntOrThrow';
|
||||||
import { SECOND, HOUR } from './util/durations';
|
import { HOUR } from './util/durations';
|
||||||
import * as Bytes from './Bytes';
|
import * as Bytes from './Bytes';
|
||||||
import { uuidToBytes } from './util/uuidToBytes';
|
import { uuidToBytes } from './util/uuidToBytes';
|
||||||
import { dropNull } from './util/dropNull';
|
import { dropNull } from './util/dropNull';
|
||||||
|
@ -87,14 +87,14 @@ export const refreshRemoteConfig = async (
|
||||||
server: WebAPIType
|
server: WebAPIType
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const { config: newConfig, serverEpochTime } = await server.getConfig();
|
const { config: newConfig, serverTimestamp } = await server.getConfig();
|
||||||
|
|
||||||
const serverTimeSkew = serverEpochTime * SECOND - now;
|
const serverTimeSkew = serverTimestamp - now;
|
||||||
|
|
||||||
if (Math.abs(serverTimeSkew) > HOUR) {
|
if (Math.abs(serverTimeSkew) > HOUR) {
|
||||||
log.warn(
|
log.warn(
|
||||||
'Remote Config: sever clock skew detected. ' +
|
'Remote Config: sever clock skew detected. ' +
|
||||||
`Server time ${serverEpochTime * SECOND}, local time ${now}`
|
`Server time ${serverTimestamp}, local time ${now}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,13 @@ import type {
|
||||||
WebAPIType,
|
WebAPIType,
|
||||||
RemoteConfigResponseType,
|
RemoteConfigResponseType,
|
||||||
} from '../../textsecure/WebAPI';
|
} from '../../textsecure/WebAPI';
|
||||||
import { SECOND } from '../../util/durations';
|
|
||||||
|
|
||||||
export async function updateRemoteConfig(
|
export async function updateRemoteConfig(
|
||||||
newConfig: RemoteConfigResponseType['config']
|
newConfig: RemoteConfigResponseType['config']
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const fakeServer = {
|
const fakeServer = {
|
||||||
async getConfig() {
|
async getConfig() {
|
||||||
return { config: newConfig, serverEpochTime: Date.now() / SECOND };
|
return { config: newConfig, serverTimestamp: Date.now() };
|
||||||
},
|
},
|
||||||
} as Partial<WebAPIType> as unknown as WebAPIType;
|
} as Partial<WebAPIType> as unknown as WebAPIType;
|
||||||
|
|
||||||
|
|
|
@ -829,9 +829,11 @@ const remoteConfigResponseZod = z.object({
|
||||||
value: z.string().or(z.null()).optional(),
|
value: z.string().or(z.null()).optional(),
|
||||||
})
|
})
|
||||||
.array(),
|
.array(),
|
||||||
serverEpochTime: z.number(),
|
|
||||||
});
|
});
|
||||||
export type RemoteConfigResponseType = z.infer<typeof remoteConfigResponseZod>;
|
export type RemoteConfigResponseType = z.infer<typeof remoteConfigResponseZod> &
|
||||||
|
Readonly<{
|
||||||
|
serverTimestamp: number;
|
||||||
|
}>;
|
||||||
|
|
||||||
export type ProfileType = Readonly<{
|
export type ProfileType = Readonly<{
|
||||||
identityKey?: string;
|
identityKey?: string;
|
||||||
|
@ -2130,16 +2132,24 @@ export function initialize({
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getConfig() {
|
async function getConfig() {
|
||||||
const rawRes = await _ajax({
|
const { data, response } = await _ajax({
|
||||||
call: 'config',
|
call: 'config',
|
||||||
httpType: 'GET',
|
httpType: 'GET',
|
||||||
responseType: 'json',
|
responseType: 'jsonwithdetails',
|
||||||
});
|
});
|
||||||
const res = parseUnknown(remoteConfigResponseZod, rawRes);
|
const json = parseUnknown(remoteConfigResponseZod, data);
|
||||||
|
|
||||||
|
const serverTimestamp = safeParseNumber(
|
||||||
|
response.headers.get('x-signal-timestamp') || ''
|
||||||
|
);
|
||||||
|
if (serverTimestamp == null) {
|
||||||
|
throw new Error('Missing required x-signal-timestamp header');
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...res,
|
...json,
|
||||||
config: res.config.filter(
|
serverTimestamp,
|
||||||
|
config: json.config.filter(
|
||||||
({ name }: { name: string }) =>
|
({ name }: { name: string }) =>
|
||||||
name.startsWith('desktop.') ||
|
name.startsWith('desktop.') ||
|
||||||
name.startsWith('global.') ||
|
name.startsWith('global.') ||
|
||||||
|
|
Loading…
Add table
Reference in a new issue