@oxyhq/services 26.0.1 → 26.0.3
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 +2 -2
- package/android/src/main/java/so/oxy/identity/OxyIdentityStore.kt +21 -127
- package/android/src/main/java/so/oxy/session/OxyBackgroundSession.kt +184 -0
- package/android/src/main/java/so/oxy/session/OxyBackgroundSessionApi.kt +162 -0
- package/android/src/main/java/so/oxy/session/OxyBackgroundSessionModule.kt +116 -0
- package/android/src/main/java/so/oxy/session/OxyBackgroundSessionStore.kt +179 -0
- package/android/src/main/java/so/oxy/session/OxyBackgroundToken.kt +61 -0
- package/android/src/main/java/so/oxy/storage/OxyEncryptedPrefs.kt +241 -0
- package/expo-module.config.json +4 -1
- package/lib/commonjs/index.js +18 -49
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/notifications/deviceNotifications.js +63 -0
- package/lib/commonjs/notifications/deviceNotifications.js.map +1 -1
- package/lib/commonjs/ui/boot/runProviderColdBoot.js +20 -64
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/components/AvatarCameraBadge.js +2 -2
- package/lib/commonjs/ui/components/AvatarCameraBadge.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +4 -4
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js +13 -1
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +29 -24
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +8 -7
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
- package/lib/commonjs/ui/components/ProfileButton.js +20 -26
- package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +68 -58
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js +2 -2
- package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js.map +1 -1
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js +2 -2
- package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/commonjs/ui/components/internal/PinInput.js +3 -1
- package/lib/commonjs/ui/components/internal/PinInput.js.map +1 -1
- package/lib/commonjs/ui/components/logo/LogoText.js +4 -4
- package/lib/commonjs/ui/components/logo/LogoText.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js +2 -2
- package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js.map +1 -1
- package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js +2 -2
- package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js.map +1 -1
- package/lib/commonjs/ui/components/styles/shadow.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +32 -46
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/commitSessionFlow.js +3 -43
- package/lib/commonjs/ui/context/commitSessionFlow.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +15 -2
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/useOxyAccountGraph.js +3 -4
- package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/mutationFactory.js +5 -5
- package/lib/commonjs/ui/hooks/mutations/mutationFactory.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +11 -11
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js +9 -9
- package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/usePaymentQueries.js +3 -2
- package/lib/commonjs/ui/hooks/queries/usePaymentQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/userCache.js +17 -17
- package/lib/commonjs/ui/hooks/queries/userCache.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAssets.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAsyncAction.js +7 -7
- package/lib/commonjs/ui/hooks/useAsyncAction.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAuth.js +8 -4
- package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAvatarPicker.js +5 -5
- package/lib/commonjs/ui/hooks/useAvatarPicker.js.map +1 -1
- package/lib/commonjs/ui/hooks/useOnlineStatus.js +3 -2
- package/lib/commonjs/ui/hooks/useOnlineStatus.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +0 -2
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSettingToggle.js +4 -4
- package/lib/commonjs/ui/hooks/useSettingToggle.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSurfaceHeader.js +68 -11
- package/lib/commonjs/ui/hooks/useSurfaceHeader.js.map +1 -1
- package/lib/commonjs/ui/navigation/accountDialogManager.js +2 -0
- package/lib/commonjs/ui/navigation/accountDialogManager.js.map +1 -1
- package/lib/commonjs/ui/oauth/browserAuthTransport.js +66 -64
- package/lib/commonjs/ui/oauth/browserAuthTransport.js.map +1 -1
- package/lib/commonjs/ui/oauth/oauthPopup.js +47 -0
- package/lib/commonjs/ui/oauth/oauthPopup.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountMembersScreen.js +10 -10
- package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +41 -19
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js +4 -4
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AppInfoScreen.js +9 -9
- package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AvatarCropScreen.js +3 -3
- package/lib/commonjs/ui/screens/AvatarCropScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ChangeAvatarScreen.js +6 -7
- package/lib/commonjs/ui/screens/ChangeAvatarScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ConnectedAppsScreen.js +4 -4
- package/lib/commonjs/ui/screens/ConnectedAppsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/CreateAccountScreen.js +46 -14
- package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +5 -5
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileScreen.js +2 -3
- package/lib/commonjs/ui/screens/EditProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FAQScreen.js +2 -2
- package/lib/commonjs/ui/screens/FAQScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FeedbackScreen.js +90 -89
- package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +20 -20
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FollowersListScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FollowingListScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/HelpSupportScreen.js +7 -7
- package/lib/commonjs/ui/screens/HelpSupportScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/HistoryViewScreen.js +9 -34
- package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +3 -3
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LegalDocumentsScreen.js +3 -3
- package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ManageAccountScreen.js +15 -15
- package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +19 -19
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +15 -15
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js +17 -7
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SavesCollectionsScreen.js +2 -2
- package/lib/commonjs/ui/screens/SavesCollectionsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/UserListScreen.js +13 -7
- package/lib/commonjs/ui/screens/UserListScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +5 -6
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +1 -4
- package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js +34 -24
- package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js +1 -1
- package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js +3 -4
- package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js +1 -1
- package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
- package/lib/commonjs/ui/session/backgroundSession.js +258 -0
- package/lib/commonjs/ui/session/backgroundSession.js.map +1 -0
- package/lib/commonjs/ui/session/index.js +7 -0
- package/lib/commonjs/ui/session/index.js.map +1 -1
- package/lib/commonjs/ui/session/useBackgroundSessionSync.js +81 -0
- package/lib/commonjs/ui/session/useBackgroundSessionSync.js.map +1 -0
- package/lib/commonjs/ui/utils/fileManagement.js +24 -24
- package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
- package/lib/commonjs/ui/utils/netConnectivity.js +28 -0
- package/lib/commonjs/ui/utils/netConnectivity.js.map +1 -0
- package/lib/commonjs/ui/utils/oauthReturn.js +19 -30
- package/lib/commonjs/ui/utils/oauthReturn.js.map +1 -1
- package/lib/commonjs/ui/utils/userUtils.js +27 -28
- package/lib/commonjs/ui/utils/userUtils.js.map +1 -1
- package/lib/module/index.js +20 -8
- package/lib/module/index.js.map +1 -1
- package/lib/module/notifications/deviceNotifications.js +62 -0
- package/lib/module/notifications/deviceNotifications.js.map +1 -1
- package/lib/module/ui/boot/runProviderColdBoot.js +21 -65
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/components/AvatarCameraBadge.js +1 -1
- package/lib/module/ui/components/AvatarCameraBadge.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +1 -1
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/OxyAccountDialogScreen.js +13 -1
- package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +25 -20
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +7 -6
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js.map +1 -1
- package/lib/module/ui/components/ProfileButton.js +12 -18
- package/lib/module/ui/components/ProfileButton.js.map +1 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js +68 -58
- package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/module/ui/components/authChooser/types.js.map +1 -1
- package/lib/module/ui/components/authChooser/useUsernameAvailability.js +1 -1
- package/lib/module/ui/components/authChooser/useUsernameAvailability.js.map +1 -1
- package/lib/module/ui/components/fileManagement/UploadPreview.js +1 -1
- package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
- package/lib/module/ui/components/internal/PinInput.js +3 -1
- package/lib/module/ui/components/internal/PinInput.js.map +1 -1
- package/lib/module/ui/components/logo/LogoText.js +4 -4
- package/lib/module/ui/components/logo/LogoText.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentDetailsStep.js +1 -1
- package/lib/module/ui/components/payment/PaymentDetailsStep.js.map +1 -1
- package/lib/module/ui/components/payment/PaymentSummaryStep.js +2 -2
- package/lib/module/ui/components/payment/PaymentSummaryStep.js.map +1 -1
- package/lib/module/ui/components/styles/shadow.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +35 -49
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/commitSessionFlow.js +3 -42
- package/lib/module/ui/context/commitSessionFlow.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +15 -2
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/useOxyAccountGraph.js +3 -4
- package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/module/ui/hooks/mutations/mutationFactory.js +1 -1
- package/lib/module/ui/hooks/mutations/mutationFactory.js.map +1 -1
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +1 -1
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/module/ui/hooks/mutations/useServicesMutations.js +1 -1
- package/lib/module/ui/hooks/mutations/useServicesMutations.js.map +1 -1
- package/lib/module/ui/hooks/queries/usePaymentQueries.js +3 -2
- package/lib/module/ui/hooks/queries/usePaymentQueries.js.map +1 -1
- package/lib/module/ui/hooks/queries/userCache.js +17 -17
- package/lib/module/ui/hooks/queries/userCache.js.map +1 -1
- package/lib/module/ui/hooks/useAssets.js.map +1 -1
- package/lib/module/ui/hooks/useAsyncAction.js +1 -1
- package/lib/module/ui/hooks/useAsyncAction.js.map +1 -1
- package/lib/module/ui/hooks/useAuth.js +8 -4
- package/lib/module/ui/hooks/useAuth.js.map +1 -1
- package/lib/module/ui/hooks/useAvatarPicker.js +1 -1
- package/lib/module/ui/hooks/useAvatarPicker.js.map +1 -1
- package/lib/module/ui/hooks/useOnlineStatus.js +3 -2
- package/lib/module/ui/hooks/useOnlineStatus.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +0 -2
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/hooks/useSettingToggle.js +1 -1
- package/lib/module/ui/hooks/useSettingToggle.js.map +1 -1
- package/lib/module/ui/hooks/useSurfaceHeader.js +68 -11
- package/lib/module/ui/hooks/useSurfaceHeader.js.map +1 -1
- package/lib/module/ui/navigation/accountDialogManager.js +2 -0
- package/lib/module/ui/navigation/accountDialogManager.js.map +1 -1
- package/lib/module/ui/oauth/browserAuthTransport.js +66 -64
- package/lib/module/ui/oauth/browserAuthTransport.js.map +1 -1
- package/lib/module/ui/oauth/oauthPopup.js +48 -1
- package/lib/module/ui/oauth/oauthPopup.js.map +1 -1
- package/lib/module/ui/screens/AccountMembersScreen.js +1 -1
- package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +37 -15
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountVerificationScreen.js +1 -1
- package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
- package/lib/module/ui/screens/AppInfoScreen.js +1 -1
- package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
- package/lib/module/ui/screens/AvatarCropScreen.js +1 -1
- package/lib/module/ui/screens/AvatarCropScreen.js.map +1 -1
- package/lib/module/ui/screens/ChangeAvatarScreen.js +4 -5
- package/lib/module/ui/screens/ChangeAvatarScreen.js.map +1 -1
- package/lib/module/ui/screens/ConnectedAppsScreen.js +2 -2
- package/lib/module/ui/screens/ConnectedAppsScreen.js.map +1 -1
- package/lib/module/ui/screens/CreateAccountScreen.js +45 -13
- package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/EditProfileFieldScreen.js +4 -4
- package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/module/ui/screens/EditProfileScreen.js +3 -4
- package/lib/module/ui/screens/EditProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/FAQScreen.js +1 -1
- package/lib/module/ui/screens/FAQScreen.js.map +1 -1
- package/lib/module/ui/screens/FeedbackScreen.js +94 -91
- package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/FollowersListScreen.js.map +1 -1
- package/lib/module/ui/screens/FollowingListScreen.js.map +1 -1
- package/lib/module/ui/screens/HelpSupportScreen.js +1 -1
- package/lib/module/ui/screens/HelpSupportScreen.js.map +1 -1
- package/lib/module/ui/screens/HistoryViewScreen.js +5 -30
- package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
- package/lib/module/ui/screens/LanguageSelectorScreen.js +1 -1
- package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/module/ui/screens/LegalDocumentsScreen.js +1 -1
- package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -1
- package/lib/module/ui/screens/ManageAccountScreen.js +3 -3
- package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js +4 -4
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/module/ui/screens/PrivacySettingsScreen.js +11 -11
- package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js +18 -8
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/SavesCollectionsScreen.js +1 -1
- package/lib/module/ui/screens/SavesCollectionsScreen.js.map +1 -1
- package/lib/module/ui/screens/UserListScreen.js +14 -8
- package/lib/module/ui/screens/UserListScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +5 -6
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +1 -4
- package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
- package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js +15 -5
- package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustCenterScreen.js +1 -1
- package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js +4 -5
- package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustRewardsScreen.js +1 -1
- package/lib/module/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
- package/lib/module/ui/session/backgroundSession.js +252 -0
- package/lib/module/ui/session/backgroundSession.js.map +1 -0
- package/lib/module/ui/session/index.js +1 -0
- package/lib/module/ui/session/index.js.map +1 -1
- package/lib/module/ui/session/useBackgroundSessionSync.js +76 -0
- package/lib/module/ui/session/useBackgroundSessionSync.js.map +1 -0
- package/lib/module/ui/utils/fileManagement.js +25 -25
- package/lib/module/ui/utils/fileManagement.js.map +1 -1
- package/lib/module/ui/utils/netConnectivity.js +23 -0
- package/lib/module/ui/utils/netConnectivity.js.map +1 -0
- package/lib/module/ui/utils/oauthReturn.js +20 -30
- package/lib/module/ui/utils/oauthReturn.js.map +1 -1
- package/lib/module/ui/utils/userUtils.js +27 -28
- package/lib/module/ui/utils/userUtils.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +1 -2
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/notifications/deviceNotifications.d.ts +51 -0
- package/lib/typescript/commonjs/notifications/deviceNotifications.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts +11 -30
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/AvatarCameraBadge.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts +7 -0
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts +2 -1
- package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts +3 -0
- package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +6 -0
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts +1 -1
- package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/internal/PinInput.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/payment/types.d.ts +6 -6
- package/lib/typescript/commonjs/ui/components/payment/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts +0 -40
- package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +11 -2
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +26 -7
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +0 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/paymentTypes.d.ts +3 -2
- package/lib/typescript/commonjs/ui/hooks/queries/paymentTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/usePaymentQueries.d.ts +3 -2
- package/lib/typescript/commonjs/ui/hooks/queries/usePaymentQueries.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/userCache.d.ts +2 -2
- package/lib/typescript/commonjs/ui/hooks/queries/userCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAssets.d.ts +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAssets.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAsyncAction.d.ts +2 -2
- package/lib/typescript/commonjs/ui/hooks/useAsyncAction.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts +10 -6
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useOnlineStatus.d.ts +3 -2
- package/lib/typescript/commonjs/ui/hooks/useOnlineStatus.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSurfaceHeader.d.ts +7 -2
- package/lib/typescript/commonjs/ui/hooks/useSurfaceHeader.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts +8 -0
- package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/browserAuthTransport.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/oauthPopup.d.ts +6 -0
- package/lib/typescript/commonjs/ui/oauth/oauthPopup.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/EditProfileScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/FeedbackScreen.d.ts +2 -2
- package/lib/typescript/commonjs/ui/screens/FeedbackScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/FollowersListScreen.d.ts +3 -0
- package/lib/typescript/commonjs/ui/screens/FollowersListScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/FollowingListScreen.d.ts +3 -0
- package/lib/typescript/commonjs/ui/screens/FollowingListScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/HistoryViewScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/UserListScreen.d.ts +7 -0
- package/lib/typescript/commonjs/ui/screens/UserListScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/backgroundSession.d.ts +42 -0
- package/lib/typescript/commonjs/ui/session/backgroundSession.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/session/index.d.ts +1 -0
- package/lib/typescript/commonjs/ui/session/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/useBackgroundSessionSync.d.ts +23 -0
- package/lib/typescript/commonjs/ui/session/useBackgroundSessionSync.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +27 -19
- package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts +5 -5
- package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/netConnectivity.d.ts +17 -0
- package/lib/typescript/commonjs/ui/utils/netConnectivity.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/utils/oauthReturn.d.ts +15 -12
- package/lib/typescript/commonjs/ui/utils/oauthReturn.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/userUtils.d.ts +7 -14
- package/lib/typescript/commonjs/ui/utils/userUtils.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +1 -2
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/notifications/deviceNotifications.d.ts +51 -0
- package/lib/typescript/module/notifications/deviceNotifications.d.ts.map +1 -1
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts +11 -30
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/AvatarCameraBadge.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts +7 -0
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxySignInButton.d.ts +2 -1
- package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ProfileButton.d.ts +3 -0
- package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/types.d.ts +6 -0
- package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts +1 -1
- package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/internal/PinInput.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/payment/types.d.ts +6 -6
- package/lib/typescript/module/ui/components/payment/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/commitSessionFlow.d.ts +0 -40
- package/lib/typescript/module/ui/context/commitSessionFlow.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +11 -2
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +26 -7
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +0 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/paymentTypes.d.ts +3 -2
- package/lib/typescript/module/ui/hooks/queries/paymentTypes.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/usePaymentQueries.d.ts +3 -2
- package/lib/typescript/module/ui/hooks/queries/usePaymentQueries.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/userCache.d.ts +2 -2
- package/lib/typescript/module/ui/hooks/queries/userCache.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAssets.d.ts +1 -1
- package/lib/typescript/module/ui/hooks/useAssets.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAsyncAction.d.ts +2 -2
- package/lib/typescript/module/ui/hooks/useAsyncAction.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAuth.d.ts +10 -6
- package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useOnlineStatus.d.ts +3 -2
- package/lib/typescript/module/ui/hooks/useOnlineStatus.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSurfaceHeader.d.ts +7 -2
- package/lib/typescript/module/ui/hooks/useSurfaceHeader.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts +8 -0
- package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/browserAuthTransport.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/oauthPopup.d.ts +6 -0
- package/lib/typescript/module/ui/oauth/oauthPopup.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/EditProfileScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/FeedbackScreen.d.ts +2 -2
- package/lib/typescript/module/ui/screens/FeedbackScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/FollowersListScreen.d.ts +3 -0
- package/lib/typescript/module/ui/screens/FollowersListScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/FollowingListScreen.d.ts +3 -0
- package/lib/typescript/module/ui/screens/FollowingListScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/HistoryViewScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/UserListScreen.d.ts +7 -0
- package/lib/typescript/module/ui/screens/UserListScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/trustTier.d.ts +1 -1
- package/lib/typescript/module/ui/screens/trust/trustTier.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/backgroundSession.d.ts +42 -0
- package/lib/typescript/module/ui/session/backgroundSession.d.ts.map +1 -0
- package/lib/typescript/module/ui/session/index.d.ts +1 -0
- package/lib/typescript/module/ui/session/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/useBackgroundSessionSync.d.ts +23 -0
- package/lib/typescript/module/ui/session/useBackgroundSessionSync.d.ts.map +1 -0
- package/lib/typescript/module/ui/types/navigation.d.ts +27 -19
- package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/fileManagement.d.ts +5 -5
- package/lib/typescript/module/ui/utils/fileManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/netConnectivity.d.ts +17 -0
- package/lib/typescript/module/ui/utils/netConnectivity.d.ts.map +1 -0
- package/lib/typescript/module/ui/utils/oauthReturn.d.ts +15 -12
- package/lib/typescript/module/ui/utils/oauthReturn.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/userUtils.d.ts +7 -14
- package/lib/typescript/module/ui/utils/userUtils.d.ts.map +1 -1
- package/lib/typescript/types/expo-image-picker.d.ts +35 -0
- package/package.json +33 -12
- package/src/index.ts +19 -16
- package/src/notifications/deviceNotifications.ts +82 -0
- package/src/types/expo-image-picker.d.ts +35 -0
- package/src/ui/boot/__tests__/coldBootNoIdpRedirect.test.ts +215 -0
- package/src/ui/boot/runProviderColdBoot.ts +24 -101
- package/src/ui/components/AvatarCameraBadge.tsx +2 -1
- package/src/ui/components/FollowButton.tsx +1 -1
- package/src/ui/components/OxyAccountDialogScreen.tsx +13 -1
- package/src/ui/components/OxyAuthChooser.tsx +31 -20
- package/src/ui/components/OxyProvider.tsx +8 -7
- package/src/ui/components/OxySignInButton.tsx +2 -1
- package/src/ui/components/ProfileButton.tsx +15 -20
- package/src/ui/components/authChooser/AccountsMenuView.tsx +66 -57
- package/src/ui/components/authChooser/types.ts +6 -0
- package/src/ui/components/authChooser/useUsernameAvailability.ts +1 -1
- package/src/ui/components/fileManagement/UploadPreview.tsx +1 -1
- package/src/ui/components/internal/PinInput.tsx +1 -0
- package/src/ui/components/logo/LogoText.tsx +4 -4
- package/src/ui/components/payment/PaymentDetailsStep.tsx +1 -1
- package/src/ui/components/payment/PaymentSummaryStep.tsx +2 -2
- package/src/ui/components/payment/types.ts +24 -24
- package/src/ui/components/styles/shadow.tsx +2 -2
- package/src/ui/context/OxyContext.tsx +35 -46
- package/src/ui/context/__tests__/commitSessionFlow.test.ts +13 -89
- package/src/ui/context/commitSessionFlow.ts +3 -61
- package/src/ui/context/hooks/useAuthOperations.ts +26 -1
- package/src/ui/context/oxyContextTypes.ts +26 -7
- package/src/ui/context/useOxyAccountGraph.ts +4 -4
- package/src/ui/hooks/mutations/mutationFactory.ts +1 -1
- package/src/ui/hooks/mutations/useAccountMutations.ts +1 -1
- package/src/ui/hooks/mutations/useServicesMutations.ts +1 -1
- package/src/ui/hooks/queries/paymentTypes.ts +3 -2
- package/src/ui/hooks/queries/usePaymentQueries.ts +3 -2
- package/src/ui/hooks/queries/userCache.ts +169 -148
- package/src/ui/hooks/useAssets.ts +1 -1
- package/src/ui/hooks/useAsyncAction.ts +3 -3
- package/src/ui/hooks/useAuth.ts +12 -6
- package/src/ui/hooks/useAvatarPicker.ts +1 -1
- package/src/ui/hooks/useOnlineStatus.ts +3 -2
- package/src/ui/hooks/useSessionManagement.ts +0 -2
- package/src/ui/hooks/useSettingToggle.ts +1 -1
- package/src/ui/hooks/useSurfaceHeader.ts +90 -15
- package/src/ui/navigation/accountDialogManager.ts +9 -0
- package/src/ui/oauth/__tests__/oauthPopup.test.ts +26 -0
- package/src/ui/oauth/browserAuthTransport.ts +55 -49
- package/src/ui/oauth/oauthPopup.ts +61 -3
- package/src/ui/screens/AccountMembersScreen.tsx +1 -1
- package/src/ui/screens/AccountSettingsScreen.tsx +45 -17
- package/src/ui/screens/AccountVerificationScreen.tsx +1 -1
- package/src/ui/screens/AppInfoScreen.tsx +1 -1
- package/src/ui/screens/AvatarCropScreen.tsx +1 -1
- package/src/ui/screens/ChangeAvatarScreen.tsx +7 -4
- package/src/ui/screens/ConnectedAppsScreen.tsx +2 -2
- package/src/ui/screens/CreateAccountScreen.tsx +55 -15
- package/src/ui/screens/EditProfileFieldScreen.tsx +4 -4
- package/src/ui/screens/EditProfileScreen.tsx +6 -3
- package/src/ui/screens/FAQScreen.tsx +1 -1
- package/src/ui/screens/FeedbackScreen.tsx +741 -575
- package/src/ui/screens/FileManagementScreen.tsx +1 -1
- package/src/ui/screens/FollowersListScreen.tsx +3 -0
- package/src/ui/screens/FollowingListScreen.tsx +3 -0
- package/src/ui/screens/HelpSupportScreen.tsx +1 -1
- package/src/ui/screens/HistoryViewScreen.tsx +5 -20
- package/src/ui/screens/LanguageSelectorScreen.tsx +1 -1
- package/src/ui/screens/LegalDocumentsScreen.tsx +1 -1
- package/src/ui/screens/ManageAccountScreen.tsx +9 -2
- package/src/ui/screens/PremiumSubscriptionScreen.tsx +4 -4
- package/src/ui/screens/PrivacySettingsScreen.tsx +15 -12
- package/src/ui/screens/ProfileScreen.tsx +24 -6
- package/src/ui/screens/SavesCollectionsScreen.tsx +1 -1
- package/src/ui/screens/UserListScreen.tsx +18 -8
- package/src/ui/screens/WelcomeNewUserScreen.tsx +6 -6
- package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +1 -1
- package/src/ui/screens/fileManagement/hooks/useFileUploadState.ts +14 -5
- package/src/ui/screens/trust/TrustCenterScreen.tsx +2 -2
- package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +6 -5
- package/src/ui/screens/trust/TrustRewardsScreen.tsx +1 -1
- package/src/ui/screens/trust/TrustRulesScreen.tsx +1 -1
- package/src/ui/screens/trust/trustTier.ts +1 -1
- package/src/ui/session/__tests__/backgroundSession.test.ts +392 -0
- package/src/ui/session/backgroundSession.ts +313 -0
- package/src/ui/session/index.ts +1 -0
- package/src/ui/session/useBackgroundSessionSync.ts +92 -0
- package/src/ui/types/navigation.ts +27 -19
- package/src/ui/utils/fileManagement.ts +108 -92
- package/src/ui/utils/netConnectivity.ts +27 -0
- package/src/ui/utils/oauthReturn.ts +23 -33
- package/src/ui/utils/userUtils.ts +27 -32
- package/lib/commonjs/ui/oauth/legacyRedirectLanes.js +0 -48
- package/lib/commonjs/ui/oauth/legacyRedirectLanes.js.map +0 -1
- package/lib/commonjs/ui/utils/crossOriginRestore.js +0 -140
- package/lib/commonjs/ui/utils/crossOriginRestore.js.map +0 -1
- package/lib/module/ui/oauth/legacyRedirectLanes.js +0 -44
- package/lib/module/ui/oauth/legacyRedirectLanes.js.map +0 -1
- package/lib/module/ui/utils/crossOriginRestore.js +0 -131
- package/lib/module/ui/utils/crossOriginRestore.js.map +0 -1
- package/lib/typescript/commonjs/ui/oauth/legacyRedirectLanes.d.ts +0 -40
- package/lib/typescript/commonjs/ui/oauth/legacyRedirectLanes.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/utils/crossOriginRestore.d.ts +0 -43
- package/lib/typescript/commonjs/ui/utils/crossOriginRestore.d.ts.map +0 -1
- package/lib/typescript/module/ui/oauth/legacyRedirectLanes.d.ts +0 -40
- package/lib/typescript/module/ui/oauth/legacyRedirectLanes.d.ts.map +0 -1
- package/lib/typescript/module/ui/utils/crossOriginRestore.d.ts +0 -43
- package/lib/typescript/module/ui/utils/crossOriginRestore.d.ts.map +0 -1
- package/src/ui/boot/__tests__/coldBootWebAuthMode.test.ts +0 -281
- package/src/ui/oauth/legacyRedirectLanes.ts +0 -43
- package/src/ui/utils/__tests__/crossOriginRestore.test.ts +0 -143
- package/src/ui/utils/crossOriginRestore.ts +0 -169
package/README.md
CHANGED
|
@@ -455,7 +455,7 @@ function LoginScreen() {
|
|
|
455
455
|
```
|
|
456
456
|
|
|
457
457
|
- **Official Oxy apps** (`isOfficial` / first-party types): opens the in-app account dialog.
|
|
458
|
-
- **Third-party apps** (`type: 'third_party'`): starts the standard OAuth 2.0 Authorization Code + PKCE flow against `auth.oxy.so` (the SDK generates `state` + PKCE via `@oxyhq/core`). On web the transport is `OxyProvider` prop `webAuthMode: 'popup' | 'redirect'` (default `'
|
|
458
|
+
- **Third-party apps** (`type: 'third_party'`): starts the standard OAuth 2.0 Authorization Code + PKCE flow against `auth.oxy.so` (the SDK generates `state` + PKCE via `@oxyhq/core`). On web the transport is `OxyProvider` prop `webAuthMode: 'popup' | 'redirect'` (default `'popup'`; issue #691 Phases 2/7b) — `'popup'` opens a small window and relays the result via `postMessage` without navigating your app's tab, falling back to a redirect if the browser blocks it. Pass `oauthRedirectUri`; on native handle `onOAuthResult` to complete the token exchange.
|
|
459
459
|
|
|
460
460
|
See the [integration guide](../../docs/auth/integration-guide.md) for Console registration, OAuth endpoints, and backend verification, and [AUTHENTICATION.md](../../docs/AUTHENTICATION.md) for the full model.
|
|
461
461
|
|
|
@@ -694,7 +694,7 @@ Typed returns are defined in `ui/hooks/queries/paymentTypes.ts` (`Subscription`,
|
|
|
694
694
|
|
|
695
695
|
## Sign-In Token Planting
|
|
696
696
|
|
|
697
|
-
`@oxyhq/core` `OxyServices.verifyChallenge()` plants `setTokens(accessToken
|
|
697
|
+
`@oxyhq/core` `OxyServices.verifyChallenge()` plants `setTokens(accessToken)` internally before returning. `useAuthOperations.performSignIn` no longer needs to hand-plant the token or call a session-token fallback — just await `verifyChallenge` and proceed.
|
|
698
698
|
|
|
699
699
|
## Requirements
|
|
700
700
|
|
|
@@ -2,12 +2,8 @@ package so.oxy.identity
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.content.SharedPreferences
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import androidx.security.crypto.MasterKey
|
|
8
|
-
import java.io.IOException
|
|
9
|
-
import java.security.GeneralSecurityException
|
|
10
|
-
import java.security.KeyStore
|
|
5
|
+
import so.oxy.storage.OxyEncryptedPrefs
|
|
6
|
+
import so.oxy.storage.RecoveryPolicy
|
|
11
7
|
|
|
12
8
|
/**
|
|
13
9
|
* Shared accessor for the hardware-backed EncryptedSharedPreferences that holds
|
|
@@ -15,137 +11,35 @@ import java.security.KeyStore
|
|
|
15
11
|
*
|
|
16
12
|
* Used by BOTH [OxyIdentityModule] (the JS bridge / local read + write) and
|
|
17
13
|
* [OxyIdentityProvider] (the cross-process read surface) so the store name and
|
|
18
|
-
* the
|
|
14
|
+
* the encryption scheme can never drift between the two halves.
|
|
19
15
|
*
|
|
20
|
-
*
|
|
16
|
+
* Opening the file — the single-memoized-instance rule and the two-stage keyset
|
|
17
|
+
* self-heal, both load-bearing and non-obvious — lives in [OxyEncryptedPrefs],
|
|
18
|
+
* which is shared with the background-session store. Read its documentation
|
|
19
|
+
* before changing anything about how this file is opened.
|
|
21
20
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* lazily, under a lock, keyed on the process-global application context, and
|
|
29
|
-
* reused for all reads/writes/provider calls.
|
|
30
|
-
*
|
|
31
|
-
* ## Keyset self-heal (CRITICAL)
|
|
32
|
-
*
|
|
33
|
-
* The androidx master key that wraps this file's Tink keyset lives under the
|
|
34
|
-
* UID-scoped default alias [MasterKey.DEFAULT_MASTER_KEY_ALIAS] — one entry for
|
|
35
|
-
* the whole `so.oxy.shared` UID. When a NEW package joins that shared UID, that
|
|
36
|
-
* master key can be rotated/regenerated, leaving the keyset already written into
|
|
37
|
-
* `oxy_shared_identity.xml` wrapped under the OLD key. `create()` then fails GCM
|
|
38
|
-
* verification (`AEADBadTag` -> `GeneralSecurityException`, or an unreadable
|
|
39
|
-
* keyset -> `IOException`) on EVERY read/write, and `EncryptedSharedPreferences`
|
|
40
|
-
* never self-heals — the slot stays permanently dead and cross-app "Sign in with
|
|
41
|
-
* Oxy" silently falls back to interactive import.
|
|
42
|
-
*
|
|
43
|
-
* [openOrHealPrefs] recovers ONCE, in two bounded stages (no retry loop):
|
|
44
|
-
* 1. Delete ONLY this slot's prefs file (the stale wrapped keyset) and rebuild
|
|
45
|
-
* against the current master key — heals the common rotation case without
|
|
46
|
-
* touching the master key, so other shared-UID members keep their keysets.
|
|
47
|
-
* 2. If a fresh keyset STILL can't be built, the master key itself is unusable:
|
|
48
|
-
* delete its keystore alias so a new one is generated, wipe the file again,
|
|
49
|
-
* and rebuild. If that final rebuild throws, the exception propagates and the
|
|
50
|
-
* existing `runCatching {}` at every call site degrades to null.
|
|
51
|
-
*
|
|
52
|
-
* This NEVER touches the primary self-custody identity: that lives in
|
|
53
|
-
* expo-secure-store under a DISTINCT prefs file ("SecureStore") and DISTINCT
|
|
54
|
-
* keystore aliases ("key_v1"/extended), not the androidx master key wiped here.
|
|
55
|
-
* The shared slot is a derived copy that Commons re-populates from the primary on
|
|
56
|
-
* the next boot (`migrateToSharedIdentity`), so wiping it is non-destructive.
|
|
21
|
+
* One consequence worth restating here: an open can legitimately return an EMPTY
|
|
22
|
+
* file (the self-heal wipes a keyset it cannot read). That is safe for this slot
|
|
23
|
+
* specifically because it is a DERIVED copy — Commons re-populates it from the
|
|
24
|
+
* primary self-custody identity on the next boot (`migrateToSharedIdentity`), and
|
|
25
|
+
* that primary lives in expo-secure-store under a different file and different
|
|
26
|
+
* keystore aliases, untouched by any recovery here.
|
|
57
27
|
*/
|
|
58
28
|
internal object OxyIdentityStore {
|
|
59
29
|
const val PREFS_NAME = "oxy_shared_identity"
|
|
60
30
|
const val KEY_PRIVATE = "priv"
|
|
61
31
|
const val KEY_PUBLIC = "pub"
|
|
62
32
|
|
|
63
|
-
private const val TAG = "OxyIdentityStore"
|
|
64
|
-
private const val ANDROID_KEYSTORE = "AndroidKeyStore"
|
|
65
|
-
|
|
66
|
-
@Volatile private var cachedPrefs: SharedPreferences? = null
|
|
67
|
-
|
|
68
|
-
private fun prefs(context: Context): SharedPreferences {
|
|
69
|
-
cachedPrefs?.let { return it }
|
|
70
|
-
return synchronized(this) {
|
|
71
|
-
cachedPrefs ?: openOrHealPrefs(context.applicationContext).also { cachedPrefs = it }
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
33
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
34
|
+
* [RecoveryPolicy.RegenerateSharedMasterKey] preserves this store's original
|
|
35
|
+
* behaviour: when its keyset cannot be rebuilt, the master key is regenerated.
|
|
36
|
+
* That is defensible HERE and nowhere else so far, because this slot going dead
|
|
37
|
+
* already breaks cross-app "Sign in with Oxy" for every Oxy app on the device —
|
|
38
|
+
* the collateral is not worse than the failure it recovers from — and because
|
|
39
|
+
* this slot is itself re-populated from the primary identity on the next boot.
|
|
80
40
|
*/
|
|
81
|
-
private fun
|
|
82
|
-
|
|
83
|
-
buildEncryptedPrefs(appContext)
|
|
84
|
-
} catch (corrupt: GeneralSecurityException) {
|
|
85
|
-
healCorruptedKeyset(appContext, corrupt)
|
|
86
|
-
} catch (corrupt: IOException) {
|
|
87
|
-
healCorruptedKeyset(appContext, corrupt)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Stage 1: wipe ONLY this slot's prefs file (which holds the stale wrapped
|
|
93
|
-
* keyset) and rebuild against the current master key. Escalates to stage 2 if a
|
|
94
|
-
* fresh keyset still can't be built.
|
|
95
|
-
*/
|
|
96
|
-
private fun healCorruptedKeyset(appContext: Context, cause: Exception): SharedPreferences {
|
|
97
|
-
Log.w(
|
|
98
|
-
TAG,
|
|
99
|
-
"shared-identity keyset unreadable (rotated master key); cleared and regenerated: ${cause.message}",
|
|
100
|
-
cause
|
|
101
|
-
)
|
|
102
|
-
appContext.deleteSharedPreferences(PREFS_NAME)
|
|
103
|
-
return try {
|
|
104
|
-
buildEncryptedPrefs(appContext)
|
|
105
|
-
} catch (stillCorrupt: GeneralSecurityException) {
|
|
106
|
-
regenerateMasterKeyAndRebuild(appContext, stillCorrupt)
|
|
107
|
-
} catch (stillCorrupt: IOException) {
|
|
108
|
-
regenerateMasterKeyAndRebuild(appContext, stillCorrupt)
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Stage 2 (belt-and-suspenders): the androidx master key itself is unusable, so
|
|
114
|
-
* delete its keystore alias to force a fresh one, wipe the now-stale file again,
|
|
115
|
-
* and rebuild. The alias is used ONLY by androidx EncryptedSharedPreferences —
|
|
116
|
-
* expo-secure-store (the primary identity) uses its own distinct aliases — so
|
|
117
|
-
* this never touches the self-custody key. A throw here propagates to the
|
|
118
|
-
* call-site `runCatching {}`, which degrades to null.
|
|
119
|
-
*/
|
|
120
|
-
private fun regenerateMasterKeyAndRebuild(appContext: Context, cause: Exception): SharedPreferences {
|
|
121
|
-
Log.w(
|
|
122
|
-
TAG,
|
|
123
|
-
"shared-identity keyset still unreadable after prefs reset; deleting master key " +
|
|
124
|
-
"'${MasterKey.DEFAULT_MASTER_KEY_ALIAS}' and regenerating: ${cause.message}",
|
|
125
|
-
cause
|
|
126
|
-
)
|
|
127
|
-
KeyStore.getInstance(ANDROID_KEYSTORE).apply {
|
|
128
|
-
load(null)
|
|
129
|
-
if (containsAlias(MasterKey.DEFAULT_MASTER_KEY_ALIAS)) {
|
|
130
|
-
deleteEntry(MasterKey.DEFAULT_MASTER_KEY_ALIAS)
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
appContext.deleteSharedPreferences(PREFS_NAME)
|
|
134
|
-
return buildEncryptedPrefs(appContext)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
private fun buildEncryptedPrefs(appContext: Context): SharedPreferences {
|
|
138
|
-
val masterKey = MasterKey.Builder(appContext)
|
|
139
|
-
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
|
140
|
-
.build()
|
|
141
|
-
return EncryptedSharedPreferences.create(
|
|
142
|
-
appContext,
|
|
143
|
-
PREFS_NAME,
|
|
144
|
-
masterKey,
|
|
145
|
-
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
146
|
-
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
147
|
-
)
|
|
148
|
-
}
|
|
41
|
+
private fun prefs(context: Context): SharedPreferences =
|
|
42
|
+
OxyEncryptedPrefs.open(context, PREFS_NAME, RecoveryPolicy.RegenerateSharedMasterKey)
|
|
149
43
|
|
|
150
44
|
/** Read the stored keypair as (privateKey, publicKey), or null when absent/blank. */
|
|
151
45
|
fun read(context: Context): Pair<String, String>? {
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
package so.oxy.session
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import java.util.concurrent.TimeUnit
|
|
6
|
+
import kotlinx.coroutines.sync.Mutex
|
|
7
|
+
import kotlinx.coroutines.sync.withLock
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* How native background code — a WorkManager worker refreshing a home-screen
|
|
11
|
+
* widget, a sync job — authenticates when there is no JS runtime in the process.
|
|
12
|
+
*
|
|
13
|
+
* ## What this is
|
|
14
|
+
*
|
|
15
|
+
* The app, while running, provisions a background credential and hands it to this
|
|
16
|
+
* store (see `OxyBackgroundSessionModule`). Background code then exchanges it for
|
|
17
|
+
* a short access token whenever it needs one. That is the whole surface:
|
|
18
|
+
*
|
|
19
|
+
* ```kotlin
|
|
20
|
+
* when (val token = OxyBackgroundSession.accessToken(applicationContext)) {
|
|
21
|
+
* is OxyBackgroundToken.Available -> fetch(token.baseUrl, token.accessToken, token.accountId)
|
|
22
|
+
* is OxyBackgroundToken.SignedOut -> renderSignedOut() // final; Result.success()
|
|
23
|
+
* is OxyBackgroundToken.Transient -> Result.retry() // keep last content
|
|
24
|
+
* is OxyBackgroundToken.Malformed -> Result.failure() // wire contract moved
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* ## What it deliberately cannot do
|
|
29
|
+
*
|
|
30
|
+
* It cannot create a session. Only the app, holding a bearer, can provision a
|
|
31
|
+
* credential — so background code can EXTEND a session the user established
|
|
32
|
+
* in-app but can never bootstrap one from nothing.
|
|
33
|
+
*
|
|
34
|
+
* It cannot damage the app's session. It never reads or writes the device
|
|
35
|
+
* credential, and the credential it does use is not rotated by anyone, so a
|
|
36
|
+
* worker killed mid-mint loses nothing. That property is the reason this exists
|
|
37
|
+
* as a separate credential instead of background code simply reading the device
|
|
38
|
+
* secret.
|
|
39
|
+
*
|
|
40
|
+
* It does not narrow authority. The minted token is a FULL user access token:
|
|
41
|
+
* access tokens carry no scopes today, so this yields exactly what the device
|
|
42
|
+
* credential yields while it is valid. The gains are isolation, a bounded
|
|
43
|
+
* lifetime and independent revocability — NOT "read-only". Do not read
|
|
44
|
+
* "background" as a permission boundary.
|
|
45
|
+
*
|
|
46
|
+
* ## Account correctness
|
|
47
|
+
*
|
|
48
|
+
* Every mint is re-authorized server-side against the live device session, so a
|
|
49
|
+
* stale record cannot resurrect a signed-out account. [OxyBackgroundToken.Available.accountId]
|
|
50
|
+
* is the server's answer, not a local guess — key cached content by it and drop
|
|
51
|
+
* content belonging to another account.
|
|
52
|
+
*/
|
|
53
|
+
object OxyBackgroundSession {
|
|
54
|
+
private const val TAG = "OxyBackgroundSession"
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Refuse to reuse a cached token this close to expiry, so it cannot die midway
|
|
58
|
+
* through the request it was fetched for.
|
|
59
|
+
*/
|
|
60
|
+
private val TOKEN_REUSE_FLOOR_MS = TimeUnit.SECONDS.toMillis(60)
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Serializes mints within a process so several widgets refreshing together make
|
|
64
|
+
* ONE network call and share its result, instead of one call each — which would
|
|
65
|
+
* also burn the server's per-device rate limit. Waiters re-check the cache after
|
|
66
|
+
* acquiring, so only the first actually mints.
|
|
67
|
+
*/
|
|
68
|
+
private val mintMutex = Mutex()
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* A usable access token for the account this device's app last provisioned, or
|
|
72
|
+
* the reason there is not one. Never throws.
|
|
73
|
+
*/
|
|
74
|
+
suspend fun accessToken(context: Context): OxyBackgroundToken {
|
|
75
|
+
val appContext = context.applicationContext
|
|
76
|
+
val credential = readCredential(appContext) ?: return OxyBackgroundToken.SignedOut
|
|
77
|
+
|
|
78
|
+
cachedToken(appContext, credential)?.let { return it }
|
|
79
|
+
|
|
80
|
+
return mintMutex.withLock {
|
|
81
|
+
// Another caller may have minted while this one waited for the lock.
|
|
82
|
+
cachedToken(appContext, credential)?.let { return@withLock it }
|
|
83
|
+
mintLocked(appContext, credential)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The account a background caller is currently provisioned for, without a
|
|
89
|
+
* network call — for keying cached content, or deciding whether cached content
|
|
90
|
+
* belongs to the current account at all. Null when there is no credential.
|
|
91
|
+
*/
|
|
92
|
+
fun activeAccountId(context: Context): String? =
|
|
93
|
+
runCatching { OxyBackgroundSessionStore.readAccountId(context.applicationContext) }
|
|
94
|
+
.getOrNull()
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Whether a credential is present, so a widget can draw its signed-out state
|
|
98
|
+
* without attempting a fetch. A `true` here is not a promise that the next mint
|
|
99
|
+
* succeeds — the server is still the authority.
|
|
100
|
+
*/
|
|
101
|
+
fun hasCredential(context: Context): Boolean = activeAccountId(context) != null
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Read the credential, degrading to null on a storage failure.
|
|
105
|
+
*
|
|
106
|
+
* The keystore can be transiently unavailable (direct-boot, a device being
|
|
107
|
+
* upgraded), and [so.oxy.storage.OxyEncryptedPrefs] can legitimately hand back
|
|
108
|
+
* an emptied file after healing an unreadable keyset. Both mean "no credential
|
|
109
|
+
* right now" rather than a crash in a background worker.
|
|
110
|
+
*/
|
|
111
|
+
private fun readCredential(appContext: Context): OxyBackgroundSessionStore.Credential? =
|
|
112
|
+
runCatching { OxyBackgroundSessionStore.readCredential(appContext) }
|
|
113
|
+
.onFailure { Log.w(TAG, "background credential store unreadable", it) }
|
|
114
|
+
.getOrNull()
|
|
115
|
+
|
|
116
|
+
private fun cachedToken(
|
|
117
|
+
appContext: Context,
|
|
118
|
+
credential: OxyBackgroundSessionStore.Credential,
|
|
119
|
+
): OxyBackgroundToken.Available? {
|
|
120
|
+
val cached = runCatching {
|
|
121
|
+
OxyBackgroundSessionStore.readToken(appContext, credential.accountId, TOKEN_REUSE_FLOOR_MS)
|
|
122
|
+
}.getOrNull() ?: return null
|
|
123
|
+
return OxyBackgroundToken.Available(
|
|
124
|
+
accessToken = cached.accessToken,
|
|
125
|
+
expiresAt = cached.expiresAt,
|
|
126
|
+
accountId = cached.accountId,
|
|
127
|
+
baseUrl = credential.baseUrl,
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Mint and map the outcome. Called with [mintMutex] held.
|
|
133
|
+
*
|
|
134
|
+
* Only [OxyBackgroundSessionApi.MintOutcome.Revoked] — an explicit server
|
|
135
|
+
* verdict — clears the record. Transient and malformed outcomes KEEP it, so a
|
|
136
|
+
* deploy window or a contract change can never sign a user out of their
|
|
137
|
+
* widgets.
|
|
138
|
+
*/
|
|
139
|
+
private suspend fun mintLocked(
|
|
140
|
+
appContext: Context,
|
|
141
|
+
credential: OxyBackgroundSessionStore.Credential,
|
|
142
|
+
): OxyBackgroundToken =
|
|
143
|
+
when (val outcome = OxyBackgroundSessionApi.mint(credential)) {
|
|
144
|
+
is OxyBackgroundSessionApi.MintOutcome.Minted -> {
|
|
145
|
+
runCatching {
|
|
146
|
+
OxyBackgroundSessionStore.writeToken(
|
|
147
|
+
appContext,
|
|
148
|
+
OxyBackgroundSessionStore.CachedToken(
|
|
149
|
+
accessToken = outcome.accessToken,
|
|
150
|
+
expiresAt = outcome.expiresAt,
|
|
151
|
+
accountId = outcome.accountId,
|
|
152
|
+
),
|
|
153
|
+
)
|
|
154
|
+
}.onFailure {
|
|
155
|
+
// Caching is an optimization; a failure to store costs one extra mint
|
|
156
|
+
// next time and must not fail the token the caller is waiting for.
|
|
157
|
+
Log.w(TAG, "could not cache the minted background token", it)
|
|
158
|
+
}
|
|
159
|
+
OxyBackgroundToken.Available(
|
|
160
|
+
accessToken = outcome.accessToken,
|
|
161
|
+
expiresAt = outcome.expiresAt,
|
|
162
|
+
accountId = outcome.accountId,
|
|
163
|
+
baseUrl = credential.baseUrl,
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
OxyBackgroundSessionApi.MintOutcome.Revoked -> {
|
|
168
|
+
Log.i(TAG, "background credential was revoked by the server; clearing it")
|
|
169
|
+
runCatching { OxyBackgroundSessionStore.clear(appContext) }
|
|
170
|
+
.onFailure { Log.w(TAG, "could not clear the revoked background credential", it) }
|
|
171
|
+
OxyBackgroundToken.SignedOut
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
is OxyBackgroundSessionApi.MintOutcome.Transient -> {
|
|
175
|
+
Log.w(TAG, "background token mint failed transiently; keeping the credential", outcome.cause)
|
|
176
|
+
OxyBackgroundToken.Transient(outcome.cause)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
is OxyBackgroundSessionApi.MintOutcome.Malformed -> {
|
|
180
|
+
Log.e(TAG, "background token response could not be read; keeping the credential", outcome.cause)
|
|
181
|
+
OxyBackgroundToken.Malformed(outcome.cause)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
package so.oxy.session
|
|
2
|
+
|
|
3
|
+
import kotlinx.coroutines.Dispatchers
|
|
4
|
+
import kotlinx.coroutines.withContext
|
|
5
|
+
import org.json.JSONException
|
|
6
|
+
import org.json.JSONObject
|
|
7
|
+
import java.io.IOException
|
|
8
|
+
import java.net.HttpURLConnection
|
|
9
|
+
import java.net.URL
|
|
10
|
+
import java.text.ParseException
|
|
11
|
+
import java.text.SimpleDateFormat
|
|
12
|
+
import java.util.Locale
|
|
13
|
+
import java.util.TimeZone
|
|
14
|
+
import java.util.concurrent.TimeUnit
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The one network call background code makes: `POST /session/device/background-token`.
|
|
18
|
+
*
|
|
19
|
+
* Bearer-less and cookie-less — possession of the provisioned secret IS the
|
|
20
|
+
* proof. Unlike `POST /session/device/token` this NEVER rotates: the presented
|
|
21
|
+
* secret stays valid until it expires or is revoked, which is what makes it safe
|
|
22
|
+
* to call from a process the OS may kill at any moment.
|
|
23
|
+
*
|
|
24
|
+
* `HttpURLConnection` rather than an HTTP client library, for the same reason
|
|
25
|
+
* Mention's widget uses it: this is one small POST, and adding an HTTP stack to
|
|
26
|
+
* every consuming app's release APK would cost more than it saves.
|
|
27
|
+
*/
|
|
28
|
+
internal object OxyBackgroundSessionApi {
|
|
29
|
+
private val CONNECT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10).toInt()
|
|
30
|
+
private val READ_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(15).toInt()
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The two server verdicts that PROVE the credential is finished, and the only
|
|
34
|
+
* two that may delete it.
|
|
35
|
+
*
|
|
36
|
+
* This mirrors `@oxyhq/core`'s `refresh.ts` rule deliberately, and for the same
|
|
37
|
+
* hard-won reason: ANY other 401 — a proxy or middleware 401, an ALB answering
|
|
38
|
+
* for a starting instance, an HTML error page during a deploy — is NOT proof
|
|
39
|
+
* that a credential is bad. Treating an ambiguous 401 as revocation is what
|
|
40
|
+
* logged users out ecosystem-wide once already. Here the blast radius is
|
|
41
|
+
* smaller (a widget going blank rather than a session dying) but the rule is
|
|
42
|
+
* the same, so the failure mode reads identically to anyone debugging it.
|
|
43
|
+
*
|
|
44
|
+
* These strings are wire contract with oxy-api, not log text.
|
|
45
|
+
*/
|
|
46
|
+
private const val ERROR_CREDENTIAL_INVALID = "background_credential_invalid"
|
|
47
|
+
private const val ERROR_ACCOUNT_NOT_ON_DEVICE = "account_not_on_device"
|
|
48
|
+
|
|
49
|
+
internal sealed interface MintOutcome {
|
|
50
|
+
data class Minted(val accessToken: String, val expiresAt: Long, val accountId: String) : MintOutcome
|
|
51
|
+
/** An explicit server verdict: this credential is finished. Delete it. */
|
|
52
|
+
data object Revoked : MintOutcome
|
|
53
|
+
data class Transient(val cause: Exception) : MintOutcome
|
|
54
|
+
data class Malformed(val cause: Exception) : MintOutcome
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
suspend fun mint(credential: OxyBackgroundSessionStore.Credential): MintOutcome =
|
|
58
|
+
withContext(Dispatchers.IO) {
|
|
59
|
+
val endpoint = "${credential.baseUrl.trimEnd('/')}/session/device/background-token"
|
|
60
|
+
val body = JSONObject()
|
|
61
|
+
.put("deviceId", credential.deviceId)
|
|
62
|
+
.put("secret", credential.secret)
|
|
63
|
+
.toString()
|
|
64
|
+
|
|
65
|
+
val connection: HttpURLConnection = try {
|
|
66
|
+
(URL(endpoint).openConnection() as HttpURLConnection).apply {
|
|
67
|
+
requestMethod = "POST"
|
|
68
|
+
connectTimeout = CONNECT_TIMEOUT_MS
|
|
69
|
+
readTimeout = READ_TIMEOUT_MS
|
|
70
|
+
doOutput = true
|
|
71
|
+
// No Authorization header and no cookies, by design.
|
|
72
|
+
setRequestProperty("Content-Type", "application/json")
|
|
73
|
+
setRequestProperty("Accept", "application/json")
|
|
74
|
+
}
|
|
75
|
+
} catch (cause: IOException) {
|
|
76
|
+
return@withContext MintOutcome.Transient(cause)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
connection.outputStream.use { it.write(body.toByteArray(Charsets.UTF_8)) }
|
|
81
|
+
val status = connection.responseCode
|
|
82
|
+
if (status != HttpURLConnection.HTTP_OK) {
|
|
83
|
+
return@withContext classifyFailure(status, readErrorBody(connection))
|
|
84
|
+
}
|
|
85
|
+
val payload = connection.inputStream.bufferedReader().use { it.readText() }
|
|
86
|
+
parseMintResponse(payload)
|
|
87
|
+
} catch (cause: IOException) {
|
|
88
|
+
// Connect/read failure, DNS, TLS, a dropped socket mid-body.
|
|
89
|
+
MintOutcome.Transient(cause)
|
|
90
|
+
} finally {
|
|
91
|
+
connection.disconnect()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Read the error body, tolerating its absence — `errorStream` is null for some
|
|
97
|
+
* statuses, and a failure to read the explanation of a failure must not become
|
|
98
|
+
* a different failure.
|
|
99
|
+
*/
|
|
100
|
+
private fun readErrorBody(connection: HttpURLConnection): String =
|
|
101
|
+
try {
|
|
102
|
+
connection.errorStream?.bufferedReader()?.use { it.readText() } ?: ""
|
|
103
|
+
} catch (cause: IOException) {
|
|
104
|
+
""
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* A non-200 becomes [MintOutcome.Revoked] ONLY on an explicit verdict; every
|
|
109
|
+
* other status and every unrecognised 401 is [MintOutcome.Transient] so the
|
|
110
|
+
* credential is kept. Substring matching on the raw body (rather than parsing
|
|
111
|
+
* it) is deliberate: it is what core does, and it survives the body being an
|
|
112
|
+
* error page rather than the JSON envelope.
|
|
113
|
+
*/
|
|
114
|
+
private fun classifyFailure(status: Int, body: String): MintOutcome {
|
|
115
|
+
if (status == HttpURLConnection.HTTP_UNAUTHORIZED &&
|
|
116
|
+
(body.contains(ERROR_CREDENTIAL_INVALID) || body.contains(ERROR_ACCOUNT_NOT_ON_DEVICE))
|
|
117
|
+
) {
|
|
118
|
+
return MintOutcome.Revoked
|
|
119
|
+
}
|
|
120
|
+
return MintOutcome.Transient(IOException("POST /session/device/background-token responded $status"))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private fun parseMintResponse(payload: String): MintOutcome =
|
|
124
|
+
try {
|
|
125
|
+
val data = JSONObject(payload).getJSONObject("data")
|
|
126
|
+
val accessToken = data.getString("accessToken")
|
|
127
|
+
val accountId = data.getString("accountId")
|
|
128
|
+
val expiresAt = parseIsoInstant(data.getString("expiresAt"))
|
|
129
|
+
if (accessToken.isEmpty() || accountId.isEmpty()) {
|
|
130
|
+
MintOutcome.Malformed(JSONException("background-token response had an empty accessToken or accountId"))
|
|
131
|
+
} else {
|
|
132
|
+
MintOutcome.Minted(accessToken, expiresAt, accountId)
|
|
133
|
+
}
|
|
134
|
+
} catch (cause: JSONException) {
|
|
135
|
+
MintOutcome.Malformed(cause)
|
|
136
|
+
} catch (cause: ParseException) {
|
|
137
|
+
MintOutcome.Malformed(cause)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Parse the server's `expiresAt` into epoch millis.
|
|
142
|
+
*
|
|
143
|
+
* The shape is fixed rather than guessed: oxy-api emits it with JavaScript's
|
|
144
|
+
* `Date.toISOString()`, which is specified to produce exactly
|
|
145
|
+
* `yyyy-MM-ddTHH:mm:ss.sssZ`. `SimpleDateFormat` (rather than `java.time`)
|
|
146
|
+
* because `Instant.parse` needs API 26 or core-library desugaring, and this
|
|
147
|
+
* module must compile for every consuming app's minSdk without imposing a
|
|
148
|
+
* build-config requirement on them.
|
|
149
|
+
*
|
|
150
|
+
* Anything else throws [ParseException], which the caller classifies as
|
|
151
|
+
* `Malformed` — never a silent fallback to a made-up expiry, which would either
|
|
152
|
+
* throw away a valid token or keep using a dead one.
|
|
153
|
+
*/
|
|
154
|
+
private fun parseIsoInstant(value: String): Long {
|
|
155
|
+
val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.US).apply {
|
|
156
|
+
timeZone = TimeZone.getTimeZone("UTC")
|
|
157
|
+
isLenient = false
|
|
158
|
+
}
|
|
159
|
+
val parsed = format.parse(value) ?: throw ParseException("unparseable expiresAt: $value", 0)
|
|
160
|
+
return parsed.time
|
|
161
|
+
}
|
|
162
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
package so.oxy.session
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import expo.modules.kotlin.exception.Exceptions
|
|
5
|
+
import expo.modules.kotlin.modules.Module
|
|
6
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* JS side of the background session credential.
|
|
10
|
+
*
|
|
11
|
+
* JS is the only writer of the credential and native background code is the only
|
|
12
|
+
* consumer, so this bridge is deliberately one-way: put, clear, and a read of the
|
|
13
|
+
* account id (which JS needs to notice that the stored credential belongs to a
|
|
14
|
+
* different account than the one now signed in). There is no `get` — JS has no
|
|
15
|
+
* use for the secret it just wrote, and not exposing it keeps the secret's only
|
|
16
|
+
* reader native.
|
|
17
|
+
*
|
|
18
|
+
* Every function is a no-op-or-null on failure rather than throwing: the app must
|
|
19
|
+
* work identically whether or not background credentials are available, and a
|
|
20
|
+
* keystore hiccup must never break sign-in.
|
|
21
|
+
*/
|
|
22
|
+
class OxyBackgroundSessionModule : Module() {
|
|
23
|
+
private val context: Context
|
|
24
|
+
get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()
|
|
25
|
+
|
|
26
|
+
override fun definition() = ModuleDefinition {
|
|
27
|
+
Name("OxyBackgroundSession")
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Store (replacing) the credential. Rejects an incomplete or already-expired
|
|
31
|
+
* one rather than persisting something no mint could ever use.
|
|
32
|
+
*
|
|
33
|
+
* ## Five scalars, not a `Record`, and that is not a style choice
|
|
34
|
+
*
|
|
35
|
+
* This took a `Record` and it did not work on a device: every call was rejected
|
|
36
|
+
* with `The 1st argument cannot be cast to type CredentialInput (received class
|
|
37
|
+
* ReadableNativeMap)`, preceded by `Introspectable data is missing for class
|
|
38
|
+
* CredentialInput. Falling back to reflection-based conversion`. The fallback
|
|
39
|
+
* then failed too, so nothing was ever stored — the provision succeeded, the
|
|
40
|
+
* server issued a credential, and the widget stayed signed out forever with only
|
|
41
|
+
* a warning in the log to show for it.
|
|
42
|
+
*
|
|
43
|
+
* The reason is where this package's Kotlin lives: it ships as SOURCE that each
|
|
44
|
+
* consuming app compiles, so the annotation processing that generates a `Record`'s
|
|
45
|
+
* introspection metadata does not necessarily run in the consumer's build. The
|
|
46
|
+
* sibling module in this same library, `OxyIdentityModule`, has always passed
|
|
47
|
+
* plain scalars (`putShared(privateKey, publicKey)`) and has always worked — so
|
|
48
|
+
* scalars are the proven shape here and `CredentialInput` was the only `Record`
|
|
49
|
+
* in the package, i.e. the one untested path.
|
|
50
|
+
*
|
|
51
|
+
* Do not "tidy" these five parameters back into a `Record` without first proving
|
|
52
|
+
* on a real device that a `Record` converts at all in a consumer build. The
|
|
53
|
+
* failure is silent from JS's side: it surfaces as a widget that never signs in.
|
|
54
|
+
*
|
|
55
|
+
* `expiresAt` is epoch MILLIS as a `Double`, already parsed from the server's
|
|
56
|
+
* ISO-8601 string on the JS side — JS has a `Date`, so there is no reason to hand
|
|
57
|
+
* native a string to re-parse. `Double` because that is what a JS number crosses
|
|
58
|
+
* the bridge as; a `Long` parameter would fail to convert for the same family of
|
|
59
|
+
* reason this comment exists.
|
|
60
|
+
*/
|
|
61
|
+
AsyncFunction("put") { baseUrl: String,
|
|
62
|
+
deviceId: String,
|
|
63
|
+
secret: String,
|
|
64
|
+
accountId: String,
|
|
65
|
+
expiresAtMs: Double ->
|
|
66
|
+
val expiresAt = expiresAtMs.toLong()
|
|
67
|
+
if (
|
|
68
|
+
baseUrl.isEmpty() ||
|
|
69
|
+
deviceId.isEmpty() ||
|
|
70
|
+
secret.isEmpty() ||
|
|
71
|
+
accountId.isEmpty() ||
|
|
72
|
+
expiresAt <= System.currentTimeMillis()
|
|
73
|
+
) {
|
|
74
|
+
return@AsyncFunction false
|
|
75
|
+
}
|
|
76
|
+
runCatching {
|
|
77
|
+
OxyBackgroundSessionStore.writeCredential(
|
|
78
|
+
context,
|
|
79
|
+
OxyBackgroundSessionStore.Credential(
|
|
80
|
+
baseUrl = baseUrl,
|
|
81
|
+
deviceId = deviceId,
|
|
82
|
+
secret = secret,
|
|
83
|
+
accountId = accountId,
|
|
84
|
+
expiresAt = expiresAt,
|
|
85
|
+
),
|
|
86
|
+
)
|
|
87
|
+
}.isSuccess
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Drop the credential and any cached token. Called on sign-out and BEFORE an
|
|
92
|
+
* account switch, so the window in which background code could serve the
|
|
93
|
+
* previous account's data is as short as a local write.
|
|
94
|
+
*/
|
|
95
|
+
AsyncFunction("clear") {
|
|
96
|
+
runCatching { OxyBackgroundSessionStore.clear(context) }.isSuccess
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* What is currently stored — the account it belongs to and when it expires
|
|
101
|
+
* (epoch millis) — or null when there is no usable credential.
|
|
102
|
+
*
|
|
103
|
+
* This is what lets JS decide whether to provision at all: a credential for
|
|
104
|
+
* the signed-in account with plenty of life left needs no network call. It
|
|
105
|
+
* deliberately does NOT return the secret; native is its only reader.
|
|
106
|
+
*/
|
|
107
|
+
AsyncFunction("peek") {
|
|
108
|
+
val stored = runCatching { OxyBackgroundSessionStore.readCredential(context) }.getOrNull()
|
|
109
|
+
?: return@AsyncFunction null
|
|
110
|
+
mapOf(
|
|
111
|
+
"accountId" to stored.accountId,
|
|
112
|
+
"expiresAt" to stored.expiresAt.toDouble(),
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|