You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/ts/session/apis/open_group_api/opengroupV2/ApiAuth.ts

153 lines
4.8 KiB
TypeScript

import { getV2OpenGroupRoomByRoomId, saveV2OpenGroupRoom } from '../../../../data/opengroups';
import { callUtilsWorker } from '../../../../webworker/workers/util_worker_interface';
import { allowOnlyOneAtATime } from '../../../utils/Promise';
import { toHex } from '../../../utils/String';
import { getIdentityKeyPair, getOurPubKeyStrFromCache } from '../../../utils/User';
import { OpenGroupRequestCommonType, OpenGroupV2Request } from './ApiUtil';
import { sendApiV2Request } from './OpenGroupAPIV2';
import { parseStatusCodeFromOnionRequest } from './OpenGroupAPIV2Parser';
async function claimAuthToken(
authToken: string,
serverUrl: string,
roomId: string
): Promise<string | null> {
// Set explicitly here because is isn't in the database yet at this point
const headers = { Authorization: authToken };
const request: OpenGroupV2Request = {
method: 'POST',
headers,
room: roomId,
server: serverUrl,
queryParams: { public_key: getOurPubKeyStrFromCache() },
isAuthRequired: false,
endpoint: 'claim_auth_token',
};
const result = await sendApiV2Request(request);
const statusCode = parseStatusCodeFromOnionRequest(result);
if (statusCode !== 200) {
window?.log?.warn(`Could not claim token, status code: ${statusCode}`);
return null;
}
return authToken;
}
async function oneAtATimeGetAuth({ serverUrl, roomId }: OpenGroupRequestCommonType) {
return allowOnlyOneAtATime(`getAuthToken${serverUrl}:${roomId}`, async () => {
try {
// first try to fetch from db a saved token.
const roomDetails = await getV2OpenGroupRoomByRoomId({ serverUrl, roomId });
if (!roomDetails) {
window?.log?.warn('getAuthToken Room does not exist.');
return null;
}
if (roomDetails?.token) {
return roomDetails.token;
}
window?.log?.info(
`Triggering getAuthToken with serverUrl:'${serverUrl}'; roomId: '${roomId}'`
);
const token = await requestNewAuthToken({ serverUrl, roomId });
if (!token) {
window?.log?.warn('invalid new auth token', token);
return;
}
window?.log?.info(`Got AuthToken for serverUrl:'${serverUrl}'; roomId: '${roomId}'`);
const claimedToken = await claimAuthToken(token, serverUrl, roomId);
if (!claimedToken) {
window?.log?.warn('Failed to claim token', claimedToken);
} else {
window?.log?.info(`Claimed AuthToken for serverUrl:'${serverUrl}'; roomId: '${roomId}'`);
}
// still save it to the db. just to mark it as to be refreshed later
roomDetails.token = claimedToken || '';
await saveV2OpenGroupRoom(roomDetails);
window?.log?.info(`AuthToken saved to DB for serverUrl:'${serverUrl}'; roomId: '${roomId}'`);
return claimedToken;
} catch (e) {
window?.log?.error('Failed to getAuthToken', e);
throw e;
}
});
}
export async function getAuthToken({
serverUrl,
roomId,
}: OpenGroupRequestCommonType): Promise<string | null> {
return oneAtATimeGetAuth({ roomId, serverUrl });
}
// tslint:disable: member-ordering
export async function requestNewAuthToken({
serverUrl,
roomId,
}: OpenGroupRequestCommonType): Promise<string | null> {
const userKeyPair = await getIdentityKeyPair();
if (!userKeyPair) {
throw new Error('Failed to fetch user keypair');
}
const ourPubkey = getOurPubKeyStrFromCache();
const parameters = {} as Record<string, string>;
parameters.public_key = ourPubkey;
const request: OpenGroupV2Request = {
method: 'GET',
room: roomId,
server: serverUrl,
queryParams: parameters,
isAuthRequired: false,
endpoint: 'auth_token_challenge',
};
const json = (await sendApiV2Request(request)) as any;
// parse the json
if (!json || !json?.result?.challenge) {
window?.log?.warn('Parsing failed');
return null;
}
const {
ciphertext: base64EncodedCiphertext,
ephemeral_public_key: base64EncodedEphemeralPublicKey,
} = json?.result?.challenge;
if (!base64EncodedCiphertext || !base64EncodedEphemeralPublicKey) {
window?.log?.warn('Parsing failed');
return null;
}
const ciphertext = (await callUtilsWorker(
'fromBase64ToArrayBuffer',
base64EncodedCiphertext
)) as ArrayBuffer;
const ephemeralPublicKey = (await callUtilsWorker(
'fromBase64ToArrayBuffer',
base64EncodedEphemeralPublicKey
)) as ArrayBuffer;
try {
const symmetricKey = (await callUtilsWorker(
'deriveSymmetricKey',
new Uint8Array(ephemeralPublicKey),
new Uint8Array(userKeyPair.privKey)
)) as ArrayBuffer;
const plaintextBuffer = await callUtilsWorker(
'DecryptAESGCM',
new Uint8Array(symmetricKey),
new Uint8Array(ciphertext)
);
const token = toHex(plaintextBuffer);
return token;
} catch (e) {
window?.log?.error('Failed to decrypt token open group v2');
return null;
}
}