@ethora/chat-component-rn 26.6.12 → 26.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/lib/commonjs/components/AuthForms/Login.js +4 -4
- package/lib/commonjs/components/AuthForms/Login.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js +4 -1
- package/lib/commonjs/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ChatWrapper.js +14 -13
- package/lib/commonjs/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/LoginWrapper.js +2 -1
- package/lib/commonjs/components/MainComponents/LoginWrapper.js.map +1 -1
- package/lib/commonjs/components/MainComponents/MediaMessage.js +17 -6
- package/lib/commonjs/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js +9 -8
- package/lib/commonjs/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js +4 -1
- package/lib/commonjs/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js +8 -7
- package/lib/commonjs/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js +2 -1
- package/lib/commonjs/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
- package/lib/commonjs/components/Modals/NewChatModal/ModalContent.js +2 -1
- package/lib/commonjs/components/Modals/NewChatModal/ModalContent.js.map +1 -1
- package/lib/commonjs/components/Modals/NewChatModal/UpdNewChatModal.js +8 -0
- package/lib/commonjs/components/Modals/NewChatModal/UpdNewChatModal.js.map +1 -1
- package/lib/commonjs/components/Modals/UserProfileModal/EditUserModal.js +7 -21
- package/lib/commonjs/components/Modals/UserProfileModal/EditUserModal.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/ChatRoomItem.js +5 -1
- package/lib/commonjs/components/RoomComponents/ChatRoomItem.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js +10 -2
- package/lib/commonjs/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js +4 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js +4 -1
- package/lib/commonjs/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
- package/lib/commonjs/components/styled/AudioMessage.js +2 -1
- package/lib/commonjs/components/styled/AudioMessage.js.map +1 -1
- package/lib/commonjs/components/styled/MessageImage.js +34 -8
- package/lib/commonjs/components/styled/MessageImage.js.map +1 -1
- package/lib/commonjs/components/styled/VideoMessage.js +7 -1
- package/lib/commonjs/components/styled/VideoMessage.js.map +1 -1
- package/lib/commonjs/context/xmppProvider.js +46 -21
- package/lib/commonjs/context/xmppProvider.js.map +1 -1
- package/lib/commonjs/helpers/ensureScopedChatCache.js +2 -2
- package/lib/commonjs/helpers/ensureScopedChatCache.js.map +1 -1
- package/lib/commonjs/helpers/initXmppRooms.js +5 -1
- package/lib/commonjs/helpers/initXmppRooms.js.map +1 -1
- package/lib/commonjs/helpers/persistCrypto.js +122 -0
- package/lib/commonjs/helpers/persistCrypto.js.map +1 -0
- package/lib/commonjs/helpers/pickImageAsset.js +68 -0
- package/lib/commonjs/helpers/pickImageAsset.js.map +1 -0
- package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js +130 -17
- package/lib/commonjs/helpers/resolveInitBeforeLoadUser.js.map +1 -1
- package/lib/commonjs/helpers/secureFileUrl.js +101 -0
- package/lib/commonjs/helpers/secureFileUrl.js.map +1 -0
- package/lib/commonjs/helpers/secureKeyValue.js +83 -0
- package/lib/commonjs/helpers/secureKeyValue.js.map +1 -0
- package/lib/commonjs/helpers/secureStoreRuntime.js +59 -0
- package/lib/commonjs/helpers/secureStoreRuntime.js.map +1 -0
- package/lib/commonjs/helpers/secureUserStorage.js +75 -0
- package/lib/commonjs/helpers/secureUserStorage.js.map +1 -0
- package/lib/commonjs/hooks/useChatWrapperInit.js +3 -3
- package/lib/commonjs/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/commonjs/hooks/useFileToken.js +10 -0
- package/lib/commonjs/hooks/useFileToken.js.map +1 -0
- package/lib/commonjs/hooks/useLogout.js +15 -7
- package/lib/commonjs/hooks/useLogout.js.map +1 -1
- package/lib/commonjs/hooks/useSendMessage.js +23 -13
- package/lib/commonjs/hooks/useSendMessage.js.map +1 -1
- package/lib/commonjs/main.js +25 -0
- package/lib/commonjs/main.js.map +1 -1
- package/lib/commonjs/networking/api-requests/auth.api.js +99 -54
- package/lib/commonjs/networking/api-requests/auth.api.js.map +1 -1
- package/lib/commonjs/networking/api-requests/roomMembers.api.js +1 -1
- package/lib/commonjs/networking/api-requests/roomMembers.api.js.map +1 -1
- package/lib/commonjs/networking/api-requests/rooms.api.js +9 -9
- package/lib/commonjs/networking/api-requests/rooms.api.js.map +1 -1
- package/lib/commonjs/networking/api-requests/user.api.js +9 -9
- package/lib/commonjs/networking/api-requests/user.api.js.map +1 -1
- package/lib/commonjs/networking/apiClient.js +62 -112
- package/lib/commonjs/networking/apiClient.js.map +1 -1
- package/lib/commonjs/networking/authRefresh.js +322 -0
- package/lib/commonjs/networking/authRefresh.js.map +1 -0
- package/lib/commonjs/networking/xmppClient.js +33 -5
- package/lib/commonjs/networking/xmppClient.js.map +1 -1
- package/lib/commonjs/roomStore/chatSettingsSlice.js +23 -6
- package/lib/commonjs/roomStore/chatSettingsSlice.js.map +1 -1
- package/lib/commonjs/roomStore/persistence.js +29 -15
- package/lib/commonjs/roomStore/persistence.js.map +1 -1
- package/lib/commonjs/roomStore/roomsSlice.js +2 -0
- package/lib/commonjs/roomStore/roomsSlice.js.map +1 -1
- package/lib/module/components/AuthForms/Login.js +4 -4
- package/lib/module/components/AuthForms/Login.js.map +1 -1
- package/lib/module/components/MainComponents/ChatHeader.js +4 -1
- package/lib/module/components/MainComponents/ChatHeader.js.map +1 -1
- package/lib/module/components/MainComponents/ChatWrapper.js +14 -13
- package/lib/module/components/MainComponents/ChatWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/LoginWrapper.js +2 -1
- package/lib/module/components/MainComponents/LoginWrapper.js.map +1 -1
- package/lib/module/components/MainComponents/MediaMessage.js +17 -6
- package/lib/module/components/MainComponents/MediaMessage.js.map +1 -1
- package/lib/module/components/MainComponents/ProfileImagePlaceholder.js +9 -8
- package/lib/module/components/MainComponents/ProfileImagePlaceholder.js.map +1 -1
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js +4 -1
- package/lib/module/components/Modals/ChatProfileModal/ChatProfileModal.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js +8 -7
- package/lib/module/components/Modals/FilePreviewModal/FilePreviewModal.js.map +1 -1
- package/lib/module/components/Modals/FilePreviewModal/PdfView.js +2 -1
- package/lib/module/components/Modals/FilePreviewModal/PdfView.js.map +1 -1
- package/lib/module/components/Modals/NewChatModal/ModalContent.js +2 -1
- package/lib/module/components/Modals/NewChatModal/ModalContent.js.map +1 -1
- package/lib/module/components/Modals/NewChatModal/UpdNewChatModal.js +8 -0
- package/lib/module/components/Modals/NewChatModal/UpdNewChatModal.js.map +1 -1
- package/lib/module/components/Modals/UserProfileModal/EditUserModal.js +9 -23
- package/lib/module/components/Modals/UserProfileModal/EditUserModal.js.map +1 -1
- package/lib/module/components/RoomComponents/ChatRoomItem.js +5 -1
- package/lib/module/components/RoomComponents/ChatRoomItem.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessageFile.js +10 -2
- package/lib/module/components/RoomComponents/styled/LastMessageFile.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js +4 -1
- package/lib/module/components/RoomComponents/styled/LastMessagePhoto.js.map +1 -1
- package/lib/module/components/RoomComponents/styled/LastMessageVideo.js +4 -1
- package/lib/module/components/RoomComponents/styled/LastMessageVideo.js.map +1 -1
- package/lib/module/components/styled/AudioMessage.js +2 -1
- package/lib/module/components/styled/AudioMessage.js.map +1 -1
- package/lib/module/components/styled/MessageImage.js +34 -8
- package/lib/module/components/styled/MessageImage.js.map +1 -1
- package/lib/module/components/styled/VideoMessage.js +7 -1
- package/lib/module/components/styled/VideoMessage.js.map +1 -1
- package/lib/module/context/xmppProvider.js +45 -21
- package/lib/module/context/xmppProvider.js.map +1 -1
- package/lib/module/helpers/ensureScopedChatCache.js +2 -2
- package/lib/module/helpers/ensureScopedChatCache.js.map +1 -1
- package/lib/module/helpers/initXmppRooms.js +5 -1
- package/lib/module/helpers/initXmppRooms.js.map +1 -1
- package/lib/module/helpers/persistCrypto.js +115 -0
- package/lib/module/helpers/persistCrypto.js.map +1 -0
- package/lib/module/helpers/pickImageAsset.js +63 -0
- package/lib/module/helpers/pickImageAsset.js.map +1 -0
- package/lib/module/helpers/resolveInitBeforeLoadUser.js +130 -16
- package/lib/module/helpers/resolveInitBeforeLoadUser.js.map +1 -1
- package/lib/module/helpers/secureFileUrl.js +91 -0
- package/lib/module/helpers/secureFileUrl.js.map +1 -0
- package/lib/module/helpers/secureKeyValue.js +75 -0
- package/lib/module/helpers/secureKeyValue.js.map +1 -0
- package/lib/module/helpers/secureStoreRuntime.js +52 -0
- package/lib/module/helpers/secureStoreRuntime.js.map +1 -0
- package/lib/module/helpers/secureUserStorage.js +72 -0
- package/lib/module/helpers/secureUserStorage.js.map +1 -0
- package/lib/module/hooks/useChatWrapperInit.js +3 -3
- package/lib/module/hooks/useChatWrapperInit.js.map +1 -1
- package/lib/module/hooks/useFileToken.js +5 -0
- package/lib/module/hooks/useFileToken.js.map +1 -0
- package/lib/module/hooks/useLogout.js +15 -7
- package/lib/module/hooks/useLogout.js.map +1 -1
- package/lib/module/hooks/useSendMessage.js +24 -14
- package/lib/module/hooks/useSendMessage.js.map +1 -1
- package/lib/module/main.js +12 -1
- package/lib/module/main.js.map +1 -1
- package/lib/module/networking/api-requests/auth.api.js +96 -53
- package/lib/module/networking/api-requests/auth.api.js.map +1 -1
- package/lib/module/networking/api-requests/roomMembers.api.js +1 -1
- package/lib/module/networking/api-requests/roomMembers.api.js.map +1 -1
- package/lib/module/networking/api-requests/rooms.api.js +9 -9
- package/lib/module/networking/api-requests/rooms.api.js.map +1 -1
- package/lib/module/networking/api-requests/user.api.js +8 -7
- package/lib/module/networking/api-requests/user.api.js.map +1 -1
- package/lib/module/networking/apiClient.js +61 -113
- package/lib/module/networking/apiClient.js.map +1 -1
- package/lib/module/networking/authRefresh.js +311 -0
- package/lib/module/networking/authRefresh.js.map +1 -0
- package/lib/module/networking/xmppClient.js +33 -5
- package/lib/module/networking/xmppClient.js.map +1 -1
- package/lib/module/roomStore/chatSettingsSlice.js +23 -6
- package/lib/module/roomStore/chatSettingsSlice.js.map +1 -1
- package/lib/module/roomStore/persistence.js +30 -15
- package/lib/module/roomStore/persistence.js.map +1 -1
- package/lib/module/roomStore/roomsSlice.js +2 -0
- package/lib/module/roomStore/roomsSlice.js.map +1 -1
- package/lib/typescript/components/AuthForms/Login.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatHeader.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ChatWrapper.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/LoginWrapper.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/MediaMessage.d.ts.map +1 -1
- package/lib/typescript/components/MainComponents/ProfileImagePlaceholder.d.ts.map +1 -1
- package/lib/typescript/components/Modals/ChatProfileModal/ChatProfileModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/FilePreviewModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/FilePreviewModal/PdfView.d.ts.map +1 -1
- package/lib/typescript/components/Modals/NewChatModal/ModalContent.d.ts +4 -2
- package/lib/typescript/components/Modals/NewChatModal/ModalContent.d.ts.map +1 -1
- package/lib/typescript/components/Modals/NewChatModal/UpdNewChatModal.d.ts.map +1 -1
- package/lib/typescript/components/Modals/UserProfileModal/EditUserModal.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/ChatRoomItem.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageFile.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessagePhoto.d.ts.map +1 -1
- package/lib/typescript/components/RoomComponents/styled/LastMessageVideo.d.ts.map +1 -1
- package/lib/typescript/components/styled/AudioMessage.d.ts.map +1 -1
- package/lib/typescript/components/styled/MessageImage.d.ts +8 -0
- package/lib/typescript/components/styled/MessageImage.d.ts.map +1 -1
- package/lib/typescript/components/styled/VideoMessage.d.ts.map +1 -1
- package/lib/typescript/context/xmppProvider.d.ts.map +1 -1
- package/lib/typescript/helpers/initXmppRooms.d.ts.map +1 -1
- package/lib/typescript/helpers/persistCrypto.d.ts +13 -0
- package/lib/typescript/helpers/persistCrypto.d.ts.map +1 -0
- package/lib/typescript/helpers/pickImageAsset.d.ts +27 -0
- package/lib/typescript/helpers/pickImageAsset.d.ts.map +1 -0
- package/lib/typescript/helpers/resolveInitBeforeLoadUser.d.ts.map +1 -1
- package/lib/typescript/helpers/secureFileUrl.d.ts +7 -0
- package/lib/typescript/helpers/secureFileUrl.d.ts.map +1 -0
- package/lib/typescript/helpers/secureKeyValue.d.ts +13 -0
- package/lib/typescript/helpers/secureKeyValue.d.ts.map +1 -0
- package/lib/typescript/helpers/secureStoreRuntime.d.ts +29 -0
- package/lib/typescript/helpers/secureStoreRuntime.d.ts.map +1 -0
- package/lib/typescript/helpers/secureUserStorage.d.ts +23 -0
- package/lib/typescript/helpers/secureUserStorage.d.ts.map +1 -0
- package/lib/typescript/hooks/useFileToken.d.ts +2 -0
- package/lib/typescript/hooks/useFileToken.d.ts.map +1 -0
- package/lib/typescript/hooks/useLogout.d.ts.map +1 -1
- package/lib/typescript/hooks/useSendMessage.d.ts.map +1 -1
- package/lib/typescript/main.d.ts +6 -1
- package/lib/typescript/main.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/auth.api.d.ts +3 -27
- package/lib/typescript/networking/api-requests/auth.api.d.ts.map +1 -1
- package/lib/typescript/networking/api-requests/user.api.d.ts.map +1 -1
- package/lib/typescript/networking/apiClient.d.ts +12 -6
- package/lib/typescript/networking/apiClient.d.ts.map +1 -1
- package/lib/typescript/networking/authRefresh.d.ts +92 -0
- package/lib/typescript/networking/authRefresh.d.ts.map +1 -0
- package/lib/typescript/networking/xmppClient.d.ts +6 -0
- package/lib/typescript/networking/xmppClient.d.ts.map +1 -1
- package/lib/typescript/roomStore/chatSettingsSlice.d.ts +16 -0
- package/lib/typescript/roomStore/chatSettingsSlice.d.ts.map +1 -1
- package/lib/typescript/roomStore/persistence.d.ts.map +1 -1
- package/lib/typescript/roomStore/roomsSlice.d.ts.map +1 -1
- package/lib/typescript/types/types.d.ts +17 -0
- package/lib/typescript/types/types.d.ts.map +1 -1
- package/package.json +8 -1
- package/src/components/AuthForms/Login.tsx +3 -3
- package/src/components/MainComponents/ChatHeader.tsx +4 -1
- package/src/components/MainComponents/ChatWrapper.tsx +14 -19
- package/src/components/MainComponents/LoginWrapper.tsx +1 -0
- package/src/components/MainComponents/MediaMessage.tsx +14 -4
- package/src/components/MainComponents/ProfileImagePlaceholder.tsx +16 -9
- package/src/components/Modals/ChatProfileModal/ChatProfileModal.tsx +4 -1
- package/src/components/Modals/FilePreviewModal/FilePreviewModal.tsx +8 -7
- package/src/components/Modals/FilePreviewModal/PdfView.tsx +2 -1
- package/src/components/Modals/NewChatModal/ModalContent.tsx +6 -3
- package/src/components/Modals/NewChatModal/UpdNewChatModal.tsx +12 -1
- package/src/components/Modals/UserProfileModal/EditUserModal.tsx +9 -25
- package/src/components/RoomComponents/ChatRoomItem.tsx +9 -1
- package/src/components/RoomComponents/styled/LastMessageFile.tsx +14 -2
- package/src/components/RoomComponents/styled/LastMessagePhoto.tsx +4 -1
- package/src/components/RoomComponents/styled/LastMessageVideo.tsx +4 -1
- package/src/components/styled/AudioMessage.tsx +2 -1
- package/src/components/styled/MessageImage.tsx +45 -6
- package/src/components/styled/VideoMessage.tsx +10 -1
- package/src/context/xmppProvider.tsx +47 -23
- package/src/helpers/ensureScopedChatCache.ts +2 -2
- package/src/helpers/initXmppRooms.tsx +5 -1
- package/src/helpers/persistCrypto.ts +127 -0
- package/src/helpers/pickImageAsset.ts +68 -0
- package/src/helpers/resolveInitBeforeLoadUser.ts +125 -18
- package/src/helpers/secureFileUrl.ts +103 -0
- package/src/helpers/secureKeyValue.ts +80 -0
- package/src/helpers/secureStoreRuntime.ts +62 -0
- package/src/helpers/secureUserStorage.ts +80 -0
- package/src/hooks/useChatWrapperInit.ts +3 -3
- package/src/hooks/useFileToken.ts +7 -0
- package/src/hooks/useLogout.tsx +15 -9
- package/src/hooks/useSendMessage.tsx +28 -13
- package/src/main.ts +22 -1
- package/src/networking/api-requests/auth.api.ts +125 -52
- package/src/networking/api-requests/roomMembers.api.ts +1 -1
- package/src/networking/api-requests/rooms.api.ts +9 -9
- package/src/networking/api-requests/user.api.ts +8 -7
- package/src/networking/apiClient.ts +68 -123
- package/src/networking/authRefresh.ts +430 -0
- package/src/networking/xmppClient.ts +37 -5
- package/src/roomStore/chatSettingsSlice.ts +31 -9
- package/src/roomStore/persistence.ts +39 -19
- package/src/roomStore/roomsSlice.ts +2 -0
- package/src/types/types.ts +17 -0
|
@@ -94,6 +94,13 @@ interface MamInFlightEntry {
|
|
|
94
94
|
startedAt: number;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Floor between two credential rotations driven by reconnects. Well under
|
|
99
|
+
* the XMPP password's one-hour life, but high enough that a link flapping
|
|
100
|
+
* every few seconds rotates once, not thirty times.
|
|
101
|
+
*/
|
|
102
|
+
const CREDENTIALS_REFRESH_MIN_INTERVAL_MS = 60_000;
|
|
103
|
+
|
|
97
104
|
export type XmppCredentialsProvider = () => Promise<{
|
|
98
105
|
username: string;
|
|
99
106
|
password: string;
|
|
@@ -137,6 +144,12 @@ export class XmppClient {
|
|
|
137
144
|
// derived XMPP password after idle). Triggers the credentialsProvider
|
|
138
145
|
// refresh path in reconnect().
|
|
139
146
|
lastAuthError: 'not-authorized' | null = null;
|
|
147
|
+
/**
|
|
148
|
+
* When the credentials currently held were last (re)minted. Drives the
|
|
149
|
+
* throttle in `reconnect()` so a flapping connection can't rotate the
|
|
150
|
+
* session once per flap.
|
|
151
|
+
*/
|
|
152
|
+
private credentialsRefreshedAt = 0;
|
|
140
153
|
private credentialsProvider: XmppCredentialsProvider | null = null;
|
|
141
154
|
private credentialsRefreshInFlight: Promise<void> | null = null;
|
|
142
155
|
// Fired by the provider on every 'online' so it can re-join MUC rooms
|
|
@@ -700,14 +713,32 @@ export class XmppClient {
|
|
|
700
713
|
console.log('Attempting to reconnect xmpp client...');
|
|
701
714
|
|
|
702
715
|
try {
|
|
703
|
-
//
|
|
704
|
-
//
|
|
705
|
-
//
|
|
706
|
-
//
|
|
707
|
-
|
|
716
|
+
// Re-mint credentials BEFORE reconnecting.
|
|
717
|
+
//
|
|
718
|
+
// This deliberately no longer waits for a SASL `not-authorized`.
|
|
719
|
+
// The XMPP password expires after an hour, and an expiry does NOT
|
|
720
|
+
// drop an established connection — authentication happens once, at
|
|
721
|
+
// bind. So by the time anything disconnects us (a network blip, a
|
|
722
|
+
// sleeping device, a server restart) the password we still hold may
|
|
723
|
+
// well be dead, and we have no way to know. Reconnecting with it
|
|
724
|
+
// burns a guaranteed-failed bind plus a backoff delay before the
|
|
725
|
+
// old `lastAuthError` path could even start recovering.
|
|
726
|
+
//
|
|
727
|
+
// Throttled, because reconnects are not rare: a flapping link would
|
|
728
|
+
// otherwise rotate the whole session (access + refresh + file token
|
|
729
|
+
// + XMPP password) once per flap. A SASL failure bypasses the
|
|
730
|
+
// throttle — there the credentials are known bad.
|
|
731
|
+
const authFailed = this.lastAuthError === 'not-authorized';
|
|
732
|
+
const staleCreds =
|
|
733
|
+
Date.now() - this.credentialsRefreshedAt >=
|
|
734
|
+
CREDENTIALS_REFRESH_MIN_INTERVAL_MS;
|
|
735
|
+
|
|
736
|
+
if (this.credentialsProvider && (authFailed || staleCreds)) {
|
|
708
737
|
try {
|
|
709
738
|
await this.refreshCredentialsOnce();
|
|
710
739
|
} catch (err) {
|
|
740
|
+
// Offline is the common case here — reconnect with what we
|
|
741
|
+
// have and let the next attempt try again.
|
|
711
742
|
console.warn(
|
|
712
743
|
'XMPP credential refresh failed; reconnecting with cached creds',
|
|
713
744
|
err
|
|
@@ -763,6 +794,7 @@ export class XmppClient {
|
|
|
763
794
|
// creds really are stale we simply get not-authorized again and
|
|
764
795
|
// retry, which is the confirmed-good 26.5.7 behaviour.
|
|
765
796
|
this.updateCredentials(fresh.username, fresh.password);
|
|
797
|
+
this.credentialsRefreshedAt = Date.now();
|
|
766
798
|
this.lastAuthError = null;
|
|
767
799
|
}
|
|
768
800
|
} finally {
|
|
@@ -11,8 +11,7 @@ import {
|
|
|
11
11
|
} from '../types/types';
|
|
12
12
|
import { Iso639_1Codes } from '../types/models/language.model';
|
|
13
13
|
import type { TranslateMode } from '../utils/translateModePolicy';
|
|
14
|
-
import {
|
|
15
|
-
import { asyncLocalStorage } from '../hooks/useLocalStorage';
|
|
14
|
+
import { secureUserStorage } from '../helpers/secureUserStorage';
|
|
16
15
|
|
|
17
16
|
export interface ChatState {
|
|
18
17
|
user: User;
|
|
@@ -40,6 +39,7 @@ export const unpackAndTransform = (input?: User): User => {
|
|
|
40
39
|
_id: input?._id || '',
|
|
41
40
|
walletAddress: input?.defaultWallet?.walletAddress || '',
|
|
42
41
|
xmppPassword: input?.xmppPassword || '',
|
|
42
|
+
fileToken: input?.fileToken || '',
|
|
43
43
|
refreshToken: input?.refreshToken || '',
|
|
44
44
|
firstName: input?.firstName || '',
|
|
45
45
|
lastName: input?.lastName || '',
|
|
@@ -73,6 +73,7 @@ const initialState: ChatState = {
|
|
|
73
73
|
_id: '',
|
|
74
74
|
walletAddress: '',
|
|
75
75
|
xmppPassword: '',
|
|
76
|
+
fileToken: '',
|
|
76
77
|
refreshToken: '',
|
|
77
78
|
firstName: '',
|
|
78
79
|
lastName: '',
|
|
@@ -111,9 +112,7 @@ const initialState: ChatState = {
|
|
|
111
112
|
const reducers = {
|
|
112
113
|
setUser: (state: WritableDraft<ChatState>, action: PayloadAction<User>) => {
|
|
113
114
|
state.user = unpackAndTransform(action.payload);
|
|
114
|
-
|
|
115
|
-
unpackAndTransform(action.payload)
|
|
116
|
-
);
|
|
115
|
+
secureUserStorage().set(unpackAndTransform(action.payload));
|
|
117
116
|
},
|
|
118
117
|
updateUser(state: WritableDraft<ChatState>, action: PayloadAction<{ updates: Partial<User> }>) {
|
|
119
118
|
const { updates } = action.payload;
|
|
@@ -146,14 +145,37 @@ const reducers = {
|
|
|
146
145
|
setSelectedUser: (state: WritableDraft<ChatState>, action: PayloadAction<IUser | undefined>) => {
|
|
147
146
|
state.selectedUser = action.payload;
|
|
148
147
|
},
|
|
148
|
+
/**
|
|
149
|
+
* Token rotation. Dispatched from ONE place only — `authRefresh` —
|
|
150
|
+
* which awaits the AsyncStorage write immediately afterwards, before
|
|
151
|
+
* resolving its promise.
|
|
152
|
+
*
|
|
153
|
+
* The reducer used to fire its own best-effort write here. That was
|
|
154
|
+
* both redundant and racy (two writers, no ordering), and a
|
|
155
|
+
* fire-and-forget write can be lost if the app dies right after a
|
|
156
|
+
* rotation — which on a backend with reuse detection means the next
|
|
157
|
+
* launch presents a burned token. Persistence now belongs to the
|
|
158
|
+
* caller, which can actually await it.
|
|
159
|
+
*/
|
|
149
160
|
refreshTokens: (
|
|
150
161
|
state: WritableDraft<ChatState>,
|
|
151
|
-
action: PayloadAction<{
|
|
162
|
+
action: PayloadAction<{
|
|
163
|
+
token: string;
|
|
164
|
+
refreshToken: string;
|
|
165
|
+
xmppPassword?: string;
|
|
166
|
+
fileToken?: string;
|
|
167
|
+
}>
|
|
152
168
|
) => {
|
|
153
169
|
state.user.refreshToken = action.payload.refreshToken;
|
|
154
170
|
state.user.token = action.payload.token;
|
|
155
|
-
|
|
156
|
-
|
|
171
|
+
|
|
172
|
+
if (action.payload.xmppPassword) {
|
|
173
|
+
state.user.xmppPassword = action.payload.xmppPassword;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (action.payload.fileToken) {
|
|
177
|
+
state.user.fileToken = action.payload.fileToken;
|
|
178
|
+
}
|
|
157
179
|
},
|
|
158
180
|
logout: (state: WritableDraft<ChatState>) => {
|
|
159
181
|
state.user = unpackAndTransform();
|
|
@@ -161,7 +183,7 @@ const reducers = {
|
|
|
161
183
|
state.client = undefined;
|
|
162
184
|
state.langSource = undefined;
|
|
163
185
|
state.translateMode = undefined;
|
|
164
|
-
|
|
186
|
+
secureUserStorage().remove();
|
|
165
187
|
},
|
|
166
188
|
setLangSource: (
|
|
167
189
|
state: WritableDraft<ChatState>,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
2
2
|
import { Middleware } from '@reduxjs/toolkit';
|
|
3
3
|
import { IMessage, IRoom, User } from '../types/types';
|
|
4
|
+
import { encryptForPersist, decryptFromPersist } from '../helpers/persistCrypto';
|
|
4
5
|
|
|
5
6
|
// -------------------------------------------------------------------
|
|
6
7
|
// Lightweight RN persistence layer. Mirrors what redux-persist gives
|
|
@@ -11,7 +12,11 @@ import { IMessage, IRoom, User } from '../types/types';
|
|
|
11
12
|
// - cap each room's messages to the most recent MESSAGE_LIMIT (100)
|
|
12
13
|
// - debounced writes (200ms) on relevant action types
|
|
13
14
|
//
|
|
14
|
-
//
|
|
15
|
+
// AES-256-CBC at rest (see `helpers/persistCrypto.ts`): the JSON blob
|
|
16
|
+
// under each key below is encrypted before it reaches AsyncStorage, with
|
|
17
|
+
// the symmetric key held in the platform Keychain/Keystore. `user` here
|
|
18
|
+
// is already secret-free (see `sanitizeUser`) — the encryption is for
|
|
19
|
+
// the message bodies in `rooms`, which aren't.
|
|
15
20
|
// -------------------------------------------------------------------
|
|
16
21
|
|
|
17
22
|
const KEY_CHAT = '@ethora/persist:chatSettingStore';
|
|
@@ -199,21 +204,27 @@ export const persistenceMiddleware: Middleware = (storeAPI) => (next) => (
|
|
|
199
204
|
|
|
200
205
|
if (writeTimer) {clearTimeout(writeTimer);}
|
|
201
206
|
writeTimer = setTimeout(() => {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
[
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
207
|
+
(async () => {
|
|
208
|
+
try {
|
|
209
|
+
const state = storeAPI.getState();
|
|
210
|
+
const chatPayload: PersistedChatState = {
|
|
211
|
+
user: sanitizeUser(state.chatSettingStore?.user) as User,
|
|
212
|
+
};
|
|
213
|
+
const roomsPayload: PersistedRoomsState = {
|
|
214
|
+
rooms: sanitizeRooms(state.rooms?.rooms || {}),
|
|
215
|
+
};
|
|
216
|
+
const [chatCipher, roomsCipher] = await Promise.all([
|
|
217
|
+
encryptForPersist(JSON.stringify(chatPayload)),
|
|
218
|
+
encryptForPersist(JSON.stringify(roomsPayload)),
|
|
219
|
+
]);
|
|
220
|
+
await AsyncStorage.multiSet([
|
|
221
|
+
[KEY_CHAT, chatCipher],
|
|
222
|
+
[KEY_ROOMS, roomsCipher],
|
|
223
|
+
]);
|
|
224
|
+
} catch (e) {
|
|
225
|
+
console.warn('persist write failed', e);
|
|
226
|
+
}
|
|
227
|
+
})();
|
|
217
228
|
}, 200);
|
|
218
229
|
|
|
219
230
|
return result;
|
|
@@ -233,9 +244,18 @@ export async function readPersistedState(): Promise<{
|
|
|
233
244
|
KEY_CHAT,
|
|
234
245
|
KEY_ROOMS,
|
|
235
246
|
]);
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
? (
|
|
247
|
+
const [chatPlain, roomsPlain] = await Promise.all([
|
|
248
|
+
chatRaw[1] ? decryptFromPersist(chatRaw[1]) : Promise.resolve(null),
|
|
249
|
+
roomsRaw[1] ? decryptFromPersist(roomsRaw[1]) : Promise.resolve(null),
|
|
250
|
+
]);
|
|
251
|
+
// `decryptFromPersist` returns null both for "nothing stored" and
|
|
252
|
+
// for "stored value isn't a valid envelope for the current key" —
|
|
253
|
+
// notably a plaintext blob left over from a pre-encryption install.
|
|
254
|
+
// Either way there is nothing safe to parse, so the caller treats it
|
|
255
|
+
// as a cold start and re-hydrates from the server.
|
|
256
|
+
const chat = chatPlain ? (JSON.parse(chatPlain) as PersistedChatState) : null;
|
|
257
|
+
const rooms = roomsPlain
|
|
258
|
+
? (JSON.parse(roomsPlain) as PersistedRoomsState)
|
|
239
259
|
: null;
|
|
240
260
|
return { chat, rooms };
|
|
241
261
|
} catch (e) {
|
|
@@ -317,6 +317,8 @@ const reducers = {
|
|
|
317
317
|
: [];
|
|
318
318
|
state.rooms[roomData.jid] = {
|
|
319
319
|
...roomData,
|
|
320
|
+
icon: roomData.icon !== undefined ? roomData.icon : existing?.icon,
|
|
321
|
+
roomBg: roomData.roomBg !== undefined ? roomData.roomBg : existing?.roomBg,
|
|
320
322
|
messages:
|
|
321
323
|
existingMessages.length > 0 ? existingMessages : incomingMessages,
|
|
322
324
|
lastViewedTimestamp: lastViewed,
|
package/src/types/types.ts
CHANGED
|
@@ -105,6 +105,7 @@ export interface User {
|
|
|
105
105
|
];
|
|
106
106
|
appId: string;
|
|
107
107
|
xmppPassword: string;
|
|
108
|
+
fileToken?: string;
|
|
108
109
|
|
|
109
110
|
homeScreen?: string;
|
|
110
111
|
registrationChannelType?: string;
|
|
@@ -455,9 +456,25 @@ export interface IConfig {
|
|
|
455
456
|
defaultLogin?: boolean;
|
|
456
457
|
refreshTokens?: {
|
|
457
458
|
enabled: boolean;
|
|
459
|
+
/**
|
|
460
|
+
* Host-owned rotation. When set, the SDK calls this INSTEAD of
|
|
461
|
+
* `/users/login/refresh` — always, including during bootstrap — and
|
|
462
|
+
* never rotates the Ethora refresh token itself. Serialised through
|
|
463
|
+
* the same lock as the built-in path.
|
|
464
|
+
*
|
|
465
|
+
* Return `refreshToken` whenever the host's flow actually rotates an
|
|
466
|
+
* Ethora refresh token: the backend now treats a re-presented one as
|
|
467
|
+
* theft, so omitting it leaves a burned token in storage that ends
|
|
468
|
+
* the session the next time it is used. It stays optional because
|
|
469
|
+
* some hosts re-mint an access token from a different authority
|
|
470
|
+
* (SuperTokens, an owner-session endpoint) and hold no Ethora
|
|
471
|
+
* refresh token at all.
|
|
472
|
+
*/
|
|
458
473
|
refreshFunction?: () => Promise<{
|
|
459
474
|
accessToken: string;
|
|
460
475
|
refreshToken?: string;
|
|
476
|
+
fileToken?: string;
|
|
477
|
+
xmppPassword?: string;
|
|
461
478
|
} | null>;
|
|
462
479
|
};
|
|
463
480
|
|