@oxyhq/services 5.17.17 → 5.17.18
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/README.md +32 -38
- package/lib/commonjs/core/CrossDomainAuth.js +277 -0
- package/lib/commonjs/core/CrossDomainAuth.js.map +1 -0
- package/lib/commonjs/core/HttpService.js +82 -15
- package/lib/commonjs/core/HttpService.js.map +1 -1
- package/lib/commonjs/core/OxyServices.base.js +11 -3
- package/lib/commonjs/core/OxyServices.base.js.map +1 -1
- package/lib/commonjs/core/OxyServices.js +4 -1
- package/lib/commonjs/core/OxyServices.js.map +1 -1
- package/lib/commonjs/core/index.js +30 -0
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.assets.js +16 -3
- package/lib/commonjs/core/mixins/OxyServices.assets.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.auth.js +73 -32
- package/lib/commonjs/core/mixins/OxyServices.auth.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.fedcm.js +289 -0
- package/lib/commonjs/core/mixins/OxyServices.fedcm.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.popup.js +352 -0
- package/lib/commonjs/core/mixins/OxyServices.popup.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.redirect.js +378 -0
- package/lib/commonjs/core/mixins/OxyServices.redirect.js.map +1 -0
- package/lib/commonjs/core/mixins/OxyServices.user.js +35 -24
- package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/commonjs/core/mixins/index.js +27 -15
- package/lib/commonjs/core/mixins/index.js.map +1 -1
- package/lib/commonjs/crypto/index.js +30 -0
- package/lib/commonjs/crypto/index.js.map +1 -1
- package/lib/commonjs/crypto/keyManager.js +902 -0
- package/lib/commonjs/crypto/keyManager.js.map +1 -0
- package/lib/commonjs/crypto/polyfill.js +14 -5
- package/lib/commonjs/crypto/polyfill.js.map +1 -1
- package/lib/commonjs/crypto/recoveryPhrase.js +152 -0
- package/lib/commonjs/crypto/recoveryPhrase.js.map +1 -0
- package/lib/commonjs/crypto/signatureService.js +289 -0
- package/lib/commonjs/crypto/signatureService.js.map +1 -0
- package/lib/commonjs/i18n/locales/en-US.json +1 -1
- package/lib/commonjs/index.js +40 -26
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/models/interfaces.js +0 -15
- package/lib/commonjs/models/interfaces.js.map +1 -1
- package/lib/commonjs/ui/components/BottomSheetRouter.js +9 -1
- package/lib/commonjs/ui/components/BottomSheetRouter.js.map +1 -1
- package/lib/commonjs/ui/components/Icon.js.map +1 -1
- package/lib/commonjs/ui/components/IconButton/utils.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +41 -11
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/TextField/Adornment/utils.js.map +1 -1
- package/lib/commonjs/ui/components/TextField/helpers.js.map +1 -1
- package/lib/commonjs/ui/components/TouchableRipple/utils.js.map +1 -1
- package/lib/commonjs/ui/components/Typography/AnimatedText.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +110 -199
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +150 -19
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useSessionManagement.js +279 -0
- package/lib/commonjs/ui/context/hooks/useSessionManagement.js.map +1 -0
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +79 -72
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +38 -51
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useSecurityQueries.js +3 -3
- package/lib/commonjs/ui/hooks/queries/useSecurityQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +18 -12
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/useProfileEditing.js +3 -5
- package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +4 -8
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +162 -315
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/commonjs/ui/hooks/useStorage.js +24 -58
- package/lib/commonjs/ui/hooks/useStorage.js.map +1 -1
- package/lib/commonjs/ui/index.js +50 -21
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/navigation/routes.js +5 -1
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountCenterScreen.js +2 -2
- package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +29 -24
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +3 -3
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/OxyAuthScreen.js +134 -66
- package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +6 -13
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/stores/accountStore.js +2 -4
- package/lib/commonjs/ui/stores/accountStore.js.map +1 -1
- package/lib/commonjs/ui/stores/authStore.js +45 -32
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/commonjs/ui/styles/spacing.js +54 -2
- package/lib/commonjs/ui/styles/spacing.js.map +1 -1
- package/lib/commonjs/ui/utils/avatarUtils.js +37 -41
- package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
- package/lib/commonjs/ui/utils/storageHelpers.js.map +1 -1
- package/lib/commonjs/utils/errorUtils.js +13 -0
- package/lib/commonjs/utils/errorUtils.js.map +1 -1
- package/lib/commonjs/utils/validationUtils.js +15 -1
- package/lib/commonjs/utils/validationUtils.js.map +1 -1
- package/lib/module/core/CrossDomainAuth.js +271 -0
- package/lib/module/core/CrossDomainAuth.js.map +1 -0
- package/lib/module/core/HttpService.js +82 -15
- package/lib/module/core/HttpService.js.map +1 -1
- package/lib/module/core/OxyServices.base.js +11 -4
- package/lib/module/core/OxyServices.base.js.map +1 -1
- package/lib/module/core/OxyServices.js +4 -1
- package/lib/module/core/OxyServices.js.map +1 -1
- package/lib/module/core/index.js +6 -1
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.assets.js +16 -3
- package/lib/module/core/mixins/OxyServices.assets.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.auth.js +73 -32
- package/lib/module/core/mixins/OxyServices.auth.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.fedcm.js +286 -0
- package/lib/module/core/mixins/OxyServices.fedcm.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.popup.js +349 -0
- package/lib/module/core/mixins/OxyServices.popup.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.redirect.js +375 -0
- package/lib/module/core/mixins/OxyServices.redirect.js.map +1 -0
- package/lib/module/core/mixins/OxyServices.user.js +35 -24
- package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/module/core/mixins/index.js +15 -3
- package/lib/module/core/mixins/index.js.map +1 -1
- package/lib/module/crypto/index.js +8 -4
- package/lib/module/crypto/index.js.map +1 -1
- package/lib/module/crypto/keyManager.js +899 -0
- package/lib/module/crypto/keyManager.js.map +1 -0
- package/lib/module/crypto/polyfill.js +6 -5
- package/lib/module/crypto/polyfill.js.map +1 -1
- package/lib/module/crypto/recoveryPhrase.js +147 -0
- package/lib/module/crypto/recoveryPhrase.js.map +1 -0
- package/lib/module/crypto/signatureService.js +286 -0
- package/lib/module/crypto/signatureService.js.map +1 -0
- package/lib/module/i18n/locales/en-US.json +1 -1
- package/lib/module/index.js +6 -9
- package/lib/module/index.js.map +1 -1
- package/lib/module/models/interfaces.js +0 -15
- package/lib/module/models/interfaces.js.map +1 -1
- package/lib/module/ui/components/BottomSheetRouter.js +6 -2
- package/lib/module/ui/components/BottomSheetRouter.js.map +1 -1
- package/lib/module/ui/components/Icon.js.map +1 -1
- package/lib/module/ui/components/IconButton/utils.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +41 -11
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/TextField/Adornment/utils.js.map +1 -1
- package/lib/module/ui/components/TextField/helpers.js.map +1 -1
- package/lib/module/ui/components/TouchableRipple/utils.js.map +1 -1
- package/lib/module/ui/components/Typography/AnimatedText.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +112 -191
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +150 -19
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/hooks/useSessionManagement.js +274 -0
- package/lib/module/ui/context/hooks/useSessionManagement.js.map +1 -0
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +80 -72
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/module/ui/hooks/queries/useAccountQueries.js +31 -44
- package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/module/ui/hooks/queries/useSecurityQueries.js +1 -1
- package/lib/module/ui/hooks/queries/useSecurityQueries.js.map +1 -1
- package/lib/module/ui/hooks/queries/useServicesQueries.js +13 -7
- package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/module/ui/hooks/useProfileEditing.js +3 -5
- package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +4 -8
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/hooks/useSessionSocket.js +162 -315
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/module/ui/hooks/useStorage.js +25 -59
- package/lib/module/ui/hooks/useStorage.js.map +1 -1
- package/lib/module/ui/index.js +15 -10
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/navigation/routes.js +5 -1
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/screens/AccountCenterScreen.js +2 -2
- package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +29 -24
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +3 -3
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/OxyAuthScreen.js +135 -68
- package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/screens/PrivacySettingsScreen.js +6 -13
- package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/module/ui/stores/accountStore.js +2 -4
- package/lib/module/ui/stores/accountStore.js.map +1 -1
- package/lib/module/ui/stores/authStore.js +45 -32
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/module/ui/styles/spacing.js +6 -2
- package/lib/module/ui/styles/spacing.js.map +1 -1
- package/lib/module/ui/utils/avatarUtils.js +37 -40
- package/lib/module/ui/utils/avatarUtils.js.map +1 -1
- package/lib/module/ui/utils/storageHelpers.js.map +1 -1
- package/lib/module/utils/errorUtils.js +7 -0
- package/lib/module/utils/errorUtils.js.map +1 -1
- package/lib/module/utils/validationUtils.js +13 -0
- package/lib/module/utils/validationUtils.js.map +1 -1
- package/lib/typescript/core/CrossDomainAuth.d.ts +161 -0
- package/lib/typescript/core/CrossDomainAuth.d.ts.map +1 -0
- package/lib/typescript/core/HttpService.d.ts +1 -1
- package/lib/typescript/core/HttpService.d.ts.map +1 -1
- package/lib/typescript/core/OxyServices.base.d.ts +0 -6
- package/lib/typescript/core/OxyServices.base.d.ts.map +1 -1
- package/lib/typescript/core/OxyServices.d.ts +5 -36
- package/lib/typescript/core/OxyServices.d.ts.map +1 -1
- package/lib/typescript/core/index.d.ts +4 -0
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.analytics.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts +40 -20
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.developer.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.devices.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.fedcm.d.ts +195 -0
- package/lib/typescript/core/mixins/OxyServices.fedcm.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.karma.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.language.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.location.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.payment.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.popup.d.ts +206 -0
- package/lib/typescript/core/mixins/OxyServices.popup.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.privacy.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.redirect.d.ts +246 -0
- package/lib/typescript/core/mixins/OxyServices.redirect.d.ts.map +1 -0
- package/lib/typescript/core/mixins/OxyServices.security.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.user.d.ts +6 -4
- package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.utility.d.ts.map +1 -1
- package/lib/typescript/core/mixins/index.d.ts +220 -8
- package/lib/typescript/core/mixins/index.d.ts.map +1 -1
- package/lib/typescript/crypto/index.d.ts +6 -3
- package/lib/typescript/crypto/index.d.ts.map +1 -1
- package/lib/typescript/crypto/keyManager.d.ts +190 -0
- package/lib/typescript/crypto/keyManager.d.ts.map +1 -0
- package/lib/typescript/crypto/polyfill.d.ts +4 -3
- package/lib/typescript/crypto/polyfill.d.ts.map +1 -1
- package/lib/typescript/crypto/recoveryPhrase.d.ts +59 -0
- package/lib/typescript/crypto/recoveryPhrase.d.ts.map +1 -0
- package/lib/typescript/crypto/signatureService.d.ts +87 -0
- package/lib/typescript/crypto/signatureService.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +5 -6
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +2 -14
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/models/session.d.ts +0 -9
- package/lib/typescript/models/session.d.ts.map +1 -1
- package/lib/typescript/types/bip39.d.ts +32 -0
- package/lib/typescript/ui/components/BottomSheetRouter.d.ts +5 -0
- package/lib/typescript/ui/components/BottomSheetRouter.d.ts.map +1 -1
- package/lib/typescript/ui/components/IconButton/utils.d.ts +1 -1
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/components/TextField/Addons/Outline.d.ts +2 -2
- package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts +1 -1
- package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts.map +1 -1
- package/lib/typescript/ui/components/TextField/helpers.d.ts +8 -8
- package/lib/typescript/ui/components/TextField/types.d.ts +1 -0
- package/lib/typescript/ui/components/TextField/types.d.ts.map +1 -1
- package/lib/typescript/ui/components/types.d.ts +4 -0
- package/lib/typescript/ui/components/types.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +57 -3
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +10 -3
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts +41 -0
- package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts +1 -2
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useStorage.d.ts +3 -9
- package/lib/typescript/ui/hooks/useStorage.d.ts.map +1 -1
- package/lib/typescript/ui/index.d.ts +6 -2
- package/lib/typescript/ui/index.d.ts.map +1 -1
- package/lib/typescript/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/OxyAuthScreen.d.ts +1 -1
- package/lib/typescript/ui/screens/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/stores/accountStore.d.ts.map +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts +8 -7
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/ui/styles/spacing.d.ts +5 -0
- package/lib/typescript/ui/styles/spacing.d.ts.map +1 -1
- package/lib/typescript/ui/types/navigation.d.ts +2 -1
- package/lib/typescript/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/ui/utils/avatarUtils.d.ts +2 -13
- package/lib/typescript/ui/utils/avatarUtils.d.ts.map +1 -1
- package/lib/typescript/ui/utils/storageHelpers.d.ts +0 -3
- package/lib/typescript/ui/utils/storageHelpers.d.ts.map +1 -1
- package/lib/typescript/utils/errorUtils.d.ts +6 -0
- package/lib/typescript/utils/errorUtils.d.ts.map +1 -1
- package/lib/typescript/utils/validationUtils.d.ts +8 -0
- package/lib/typescript/utils/validationUtils.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/core/CrossDomainAuth.ts +307 -0
- package/src/core/HttpService.ts +99 -16
- package/src/core/OxyServices.base.ts +20 -3
- package/src/core/OxyServices.ts +7 -3
- package/src/core/index.ts +9 -1
- package/src/core/mixins/OxyServices.assets.ts +14 -3
- package/src/core/mixins/OxyServices.auth.ts +105 -36
- package/src/core/mixins/OxyServices.fedcm.ts +315 -0
- package/src/core/mixins/OxyServices.popup.ts +402 -0
- package/src/core/mixins/OxyServices.redirect.ts +397 -0
- package/src/core/mixins/OxyServices.user.ts +39 -24
- package/src/core/mixins/index.ts +19 -3
- package/src/crypto/index.ts +16 -5
- package/src/crypto/keyManager.ts +966 -0
- package/src/crypto/polyfill.ts +6 -5
- package/src/crypto/recoveryPhrase.ts +166 -0
- package/src/crypto/signatureService.ts +323 -0
- package/src/i18n/locales/en-US.json +1 -1
- package/src/index.ts +19 -15
- package/src/models/interfaces.ts +4 -16
- package/src/models/session.ts +2 -11
- package/src/types/bip39.d.ts +32 -0
- package/src/ui/components/BottomSheetRouter.tsx +6 -1
- package/src/ui/components/Icon.tsx +1 -1
- package/src/ui/components/IconButton/utils.ts +1 -1
- package/src/ui/components/OxyProvider.tsx +44 -12
- package/src/ui/components/TextField/Addons/Outline.tsx +2 -2
- package/src/ui/components/TextField/Adornment/utils.ts +2 -2
- package/src/ui/components/TextField/helpers.tsx +10 -10
- package/src/ui/components/TextField/types.tsx +1 -1
- package/src/ui/components/TouchableRipple/utils.ts +2 -2
- package/src/ui/components/Typography/AnimatedText.tsx +2 -2
- package/src/ui/components/types.tsx +6 -0
- package/src/ui/context/OxyContext.tsx +173 -195
- package/src/ui/context/hooks/useAuthOperations.ts +177 -36
- package/src/ui/context/hooks/useSessionManagement.ts +399 -0
- package/src/ui/hooks/mutations/useAccountMutations.ts +83 -76
- package/src/ui/hooks/queries/useAccountQueries.ts +29 -35
- package/src/ui/hooks/queries/useSecurityQueries.ts +1 -1
- package/src/ui/hooks/queries/useServicesQueries.ts +14 -6
- package/src/ui/hooks/useProfileEditing.ts +3 -3
- package/src/ui/hooks/useSessionManagement.ts +5 -10
- package/src/ui/hooks/useSessionSocket.ts +46 -175
- package/src/ui/hooks/useStorage.ts +24 -76
- package/src/ui/index.ts +22 -13
- package/src/ui/navigation/routes.ts +6 -2
- package/src/ui/screens/AccountCenterScreen.tsx +2 -2
- package/src/ui/screens/AccountOverviewScreen.tsx +1 -1
- package/src/ui/screens/AccountSettingsScreen.tsx +34 -37
- package/src/ui/screens/AccountSwitcherScreen.tsx +4 -4
- package/src/ui/screens/OxyAuthScreen.tsx +138 -64
- package/src/ui/screens/PrivacySettingsScreen.tsx +6 -12
- package/src/ui/stores/accountStore.ts +1 -11
- package/src/ui/stores/authStore.ts +43 -44
- package/src/ui/styles/spacing.ts +15 -2
- package/src/ui/types/navigation.ts +2 -2
- package/src/ui/utils/avatarUtils.ts +39 -46
- package/src/ui/utils/storageHelpers.ts +0 -4
- package/src/utils/__tests__/validationUtils.test.ts +16 -1
- package/src/utils/errorUtils.ts +8 -1
- package/src/utils/validationUtils.ts +12 -0
- package/lib/commonjs/core/services/SessionService.js +0 -163
- package/lib/commonjs/core/services/SessionService.js.map +0 -1
- package/lib/commonjs/core/services/TokenService.js +0 -220
- package/lib/commonjs/core/services/TokenService.js.map +0 -1
- package/lib/commonjs/crypto/types.js +0 -2
- package/lib/commonjs/crypto/types.js.map +0 -1
- package/lib/commonjs/ui/context/OxyContextBase.js +0 -21
- package/lib/commonjs/ui/context/OxyContextBase.js.map +0 -1
- package/lib/commonjs/ui/context/hooks/useStorage.js +0 -79
- package/lib/commonjs/ui/context/hooks/useStorage.js.map +0 -1
- package/lib/commonjs/ui/hooks/useAvatarPicker.js +0 -56
- package/lib/commonjs/ui/hooks/useAvatarPicker.js.map +0 -1
- package/lib/module/core/services/SessionService.js +0 -159
- package/lib/module/core/services/SessionService.js.map +0 -1
- package/lib/module/core/services/TokenService.js +0 -217
- package/lib/module/core/services/TokenService.js.map +0 -1
- package/lib/module/crypto/types.js +0 -2
- package/lib/module/crypto/types.js.map +0 -1
- package/lib/module/ui/context/OxyContextBase.js +0 -16
- package/lib/module/ui/context/OxyContextBase.js.map +0 -1
- package/lib/module/ui/context/hooks/useStorage.js +0 -74
- package/lib/module/ui/context/hooks/useStorage.js.map +0 -1
- package/lib/module/ui/hooks/useAvatarPicker.js +0 -50
- package/lib/module/ui/hooks/useAvatarPicker.js.map +0 -1
- package/lib/typescript/core/services/SessionService.d.ts +0 -78
- package/lib/typescript/core/services/SessionService.d.ts.map +0 -1
- package/lib/typescript/core/services/TokenService.d.ts +0 -72
- package/lib/typescript/core/services/TokenService.d.ts.map +0 -1
- package/lib/typescript/crypto/types.d.ts +0 -22
- package/lib/typescript/crypto/types.d.ts.map +0 -1
- package/lib/typescript/ui/context/OxyContextBase.d.ts +0 -63
- package/lib/typescript/ui/context/OxyContextBase.d.ts.map +0 -1
- package/lib/typescript/ui/context/hooks/useStorage.d.ts +0 -22
- package/lib/typescript/ui/context/hooks/useStorage.d.ts.map +0 -1
- package/lib/typescript/ui/hooks/useAvatarPicker.d.ts +0 -19
- package/lib/typescript/ui/hooks/useAvatarPicker.d.ts.map +0 -1
- package/src/core/services/SessionService.ts +0 -173
- package/src/core/services/TokenService.ts +0 -237
- package/src/crypto/types.ts +0 -23
- package/src/ui/context/OxyContextBase.tsx +0 -78
- package/src/ui/context/hooks/useStorage.ts +0 -104
- package/src/ui/hooks/useAvatarPicker.ts +0 -61
|
@@ -2,10 +2,12 @@ import { useCallback } from 'react';
|
|
|
2
2
|
import type { ApiError, User } from '../../../models/interfaces';
|
|
3
3
|
import type { AuthState } from '../../stores/authStore';
|
|
4
4
|
import type { ClientSession, SessionLoginResponse } from '../../../models/session';
|
|
5
|
-
import {
|
|
5
|
+
import { DeviceManager } from '../../../utils/deviceManager';
|
|
6
|
+
import { fetchSessionsWithFallback, mapSessionsToClient } from '../../utils/sessionHelpers';
|
|
6
7
|
import { handleAuthError, isInvalidSessionError } from '../../utils/errorHandlers';
|
|
7
8
|
import type { StorageInterface } from '../../utils/storageHelpers';
|
|
8
9
|
import type { OxyServices } from '../../../core';
|
|
10
|
+
import { SignatureService } from '../../../crypto';
|
|
9
11
|
|
|
10
12
|
export interface UseAuthOperationsOptions {
|
|
11
13
|
oxyServices: OxyServices;
|
|
@@ -20,7 +22,7 @@ export interface UseAuthOperationsOptions {
|
|
|
20
22
|
applyLanguagePreference: (user: User) => Promise<void>;
|
|
21
23
|
onAuthStateChange?: (user: User | null) => void;
|
|
22
24
|
onError?: (error: ApiError) => void;
|
|
23
|
-
loginSuccess: () => void;
|
|
25
|
+
loginSuccess: (user: User) => void;
|
|
24
26
|
loginFailure: (message: string) => void;
|
|
25
27
|
logoutStore: () => void;
|
|
26
28
|
setAuthState: (state: Partial<AuthState>) => void;
|
|
@@ -28,15 +30,22 @@ export interface UseAuthOperationsOptions {
|
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export interface UseAuthOperationsResult {
|
|
31
|
-
|
|
33
|
+
/** Sign in with existing public key */
|
|
34
|
+
signIn: (publicKey: string, deviceName?: string) => Promise<User>;
|
|
35
|
+
/** Logout from current session */
|
|
32
36
|
logout: (targetSessionId?: string) => Promise<void>;
|
|
37
|
+
/** Logout from all sessions */
|
|
33
38
|
logoutAll: () => Promise<void>;
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
const LOGIN_ERROR_CODE = 'LOGIN_ERROR';
|
|
36
42
|
const LOGOUT_ERROR_CODE = 'LOGOUT_ERROR';
|
|
37
43
|
const LOGOUT_ALL_ERROR_CODE = 'LOGOUT_ALL_ERROR';
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Authentication operations using public key cryptography.
|
|
47
|
+
* Accepts public key as parameter - identity management is handled by the app layer.
|
|
48
|
+
*/
|
|
40
49
|
export const useAuthOperations = ({
|
|
41
50
|
oxyServices,
|
|
42
51
|
storage,
|
|
@@ -57,17 +66,136 @@ export const useAuthOperations = ({
|
|
|
57
66
|
logger,
|
|
58
67
|
}: UseAuthOperationsOptions): UseAuthOperationsResult => {
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Internal function to perform challenge-response sign in (works offline)
|
|
71
|
+
*/
|
|
72
|
+
const performSignIn = useCallback(
|
|
73
|
+
async (publicKey: string): Promise<User> => {
|
|
74
|
+
const deviceFingerprintObj = DeviceManager.getDeviceFingerprint();
|
|
75
|
+
const deviceFingerprint = JSON.stringify(deviceFingerprintObj);
|
|
76
|
+
const deviceInfo = await DeviceManager.getDeviceInfo();
|
|
77
|
+
const deviceName = deviceInfo.deviceName || DeviceManager.getDefaultDeviceName();
|
|
63
78
|
|
|
64
|
-
|
|
79
|
+
let challenge: string;
|
|
80
|
+
let isOffline = false;
|
|
65
81
|
|
|
82
|
+
// Try to request challenge from server (online)
|
|
66
83
|
try {
|
|
67
|
-
oxyServices.
|
|
84
|
+
const challengeResponse = await oxyServices.requestChallenge(publicKey);
|
|
85
|
+
challenge = challengeResponse.challenge;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// Network error - generate challenge locally for offline sign-in
|
|
88
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
89
|
+
const isNetworkError =
|
|
90
|
+
errorMessage.includes('Network') ||
|
|
91
|
+
errorMessage.includes('network') ||
|
|
92
|
+
errorMessage.includes('Failed to fetch') ||
|
|
93
|
+
errorMessage.includes('fetch failed') ||
|
|
94
|
+
(error as any)?.code === 'NETWORK_ERROR' ||
|
|
95
|
+
(error as any)?.status === 0;
|
|
96
|
+
|
|
97
|
+
if (isNetworkError) {
|
|
98
|
+
if (__DEV__ && logger) {
|
|
99
|
+
logger('Network unavailable, performing offline sign-in');
|
|
100
|
+
}
|
|
101
|
+
// Generate challenge locally
|
|
102
|
+
challenge = await SignatureService.generateChallenge();
|
|
103
|
+
isOffline = true;
|
|
104
|
+
} else {
|
|
105
|
+
// Re-throw non-network errors
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Note: Biometric authentication check should be handled by the app layer
|
|
111
|
+
// (e.g., accounts app) before calling signIn. The biometric preference is stored
|
|
112
|
+
// in local storage as 'oxy_biometric_enabled' and can be checked there.
|
|
113
|
+
|
|
114
|
+
// Sign the challenge
|
|
115
|
+
const { challenge: signature, timestamp } = await SignatureService.signChallenge(challenge);
|
|
116
|
+
|
|
117
|
+
let fullUser: User;
|
|
118
|
+
let sessionResponse: SessionLoginResponse;
|
|
119
|
+
|
|
120
|
+
if (isOffline) {
|
|
121
|
+
// Offline sign-in: create local session and minimal user object
|
|
122
|
+
if (__DEV__ && logger) {
|
|
123
|
+
logger('Creating offline session');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Generate a local session ID
|
|
127
|
+
const localSessionId = `offline_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
128
|
+
const localDeviceId = `device_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
129
|
+
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(); // 7 days
|
|
130
|
+
|
|
131
|
+
// Create minimal user object with publicKey as id
|
|
132
|
+
fullUser = {
|
|
133
|
+
id: publicKey, // Use publicKey as id (per migration document)
|
|
134
|
+
publicKey,
|
|
135
|
+
username: '',
|
|
136
|
+
privacySettings: {},
|
|
137
|
+
} as User;
|
|
68
138
|
|
|
69
|
-
|
|
139
|
+
sessionResponse = {
|
|
140
|
+
sessionId: localSessionId,
|
|
141
|
+
deviceId: localDeviceId,
|
|
142
|
+
expiresAt,
|
|
143
|
+
user: {
|
|
144
|
+
id: publicKey,
|
|
145
|
+
username: '',
|
|
146
|
+
},
|
|
147
|
+
};
|
|
70
148
|
|
|
149
|
+
// Store offline session locally
|
|
150
|
+
const offlineSession: ClientSession = {
|
|
151
|
+
sessionId: localSessionId,
|
|
152
|
+
deviceId: localDeviceId,
|
|
153
|
+
expiresAt,
|
|
154
|
+
lastActive: new Date().toISOString(),
|
|
155
|
+
userId: publicKey,
|
|
156
|
+
isCurrent: true,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
setActiveSessionId(localSessionId);
|
|
160
|
+
await saveActiveSessionId(localSessionId);
|
|
161
|
+
updateSessions([offlineSession], { merge: true });
|
|
162
|
+
|
|
163
|
+
// Mark session as offline for later sync
|
|
164
|
+
if (storage) {
|
|
165
|
+
await storage.setItem(`oxy_session_${localSessionId}_offline`, 'true');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (__DEV__ && logger) {
|
|
169
|
+
logger('Offline sign-in successful');
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
// Online sign-in: use normal flow
|
|
173
|
+
// Verify and create session
|
|
174
|
+
sessionResponse = await oxyServices.verifyChallenge(
|
|
175
|
+
publicKey,
|
|
176
|
+
challenge,
|
|
177
|
+
signature,
|
|
178
|
+
timestamp,
|
|
179
|
+
deviceName,
|
|
180
|
+
deviceFingerprint,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
// Get token for the session
|
|
184
|
+
try {
|
|
185
|
+
await oxyServices.getTokenBySession(sessionResponse.sessionId);
|
|
186
|
+
} catch (tokenError: unknown) {
|
|
187
|
+
const errorMessage = tokenError instanceof Error ? tokenError.message : String(tokenError);
|
|
188
|
+
const status = (tokenError as any)?.status;
|
|
189
|
+
if (status === 404 || errorMessage.includes('404')) {
|
|
190
|
+
throw new Error(`Session was created but token could not be retrieved. Session ID: ${sessionResponse.sessionId.substring(0, 8)}...`);
|
|
191
|
+
}
|
|
192
|
+
throw tokenError;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Get full user data
|
|
196
|
+
fullUser = await oxyServices.getUserBySession(sessionResponse.sessionId);
|
|
197
|
+
|
|
198
|
+
// Fetch device sessions
|
|
71
199
|
let allDeviceSessions: ClientSession[] = [];
|
|
72
200
|
try {
|
|
73
201
|
allDeviceSessions = await fetchSessionsWithFallback(oxyServices, sessionResponse.sessionId, {
|
|
@@ -76,11 +204,12 @@ export const useAuthOperations = ({
|
|
|
76
204
|
logger,
|
|
77
205
|
});
|
|
78
206
|
} catch (error) {
|
|
79
|
-
if (__DEV__) {
|
|
80
|
-
|
|
207
|
+
if (__DEV__ && logger) {
|
|
208
|
+
logger('Failed to fetch device sessions after login', error);
|
|
81
209
|
}
|
|
82
210
|
}
|
|
83
211
|
|
|
212
|
+
// Check for existing session for same user and switch to it to avoid duplicates
|
|
84
213
|
const existingSession = allDeviceSessions.find(
|
|
85
214
|
(session) =>
|
|
86
215
|
session.userId?.toString() === fullUser.id?.toString() &&
|
|
@@ -88,11 +217,13 @@ export const useAuthOperations = ({
|
|
|
88
217
|
);
|
|
89
218
|
|
|
90
219
|
if (existingSession) {
|
|
220
|
+
// Switch to existing session instead of creating duplicate
|
|
91
221
|
try {
|
|
92
222
|
await oxyServices.logoutSession(sessionResponse.sessionId, sessionResponse.sessionId);
|
|
93
223
|
} catch (logoutError) {
|
|
94
|
-
if
|
|
95
|
-
|
|
224
|
+
// Non-critical - continue to switch session even if logout fails
|
|
225
|
+
if (__DEV__ && logger) {
|
|
226
|
+
logger('Failed to logout duplicate session, continuing with switch', logoutError);
|
|
96
227
|
}
|
|
97
228
|
}
|
|
98
229
|
await switchSession(existingSession.sessionId);
|
|
@@ -107,16 +238,41 @@ export const useAuthOperations = ({
|
|
|
107
238
|
setActiveSessionId(sessionResponse.sessionId);
|
|
108
239
|
await saveActiveSessionId(sessionResponse.sessionId);
|
|
109
240
|
updateSessions(allDeviceSessions, { merge: true });
|
|
241
|
+
}
|
|
110
242
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
243
|
+
await applyLanguagePreference(fullUser);
|
|
244
|
+
loginSuccess(fullUser);
|
|
245
|
+
onAuthStateChange?.(fullUser);
|
|
246
|
+
|
|
247
|
+
return fullUser;
|
|
248
|
+
},
|
|
249
|
+
[
|
|
250
|
+
applyLanguagePreference,
|
|
251
|
+
logger,
|
|
252
|
+
loginSuccess,
|
|
253
|
+
onAuthStateChange,
|
|
254
|
+
oxyServices,
|
|
255
|
+
saveActiveSessionId,
|
|
256
|
+
setActiveSessionId,
|
|
257
|
+
switchSession,
|
|
258
|
+
updateSessions,
|
|
259
|
+
storage,
|
|
260
|
+
],
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Sign in with existing public key
|
|
265
|
+
*/
|
|
266
|
+
const signIn = useCallback(
|
|
267
|
+
async (publicKey: string, deviceName?: string): Promise<User> => {
|
|
268
|
+
setAuthState({ isLoading: true, error: null });
|
|
114
269
|
|
|
115
|
-
|
|
270
|
+
try {
|
|
271
|
+
return await performSignIn(publicKey);
|
|
116
272
|
} catch (error) {
|
|
117
273
|
const message = handleAuthError(error, {
|
|
118
274
|
defaultMessage: 'Sign in failed',
|
|
119
|
-
code:
|
|
275
|
+
code: LOGIN_ERROR_CODE,
|
|
120
276
|
onError,
|
|
121
277
|
setAuthError: (msg: string) => setAuthState({ error: msg }),
|
|
122
278
|
logger,
|
|
@@ -127,24 +283,9 @@ export const useAuthOperations = ({
|
|
|
127
283
|
setAuthState({ isLoading: false });
|
|
128
284
|
}
|
|
129
285
|
},
|
|
130
|
-
[
|
|
131
|
-
storage,
|
|
132
|
-
setAuthState,
|
|
133
|
-
oxyServices,
|
|
134
|
-
logger,
|
|
135
|
-
saveActiveSessionId,
|
|
136
|
-
setActiveSessionId,
|
|
137
|
-
switchSession,
|
|
138
|
-
updateSessions,
|
|
139
|
-
applyLanguagePreference,
|
|
140
|
-
loginSuccess,
|
|
141
|
-
onAuthStateChange,
|
|
142
|
-
onError,
|
|
143
|
-
loginFailure,
|
|
144
|
-
],
|
|
286
|
+
[setAuthState, performSignIn, loginFailure, onError, logger],
|
|
145
287
|
);
|
|
146
288
|
|
|
147
|
-
|
|
148
289
|
/**
|
|
149
290
|
* Logout from session
|
|
150
291
|
*/
|
|
@@ -225,7 +366,7 @@ export const useAuthOperations = ({
|
|
|
225
366
|
}, [activeSessionId, clearSessionState, logger, onError, oxyServices, setAuthState]);
|
|
226
367
|
|
|
227
368
|
return {
|
|
228
|
-
|
|
369
|
+
signIn,
|
|
229
370
|
logout,
|
|
230
371
|
logoutAll,
|
|
231
372
|
};
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import type { ApiError, User } from '../../../models/interfaces';
|
|
3
|
+
import type { ClientSession } from '../../../models/session';
|
|
4
|
+
import { mergeSessions, normalizeAndSortSessions, sessionsArraysEqual } from '../../../utils/sessionUtils';
|
|
5
|
+
import { fetchSessionsWithFallback, mapSessionsToClient, validateSessionBatch } from '../../utils/sessionHelpers';
|
|
6
|
+
import { getStorageKeys, type StorageInterface } from '../../utils/storageHelpers';
|
|
7
|
+
import { handleAuthError, isInvalidSessionError } from '../../utils/errorHandlers';
|
|
8
|
+
import type { OxyServices } from '../../../core';
|
|
9
|
+
import type { QueryClient } from '@tanstack/react-query';
|
|
10
|
+
import { clearQueryCache } from '../../hooks/queryClient';
|
|
11
|
+
|
|
12
|
+
export interface UseSessionManagementOptions {
|
|
13
|
+
oxyServices: OxyServices;
|
|
14
|
+
storage: StorageInterface | null;
|
|
15
|
+
storageKeyPrefix?: string;
|
|
16
|
+
loginSuccess: (user: User) => void;
|
|
17
|
+
logoutStore: () => void;
|
|
18
|
+
applyLanguagePreference: (user: User) => Promise<void>;
|
|
19
|
+
onAuthStateChange?: (user: User | null) => void;
|
|
20
|
+
onError?: (error: ApiError) => void;
|
|
21
|
+
setAuthError?: (message: string | null) => void;
|
|
22
|
+
logger?: (message: string, error?: unknown) => void;
|
|
23
|
+
setTokenReady?: (ready: boolean) => void;
|
|
24
|
+
queryClient?: QueryClient | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface UseSessionManagementResult {
|
|
28
|
+
sessions: ClientSession[];
|
|
29
|
+
activeSessionId: string | null;
|
|
30
|
+
setActiveSessionId: (sessionId: string | null) => void;
|
|
31
|
+
updateSessions: (incoming: ClientSession[], options?: { merge?: boolean }) => void;
|
|
32
|
+
switchSession: (sessionId: string) => Promise<User>;
|
|
33
|
+
refreshSessions: (activeUserId?: string) => Promise<void>;
|
|
34
|
+
clearSessionState: () => Promise<void>;
|
|
35
|
+
saveActiveSessionId: (sessionId: string) => Promise<void>;
|
|
36
|
+
trackRemovedSession: (sessionId: string) => void;
|
|
37
|
+
storageKeys: ReturnType<typeof getStorageKeys>;
|
|
38
|
+
isRefreshInFlight: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const DEFAULT_SAVE_ERROR_MESSAGE = 'Failed to save session data';
|
|
42
|
+
const CLEAR_STORAGE_ERROR = 'Failed to clear storage';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Manage session state, persistence, and high-level multi-session operations.
|
|
46
|
+
*
|
|
47
|
+
* @param options - Session management configuration
|
|
48
|
+
*/
|
|
49
|
+
export const useSessionManagement = ({
|
|
50
|
+
oxyServices,
|
|
51
|
+
storage,
|
|
52
|
+
storageKeyPrefix,
|
|
53
|
+
loginSuccess,
|
|
54
|
+
logoutStore,
|
|
55
|
+
applyLanguagePreference,
|
|
56
|
+
onAuthStateChange,
|
|
57
|
+
onError,
|
|
58
|
+
setAuthError,
|
|
59
|
+
logger,
|
|
60
|
+
setTokenReady,
|
|
61
|
+
queryClient,
|
|
62
|
+
}: UseSessionManagementOptions): UseSessionManagementResult => {
|
|
63
|
+
const [sessions, setSessions] = useState<ClientSession[]>([]);
|
|
64
|
+
const [activeSessionId, setActiveSessionId] = useState<string | null>(null);
|
|
65
|
+
|
|
66
|
+
const refreshInFlightRef = useRef<Promise<void> | null>(null);
|
|
67
|
+
const removedSessionsRef = useRef<Set<string>>(new Set());
|
|
68
|
+
const lastRefreshRef = useRef<number>(0);
|
|
69
|
+
|
|
70
|
+
const storageKeys = useMemo(() => getStorageKeys(storageKeyPrefix), [storageKeyPrefix]);
|
|
71
|
+
|
|
72
|
+
const saveSessionIds = useCallback(
|
|
73
|
+
async (sessionIds: string[]): Promise<void> => {
|
|
74
|
+
if (!storage) return;
|
|
75
|
+
try {
|
|
76
|
+
const uniqueIds = Array.from(new Set(sessionIds));
|
|
77
|
+
await storage.setItem(storageKeys.sessionIds, JSON.stringify(uniqueIds));
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (logger) {
|
|
80
|
+
logger(DEFAULT_SAVE_ERROR_MESSAGE, error);
|
|
81
|
+
} else if (__DEV__) {
|
|
82
|
+
console.warn('Failed to save session IDs:', error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
[logger, storage, storageKeys.sessionIds],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const updateSessions = useCallback(
|
|
90
|
+
(incoming: ClientSession[], options: { merge?: boolean } = {}): void => {
|
|
91
|
+
setSessions((prevSessions) => {
|
|
92
|
+
const processed = options.merge
|
|
93
|
+
? mergeSessions(prevSessions, incoming, activeSessionId, false)
|
|
94
|
+
: normalizeAndSortSessions(incoming, activeSessionId, false);
|
|
95
|
+
|
|
96
|
+
if (storage) {
|
|
97
|
+
void saveSessionIds(processed.map((session) => session.sessionId));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (sessionsArraysEqual(prevSessions, processed)) {
|
|
101
|
+
return prevSessions;
|
|
102
|
+
}
|
|
103
|
+
return processed;
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
[activeSessionId, saveSessionIds, storage],
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const saveActiveSessionId = useCallback(
|
|
110
|
+
async (sessionId: string): Promise<void> => {
|
|
111
|
+
if (!storage) return;
|
|
112
|
+
try {
|
|
113
|
+
await storage.setItem(storageKeys.activeSessionId, sessionId);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
handleAuthError(error, {
|
|
116
|
+
defaultMessage: DEFAULT_SAVE_ERROR_MESSAGE,
|
|
117
|
+
code: 'SESSION_PERSISTENCE_ERROR',
|
|
118
|
+
onError,
|
|
119
|
+
setAuthError,
|
|
120
|
+
logger,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
[logger, onError, setAuthError, storage, storageKeys.activeSessionId],
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const removeActiveSessionId = useCallback(async (): Promise<void> => {
|
|
128
|
+
if (!storage) return;
|
|
129
|
+
try {
|
|
130
|
+
await storage.removeItem(storageKeys.activeSessionId);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
handleAuthError(error, {
|
|
133
|
+
defaultMessage: DEFAULT_SAVE_ERROR_MESSAGE,
|
|
134
|
+
code: 'SESSION_PERSISTENCE_ERROR',
|
|
135
|
+
onError,
|
|
136
|
+
setAuthError,
|
|
137
|
+
logger,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}, [logger, onError, setAuthError, storage, storageKeys.activeSessionId]);
|
|
141
|
+
|
|
142
|
+
const clearSessionStorage = useCallback(async (): Promise<void> => {
|
|
143
|
+
if (!storage) return;
|
|
144
|
+
try {
|
|
145
|
+
await storage.removeItem(storageKeys.activeSessionId);
|
|
146
|
+
await storage.removeItem(storageKeys.sessionIds);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
handleAuthError(error, {
|
|
149
|
+
defaultMessage: CLEAR_STORAGE_ERROR,
|
|
150
|
+
code: 'STORAGE_ERROR',
|
|
151
|
+
onError,
|
|
152
|
+
setAuthError,
|
|
153
|
+
logger,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}, [logger, onError, setAuthError, storage, storageKeys.activeSessionId, storageKeys.sessionIds]);
|
|
157
|
+
|
|
158
|
+
const clearSessionState = useCallback(async (): Promise<void> => {
|
|
159
|
+
setSessions([]);
|
|
160
|
+
setActiveSessionId(null);
|
|
161
|
+
logoutStore();
|
|
162
|
+
|
|
163
|
+
// Clear TanStack Query cache (in-memory)
|
|
164
|
+
if (queryClient) {
|
|
165
|
+
queryClient.clear();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Clear persisted query cache
|
|
169
|
+
if (storage) {
|
|
170
|
+
try {
|
|
171
|
+
await clearQueryCache(storage);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
if (logger) {
|
|
174
|
+
logger('Failed to clear persisted query cache', error);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await clearSessionStorage();
|
|
180
|
+
onAuthStateChange?.(null);
|
|
181
|
+
}, [clearSessionStorage, logoutStore, onAuthStateChange, queryClient, storage, logger]);
|
|
182
|
+
|
|
183
|
+
const activateSession = useCallback(
|
|
184
|
+
async (sessionId: string, user: User): Promise<void> => {
|
|
185
|
+
await oxyServices.getTokenBySession(sessionId);
|
|
186
|
+
setTokenReady?.(true);
|
|
187
|
+
setActiveSessionId(sessionId);
|
|
188
|
+
loginSuccess(user);
|
|
189
|
+
await saveActiveSessionId(sessionId);
|
|
190
|
+
await applyLanguagePreference(user);
|
|
191
|
+
onAuthStateChange?.(user);
|
|
192
|
+
},
|
|
193
|
+
[
|
|
194
|
+
applyLanguagePreference,
|
|
195
|
+
loginSuccess,
|
|
196
|
+
onAuthStateChange,
|
|
197
|
+
oxyServices,
|
|
198
|
+
saveActiveSessionId,
|
|
199
|
+
setTokenReady,
|
|
200
|
+
],
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
const trackRemovedSession = useCallback((sessionId: string) => {
|
|
204
|
+
removedSessionsRef.current.add(sessionId);
|
|
205
|
+
setTimeout(() => {
|
|
206
|
+
removedSessionsRef.current.delete(sessionId);
|
|
207
|
+
}, 5000);
|
|
208
|
+
}, []);
|
|
209
|
+
|
|
210
|
+
const findReplacementSession = useCallback(
|
|
211
|
+
async (sessionIds: string[]): Promise<User | null> => {
|
|
212
|
+
if (!sessionIds.length) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const validationResults = await validateSessionBatch(oxyServices, sessionIds, {
|
|
217
|
+
maxConcurrency: 3,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const validSession = validationResults.find((result) => result.valid);
|
|
221
|
+
if (!validSession) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const validation = await oxyServices.validateSession(validSession.sessionId, {
|
|
226
|
+
useHeaderValidation: true,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (!validation?.valid || !validation.user) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const user = validation.user as User;
|
|
234
|
+
await activateSession(validSession.sessionId, user);
|
|
235
|
+
return user;
|
|
236
|
+
},
|
|
237
|
+
[activateSession, oxyServices],
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
const switchSession = useCallback(
|
|
241
|
+
async (sessionId: string): Promise<User> => {
|
|
242
|
+
try {
|
|
243
|
+
const validation = await oxyServices.validateSession(sessionId, { useHeaderValidation: true });
|
|
244
|
+
if (!validation?.valid) {
|
|
245
|
+
throw new Error('Session is invalid or expired');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (!validation.user) {
|
|
249
|
+
throw new Error('User data not available from session validation');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const user = validation.user as User;
|
|
253
|
+
await activateSession(sessionId, user);
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const deviceSessions = await fetchSessionsWithFallback(oxyServices, sessionId, {
|
|
257
|
+
fallbackUserId: user.id,
|
|
258
|
+
logger,
|
|
259
|
+
});
|
|
260
|
+
updateSessions(deviceSessions, { merge: true });
|
|
261
|
+
} catch (error) {
|
|
262
|
+
if (__DEV__) {
|
|
263
|
+
console.warn('Failed to synchronize sessions after switch:', error);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return user;
|
|
268
|
+
} catch (error) {
|
|
269
|
+
const invalidSession = isInvalidSessionError(error);
|
|
270
|
+
|
|
271
|
+
if (invalidSession) {
|
|
272
|
+
updateSessions(sessions.filter((session) => session.sessionId !== sessionId), {
|
|
273
|
+
merge: false,
|
|
274
|
+
});
|
|
275
|
+
if (sessionId === activeSessionId) {
|
|
276
|
+
const otherSessionIds = sessions
|
|
277
|
+
.filter(
|
|
278
|
+
(session) =>
|
|
279
|
+
session.sessionId !== sessionId && !removedSessionsRef.current.has(session.sessionId),
|
|
280
|
+
)
|
|
281
|
+
.map((session) => session.sessionId);
|
|
282
|
+
|
|
283
|
+
const replacementUser = await findReplacementSession(otherSessionIds);
|
|
284
|
+
if (replacementUser) {
|
|
285
|
+
return replacementUser;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
handleAuthError(error, {
|
|
291
|
+
defaultMessage: 'Failed to switch session',
|
|
292
|
+
code: invalidSession ? 'INVALID_SESSION' : 'SESSION_SWITCH_ERROR',
|
|
293
|
+
onError,
|
|
294
|
+
setAuthError,
|
|
295
|
+
logger,
|
|
296
|
+
});
|
|
297
|
+
throw error instanceof Error ? error : new Error('Failed to switch session');
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
[
|
|
301
|
+
activateSession,
|
|
302
|
+
activeSessionId,
|
|
303
|
+
findReplacementSession,
|
|
304
|
+
logger,
|
|
305
|
+
loginSuccess,
|
|
306
|
+
onError,
|
|
307
|
+
oxyServices,
|
|
308
|
+
sessions,
|
|
309
|
+
setAuthError,
|
|
310
|
+
updateSessions,
|
|
311
|
+
],
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const refreshSessions = useCallback(
|
|
315
|
+
async (activeUserId?: string): Promise<void> => {
|
|
316
|
+
if (!activeSessionId) return;
|
|
317
|
+
|
|
318
|
+
if (refreshInFlightRef.current) {
|
|
319
|
+
await refreshInFlightRef.current;
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const now = Date.now();
|
|
324
|
+
if (now - lastRefreshRef.current < 500) {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
lastRefreshRef.current = now;
|
|
328
|
+
|
|
329
|
+
const refreshPromise = (async () => {
|
|
330
|
+
try {
|
|
331
|
+
const deviceSessions = await fetchSessionsWithFallback(oxyServices, activeSessionId, {
|
|
332
|
+
fallbackUserId: activeUserId,
|
|
333
|
+
logger,
|
|
334
|
+
});
|
|
335
|
+
updateSessions(deviceSessions, { merge: true });
|
|
336
|
+
} catch (error) {
|
|
337
|
+
if (isInvalidSessionError(error)) {
|
|
338
|
+
const otherSessions = sessions
|
|
339
|
+
.filter(
|
|
340
|
+
(session) =>
|
|
341
|
+
session.sessionId !== activeSessionId &&
|
|
342
|
+
!removedSessionsRef.current.has(session.sessionId),
|
|
343
|
+
)
|
|
344
|
+
.map((session) => session.sessionId);
|
|
345
|
+
|
|
346
|
+
const replacementUser = await findReplacementSession(otherSessions);
|
|
347
|
+
if (!replacementUser) {
|
|
348
|
+
await clearSessionState();
|
|
349
|
+
}
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
handleAuthError(error, {
|
|
354
|
+
defaultMessage: 'Failed to refresh sessions',
|
|
355
|
+
code: 'SESSION_REFRESH_ERROR',
|
|
356
|
+
onError,
|
|
357
|
+
setAuthError,
|
|
358
|
+
logger,
|
|
359
|
+
});
|
|
360
|
+
} finally {
|
|
361
|
+
refreshInFlightRef.current = null;
|
|
362
|
+
lastRefreshRef.current = Date.now();
|
|
363
|
+
}
|
|
364
|
+
})();
|
|
365
|
+
|
|
366
|
+
refreshInFlightRef.current = refreshPromise;
|
|
367
|
+
await refreshPromise;
|
|
368
|
+
},
|
|
369
|
+
[
|
|
370
|
+
activeSessionId,
|
|
371
|
+
clearSessionState,
|
|
372
|
+
findReplacementSession,
|
|
373
|
+
logger,
|
|
374
|
+
onError,
|
|
375
|
+
oxyServices,
|
|
376
|
+
sessions,
|
|
377
|
+
setAuthError,
|
|
378
|
+
updateSessions,
|
|
379
|
+
],
|
|
380
|
+
);
|
|
381
|
+
|
|
382
|
+
const isRefreshInFlight = Boolean(refreshInFlightRef.current);
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
sessions,
|
|
386
|
+
activeSessionId,
|
|
387
|
+
setActiveSessionId,
|
|
388
|
+
updateSessions,
|
|
389
|
+
switchSession,
|
|
390
|
+
refreshSessions,
|
|
391
|
+
clearSessionState,
|
|
392
|
+
saveActiveSessionId,
|
|
393
|
+
trackRemovedSession,
|
|
394
|
+
storageKeys,
|
|
395
|
+
isRefreshInFlight,
|
|
396
|
+
};
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
|