diff --git a/ts/background.ts b/ts/background.ts index 5720838b1..78b7d9b13 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -818,7 +818,7 @@ export async function startApp(): Promise { await window.Signal.Data.clearAllErrorStickerPackAttempts(); } - if (window.isBeforeVersion(lastVersion, 'v5.50.0-alpha.1')) { + if (window.isBeforeVersion(lastVersion, 'v5.51.0-beta.2')) { await window.storage.put('groupCredentials', []); await window.Signal.Data.removeAllProfileKeyCredentials(); } diff --git a/ts/groups.ts b/ts/groups.ts index 4a93ca5d9..fe00cd633 100644 --- a/ts/groups.ts +++ b/ts/groups.ts @@ -60,6 +60,7 @@ import type { GroupCredentialsType, GroupLogResponseType, } from './textsecure/WebAPI'; +import { HTTPError } from './textsecure/Errors'; import type MessageSender from './textsecure/SendMessage'; import { CURRENT_SCHEMA_VERSION as MAX_MESSAGE_SCHEMA } from './types/Message2'; import type { ConversationModel } from './models/conversations'; @@ -1491,13 +1492,6 @@ export async function modifyGroupV2({ log.info(`modifyGroupV2/${logId}: Fetching profiles for ${logIds}`); } - for (const member of membersMissingCredentials) { - member.set({ - profileKeyCredential: null, - profileKeyCredentialExpiration: null, - }); - } - // eslint-disable-next-line no-await-in-loop await Promise.all( membersMissingCredentials.map(member => member.getProfiles()) @@ -1735,19 +1729,25 @@ export async function fetchMembershipProof({ // Creating a group -export async function createGroupV2({ - name, - avatar, - expireTimer, - conversationIds, - avatars, -}: Readonly<{ - name: string; - avatar: undefined | Uint8Array; - expireTimer: undefined | number; - conversationIds: Array; - avatars?: Array; -}>): Promise { +export async function createGroupV2( + options: Readonly<{ + name: string; + avatar: undefined | Uint8Array; + expireTimer: undefined | number; + conversationIds: Array; + avatars?: Array; + refreshedCredentials?: boolean; + }> +): Promise { + const { + name, + avatar, + expireTimer, + conversationIds, + avatars, + refreshedCredentials = false, + } = options; + // Ensure we have the credentials we need before attempting GroupsV2 operations await maybeFetchNewCredentials(); @@ -1770,10 +1770,6 @@ export async function createGroupV2({ window.ConversationController.getOurConversationOrThrow(); if (ourConversation.hasProfileKeyCredentialExpired()) { log.info(`createGroupV2/${logId}: fetching our own credentials`); - ourConversation.set({ - profileKeyCredential: null, - profileKeyCredentialExpiration: null, - }); await ourConversation.getProfiles(); } @@ -1869,12 +1865,45 @@ export async function createGroupV2({ ...protoAndConversationAttributes, }); - await makeRequestWithTemporalRetry({ - logId: `createGroupV2/${logId}`, - publicParams, - secretParams, - request: (sender, options) => sender.createGroup(groupProto, options), - }); + try { + await makeRequestWithTemporalRetry({ + logId: `createGroupV2/${logId}`, + publicParams, + secretParams, + request: (sender, requestOptions) => + sender.createGroup(groupProto, requestOptions), + }); + } catch (error) { + if (!(error instanceof HTTPError)) { + throw error; + } + if (error.code !== 400 || refreshedCredentials) { + throw error; + } + + const logIds = conversationIds.map(conversationId => { + const contact = window.ConversationController.get(conversationId); + if (!contact) { + return; + } + contact.set({ + profileKeyCredential: null, + profileKeyCredentialExpiration: null, + }); + + return contact.idForLogging(); + }); + + log.warn( + `createGroupV2/${logId}: Profile key credentials were not ` + + `up-to-date. Updating profiles for ${logIds} and retrying` + ); + + return createGroupV2({ + ...options, + refreshedCredentials: true, + }); + } let avatarAttribute: ConversationAttributesType['avatar']; if (uploadedAvatar) { diff --git a/ts/routineProfileRefresh.ts b/ts/routineProfileRefresh.ts index 619beb631..ba9fd54f6 100644 --- a/ts/routineProfileRefresh.ts +++ b/ts/routineProfileRefresh.ts @@ -196,10 +196,6 @@ function* getFilteredConversations( (conversation.id === ourConversationId || !conversationIdsSeen.has(conversation.id)) ) { - conversation.set({ - profileKeyCredential: null, - profileKeyCredentialExpiration: null, - }); conversationIdsSeen.add(conversation.id); yield conversation; break; diff --git a/ts/services/profiles.ts b/ts/services/profiles.ts index a20c1b46e..99c58057b 100644 --- a/ts/services/profiles.ts +++ b/ts/services/profiles.ts @@ -230,7 +230,6 @@ async function doGetProfile(c: ConversationModel): Promise { const profileKey = c.get('profileKey'); const profileKeyVersion = c.deriveProfileKeyVersion(); const uuid = c.getCheckedUuid('getProfile'); - const existingProfileKeyCredential = c.get('profileKeyCredential'); const lastProfile = c.get('lastProfile'); let profileCredentialRequestContext: @@ -246,7 +245,7 @@ async function doGetProfile(c: ConversationModel): Promise { 'profileKeyVersion and accessKey are derived from profileKey' ); - if (existingProfileKeyCredential) { + if (!c.hasProfileKeyCredentialExpired()) { getProfileOptions = { accessKey, profileKeyVersion,