@oxyhq/services 26.0.1 → 26.0.4

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.
Files changed (599) hide show
  1. package/README.md +2 -2
  2. package/android/src/main/java/so/oxy/identity/OxyIdentityStore.kt +21 -127
  3. package/android/src/main/java/so/oxy/session/OxyBackgroundSession.kt +184 -0
  4. package/android/src/main/java/so/oxy/session/OxyBackgroundSessionApi.kt +162 -0
  5. package/android/src/main/java/so/oxy/session/OxyBackgroundSessionModule.kt +116 -0
  6. package/android/src/main/java/so/oxy/session/OxyBackgroundSessionStore.kt +179 -0
  7. package/android/src/main/java/so/oxy/session/OxyBackgroundToken.kt +61 -0
  8. package/android/src/main/java/so/oxy/storage/OxyEncryptedPrefs.kt +241 -0
  9. package/expo-module.config.json +4 -1
  10. package/lib/commonjs/index.js +18 -49
  11. package/lib/commonjs/index.js.map +1 -1
  12. package/lib/commonjs/notifications/deviceNotifications.js +63 -0
  13. package/lib/commonjs/notifications/deviceNotifications.js.map +1 -1
  14. package/lib/commonjs/ui/boot/runProviderColdBoot.js +20 -64
  15. package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
  16. package/lib/commonjs/ui/components/AvatarCameraBadge.js +2 -2
  17. package/lib/commonjs/ui/components/AvatarCameraBadge.js.map +1 -1
  18. package/lib/commonjs/ui/components/FollowButton.js +4 -4
  19. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  20. package/lib/commonjs/ui/components/OxyAccountDialogScreen.js +13 -1
  21. package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
  22. package/lib/commonjs/ui/components/OxyAuthChooser.js +29 -24
  23. package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
  24. package/lib/commonjs/ui/components/OxyProvider.js +8 -7
  25. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  26. package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
  27. package/lib/commonjs/ui/components/ProfileButton.js +20 -26
  28. package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
  29. package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +68 -58
  30. package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
  31. package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
  32. package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js +2 -2
  33. package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js.map +1 -1
  34. package/lib/commonjs/ui/components/fileManagement/UploadPreview.js +2 -2
  35. package/lib/commonjs/ui/components/fileManagement/UploadPreview.js.map +1 -1
  36. package/lib/commonjs/ui/components/internal/PinInput.js +3 -1
  37. package/lib/commonjs/ui/components/internal/PinInput.js.map +1 -1
  38. package/lib/commonjs/ui/components/logo/LogoText.js +4 -4
  39. package/lib/commonjs/ui/components/logo/LogoText.js.map +1 -1
  40. package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js +2 -2
  41. package/lib/commonjs/ui/components/payment/PaymentDetailsStep.js.map +1 -1
  42. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js +2 -2
  43. package/lib/commonjs/ui/components/payment/PaymentSummaryStep.js.map +1 -1
  44. package/lib/commonjs/ui/components/styles/shadow.js.map +1 -1
  45. package/lib/commonjs/ui/context/OxyContext.js +32 -46
  46. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  47. package/lib/commonjs/ui/context/commitSessionFlow.js +3 -43
  48. package/lib/commonjs/ui/context/commitSessionFlow.js.map +1 -1
  49. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +15 -2
  50. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  51. package/lib/commonjs/ui/context/useOxyAccountGraph.js +3 -4
  52. package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
  53. package/lib/commonjs/ui/hooks/mutations/mutationFactory.js +5 -5
  54. package/lib/commonjs/ui/hooks/mutations/mutationFactory.js.map +1 -1
  55. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +11 -11
  56. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  57. package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js +9 -9
  58. package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js.map +1 -1
  59. package/lib/commonjs/ui/hooks/queries/usePaymentQueries.js +3 -2
  60. package/lib/commonjs/ui/hooks/queries/usePaymentQueries.js.map +1 -1
  61. package/lib/commonjs/ui/hooks/queries/userCache.js +17 -17
  62. package/lib/commonjs/ui/hooks/queries/userCache.js.map +1 -1
  63. package/lib/commonjs/ui/hooks/useAssets.js.map +1 -1
  64. package/lib/commonjs/ui/hooks/useAsyncAction.js +7 -7
  65. package/lib/commonjs/ui/hooks/useAsyncAction.js.map +1 -1
  66. package/lib/commonjs/ui/hooks/useAuth.js +8 -4
  67. package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
  68. package/lib/commonjs/ui/hooks/useAvatarPicker.js +5 -5
  69. package/lib/commonjs/ui/hooks/useAvatarPicker.js.map +1 -1
  70. package/lib/commonjs/ui/hooks/useOnlineStatus.js +3 -2
  71. package/lib/commonjs/ui/hooks/useOnlineStatus.js.map +1 -1
  72. package/lib/commonjs/ui/hooks/useSessionManagement.js +0 -2
  73. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
  74. package/lib/commonjs/ui/hooks/useSettingToggle.js +4 -4
  75. package/lib/commonjs/ui/hooks/useSettingToggle.js.map +1 -1
  76. package/lib/commonjs/ui/hooks/useSurfaceHeader.js +68 -11
  77. package/lib/commonjs/ui/hooks/useSurfaceHeader.js.map +1 -1
  78. package/lib/commonjs/ui/navigation/accountDialogManager.js +2 -0
  79. package/lib/commonjs/ui/navigation/accountDialogManager.js.map +1 -1
  80. package/lib/commonjs/ui/oauth/browserAuthTransport.js +66 -64
  81. package/lib/commonjs/ui/oauth/browserAuthTransport.js.map +1 -1
  82. package/lib/commonjs/ui/oauth/oauthPopup.js +47 -0
  83. package/lib/commonjs/ui/oauth/oauthPopup.js.map +1 -1
  84. package/lib/commonjs/ui/screens/AccountMembersScreen.js +10 -10
  85. package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
  86. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +41 -19
  87. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  88. package/lib/commonjs/ui/screens/AccountVerificationScreen.js +4 -4
  89. package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -1
  90. package/lib/commonjs/ui/screens/AppInfoScreen.js +9 -9
  91. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  92. package/lib/commonjs/ui/screens/AvatarCropScreen.js +3 -3
  93. package/lib/commonjs/ui/screens/AvatarCropScreen.js.map +1 -1
  94. package/lib/commonjs/ui/screens/ChangeAvatarScreen.js +6 -7
  95. package/lib/commonjs/ui/screens/ChangeAvatarScreen.js.map +1 -1
  96. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js +4 -4
  97. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js.map +1 -1
  98. package/lib/commonjs/ui/screens/CreateAccountScreen.js +46 -14
  99. package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
  100. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +5 -5
  101. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
  102. package/lib/commonjs/ui/screens/EditProfileScreen.js +2 -3
  103. package/lib/commonjs/ui/screens/EditProfileScreen.js.map +1 -1
  104. package/lib/commonjs/ui/screens/FAQScreen.js +2 -2
  105. package/lib/commonjs/ui/screens/FAQScreen.js.map +1 -1
  106. package/lib/commonjs/ui/screens/FeedbackScreen.js +90 -89
  107. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  108. package/lib/commonjs/ui/screens/FileManagementScreen.js +20 -20
  109. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  110. package/lib/commonjs/ui/screens/FollowersListScreen.js.map +1 -1
  111. package/lib/commonjs/ui/screens/FollowingListScreen.js.map +1 -1
  112. package/lib/commonjs/ui/screens/HelpSupportScreen.js +7 -7
  113. package/lib/commonjs/ui/screens/HelpSupportScreen.js.map +1 -1
  114. package/lib/commonjs/ui/screens/HistoryViewScreen.js +9 -34
  115. package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -1
  116. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +3 -3
  117. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
  118. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js +3 -3
  119. package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -1
  120. package/lib/commonjs/ui/screens/ManageAccountScreen.js +15 -15
  121. package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
  122. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +19 -19
  123. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  124. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +15 -15
  125. package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
  126. package/lib/commonjs/ui/screens/ProfileScreen.js +17 -7
  127. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  128. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js +2 -2
  129. package/lib/commonjs/ui/screens/SavesCollectionsScreen.js.map +1 -1
  130. package/lib/commonjs/ui/screens/UserListScreen.js +13 -7
  131. package/lib/commonjs/ui/screens/UserListScreen.js.map +1 -1
  132. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +5 -6
  133. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  134. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js +1 -4
  135. package/lib/commonjs/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  136. package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js +34 -24
  137. package/lib/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  138. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js +1 -1
  139. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  140. package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js +3 -4
  141. package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
  142. package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js +1 -1
  143. package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
  144. package/lib/commonjs/ui/session/backgroundSession.js +258 -0
  145. package/lib/commonjs/ui/session/backgroundSession.js.map +1 -0
  146. package/lib/commonjs/ui/session/index.js +7 -0
  147. package/lib/commonjs/ui/session/index.js.map +1 -1
  148. package/lib/commonjs/ui/session/useBackgroundSessionSync.js +81 -0
  149. package/lib/commonjs/ui/session/useBackgroundSessionSync.js.map +1 -0
  150. package/lib/commonjs/ui/utils/fileManagement.js +24 -24
  151. package/lib/commonjs/ui/utils/fileManagement.js.map +1 -1
  152. package/lib/commonjs/ui/utils/netConnectivity.js +28 -0
  153. package/lib/commonjs/ui/utils/netConnectivity.js.map +1 -0
  154. package/lib/commonjs/ui/utils/oauthReturn.js +19 -30
  155. package/lib/commonjs/ui/utils/oauthReturn.js.map +1 -1
  156. package/lib/commonjs/ui/utils/userUtils.js +27 -28
  157. package/lib/commonjs/ui/utils/userUtils.js.map +1 -1
  158. package/lib/module/index.js +20 -8
  159. package/lib/module/index.js.map +1 -1
  160. package/lib/module/notifications/deviceNotifications.js +62 -0
  161. package/lib/module/notifications/deviceNotifications.js.map +1 -1
  162. package/lib/module/ui/boot/runProviderColdBoot.js +21 -65
  163. package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
  164. package/lib/module/ui/components/AvatarCameraBadge.js +1 -1
  165. package/lib/module/ui/components/AvatarCameraBadge.js.map +1 -1
  166. package/lib/module/ui/components/FollowButton.js +1 -1
  167. package/lib/module/ui/components/FollowButton.js.map +1 -1
  168. package/lib/module/ui/components/OxyAccountDialogScreen.js +13 -1
  169. package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
  170. package/lib/module/ui/components/OxyAuthChooser.js +25 -20
  171. package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
  172. package/lib/module/ui/components/OxyProvider.js +7 -6
  173. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  174. package/lib/module/ui/components/OxySignInButton.js.map +1 -1
  175. package/lib/module/ui/components/ProfileButton.js +12 -18
  176. package/lib/module/ui/components/ProfileButton.js.map +1 -1
  177. package/lib/module/ui/components/authChooser/AccountsMenuView.js +68 -58
  178. package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
  179. package/lib/module/ui/components/authChooser/types.js.map +1 -1
  180. package/lib/module/ui/components/authChooser/useUsernameAvailability.js +1 -1
  181. package/lib/module/ui/components/authChooser/useUsernameAvailability.js.map +1 -1
  182. package/lib/module/ui/components/fileManagement/UploadPreview.js +1 -1
  183. package/lib/module/ui/components/fileManagement/UploadPreview.js.map +1 -1
  184. package/lib/module/ui/components/internal/PinInput.js +3 -1
  185. package/lib/module/ui/components/internal/PinInput.js.map +1 -1
  186. package/lib/module/ui/components/logo/LogoText.js +4 -4
  187. package/lib/module/ui/components/logo/LogoText.js.map +1 -1
  188. package/lib/module/ui/components/payment/PaymentDetailsStep.js +1 -1
  189. package/lib/module/ui/components/payment/PaymentDetailsStep.js.map +1 -1
  190. package/lib/module/ui/components/payment/PaymentSummaryStep.js +2 -2
  191. package/lib/module/ui/components/payment/PaymentSummaryStep.js.map +1 -1
  192. package/lib/module/ui/components/styles/shadow.js.map +1 -1
  193. package/lib/module/ui/context/OxyContext.js +35 -49
  194. package/lib/module/ui/context/OxyContext.js.map +1 -1
  195. package/lib/module/ui/context/commitSessionFlow.js +3 -42
  196. package/lib/module/ui/context/commitSessionFlow.js.map +1 -1
  197. package/lib/module/ui/context/hooks/useAuthOperations.js +15 -2
  198. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  199. package/lib/module/ui/context/useOxyAccountGraph.js +3 -4
  200. package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
  201. package/lib/module/ui/hooks/mutations/mutationFactory.js +1 -1
  202. package/lib/module/ui/hooks/mutations/mutationFactory.js.map +1 -1
  203. package/lib/module/ui/hooks/mutations/useAccountMutations.js +1 -1
  204. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  205. package/lib/module/ui/hooks/mutations/useServicesMutations.js +1 -1
  206. package/lib/module/ui/hooks/mutations/useServicesMutations.js.map +1 -1
  207. package/lib/module/ui/hooks/queries/usePaymentQueries.js +3 -2
  208. package/lib/module/ui/hooks/queries/usePaymentQueries.js.map +1 -1
  209. package/lib/module/ui/hooks/queries/userCache.js +17 -17
  210. package/lib/module/ui/hooks/queries/userCache.js.map +1 -1
  211. package/lib/module/ui/hooks/useAssets.js.map +1 -1
  212. package/lib/module/ui/hooks/useAsyncAction.js +1 -1
  213. package/lib/module/ui/hooks/useAsyncAction.js.map +1 -1
  214. package/lib/module/ui/hooks/useAuth.js +8 -4
  215. package/lib/module/ui/hooks/useAuth.js.map +1 -1
  216. package/lib/module/ui/hooks/useAvatarPicker.js +1 -1
  217. package/lib/module/ui/hooks/useAvatarPicker.js.map +1 -1
  218. package/lib/module/ui/hooks/useOnlineStatus.js +3 -2
  219. package/lib/module/ui/hooks/useOnlineStatus.js.map +1 -1
  220. package/lib/module/ui/hooks/useSessionManagement.js +0 -2
  221. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
  222. package/lib/module/ui/hooks/useSettingToggle.js +1 -1
  223. package/lib/module/ui/hooks/useSettingToggle.js.map +1 -1
  224. package/lib/module/ui/hooks/useSurfaceHeader.js +68 -11
  225. package/lib/module/ui/hooks/useSurfaceHeader.js.map +1 -1
  226. package/lib/module/ui/navigation/accountDialogManager.js +2 -0
  227. package/lib/module/ui/navigation/accountDialogManager.js.map +1 -1
  228. package/lib/module/ui/oauth/browserAuthTransport.js +66 -64
  229. package/lib/module/ui/oauth/browserAuthTransport.js.map +1 -1
  230. package/lib/module/ui/oauth/oauthPopup.js +48 -1
  231. package/lib/module/ui/oauth/oauthPopup.js.map +1 -1
  232. package/lib/module/ui/screens/AccountMembersScreen.js +1 -1
  233. package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
  234. package/lib/module/ui/screens/AccountSettingsScreen.js +37 -15
  235. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  236. package/lib/module/ui/screens/AccountVerificationScreen.js +1 -1
  237. package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -1
  238. package/lib/module/ui/screens/AppInfoScreen.js +1 -1
  239. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  240. package/lib/module/ui/screens/AvatarCropScreen.js +1 -1
  241. package/lib/module/ui/screens/AvatarCropScreen.js.map +1 -1
  242. package/lib/module/ui/screens/ChangeAvatarScreen.js +4 -5
  243. package/lib/module/ui/screens/ChangeAvatarScreen.js.map +1 -1
  244. package/lib/module/ui/screens/ConnectedAppsScreen.js +2 -2
  245. package/lib/module/ui/screens/ConnectedAppsScreen.js.map +1 -1
  246. package/lib/module/ui/screens/CreateAccountScreen.js +45 -13
  247. package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
  248. package/lib/module/ui/screens/EditProfileFieldScreen.js +4 -4
  249. package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
  250. package/lib/module/ui/screens/EditProfileScreen.js +3 -4
  251. package/lib/module/ui/screens/EditProfileScreen.js.map +1 -1
  252. package/lib/module/ui/screens/FAQScreen.js +1 -1
  253. package/lib/module/ui/screens/FAQScreen.js.map +1 -1
  254. package/lib/module/ui/screens/FeedbackScreen.js +94 -91
  255. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  256. package/lib/module/ui/screens/FileManagementScreen.js +1 -1
  257. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  258. package/lib/module/ui/screens/FollowersListScreen.js.map +1 -1
  259. package/lib/module/ui/screens/FollowingListScreen.js.map +1 -1
  260. package/lib/module/ui/screens/HelpSupportScreen.js +1 -1
  261. package/lib/module/ui/screens/HelpSupportScreen.js.map +1 -1
  262. package/lib/module/ui/screens/HistoryViewScreen.js +5 -30
  263. package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -1
  264. package/lib/module/ui/screens/LanguageSelectorScreen.js +1 -1
  265. package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
  266. package/lib/module/ui/screens/LegalDocumentsScreen.js +1 -1
  267. package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -1
  268. package/lib/module/ui/screens/ManageAccountScreen.js +3 -3
  269. package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
  270. package/lib/module/ui/screens/PremiumSubscriptionScreen.js +4 -4
  271. package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
  272. package/lib/module/ui/screens/PrivacySettingsScreen.js +11 -11
  273. package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
  274. package/lib/module/ui/screens/ProfileScreen.js +18 -8
  275. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  276. package/lib/module/ui/screens/SavesCollectionsScreen.js +1 -1
  277. package/lib/module/ui/screens/SavesCollectionsScreen.js.map +1 -1
  278. package/lib/module/ui/screens/UserListScreen.js +14 -8
  279. package/lib/module/ui/screens/UserListScreen.js.map +1 -1
  280. package/lib/module/ui/screens/WelcomeNewUserScreen.js +5 -6
  281. package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  282. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js +1 -4
  283. package/lib/module/ui/screens/fileManagement/PhotoPickerSection.js.map +1 -1
  284. package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js +15 -5
  285. package/lib/module/ui/screens/fileManagement/hooks/useFileUploadState.js.map +1 -1
  286. package/lib/module/ui/screens/trust/TrustCenterScreen.js +1 -1
  287. package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
  288. package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js +4 -5
  289. package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
  290. package/lib/module/ui/screens/trust/TrustRewardsScreen.js +1 -1
  291. package/lib/module/ui/screens/trust/TrustRewardsScreen.js.map +1 -1
  292. package/lib/module/ui/session/backgroundSession.js +252 -0
  293. package/lib/module/ui/session/backgroundSession.js.map +1 -0
  294. package/lib/module/ui/session/index.js +1 -0
  295. package/lib/module/ui/session/index.js.map +1 -1
  296. package/lib/module/ui/session/useBackgroundSessionSync.js +76 -0
  297. package/lib/module/ui/session/useBackgroundSessionSync.js.map +1 -0
  298. package/lib/module/ui/utils/fileManagement.js +25 -25
  299. package/lib/module/ui/utils/fileManagement.js.map +1 -1
  300. package/lib/module/ui/utils/netConnectivity.js +23 -0
  301. package/lib/module/ui/utils/netConnectivity.js.map +1 -0
  302. package/lib/module/ui/utils/oauthReturn.js +20 -30
  303. package/lib/module/ui/utils/oauthReturn.js.map +1 -1
  304. package/lib/module/ui/utils/userUtils.js +27 -28
  305. package/lib/module/ui/utils/userUtils.js.map +1 -1
  306. package/lib/typescript/commonjs/index.d.ts +1 -2
  307. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  308. package/lib/typescript/commonjs/notifications/deviceNotifications.d.ts +51 -0
  309. package/lib/typescript/commonjs/notifications/deviceNotifications.d.ts.map +1 -1
  310. package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts +11 -30
  311. package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  312. package/lib/typescript/commonjs/ui/components/AvatarCameraBadge.d.ts.map +1 -1
  313. package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
  314. package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts +7 -0
  315. package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
  316. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  317. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts +2 -1
  318. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
  319. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts +3 -0
  320. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
  321. package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
  322. package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +6 -0
  323. package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
  324. package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts +1 -1
  325. package/lib/typescript/commonjs/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
  326. package/lib/typescript/commonjs/ui/components/internal/PinInput.d.ts.map +1 -1
  327. package/lib/typescript/commonjs/ui/components/payment/types.d.ts +6 -6
  328. package/lib/typescript/commonjs/ui/components/payment/types.d.ts.map +1 -1
  329. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  330. package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts +0 -40
  331. package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts.map +1 -1
  332. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +11 -2
  333. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  334. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +26 -7
  335. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
  336. package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +0 -1
  337. package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  338. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  339. package/lib/typescript/commonjs/ui/hooks/queries/paymentTypes.d.ts +3 -2
  340. package/lib/typescript/commonjs/ui/hooks/queries/paymentTypes.d.ts.map +1 -1
  341. package/lib/typescript/commonjs/ui/hooks/queries/usePaymentQueries.d.ts +3 -2
  342. package/lib/typescript/commonjs/ui/hooks/queries/usePaymentQueries.d.ts.map +1 -1
  343. package/lib/typescript/commonjs/ui/hooks/queries/userCache.d.ts +2 -2
  344. package/lib/typescript/commonjs/ui/hooks/queries/userCache.d.ts.map +1 -1
  345. package/lib/typescript/commonjs/ui/hooks/useAssets.d.ts +1 -1
  346. package/lib/typescript/commonjs/ui/hooks/useAssets.d.ts.map +1 -1
  347. package/lib/typescript/commonjs/ui/hooks/useAsyncAction.d.ts +2 -2
  348. package/lib/typescript/commonjs/ui/hooks/useAsyncAction.d.ts.map +1 -1
  349. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts +10 -6
  350. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
  351. package/lib/typescript/commonjs/ui/hooks/useOnlineStatus.d.ts +3 -2
  352. package/lib/typescript/commonjs/ui/hooks/useOnlineStatus.d.ts.map +1 -1
  353. package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
  354. package/lib/typescript/commonjs/ui/hooks/useSurfaceHeader.d.ts +7 -2
  355. package/lib/typescript/commonjs/ui/hooks/useSurfaceHeader.d.ts.map +1 -1
  356. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts +8 -0
  357. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts.map +1 -1
  358. package/lib/typescript/commonjs/ui/oauth/browserAuthTransport.d.ts.map +1 -1
  359. package/lib/typescript/commonjs/ui/oauth/oauthPopup.d.ts +6 -0
  360. package/lib/typescript/commonjs/ui/oauth/oauthPopup.d.ts.map +1 -1
  361. package/lib/typescript/commonjs/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  362. package/lib/typescript/commonjs/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
  363. package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  364. package/lib/typescript/commonjs/ui/screens/EditProfileScreen.d.ts.map +1 -1
  365. package/lib/typescript/commonjs/ui/screens/FeedbackScreen.d.ts +2 -2
  366. package/lib/typescript/commonjs/ui/screens/FeedbackScreen.d.ts.map +1 -1
  367. package/lib/typescript/commonjs/ui/screens/FollowersListScreen.d.ts +3 -0
  368. package/lib/typescript/commonjs/ui/screens/FollowersListScreen.d.ts.map +1 -1
  369. package/lib/typescript/commonjs/ui/screens/FollowingListScreen.d.ts +3 -0
  370. package/lib/typescript/commonjs/ui/screens/FollowingListScreen.d.ts.map +1 -1
  371. package/lib/typescript/commonjs/ui/screens/HistoryViewScreen.d.ts.map +1 -1
  372. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  373. package/lib/typescript/commonjs/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  374. package/lib/typescript/commonjs/ui/screens/ProfileScreen.d.ts.map +1 -1
  375. package/lib/typescript/commonjs/ui/screens/UserListScreen.d.ts +7 -0
  376. package/lib/typescript/commonjs/ui/screens/UserListScreen.d.ts.map +1 -1
  377. package/lib/typescript/commonjs/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
  378. package/lib/typescript/commonjs/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  379. package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
  380. package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts +1 -1
  381. package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts.map +1 -1
  382. package/lib/typescript/commonjs/ui/session/backgroundSession.d.ts +42 -0
  383. package/lib/typescript/commonjs/ui/session/backgroundSession.d.ts.map +1 -0
  384. package/lib/typescript/commonjs/ui/session/index.d.ts +1 -0
  385. package/lib/typescript/commonjs/ui/session/index.d.ts.map +1 -1
  386. package/lib/typescript/commonjs/ui/session/useBackgroundSessionSync.d.ts +23 -0
  387. package/lib/typescript/commonjs/ui/session/useBackgroundSessionSync.d.ts.map +1 -0
  388. package/lib/typescript/commonjs/ui/types/navigation.d.ts +27 -19
  389. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  390. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts +5 -5
  391. package/lib/typescript/commonjs/ui/utils/fileManagement.d.ts.map +1 -1
  392. package/lib/typescript/commonjs/ui/utils/netConnectivity.d.ts +17 -0
  393. package/lib/typescript/commonjs/ui/utils/netConnectivity.d.ts.map +1 -0
  394. package/lib/typescript/commonjs/ui/utils/oauthReturn.d.ts +15 -12
  395. package/lib/typescript/commonjs/ui/utils/oauthReturn.d.ts.map +1 -1
  396. package/lib/typescript/commonjs/ui/utils/userUtils.d.ts +7 -14
  397. package/lib/typescript/commonjs/ui/utils/userUtils.d.ts.map +1 -1
  398. package/lib/typescript/module/index.d.ts +1 -2
  399. package/lib/typescript/module/index.d.ts.map +1 -1
  400. package/lib/typescript/module/notifications/deviceNotifications.d.ts +51 -0
  401. package/lib/typescript/module/notifications/deviceNotifications.d.ts.map +1 -1
  402. package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts +11 -30
  403. package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  404. package/lib/typescript/module/ui/components/AvatarCameraBadge.d.ts.map +1 -1
  405. package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
  406. package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts +7 -0
  407. package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
  408. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  409. package/lib/typescript/module/ui/components/OxySignInButton.d.ts +2 -1
  410. package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
  411. package/lib/typescript/module/ui/components/ProfileButton.d.ts +3 -0
  412. package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
  413. package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
  414. package/lib/typescript/module/ui/components/authChooser/types.d.ts +6 -0
  415. package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
  416. package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts +1 -1
  417. package/lib/typescript/module/ui/components/fileManagement/UploadPreview.d.ts.map +1 -1
  418. package/lib/typescript/module/ui/components/internal/PinInput.d.ts.map +1 -1
  419. package/lib/typescript/module/ui/components/payment/types.d.ts +6 -6
  420. package/lib/typescript/module/ui/components/payment/types.d.ts.map +1 -1
  421. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  422. package/lib/typescript/module/ui/context/commitSessionFlow.d.ts +0 -40
  423. package/lib/typescript/module/ui/context/commitSessionFlow.d.ts.map +1 -1
  424. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +11 -2
  425. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  426. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +26 -7
  427. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
  428. package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +0 -1
  429. package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  430. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  431. package/lib/typescript/module/ui/hooks/queries/paymentTypes.d.ts +3 -2
  432. package/lib/typescript/module/ui/hooks/queries/paymentTypes.d.ts.map +1 -1
  433. package/lib/typescript/module/ui/hooks/queries/usePaymentQueries.d.ts +3 -2
  434. package/lib/typescript/module/ui/hooks/queries/usePaymentQueries.d.ts.map +1 -1
  435. package/lib/typescript/module/ui/hooks/queries/userCache.d.ts +2 -2
  436. package/lib/typescript/module/ui/hooks/queries/userCache.d.ts.map +1 -1
  437. package/lib/typescript/module/ui/hooks/useAssets.d.ts +1 -1
  438. package/lib/typescript/module/ui/hooks/useAssets.d.ts.map +1 -1
  439. package/lib/typescript/module/ui/hooks/useAsyncAction.d.ts +2 -2
  440. package/lib/typescript/module/ui/hooks/useAsyncAction.d.ts.map +1 -1
  441. package/lib/typescript/module/ui/hooks/useAuth.d.ts +10 -6
  442. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
  443. package/lib/typescript/module/ui/hooks/useOnlineStatus.d.ts +3 -2
  444. package/lib/typescript/module/ui/hooks/useOnlineStatus.d.ts.map +1 -1
  445. package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
  446. package/lib/typescript/module/ui/hooks/useSurfaceHeader.d.ts +7 -2
  447. package/lib/typescript/module/ui/hooks/useSurfaceHeader.d.ts.map +1 -1
  448. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts +8 -0
  449. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts.map +1 -1
  450. package/lib/typescript/module/ui/oauth/browserAuthTransport.d.ts.map +1 -1
  451. package/lib/typescript/module/ui/oauth/oauthPopup.d.ts +6 -0
  452. package/lib/typescript/module/ui/oauth/oauthPopup.d.ts.map +1 -1
  453. package/lib/typescript/module/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  454. package/lib/typescript/module/ui/screens/ChangeAvatarScreen.d.ts.map +1 -1
  455. package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
  456. package/lib/typescript/module/ui/screens/EditProfileScreen.d.ts.map +1 -1
  457. package/lib/typescript/module/ui/screens/FeedbackScreen.d.ts +2 -2
  458. package/lib/typescript/module/ui/screens/FeedbackScreen.d.ts.map +1 -1
  459. package/lib/typescript/module/ui/screens/FollowersListScreen.d.ts +3 -0
  460. package/lib/typescript/module/ui/screens/FollowersListScreen.d.ts.map +1 -1
  461. package/lib/typescript/module/ui/screens/FollowingListScreen.d.ts +3 -0
  462. package/lib/typescript/module/ui/screens/FollowingListScreen.d.ts.map +1 -1
  463. package/lib/typescript/module/ui/screens/HistoryViewScreen.d.ts.map +1 -1
  464. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  465. package/lib/typescript/module/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
  466. package/lib/typescript/module/ui/screens/ProfileScreen.d.ts.map +1 -1
  467. package/lib/typescript/module/ui/screens/UserListScreen.d.ts +7 -0
  468. package/lib/typescript/module/ui/screens/UserListScreen.d.ts.map +1 -1
  469. package/lib/typescript/module/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
  470. package/lib/typescript/module/ui/screens/fileManagement/hooks/useFileUploadState.d.ts.map +1 -1
  471. package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
  472. package/lib/typescript/module/ui/screens/trust/trustTier.d.ts +1 -1
  473. package/lib/typescript/module/ui/screens/trust/trustTier.d.ts.map +1 -1
  474. package/lib/typescript/module/ui/session/backgroundSession.d.ts +42 -0
  475. package/lib/typescript/module/ui/session/backgroundSession.d.ts.map +1 -0
  476. package/lib/typescript/module/ui/session/index.d.ts +1 -0
  477. package/lib/typescript/module/ui/session/index.d.ts.map +1 -1
  478. package/lib/typescript/module/ui/session/useBackgroundSessionSync.d.ts +23 -0
  479. package/lib/typescript/module/ui/session/useBackgroundSessionSync.d.ts.map +1 -0
  480. package/lib/typescript/module/ui/types/navigation.d.ts +27 -19
  481. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  482. package/lib/typescript/module/ui/utils/fileManagement.d.ts +5 -5
  483. package/lib/typescript/module/ui/utils/fileManagement.d.ts.map +1 -1
  484. package/lib/typescript/module/ui/utils/netConnectivity.d.ts +17 -0
  485. package/lib/typescript/module/ui/utils/netConnectivity.d.ts.map +1 -0
  486. package/lib/typescript/module/ui/utils/oauthReturn.d.ts +15 -12
  487. package/lib/typescript/module/ui/utils/oauthReturn.d.ts.map +1 -1
  488. package/lib/typescript/module/ui/utils/userUtils.d.ts +7 -14
  489. package/lib/typescript/module/ui/utils/userUtils.d.ts.map +1 -1
  490. package/lib/typescript/types/expo-image-picker.d.ts +35 -0
  491. package/package.json +32 -12
  492. package/src/index.ts +19 -16
  493. package/src/notifications/deviceNotifications.ts +82 -0
  494. package/src/types/expo-image-picker.d.ts +35 -0
  495. package/src/ui/boot/__tests__/coldBootNoIdpRedirect.test.ts +215 -0
  496. package/src/ui/boot/runProviderColdBoot.ts +24 -101
  497. package/src/ui/components/AvatarCameraBadge.tsx +2 -1
  498. package/src/ui/components/FollowButton.tsx +1 -1
  499. package/src/ui/components/OxyAccountDialogScreen.tsx +13 -1
  500. package/src/ui/components/OxyAuthChooser.tsx +31 -20
  501. package/src/ui/components/OxyProvider.tsx +8 -7
  502. package/src/ui/components/OxySignInButton.tsx +2 -1
  503. package/src/ui/components/ProfileButton.tsx +15 -20
  504. package/src/ui/components/authChooser/AccountsMenuView.tsx +66 -57
  505. package/src/ui/components/authChooser/types.ts +6 -0
  506. package/src/ui/components/authChooser/useUsernameAvailability.ts +1 -1
  507. package/src/ui/components/fileManagement/UploadPreview.tsx +1 -1
  508. package/src/ui/components/internal/PinInput.tsx +1 -0
  509. package/src/ui/components/logo/LogoText.tsx +4 -4
  510. package/src/ui/components/payment/PaymentDetailsStep.tsx +1 -1
  511. package/src/ui/components/payment/PaymentSummaryStep.tsx +2 -2
  512. package/src/ui/components/payment/types.ts +24 -24
  513. package/src/ui/components/styles/shadow.tsx +2 -2
  514. package/src/ui/context/OxyContext.tsx +35 -46
  515. package/src/ui/context/__tests__/commitSessionFlow.test.ts +13 -89
  516. package/src/ui/context/commitSessionFlow.ts +3 -61
  517. package/src/ui/context/hooks/useAuthOperations.ts +26 -1
  518. package/src/ui/context/oxyContextTypes.ts +26 -7
  519. package/src/ui/context/useOxyAccountGraph.ts +4 -4
  520. package/src/ui/hooks/mutations/mutationFactory.ts +1 -1
  521. package/src/ui/hooks/mutations/useAccountMutations.ts +1 -1
  522. package/src/ui/hooks/mutations/useServicesMutations.ts +1 -1
  523. package/src/ui/hooks/queries/paymentTypes.ts +3 -2
  524. package/src/ui/hooks/queries/usePaymentQueries.ts +3 -2
  525. package/src/ui/hooks/queries/userCache.ts +169 -148
  526. package/src/ui/hooks/useAssets.ts +1 -1
  527. package/src/ui/hooks/useAsyncAction.ts +3 -3
  528. package/src/ui/hooks/useAuth.ts +12 -6
  529. package/src/ui/hooks/useAvatarPicker.ts +1 -1
  530. package/src/ui/hooks/useOnlineStatus.ts +3 -2
  531. package/src/ui/hooks/useSessionManagement.ts +0 -2
  532. package/src/ui/hooks/useSettingToggle.ts +1 -1
  533. package/src/ui/hooks/useSurfaceHeader.ts +90 -15
  534. package/src/ui/navigation/accountDialogManager.ts +9 -0
  535. package/src/ui/oauth/__tests__/oauthPopup.test.ts +26 -0
  536. package/src/ui/oauth/browserAuthTransport.ts +55 -49
  537. package/src/ui/oauth/oauthPopup.ts +61 -3
  538. package/src/ui/screens/AccountMembersScreen.tsx +1 -1
  539. package/src/ui/screens/AccountSettingsScreen.tsx +45 -17
  540. package/src/ui/screens/AccountVerificationScreen.tsx +1 -1
  541. package/src/ui/screens/AppInfoScreen.tsx +1 -1
  542. package/src/ui/screens/AvatarCropScreen.tsx +1 -1
  543. package/src/ui/screens/ChangeAvatarScreen.tsx +7 -4
  544. package/src/ui/screens/ConnectedAppsScreen.tsx +2 -2
  545. package/src/ui/screens/CreateAccountScreen.tsx +55 -15
  546. package/src/ui/screens/EditProfileFieldScreen.tsx +4 -4
  547. package/src/ui/screens/EditProfileScreen.tsx +6 -3
  548. package/src/ui/screens/FAQScreen.tsx +1 -1
  549. package/src/ui/screens/FeedbackScreen.tsx +741 -575
  550. package/src/ui/screens/FileManagementScreen.tsx +1 -1
  551. package/src/ui/screens/FollowersListScreen.tsx +3 -0
  552. package/src/ui/screens/FollowingListScreen.tsx +3 -0
  553. package/src/ui/screens/HelpSupportScreen.tsx +1 -1
  554. package/src/ui/screens/HistoryViewScreen.tsx +5 -20
  555. package/src/ui/screens/LanguageSelectorScreen.tsx +1 -1
  556. package/src/ui/screens/LegalDocumentsScreen.tsx +1 -1
  557. package/src/ui/screens/ManageAccountScreen.tsx +9 -2
  558. package/src/ui/screens/PremiumSubscriptionScreen.tsx +4 -4
  559. package/src/ui/screens/PrivacySettingsScreen.tsx +15 -12
  560. package/src/ui/screens/ProfileScreen.tsx +24 -6
  561. package/src/ui/screens/SavesCollectionsScreen.tsx +1 -1
  562. package/src/ui/screens/UserListScreen.tsx +18 -8
  563. package/src/ui/screens/WelcomeNewUserScreen.tsx +6 -6
  564. package/src/ui/screens/fileManagement/PhotoPickerSection.tsx +1 -1
  565. package/src/ui/screens/fileManagement/hooks/useFileUploadState.ts +14 -5
  566. package/src/ui/screens/trust/TrustCenterScreen.tsx +2 -2
  567. package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +6 -5
  568. package/src/ui/screens/trust/TrustRewardsScreen.tsx +1 -1
  569. package/src/ui/screens/trust/TrustRulesScreen.tsx +1 -1
  570. package/src/ui/screens/trust/trustTier.ts +1 -1
  571. package/src/ui/session/__tests__/backgroundSession.test.ts +392 -0
  572. package/src/ui/session/backgroundSession.ts +313 -0
  573. package/src/ui/session/index.ts +1 -0
  574. package/src/ui/session/useBackgroundSessionSync.ts +92 -0
  575. package/src/ui/types/navigation.ts +27 -19
  576. package/src/ui/utils/fileManagement.ts +108 -92
  577. package/src/ui/utils/netConnectivity.ts +27 -0
  578. package/src/ui/utils/oauthReturn.ts +23 -33
  579. package/src/ui/utils/userUtils.ts +27 -32
  580. package/lib/commonjs/ui/oauth/legacyRedirectLanes.js +0 -48
  581. package/lib/commonjs/ui/oauth/legacyRedirectLanes.js.map +0 -1
  582. package/lib/commonjs/ui/utils/crossOriginRestore.js +0 -140
  583. package/lib/commonjs/ui/utils/crossOriginRestore.js.map +0 -1
  584. package/lib/module/ui/oauth/legacyRedirectLanes.js +0 -44
  585. package/lib/module/ui/oauth/legacyRedirectLanes.js.map +0 -1
  586. package/lib/module/ui/utils/crossOriginRestore.js +0 -131
  587. package/lib/module/ui/utils/crossOriginRestore.js.map +0 -1
  588. package/lib/typescript/commonjs/ui/oauth/legacyRedirectLanes.d.ts +0 -40
  589. package/lib/typescript/commonjs/ui/oauth/legacyRedirectLanes.d.ts.map +0 -1
  590. package/lib/typescript/commonjs/ui/utils/crossOriginRestore.d.ts +0 -43
  591. package/lib/typescript/commonjs/ui/utils/crossOriginRestore.d.ts.map +0 -1
  592. package/lib/typescript/module/ui/oauth/legacyRedirectLanes.d.ts +0 -40
  593. package/lib/typescript/module/ui/oauth/legacyRedirectLanes.d.ts.map +0 -1
  594. package/lib/typescript/module/ui/utils/crossOriginRestore.d.ts +0 -43
  595. package/lib/typescript/module/ui/utils/crossOriginRestore.d.ts.map +0 -1
  596. package/src/ui/boot/__tests__/coldBootWebAuthMode.test.ts +0 -281
  597. package/src/ui/oauth/legacyRedirectLanes.ts +0 -43
  598. package/src/ui/utils/__tests__/crossOriginRestore.test.ts +0 -143
  599. 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 `'redirect'`; issue #691 Phase 2) — `'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.
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, refreshToken ?? '')` internally before returning. `useAuthOperations.performSignIn` no longer needs to hand-plant the token or call a session-token fallback — just await `verifyChallenge` and proceed.
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 android.util.Log
6
- import androidx.security.crypto.EncryptedSharedPreferences
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 AES256 encryption scheme can never drift between the two halves.
14
+ * the encryption scheme can never drift between the two halves.
19
15
  *
20
- * ## Single memoized instance (CRITICAL)
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
- * `EncryptedSharedPreferences.create()` must be called AT MOST ONCE per file per
23
- * process. It is NOT safe to re-instantiate: when a second instance is created
24
- * for the same file while another is live (e.g. the JS write thread and a Binder
25
- * thread serving [OxyIdentityProvider.call] concurrently), Tink's keyset load
26
- * races and the next decrypt throws `AEADBadTagException` which silently turned
27
- * every cross-app read into "no shared identity". So the instance is created once,
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
- * Opens the EncryptedSharedPreferences, self-healing a corrupted/unreadable
77
- * keyset once (see the "Keyset self-heal" note above). Both exception types the
78
- * androidx `create()` declares `GeneralSecurityException` (AEAD/GCM tag
79
- * mismatch) and `IOException` (unparseable keyset) route into recovery.
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 openOrHealPrefs(appContext: Context): SharedPreferences {
82
- return try {
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
+ }