Fix self badge retrieval

This commit is contained in:
yash-signal
2025-08-07 11:59:40 -05:00
committed by GitHub
parent 00efbb321b
commit 5d4beb277d
3 changed files with 72 additions and 21 deletions

View File

@@ -311,23 +311,15 @@ async function buildProfileFetchOptions({
const accessKey = conversation.get('accessKey');
const serviceId = conversation.getCheckedServiceId('getProfile');
if (
profileKey &&
profileKeyVersion &&
accessKey &&
!options.ignoreProfileKey
) {
function getProfileCredentialsToUseIfExpired(profileKeyArg: string): {
credentialRequestContext: ProfileKeyCredentialRequestContext | null;
credentialRequestHex: string | null;
} {
if (!conversation.hasProfileKeyCredentialExpired()) {
log.info(`${logId}: using unexpired profile key credential`);
return {
profileKey,
profileCredentialRequestContext: null,
request: {
accessKey,
groupSendToken: null,
profileKeyVersion,
profileKeyCredentialRequest: null,
},
credentialRequestContext: null,
credentialRequestHex: null,
};
}
@@ -335,17 +327,32 @@ async function buildProfileFetchOptions({
const result = generateProfileKeyCredentialRequest(
clientZkProfileCipher,
serviceId,
profileKey
profileKeyArg
);
return {
credentialRequestContext: result.context,
credentialRequestHex: result.requestHex,
};
}
if (
profileKey &&
profileKeyVersion &&
accessKey &&
!options.ignoreProfileKey &&
!isMe(conversation.attributes)
) {
const { credentialRequestContext, credentialRequestHex } =
getProfileCredentialsToUseIfExpired(profileKey);
return {
profileKey,
profileCredentialRequestContext: result.context,
profileCredentialRequestContext: credentialRequestContext,
request: {
accessKey,
groupSendToken: null,
profileKeyVersion,
profileKeyCredentialRequest: result.requestHex,
profileKeyCredentialRequest: credentialRequestHex,
},
};
}
@@ -373,6 +380,23 @@ async function buildProfileFetchOptions({
};
}
// For self we also use the versioned profile on the authenticated socket,
// with profile key credentials if needed.
if (profileKey && profileKeyVersion && isMe(conversation.attributes)) {
const { credentialRequestContext, credentialRequestHex } =
getProfileCredentialsToUseIfExpired(profileKey);
return {
profileKey,
profileCredentialRequestContext: credentialRequestContext,
request: {
accessKey: null,
groupSendToken: null,
profileKeyVersion,
profileKeyCredentialRequest: credentialRequestHex,
},
};
}
// Fallback to group send tokens for unversioned profiles
if (groupId != null && !options.ignoreGroupSendToken) {
log.info(`${logId}: fetching group endorsements`);

View File

@@ -8,6 +8,7 @@ import type { StateType } from '../reducer';
import type { BadgesStateType } from '../ducks/badges';
import type { BadgeType } from '../../badges/types';
import { getOwn } from '../../util/getOwn';
import type { ConversationType } from '../ducks/conversations';
const log = createLogger('badges');
@@ -54,19 +55,25 @@ export const getBadgesSelector = createSelector(
);
export type PreferredBadgeSelectorType = (
conversationBadges: ReadonlyArray<Pick<BadgeType, 'id'>>
conversationBadges: ConversationType['badges']
) => undefined | BadgeType;
export const getPreferredBadgeSelector = createSelector(
getBadgesById,
(badgesById): PreferredBadgeSelectorType =>
conversationBadges => {
const firstId: undefined | string = conversationBadges[0]?.id;
if (!firstId) {
// Find the first visible badge. For other people's badges, isVisible will be
// unset and the badge is guaranteed to be visible.
// For the local user's badges, isVisible will be set and we need to check it.
const firstVisibleBadge = conversationBadges.find(conversationBadge =>
'isVisible' in conversationBadge ? conversationBadge.isVisible : true
);
if (!firstVisibleBadge) {
return undefined;
}
const badge = getOwn(badgesById, firstId);
const badge = getOwn(badgesById, firstVisibleBadge.id);
if (!badge) {
log.error(
'getPreferredBadgeSelector: conversation badge was not found'

View File

@@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import createDebug from 'debug';
import type { Page } from 'playwright';
import { StorageState, type PrimaryDevice } from '@signalapp/mock-server';
import type { App } from '../playwright';
import { Bootstrap } from '../bootstrap';
@@ -18,10 +19,25 @@ describe('serverAlerts', function (this: Mocha.Suite) {
this.timeout(MINUTE);
let bootstrap: Bootstrap;
let app: App;
let pinned: PrimaryDevice;
beforeEach(async () => {
bootstrap = new Bootstrap();
await bootstrap.init();
// Set up a pinned contact to trigger profile fetch to test unauth socket
let state = StorageState.getEmpty();
const { phone, contacts } = bootstrap;
[pinned] = contacts;
state = state.addContact(pinned, {
identityKey: pinned.publicKey.serialize(),
profileKey: pinned.profileKey.serialize(),
whitelisted: true,
});
state = state.pin(pinned);
await phone.setStorageState(state);
});
afterEach(async function (this: Mocha.Context) {
@@ -81,6 +97,10 @@ describe('serverAlerts', function (this: Mocha.Suite) {
? await bootstrap.link()
: await setupAppToUseLibsignalWebsockets(bootstrap);
const window = await app.getWindow();
// Trigger a profile fetch for a contact to ensure unauth websocket is used
await window.getByTestId(pinned.device.aci).click();
await testCase.test(window);
if (transport === 'libsignal') {