diff --git a/lib/actions/holder-actions.js b/lib/actions/holder-actions.js --- a/lib/actions/holder-actions.js +++ b/lib/actions/holder-actions.js @@ -55,7 +55,7 @@ action: 'establish' | 'remove', inputs: MultipleBlobHolders, authMetadata: AuthMetadata, - handleInvalidCSAT?: () => Promise, + handleInvalidCSAT?: (source: string) => Promise, ): Promise { if (inputs.length === 0) { return { succeeded: [], failed: [] }; @@ -76,7 +76,7 @@ if (response.result === 'error') { return { succeeded: [], failed: inputs }; } else if (response.result === 'invalid_csat') { - void handleInvalidCSAT?.(); + void handleInvalidCSAT?.(`perform_blob_${action}`); return { succeeded: [], failed: inputs }; } @@ -106,7 +106,7 @@ async function processHoldersAction( input: ProcessHoldersStartedPayload, authMetadata: AuthMetadata, - handleInvalidCSAT?: () => Promise, + handleInvalidCSAT?: (source: string) => Promise, ): Promise { const [ { succeeded: added, failed: notAdded }, diff --git a/lib/actions/user-actions.js b/lib/actions/user-actions.js --- a/lib/actions/user-actions.js +++ b/lib/actions/user-actions.js @@ -377,7 +377,7 @@ ]); } -function useInvalidCSATLogOut(): () => Promise { +function useInvalidCSATLogOut(): (source: string) => Promise { const dispatchActionPromise = useDispatchActionPromise(); const preRequestUserState = usePreRequestUserState(); @@ -387,27 +387,34 @@ state => state.commServicesAccessToken, ); - return React.useCallback(() => { - const keyserverLogOutPromise = async () => { - // errors are swallowed inside `keyserverLogOut` above - const { keyserverIDs: _, ...result } = await callKeyserverLogOut({ - preRequestUserState, - }); - return { - ...result, - preRequestUserState: { - ...result.preRequestUserState, - commServicesAccessToken, - }, + return React.useCallback( + (source: string) => { + const { showAlert, isStaffRelease } = getConfig(); + if (isStaffRelease) { + showAlert('InvalidCSATLogOut', source); + } + const keyserverLogOutPromise = async () => { + // errors are swallowed inside `keyserverLogOut` above + const { keyserverIDs: _, ...result } = await callKeyserverLogOut({ + preRequestUserState, + }); + return { + ...result, + preRequestUserState: { + ...result.preRequestUserState, + commServicesAccessToken, + }, + }; }; - }; - return dispatchActionPromise(logOutActionTypes, keyserverLogOutPromise()); - }, [ - dispatchActionPromise, - commServicesAccessToken, - callKeyserverLogOut, - preRequestUserState, - ]); + return dispatchActionPromise(logOutActionTypes, keyserverLogOutPromise()); + }, + [ + dispatchActionPromise, + commServicesAccessToken, + callKeyserverLogOut, + preRequestUserState, + ], + ); } const primaryDeviceLogOutOptions = Object.freeze({ diff --git a/lib/components/base-auto-join-community-handler.react.js b/lib/components/base-auto-join-community-handler.react.js --- a/lib/components/base-auto-join-community-handler.react.js +++ b/lib/components/base-auto-join-community-handler.react.js @@ -120,7 +120,7 @@ const blobResult = await downloadBlob(blobHash, headers); if (blobResult.result !== 'success') { if (blobResult.result === 'invalid_csat') { - void invalidTokenLogOut(); + void invalidTokenLogOut('farcaster_channel_blob'); } return null; } diff --git a/lib/identity-search/identity-search-context.js b/lib/identity-search/identity-search-context.js --- a/lib/identity-search/identity-search-context.js +++ b/lib/identity-search/identity-search-context.js @@ -175,7 +175,7 @@ ); } else { if (message.status.data?.includes('UnauthorizedDevice')) { - void invalidTokenLogOut(); + void invalidTokenLogOut('identity_search_unauthorized_device'); return; } setConnected(false); diff --git a/lib/tunnelbroker/tunnelbroker-context.js b/lib/tunnelbroker/tunnelbroker-context.js --- a/lib/tunnelbroker/tunnelbroker-context.js +++ b/lib/tunnelbroker/tunnelbroker-context.js @@ -323,7 +323,7 @@ ); } else { if (message.status.data?.includes('UnauthorizedDevice')) { - void invalidTokenLogOut(); + void invalidTokenLogOut('tunnelbroker_unauthorized_device'); return; } resetSocketState(); diff --git a/native/backup/use-client-backup.js b/native/backup/use-client-backup.js --- a/native/backup/use-client-backup.js +++ b/native/backup/use-client-backup.js @@ -79,7 +79,7 @@ } catch (e) { const message = getMessageForException(e); if (message === 'Unauthenticated') { - void invalidTokenLogOut(); + void invalidTokenLogOut('backup_service_unauthenticated'); } throw e; } diff --git a/native/identity-service/identity-service-context-provider.react.js b/native/identity-service/identity-service-context-provider.react.js --- a/native/identity-service/identity-service-context-provider.react.js +++ b/native/identity-service/identity-service-context-provider.react.js @@ -111,15 +111,16 @@ const invalidTokenLogOut = useInvalidCSATLogOut(); const authVerifiedEndpoint: ( identityRPCPromise: Promise, + method: string, ) => Promise = React.useCallback( - async identityRPCPromise => { + async (identityRPCPromise, method) => { try { const result = await identityRPCPromise; return result; } catch (e) { const message = getMessageForException(e); if (message === 'bad_credentials') { - void invalidTokenLogOut(); + void invalidTokenLogOut(`identity_client_${method}`); } throw e; } @@ -137,6 +138,7 @@ } = await getAuthMetadata(); return authVerifiedEndpoint( commRustModule.deleteWalletUser(userID, deviceID, token), + 'deleteWalletUser', ); }, deletePasswordUser: async (password: string) => { @@ -147,6 +149,7 @@ } = await getAuthMetadata(); return authVerifiedEndpoint( commRustModule.deletePasswordUser(userID, deviceID, token, password), + 'deletePasswordUser', ); }, logOut: async (preRequestAuthMetadata?: AuthMetadata) => { @@ -160,6 +163,7 @@ } return authVerifiedEndpoint( commRustModule.logOut(userID, deviceID, token), + 'logOut', ); }, logOutPrimaryDevice: async ( @@ -185,6 +189,7 @@ token, JSON.stringify(signedDeviceList), ), + 'logOutPrimaryDevice', ); }, logOutSecondaryDevice: async (preRequestAuthMetadata?: AuthMetadata) => { @@ -198,6 +203,7 @@ } return authVerifiedEndpoint( commRustModule.logOutSecondaryDevice(userID, deviceID, token), + 'logOutSecondaryDevice', ); }, getKeyserverKeys: async ( @@ -210,6 +216,7 @@ } = await getAuthMetadata(); const result = await authVerifiedEndpoint( commRustModule.getKeyserverKeys(userID, deviceID, token, keyserverID), + 'getKeyserverKeys', ); const resultObject = JSON.parse(result); const payload = resultObject?.payload; @@ -251,6 +258,7 @@ targetUserID, selectedDeviceIDs, ), + 'getOutboundKeysForUser', ); const resultArray = JSON.parse(result); @@ -320,6 +328,7 @@ targetUserID, selectedDeviceIDs, ), + 'getInboundKeysForUser', ); const resultArray = JSON.parse(result); @@ -388,6 +397,7 @@ oneTimeKeys.contentOneTimeKeys, oneTimeKeys.notificationsOneTimeKeys, ), + 'uploadOneTimeKeys', ); }, registerPasswordUser: async ( @@ -667,6 +677,7 @@ userID, sinceTimestamp, ), + 'getDeviceListHistoryForUser', ); const rawPayloads: string[] = JSON.parse(result); const deviceLists: SignedDeviceList[] = rawPayloads.map(payload => @@ -681,6 +692,7 @@ const authMetadata = await getAuthMetadata(); return await authVerifiedEndpoint( rawGetDeviceListsForUsers(authMetadata, userIDs), + 'getDeviceListsForUsers', ); }, updateDeviceList: async (newDeviceList: SignedDeviceList) => { @@ -697,6 +709,7 @@ authAccessToken, payload, ), + 'updateDeviceList', ); }, syncPlatformDetails: async () => { @@ -711,6 +724,7 @@ authDeviceID, authAccessToken, ), + 'syncPlatformDetails', ); }, getFarcasterUsers: async (farcasterIDs: $ReadOnlyArray) => { @@ -732,6 +746,7 @@ token, farcasterID, ), + 'linkFarcasterAccount', ); }, unlinkFarcasterAccount: async () => { @@ -742,6 +757,7 @@ } = await getAuthMetadata(); return authVerifiedEndpoint( commRustModule.unlinkFarcasterAccount(userID, deviceID, token), + 'unlinkFarcasterAccount', ); }, findUserIdentities: async (userIDs: $ReadOnlyArray) => { @@ -752,6 +768,7 @@ } = await getAuthMetadata(); const result = await authVerifiedEndpoint( commRustModule.findUserIdentities(userID, deviceID, token, userIDs), + 'findUserIdentities', ); return assertWithValidator( JSON.parse(result), @@ -775,6 +792,7 @@ oldPassword, newPassword, ), + 'changePassword', ); }, }), diff --git a/native/input/input-state-container.react.js b/native/input/input-state-container.react.js --- a/native/input/input-state-container.react.js +++ b/native/input/input-state-container.react.js @@ -152,7 +152,7 @@ request: ClientNewThinThreadRequest, ) => Promise, +newThickThread: (request: NewThickThreadRequest) => Promise, - +invalidTokenLogOut: () => Promise, + +invalidTokenLogOut: (source: string) => Promise, }; type State = { +pendingUploads: PendingMultimediaUploads, @@ -391,7 +391,7 @@ } const exceptionMessage = getMessageForException(e) ?? ''; if (exceptionMessage === 'invalid_csat') { - void this.props.invalidTokenLogOut(); + void this.props.invalidTokenLogOut('send_multimedia_message'); } throw new SendMessageError( `Exception when sending multimedia message: ${exceptionMessage}`, @@ -912,7 +912,7 @@ } catch (e) { uploadExceptionMessage = getMessageForException(e); if (uploadExceptionMessage === 'invalid_csat') { - void this.props.invalidTokenLogOut(); + void this.props.invalidTokenLogOut('blob_input_upload'); return undefined; } onUploadFailed('upload failed'); diff --git a/native/media/encryption-utils.js b/native/media/encryption-utils.js --- a/native/media/encryption-utils.js +++ b/native/media/encryption-utils.js @@ -436,7 +436,7 @@ ); if (!output.result.success && output.result.reason === 'invalid_csat') { - void invalidTokenLogOut(); + void invalidTokenLogOut('fetch_and_decrypt_media'); } return output; }, diff --git a/native/media/save-media.js b/native/media/save-media.js --- a/native/media/save-media.js +++ b/native/media/save-media.js @@ -86,7 +86,7 @@ if (result.success) { message = 'saved!'; } else if (result.reason === 'invalid_csat') { - void invalidTokenLogOut(); + void invalidTokenLogOut('save_media'); return; } else if (result.reason === 'save_unsupported') { const os: string = Platform.select({ diff --git a/native/navigation/deep-links-context-provider.react.js b/native/navigation/deep-links-context-provider.react.js --- a/native/navigation/deep-links-context-provider.react.js +++ b/native/navigation/deep-links-context-provider.react.js @@ -139,7 +139,7 @@ setKeyserverOverride(newKeyserverOverride); } catch (e) { if (errorMessageIsInvalidCSAT(e)) { - void invalidTokenLogOut(); + void invalidTokenLogOut('invite_link_download'); return; } console.log('Error while downloading an invite link blob', e); diff --git a/web/grpc/identity-service-context-provider.react.js b/web/grpc/identity-service-context-provider.react.js --- a/web/grpc/identity-service-context-provider.react.js +++ b/web/grpc/identity-service-context-provider.react.js @@ -102,7 +102,9 @@ } catch (e) { const message = getMessageForException(e); if (message === 'bad_credentials') { - void invalidTokenLogOut(); + void invalidTokenLogOut( + `identity_client_${method}_bad_credentials`, + ); } else if ( // These come from grpc-web: // - http status code: 0 is added when XmlHttpRequest request fails diff --git a/web/input/input-state-container.react.js b/web/input/input-state-container.react.js --- a/web/input/input-state-container.react.js +++ b/web/input/input-state-container.react.js @@ -152,7 +152,7 @@ +registerSendCallback: (() => mixed) => void, +unregisterSendCallback: (() => mixed) => void, +identityContext: ?IdentityClientContextType, - +invalidTokenLogOut: () => Promise, + +invalidTokenLogOut: (source: string) => Promise, }; type WritableState = { pendingUploads: { @@ -558,7 +558,7 @@ } const exceptionMessage = getMessageForException(e) ?? ''; if (exceptionMessage === 'invalid_csat') { - void this.props.invalidTokenLogOut(); + void this.props.invalidTokenLogOut('send_multimedia_message'); } throw new SendMessageError( `Exception while sending multimedia message: ${exceptionMessage}`, @@ -939,7 +939,7 @@ } catch (e) { uploadExceptionMessage = getMessageForException(e); if (uploadExceptionMessage === 'invalid_csat') { - void this.props.invalidTokenLogOut(); + void this.props.invalidTokenLogOut('blob_input_upload'); return; } this.handleUploadFailure(threadID, localID); @@ -1234,7 +1234,7 @@ defaultHeaders, ); if (!result.success && result.reason === 'INVALID_CSAT') { - void invalidTokenLogOut(); + void invalidTokenLogOut('blob_input_removal'); } })(); } diff --git a/web/invite-links/invite-link-handler.react.js b/web/invite-links/invite-link-handler.react.js --- a/web/invite-links/invite-link-handler.react.js +++ b/web/invite-links/invite-link-handler.react.js @@ -61,7 +61,7 @@ setKeyserverOverride(newKeyserverOverride); } catch (e) { if (errorMessageIsInvalidCSAT(e)) { - void invalidTokenLogOut(); + void invalidTokenLogOut('invite_link_download'); return; } console.error('Error while downloading an invite link blob', e); diff --git a/web/media/encryption-utils.js b/web/media/encryption-utils.js --- a/web/media/encryption-utils.js +++ b/web/media/encryption-utils.js @@ -280,7 +280,7 @@ ); if (!output.result.success && output.result.reason === 'invalid_csat') { - void invalidTokenLogOut(); + void invalidTokenLogOut('fetch_and_decrypt_media'); } return output; },