@oxyhq/services 22.12.4 → 22.13.0

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 (446) hide show
  1. package/README.md +16 -46
  2. package/lib/commonjs/index.js +18 -12
  3. package/lib/commonjs/index.js.map +1 -1
  4. package/lib/commonjs/ui/boot/runProviderColdBoot.js +51 -9
  5. package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
  6. package/lib/commonjs/ui/client.js +0 -13
  7. package/lib/commonjs/ui/client.js.map +1 -1
  8. package/lib/commonjs/ui/components/OxyAccountDialogScreen.js +27 -4
  9. package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
  10. package/lib/commonjs/ui/components/OxyAuthChooser.js +72 -1123
  11. package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
  12. package/lib/commonjs/ui/components/OxyConsentScreen.js +50 -12
  13. package/lib/commonjs/ui/components/OxyConsentScreen.js.map +1 -1
  14. package/lib/commonjs/ui/components/OxyProvider.js +4 -4
  15. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  16. package/lib/commonjs/ui/components/OxySignInButton.js +74 -40
  17. package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
  18. package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +527 -0
  19. package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -0
  20. package/lib/commonjs/ui/components/authChooser/SignInEntryView.js +90 -0
  21. package/lib/commonjs/ui/components/authChooser/SignInEntryView.js.map +1 -0
  22. package/lib/commonjs/ui/components/authChooser/SignInRequestView.js +144 -0
  23. package/lib/commonjs/ui/components/authChooser/SignInRequestView.js.map +1 -0
  24. package/lib/commonjs/ui/components/authChooser/SignUpView.js +188 -0
  25. package/lib/commonjs/ui/components/authChooser/SignUpView.js.map +1 -0
  26. package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js +63 -0
  27. package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js.map +1 -0
  28. package/lib/commonjs/ui/components/authChooser/primitives.js +169 -0
  29. package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -0
  30. package/lib/commonjs/ui/components/authChooser/requestSurfaces.js +149 -0
  31. package/lib/commonjs/ui/components/authChooser/requestSurfaces.js.map +1 -0
  32. package/lib/commonjs/ui/components/authChooser/signInProgress.js +54 -0
  33. package/lib/commonjs/ui/components/authChooser/signInProgress.js.map +1 -0
  34. package/lib/commonjs/ui/components/{oxyAuthChooserStyles.js → authChooser/styles.js} +19 -1
  35. package/lib/commonjs/ui/components/authChooser/styles.js.map +1 -0
  36. package/lib/commonjs/ui/components/authChooser/types.js +59 -0
  37. package/lib/commonjs/ui/components/authChooser/types.js.map +1 -0
  38. package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js +50 -0
  39. package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js.map +1 -0
  40. package/lib/commonjs/ui/context/OxyContext.js +205 -30
  41. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  42. package/lib/commonjs/ui/context/commitSessionFlow.js +46 -0
  43. package/lib/commonjs/ui/context/commitSessionFlow.js.map +1 -1
  44. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +15 -2
  45. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  46. package/lib/commonjs/ui/context/useOxyAccountGraph.js +13 -5
  47. package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
  48. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +6 -1
  49. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +1 -1
  50. package/lib/commonjs/ui/index.js +0 -13
  51. package/lib/commonjs/ui/index.js.map +1 -1
  52. package/lib/commonjs/ui/oauth/browserAuthTransport.js +170 -0
  53. package/lib/commonjs/ui/oauth/browserAuthTransport.js.map +1 -0
  54. package/lib/commonjs/ui/oauth/completeOAuthCode.js +78 -0
  55. package/lib/commonjs/ui/oauth/completeOAuthCode.js.map +1 -0
  56. package/lib/commonjs/ui/oauth/legacyRedirectLanes.js +48 -0
  57. package/lib/commonjs/ui/oauth/legacyRedirectLanes.js.map +1 -0
  58. package/lib/commonjs/ui/oauth/oauthHandshake.js +53 -0
  59. package/lib/commonjs/ui/oauth/oauthHandshake.js.map +1 -0
  60. package/lib/commonjs/ui/oauth/oauthPopup.js +276 -0
  61. package/lib/commonjs/ui/oauth/oauthPopup.js.map +1 -0
  62. package/lib/commonjs/ui/oauth/oauthPopupMessages.js +141 -0
  63. package/lib/commonjs/ui/oauth/oauthPopupMessages.js.map +1 -0
  64. package/lib/commonjs/ui/oauth/types.js +2 -0
  65. package/lib/commonjs/ui/oauth/types.js.map +1 -0
  66. package/lib/commonjs/ui/server.js +2 -5
  67. package/lib/commonjs/ui/server.js.map +1 -1
  68. package/lib/commonjs/ui/session/authStore.js +6 -75
  69. package/lib/commonjs/ui/session/authStore.js.map +1 -1
  70. package/lib/commonjs/ui/session/createSessionClient.js +11 -3
  71. package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
  72. package/lib/commonjs/ui/session/identityBinding.js +95 -0
  73. package/lib/commonjs/ui/session/identityBinding.js.map +1 -0
  74. package/lib/commonjs/ui/session/index.js +13 -0
  75. package/lib/commonjs/ui/session/index.js.map +1 -1
  76. package/lib/commonjs/ui/session/nativeSecureStorage.js +90 -0
  77. package/lib/commonjs/ui/session/nativeSecureStorage.js.map +1 -0
  78. package/lib/commonjs/ui/session/tokenTransport.js +11 -1
  79. package/lib/commonjs/ui/session/tokenTransport.js.map +1 -1
  80. package/lib/commonjs/ui/utils/crossOriginRestore.js +5 -4
  81. package/lib/commonjs/ui/utils/crossOriginRestore.js.map +1 -1
  82. package/lib/commonjs/ui/utils/deliveryPlatform.js +58 -0
  83. package/lib/commonjs/ui/utils/deliveryPlatform.js.map +1 -0
  84. package/lib/commonjs/ui/utils/oauthReturn.js +72 -46
  85. package/lib/commonjs/ui/utils/oauthReturn.js.map +1 -1
  86. package/lib/module/index.js +9 -4
  87. package/lib/module/index.js.map +1 -1
  88. package/lib/module/ui/boot/runProviderColdBoot.js +51 -9
  89. package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
  90. package/lib/module/ui/client.js +0 -1
  91. package/lib/module/ui/client.js.map +1 -1
  92. package/lib/module/ui/components/OxyAccountDialogScreen.js +27 -4
  93. package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
  94. package/lib/module/ui/components/OxyAuthChooser.js +76 -1128
  95. package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
  96. package/lib/module/ui/components/OxyConsentScreen.js +50 -12
  97. package/lib/module/ui/components/OxyConsentScreen.js.map +1 -1
  98. package/lib/module/ui/components/OxyProvider.js +4 -4
  99. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  100. package/lib/module/ui/components/OxySignInButton.js +76 -42
  101. package/lib/module/ui/components/OxySignInButton.js.map +1 -1
  102. package/lib/module/ui/components/authChooser/AccountsMenuView.js +523 -0
  103. package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -0
  104. package/lib/module/ui/components/authChooser/SignInEntryView.js +85 -0
  105. package/lib/module/ui/components/authChooser/SignInEntryView.js.map +1 -0
  106. package/lib/module/ui/components/authChooser/SignInRequestView.js +139 -0
  107. package/lib/module/ui/components/authChooser/SignInRequestView.js.map +1 -0
  108. package/lib/module/ui/components/authChooser/SignUpView.js +184 -0
  109. package/lib/module/ui/components/authChooser/SignUpView.js.map +1 -0
  110. package/lib/module/ui/components/authChooser/TroubleDisclosure.js +59 -0
  111. package/lib/module/ui/components/authChooser/TroubleDisclosure.js.map +1 -0
  112. package/lib/module/ui/components/authChooser/primitives.js +163 -0
  113. package/lib/module/ui/components/authChooser/primitives.js.map +1 -0
  114. package/lib/module/ui/components/authChooser/requestSurfaces.js +139 -0
  115. package/lib/module/ui/components/authChooser/requestSurfaces.js.map +1 -0
  116. package/lib/module/ui/components/authChooser/signInProgress.js +50 -0
  117. package/lib/module/ui/components/authChooser/signInProgress.js.map +1 -0
  118. package/lib/module/ui/components/{oxyAuthChooserStyles.js → authChooser/styles.js} +19 -1
  119. package/lib/module/ui/components/authChooser/styles.js.map +1 -0
  120. package/lib/module/ui/components/authChooser/types.js +55 -0
  121. package/lib/module/ui/components/authChooser/types.js.map +1 -0
  122. package/lib/module/ui/components/authChooser/useUsernameAvailability.js +46 -0
  123. package/lib/module/ui/components/authChooser/useUsernameAvailability.js.map +1 -0
  124. package/lib/module/ui/context/OxyContext.js +208 -33
  125. package/lib/module/ui/context/OxyContext.js.map +1 -1
  126. package/lib/module/ui/context/commitSessionFlow.js +45 -0
  127. package/lib/module/ui/context/commitSessionFlow.js.map +1 -1
  128. package/lib/module/ui/context/hooks/useAuthOperations.js +15 -2
  129. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  130. package/lib/module/ui/context/useOxyAccountGraph.js +13 -5
  131. package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
  132. package/lib/module/ui/hooks/useSwitchableAccounts.js +6 -1
  133. package/lib/module/ui/hooks/useSwitchableAccounts.js.map +1 -1
  134. package/lib/module/ui/index.js +0 -1
  135. package/lib/module/ui/index.js.map +1 -1
  136. package/lib/module/ui/oauth/browserAuthTransport.js +167 -0
  137. package/lib/module/ui/oauth/browserAuthTransport.js.map +1 -0
  138. package/lib/module/ui/oauth/completeOAuthCode.js +75 -0
  139. package/lib/module/ui/oauth/completeOAuthCode.js.map +1 -0
  140. package/lib/module/ui/oauth/legacyRedirectLanes.js +44 -0
  141. package/lib/module/ui/oauth/legacyRedirectLanes.js.map +1 -0
  142. package/lib/module/ui/oauth/oauthHandshake.js +50 -0
  143. package/lib/module/ui/oauth/oauthHandshake.js.map +1 -0
  144. package/lib/module/ui/oauth/oauthPopup.js +268 -0
  145. package/lib/module/ui/oauth/oauthPopup.js.map +1 -0
  146. package/lib/module/ui/oauth/oauthPopupMessages.js +135 -0
  147. package/lib/module/ui/oauth/oauthPopupMessages.js.map +1 -0
  148. package/lib/module/ui/oauth/types.js +2 -0
  149. package/lib/module/ui/oauth/types.js.map +1 -0
  150. package/lib/module/ui/server.js +0 -2
  151. package/lib/module/ui/server.js.map +1 -1
  152. package/lib/module/ui/session/authStore.js +5 -75
  153. package/lib/module/ui/session/authStore.js.map +1 -1
  154. package/lib/module/ui/session/createSessionClient.js +11 -3
  155. package/lib/module/ui/session/createSessionClient.js.map +1 -1
  156. package/lib/module/ui/session/identityBinding.js +89 -0
  157. package/lib/module/ui/session/identityBinding.js.map +1 -0
  158. package/lib/module/ui/session/index.js +5 -2
  159. package/lib/module/ui/session/index.js.map +1 -1
  160. package/lib/module/ui/session/nativeSecureStorage.js +88 -0
  161. package/lib/module/ui/session/nativeSecureStorage.js.map +1 -0
  162. package/lib/module/ui/session/tokenTransport.js +11 -1
  163. package/lib/module/ui/session/tokenTransport.js.map +1 -1
  164. package/lib/module/ui/utils/crossOriginRestore.js +6 -5
  165. package/lib/module/ui/utils/crossOriginRestore.js.map +1 -1
  166. package/lib/module/ui/utils/deliveryPlatform.js +55 -0
  167. package/lib/module/ui/utils/deliveryPlatform.js.map +1 -0
  168. package/lib/module/ui/utils/oauthReturn.js +73 -47
  169. package/lib/module/ui/utils/oauthReturn.js.map +1 -1
  170. package/lib/typescript/commonjs/index.d.ts +4 -1
  171. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  172. package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts +34 -2
  173. package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  174. package/lib/typescript/commonjs/ui/client.d.ts +0 -1
  175. package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
  176. package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
  177. package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts +21 -29
  178. package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
  179. package/lib/typescript/commonjs/ui/components/OxyConsentScreen.d.ts.map +1 -1
  180. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  181. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts +17 -12
  182. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
  183. package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts +38 -0
  184. package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -0
  185. package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts +32 -0
  186. package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts.map +1 -0
  187. package/lib/typescript/commonjs/ui/components/authChooser/SignInRequestView.d.ts +34 -0
  188. package/lib/typescript/commonjs/ui/components/authChooser/SignInRequestView.d.ts.map +1 -0
  189. package/lib/typescript/commonjs/ui/components/authChooser/SignUpView.d.ts +34 -0
  190. package/lib/typescript/commonjs/ui/components/authChooser/SignUpView.d.ts.map +1 -0
  191. package/lib/typescript/commonjs/ui/components/authChooser/TroubleDisclosure.d.ts +43 -0
  192. package/lib/typescript/commonjs/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -0
  193. package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts +55 -0
  194. package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts.map +1 -0
  195. package/lib/typescript/commonjs/ui/components/authChooser/requestSurfaces.d.ts +54 -0
  196. package/lib/typescript/commonjs/ui/components/authChooser/requestSurfaces.d.ts.map +1 -0
  197. package/lib/typescript/commonjs/ui/components/authChooser/signInProgress.d.ts +26 -0
  198. package/lib/typescript/commonjs/ui/components/authChooser/signInProgress.d.ts.map +1 -0
  199. package/lib/typescript/{module/ui/components/oxyAuthChooserStyles.d.ts → commonjs/ui/components/authChooser/styles.d.ts} +19 -1
  200. package/lib/typescript/commonjs/ui/components/authChooser/styles.d.ts.map +1 -0
  201. package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +85 -0
  202. package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -0
  203. package/lib/typescript/commonjs/ui/components/authChooser/useUsernameAvailability.d.ts +16 -0
  204. package/lib/typescript/commonjs/ui/components/authChooser/useUsernameAvailability.d.ts.map +1 -0
  205. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  206. package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts +40 -0
  207. package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts.map +1 -1
  208. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +11 -2
  209. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  210. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +44 -1
  211. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
  212. package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +7 -1
  213. package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  214. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
  215. package/lib/typescript/commonjs/ui/index.d.ts +0 -1
  216. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  217. package/lib/typescript/commonjs/ui/oauth/browserAuthTransport.d.ts +56 -0
  218. package/lib/typescript/commonjs/ui/oauth/browserAuthTransport.d.ts.map +1 -0
  219. package/lib/typescript/commonjs/ui/oauth/completeOAuthCode.d.ts +49 -0
  220. package/lib/typescript/commonjs/ui/oauth/completeOAuthCode.d.ts.map +1 -0
  221. package/lib/typescript/commonjs/ui/oauth/legacyRedirectLanes.d.ts +40 -0
  222. package/lib/typescript/commonjs/ui/oauth/legacyRedirectLanes.d.ts.map +1 -0
  223. package/lib/typescript/commonjs/ui/oauth/oauthHandshake.d.ts +50 -0
  224. package/lib/typescript/commonjs/ui/oauth/oauthHandshake.d.ts.map +1 -0
  225. package/lib/typescript/commonjs/ui/oauth/oauthPopup.d.ts +83 -0
  226. package/lib/typescript/commonjs/ui/oauth/oauthPopup.d.ts.map +1 -0
  227. package/lib/typescript/commonjs/ui/oauth/oauthPopupMessages.d.ts +67 -0
  228. package/lib/typescript/commonjs/ui/oauth/oauthPopupMessages.d.ts.map +1 -0
  229. package/lib/typescript/commonjs/ui/oauth/types.d.ts +176 -0
  230. package/lib/typescript/commonjs/ui/oauth/types.d.ts.map +1 -0
  231. package/lib/typescript/commonjs/ui/server.d.ts +0 -2
  232. package/lib/typescript/commonjs/ui/server.d.ts.map +1 -1
  233. package/lib/typescript/commonjs/ui/session/authStore.d.ts +3 -7
  234. package/lib/typescript/commonjs/ui/session/authStore.d.ts.map +1 -1
  235. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +8 -1
  236. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
  237. package/lib/typescript/commonjs/ui/session/identityBinding.d.ts +66 -0
  238. package/lib/typescript/commonjs/ui/session/identityBinding.d.ts.map +1 -0
  239. package/lib/typescript/commonjs/ui/session/index.d.ts +6 -2
  240. package/lib/typescript/commonjs/ui/session/index.d.ts.map +1 -1
  241. package/lib/typescript/commonjs/ui/session/nativeSecureStorage.d.ts +25 -0
  242. package/lib/typescript/commonjs/ui/session/nativeSecureStorage.d.ts.map +1 -0
  243. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts +8 -1
  244. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts.map +1 -1
  245. package/lib/typescript/commonjs/ui/types/navigation.d.ts +44 -0
  246. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  247. package/lib/typescript/commonjs/ui/utils/crossOriginRestore.d.ts.map +1 -1
  248. package/lib/typescript/commonjs/ui/utils/deliveryPlatform.d.ts +33 -0
  249. package/lib/typescript/commonjs/ui/utils/deliveryPlatform.d.ts.map +1 -0
  250. package/lib/typescript/commonjs/ui/utils/oauthReturn.d.ts +30 -16
  251. package/lib/typescript/commonjs/ui/utils/oauthReturn.d.ts.map +1 -1
  252. package/lib/typescript/module/index.d.ts +4 -1
  253. package/lib/typescript/module/index.d.ts.map +1 -1
  254. package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts +34 -2
  255. package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  256. package/lib/typescript/module/ui/client.d.ts +0 -1
  257. package/lib/typescript/module/ui/client.d.ts.map +1 -1
  258. package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
  259. package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts +21 -29
  260. package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
  261. package/lib/typescript/module/ui/components/OxyConsentScreen.d.ts.map +1 -1
  262. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  263. package/lib/typescript/module/ui/components/OxySignInButton.d.ts +17 -12
  264. package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
  265. package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts +38 -0
  266. package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -0
  267. package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts +32 -0
  268. package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts.map +1 -0
  269. package/lib/typescript/module/ui/components/authChooser/SignInRequestView.d.ts +34 -0
  270. package/lib/typescript/module/ui/components/authChooser/SignInRequestView.d.ts.map +1 -0
  271. package/lib/typescript/module/ui/components/authChooser/SignUpView.d.ts +34 -0
  272. package/lib/typescript/module/ui/components/authChooser/SignUpView.d.ts.map +1 -0
  273. package/lib/typescript/module/ui/components/authChooser/TroubleDisclosure.d.ts +43 -0
  274. package/lib/typescript/module/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -0
  275. package/lib/typescript/module/ui/components/authChooser/primitives.d.ts +55 -0
  276. package/lib/typescript/module/ui/components/authChooser/primitives.d.ts.map +1 -0
  277. package/lib/typescript/module/ui/components/authChooser/requestSurfaces.d.ts +54 -0
  278. package/lib/typescript/module/ui/components/authChooser/requestSurfaces.d.ts.map +1 -0
  279. package/lib/typescript/module/ui/components/authChooser/signInProgress.d.ts +26 -0
  280. package/lib/typescript/module/ui/components/authChooser/signInProgress.d.ts.map +1 -0
  281. package/lib/typescript/{commonjs/ui/components/oxyAuthChooserStyles.d.ts → module/ui/components/authChooser/styles.d.ts} +19 -1
  282. package/lib/typescript/module/ui/components/authChooser/styles.d.ts.map +1 -0
  283. package/lib/typescript/module/ui/components/authChooser/types.d.ts +85 -0
  284. package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -0
  285. package/lib/typescript/module/ui/components/authChooser/useUsernameAvailability.d.ts +16 -0
  286. package/lib/typescript/module/ui/components/authChooser/useUsernameAvailability.d.ts.map +1 -0
  287. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  288. package/lib/typescript/module/ui/context/commitSessionFlow.d.ts +40 -0
  289. package/lib/typescript/module/ui/context/commitSessionFlow.d.ts.map +1 -1
  290. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +11 -2
  291. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  292. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +44 -1
  293. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
  294. package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +7 -1
  295. package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  296. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
  297. package/lib/typescript/module/ui/index.d.ts +0 -1
  298. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  299. package/lib/typescript/module/ui/oauth/browserAuthTransport.d.ts +56 -0
  300. package/lib/typescript/module/ui/oauth/browserAuthTransport.d.ts.map +1 -0
  301. package/lib/typescript/module/ui/oauth/completeOAuthCode.d.ts +49 -0
  302. package/lib/typescript/module/ui/oauth/completeOAuthCode.d.ts.map +1 -0
  303. package/lib/typescript/module/ui/oauth/legacyRedirectLanes.d.ts +40 -0
  304. package/lib/typescript/module/ui/oauth/legacyRedirectLanes.d.ts.map +1 -0
  305. package/lib/typescript/module/ui/oauth/oauthHandshake.d.ts +50 -0
  306. package/lib/typescript/module/ui/oauth/oauthHandshake.d.ts.map +1 -0
  307. package/lib/typescript/module/ui/oauth/oauthPopup.d.ts +83 -0
  308. package/lib/typescript/module/ui/oauth/oauthPopup.d.ts.map +1 -0
  309. package/lib/typescript/module/ui/oauth/oauthPopupMessages.d.ts +67 -0
  310. package/lib/typescript/module/ui/oauth/oauthPopupMessages.d.ts.map +1 -0
  311. package/lib/typescript/module/ui/oauth/types.d.ts +176 -0
  312. package/lib/typescript/module/ui/oauth/types.d.ts.map +1 -0
  313. package/lib/typescript/module/ui/server.d.ts +0 -2
  314. package/lib/typescript/module/ui/server.d.ts.map +1 -1
  315. package/lib/typescript/module/ui/session/authStore.d.ts +3 -7
  316. package/lib/typescript/module/ui/session/authStore.d.ts.map +1 -1
  317. package/lib/typescript/module/ui/session/createSessionClient.d.ts +8 -1
  318. package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
  319. package/lib/typescript/module/ui/session/identityBinding.d.ts +66 -0
  320. package/lib/typescript/module/ui/session/identityBinding.d.ts.map +1 -0
  321. package/lib/typescript/module/ui/session/index.d.ts +6 -2
  322. package/lib/typescript/module/ui/session/index.d.ts.map +1 -1
  323. package/lib/typescript/module/ui/session/nativeSecureStorage.d.ts +25 -0
  324. package/lib/typescript/module/ui/session/nativeSecureStorage.d.ts.map +1 -0
  325. package/lib/typescript/module/ui/session/tokenTransport.d.ts +8 -1
  326. package/lib/typescript/module/ui/session/tokenTransport.d.ts.map +1 -1
  327. package/lib/typescript/module/ui/types/navigation.d.ts +44 -0
  328. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  329. package/lib/typescript/module/ui/utils/crossOriginRestore.d.ts.map +1 -1
  330. package/lib/typescript/module/ui/utils/deliveryPlatform.d.ts +33 -0
  331. package/lib/typescript/module/ui/utils/deliveryPlatform.d.ts.map +1 -0
  332. package/lib/typescript/module/ui/utils/oauthReturn.d.ts +30 -16
  333. package/lib/typescript/module/ui/utils/oauthReturn.d.ts.map +1 -1
  334. package/package.json +6 -8
  335. package/src/index.ts +19 -5
  336. package/src/ui/boot/__tests__/coldBootWebAuthMode.test.ts +281 -0
  337. package/src/ui/boot/runProviderColdBoot.ts +83 -13
  338. package/src/ui/client.ts +0 -1
  339. package/src/ui/components/OxyAccountDialogScreen.tsx +33 -4
  340. package/src/ui/components/OxyAuthChooser.tsx +100 -1230
  341. package/src/ui/components/OxyConsentScreen.tsx +56 -13
  342. package/src/ui/components/OxyProvider.tsx +4 -4
  343. package/src/ui/components/OxySignInButton.tsx +119 -78
  344. package/src/ui/components/__tests__/OxyConsentScreen.test.tsx +27 -0
  345. package/src/ui/components/authChooser/AccountsMenuView.tsx +569 -0
  346. package/src/ui/components/authChooser/SignInEntryView.tsx +115 -0
  347. package/src/ui/components/authChooser/SignInRequestView.tsx +163 -0
  348. package/src/ui/components/authChooser/SignUpView.tsx +174 -0
  349. package/src/ui/components/authChooser/TroubleDisclosure.tsx +81 -0
  350. package/src/ui/components/authChooser/primitives.tsx +144 -0
  351. package/src/ui/components/authChooser/requestSurfaces.tsx +116 -0
  352. package/src/ui/components/authChooser/signInProgress.ts +54 -0
  353. package/src/ui/components/{oxyAuthChooserStyles.ts → authChooser/styles.ts} +18 -0
  354. package/src/ui/components/authChooser/types.ts +101 -0
  355. package/src/ui/components/authChooser/useUsernameAvailability.ts +60 -0
  356. package/src/ui/context/OxyContext.tsx +226 -24
  357. package/src/ui/context/__tests__/commitSessionFlow.test.ts +102 -0
  358. package/src/ui/context/commitSessionFlow.ts +64 -0
  359. package/src/ui/context/hooks/useAuthOperations.ts +26 -1
  360. package/src/ui/context/oxyContextTypes.ts +45 -1
  361. package/src/ui/context/useOxyAccountGraph.ts +19 -5
  362. package/src/ui/hooks/useSwitchableAccounts.ts +12 -1
  363. package/src/ui/index.ts +0 -1
  364. package/src/ui/oauth/__tests__/browserAuthTransport.test.ts +335 -0
  365. package/src/ui/oauth/__tests__/completeOAuthCode.test.ts +142 -0
  366. package/src/ui/oauth/__tests__/oauthPopup.test.ts +352 -0
  367. package/src/ui/oauth/__tests__/oauthPopupMessages.test.ts +189 -0
  368. package/src/ui/oauth/__tests__/sharedCompletionPath.test.ts +174 -0
  369. package/src/ui/oauth/browserAuthTransport.ts +193 -0
  370. package/src/ui/oauth/completeOAuthCode.ts +93 -0
  371. package/src/ui/oauth/legacyRedirectLanes.ts +43 -0
  372. package/src/ui/oauth/oauthHandshake.ts +74 -0
  373. package/src/ui/oauth/oauthPopup.ts +277 -0
  374. package/src/ui/oauth/oauthPopupMessages.ts +136 -0
  375. package/src/ui/oauth/types.ts +150 -0
  376. package/src/ui/server.ts +0 -2
  377. package/src/ui/session/__tests__/createSessionClient.test.ts +26 -0
  378. package/src/ui/session/__tests__/tokenTransport.test.ts +34 -0
  379. package/src/ui/session/authStore.ts +5 -89
  380. package/src/ui/session/createSessionClient.ts +15 -2
  381. package/src/ui/session/identityBinding.ts +116 -0
  382. package/src/ui/session/index.ts +6 -2
  383. package/src/ui/session/nativeSecureStorage.ts +98 -0
  384. package/src/ui/session/tokenTransport.ts +14 -1
  385. package/src/ui/types/navigation.ts +44 -1
  386. package/src/ui/utils/__tests__/crossOriginRestore.test.ts +17 -0
  387. package/src/ui/utils/crossOriginRestore.ts +9 -13
  388. package/src/ui/utils/deliveryPlatform.ts +65 -0
  389. package/src/ui/utils/oauthReturn.ts +79 -65
  390. package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-Black.ttf +0 -0
  391. package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-Bold.ttf +0 -0
  392. package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-ExtraBold.ttf +0 -0
  393. package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-Light.ttf +0 -0
  394. package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-Medium.ttf +0 -0
  395. package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-Regular.ttf +0 -0
  396. package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-SemiBold.ttf +0 -0
  397. package/lib/commonjs/assets/fonts/Inter/Inter_18pt-Black.ttf +0 -0
  398. package/lib/commonjs/assets/fonts/Inter/Inter_18pt-Bold.ttf +0 -0
  399. package/lib/commonjs/assets/fonts/Inter/Inter_18pt-ExtraBold.ttf +0 -0
  400. package/lib/commonjs/assets/fonts/Inter/Inter_18pt-Light.ttf +0 -0
  401. package/lib/commonjs/assets/fonts/Inter/Inter_18pt-Medium.ttf +0 -0
  402. package/lib/commonjs/assets/fonts/Inter/Inter_18pt-Regular.ttf +0 -0
  403. package/lib/commonjs/assets/fonts/Inter/Inter_18pt-SemiBold.ttf +0 -0
  404. package/lib/commonjs/ui/components/FontLoader.js +0 -35
  405. package/lib/commonjs/ui/components/FontLoader.js.map +0 -1
  406. package/lib/commonjs/ui/components/FontLoader.native.js +0 -88
  407. package/lib/commonjs/ui/components/FontLoader.native.js.map +0 -1
  408. package/lib/commonjs/ui/components/oxyAuthChooserStyles.js.map +0 -1
  409. package/lib/module/assets/assets/fonts/Inter/Inter_18pt-Black.ttf +0 -0
  410. package/lib/module/assets/assets/fonts/Inter/Inter_18pt-Bold.ttf +0 -0
  411. package/lib/module/assets/assets/fonts/Inter/Inter_18pt-ExtraBold.ttf +0 -0
  412. package/lib/module/assets/assets/fonts/Inter/Inter_18pt-Light.ttf +0 -0
  413. package/lib/module/assets/assets/fonts/Inter/Inter_18pt-Medium.ttf +0 -0
  414. package/lib/module/assets/assets/fonts/Inter/Inter_18pt-Regular.ttf +0 -0
  415. package/lib/module/assets/assets/fonts/Inter/Inter_18pt-SemiBold.ttf +0 -0
  416. package/lib/module/assets/fonts/Inter/Inter_18pt-Black.ttf +0 -0
  417. package/lib/module/assets/fonts/Inter/Inter_18pt-Bold.ttf +0 -0
  418. package/lib/module/assets/fonts/Inter/Inter_18pt-ExtraBold.ttf +0 -0
  419. package/lib/module/assets/fonts/Inter/Inter_18pt-Light.ttf +0 -0
  420. package/lib/module/assets/fonts/Inter/Inter_18pt-Medium.ttf +0 -0
  421. package/lib/module/assets/fonts/Inter/Inter_18pt-Regular.ttf +0 -0
  422. package/lib/module/assets/fonts/Inter/Inter_18pt-SemiBold.ttf +0 -0
  423. package/lib/module/ui/components/FontLoader.js +0 -29
  424. package/lib/module/ui/components/FontLoader.js.map +0 -1
  425. package/lib/module/ui/components/FontLoader.native.js +0 -83
  426. package/lib/module/ui/components/FontLoader.native.js.map +0 -1
  427. package/lib/module/ui/components/oxyAuthChooserStyles.js.map +0 -1
  428. package/lib/typescript/commonjs/ui/components/FontLoader.d.ts +0 -24
  429. package/lib/typescript/commonjs/ui/components/FontLoader.d.ts.map +0 -1
  430. package/lib/typescript/commonjs/ui/components/FontLoader.native.d.ts +0 -13
  431. package/lib/typescript/commonjs/ui/components/FontLoader.native.d.ts.map +0 -1
  432. package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts.map +0 -1
  433. package/lib/typescript/module/ui/components/FontLoader.d.ts +0 -24
  434. package/lib/typescript/module/ui/components/FontLoader.d.ts.map +0 -1
  435. package/lib/typescript/module/ui/components/FontLoader.native.d.ts +0 -13
  436. package/lib/typescript/module/ui/components/FontLoader.native.d.ts.map +0 -1
  437. package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts.map +0 -1
  438. package/src/assets/fonts/Inter/Inter_18pt-Black.ttf +0 -0
  439. package/src/assets/fonts/Inter/Inter_18pt-Bold.ttf +0 -0
  440. package/src/assets/fonts/Inter/Inter_18pt-ExtraBold.ttf +0 -0
  441. package/src/assets/fonts/Inter/Inter_18pt-Light.ttf +0 -0
  442. package/src/assets/fonts/Inter/Inter_18pt-Medium.ttf +0 -0
  443. package/src/assets/fonts/Inter/Inter_18pt-Regular.ttf +0 -0
  444. package/src/assets/fonts/Inter/Inter_18pt-SemiBold.ttf +0 -0
  445. package/src/ui/components/FontLoader.native.tsx +0 -88
  446. package/src/ui/components/FontLoader.tsx +0 -27
@@ -21,6 +21,7 @@ import type {
21
21
  } from '@oxyhq/core';
22
22
  import {
23
23
  KeyManager,
24
+ establishIdentitySession,
24
25
  installAuthRefreshHandler,
25
26
  startTokenRefreshScheduler,
26
27
  createAccountDialogController,
@@ -33,7 +34,13 @@ import {
33
34
  } from '../navigation/accountDialogManager';
34
35
  import { redirectToAuthorize } from '../components/oauthNavigation';
35
36
  import { openPasskeyHubPopup } from '../components/passkeyHubPopup';
37
+ import {
38
+ startWebOAuthSignIn,
39
+ type StartWebOAuthSignInOptions,
40
+ } from '../oauth/browserAuthTransport';
41
+ import type { WebOAuthSignInResult } from '../oauth/types';
36
42
  import { isWebBrowser } from '../utils/isWebBrowser';
43
+ import { resolveDeliveryPlatform } from '../utils/deliveryPlatform';
37
44
  import { runProviderColdBoot } from '../boot/runProviderColdBoot';
38
45
  import { loadPersistedDeviceCredential } from '../utils/deviceCredential';
39
46
  import { useAuthStore, type AuthState } from '../stores/authStore';
@@ -57,11 +64,14 @@ import { useAvatarPicker } from '../hooks/useAvatarPicker';
57
64
  import { useAccountStore } from '../stores/accountStore';
58
65
  import {
59
66
  createSessionClient,
67
+ createIdentitySessionBinding,
60
68
  createPlatformAuthStateStore,
61
69
  deviceStateToClientSessions,
62
70
  activeSessionIdOf,
63
71
  activeUserOf,
64
72
  accountIdsOf,
73
+ IdentityBoundSessionError,
74
+ type IdentitySessionBinding,
65
75
  } from '../session';
66
76
  import type {
67
77
  OxyContextState,
@@ -69,7 +79,7 @@ import type {
69
79
  CommitInput,
70
80
  } from './oxyContextTypes';
71
81
  import { DEFAULT_SESSION_VALIDITY_MS, loadUseFollowHook } from './oxyContextHelpers';
72
- import { commitDeviceSetAndResolve } from './commitSessionFlow';
82
+ import { commitDeviceSetAndResolve, maybeSyncHubAfterCommit } from './commitSessionFlow';
73
83
  import { runPasskeyLogin, runPasskeyRegister, runPasskeyAdd } from './passkeyFlow';
74
84
  import {
75
85
  isPasskeySupported,
@@ -92,6 +102,8 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
92
102
  authorizeBaseUrl,
93
103
  storageKeyPrefix = 'oxy_session',
94
104
  clientId: clientIdProp,
105
+ sessionMode = 'account',
106
+ webAuthMode = 'redirect',
95
107
  hubSync = true,
96
108
  onAuthStateChange,
97
109
  onError,
@@ -119,6 +131,23 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
119
131
  }
120
132
  const authStore = authStoreRef.current;
121
133
 
134
+ // Identity-bound session binding (`sessionMode: 'identity'`) — the platform pin
135
+ // store plus the memoised pinned account id every lane below binds to. Built
136
+ // ONCE per provider mount and ONLY in identity mode: an account-mode provider
137
+ // never constructs one, so `identity` is `null` there and every branch keyed on
138
+ // it collapses to today's behaviour.
139
+ //
140
+ // Like `oxyServices` / `baseURL`, `sessionMode` is mount-time configuration: it
141
+ // is read once and a later change does not re-bind the provider. Latching it
142
+ // this way also fails SAFE — a provider that started identity-bound can never
143
+ // be downgraded mid-flight into following the device's active account.
144
+ const identityRef = useRef<IdentitySessionBinding | null>(null);
145
+ if (sessionMode === 'identity' && !identityRef.current) {
146
+ identityRef.current = createIdentitySessionBinding();
147
+ }
148
+ const identity = identityRef.current;
149
+ const isIdentityBound = identity !== null;
150
+
122
151
  const {
123
152
  user,
124
153
  isAuthenticated,
@@ -309,10 +338,65 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
309
338
  logger('Failed to clear local state on remote sign-out', clearError);
310
339
  });
311
340
  },
341
+ // `null` for every account-mode provider — identical to omitting the
342
+ // option — and the pinned account id once an identity-bound provider has
343
+ // resolved its pin. Read through the ref so the resolver stays stable for
344
+ // the client's lifetime while still seeing later resolutions.
345
+ () => identityRef.current?.getPinnedAccountId() ?? null,
312
346
  );
313
347
  }
314
348
  const { client: sessionClient, host: sessionClientHost } = sessionClientPairRef.current;
315
349
 
350
+ // Re-establishment lane for an identity-bound provider whose pin is missing or
351
+ // whose pinned account is no longer part of this device's session set (another
352
+ // app signed it out, or the device was re-provisioned). Adopting whichever
353
+ // account the device is currently switched to is exactly the drift
354
+ // `sessionMode: 'identity'` exists to prevent, so the only correct answer is to
355
+ // re-derive the session from the local identity key.
356
+ //
357
+ // Bounded on purpose: at most ONE attempt per applied device revision, and
358
+ // never two concurrently — a repeated failure (locked keychain, offline, no
359
+ // local identity) must never spin a sign-in loop against the server. Nothing
360
+ // awaits it, so it can never deadlock against the projection that triggers it;
361
+ // the `bootstrap()` below re-applies device state, whose notify re-runs the
362
+ // projection with the pin now resolved.
363
+ const identityEstablishRef = useRef<{ revision: number | null; inFlight: boolean }>({
364
+ revision: null,
365
+ inFlight: false,
366
+ });
367
+
368
+ const reestablishIdentitySession = useCallback(
369
+ (binding: IdentitySessionBinding, revision: number): void => {
370
+ const tracker = identityEstablishRef.current;
371
+ if (tracker.inFlight || tracker.revision === revision) {
372
+ return;
373
+ }
374
+ tracker.revision = revision;
375
+ tracker.inFlight = true;
376
+ void (async () => {
377
+ const established = await establishIdentitySession({
378
+ oxy: oxyServices,
379
+ store: authStore,
380
+ binding: binding.binding,
381
+ });
382
+ if (!established) {
383
+ return;
384
+ }
385
+ // The verify wrote a fresh pin; adopt it before re-reading device state
386
+ // so the notify that follows projects against the new binding.
387
+ await binding.refreshPinnedAccountId();
388
+ await sessionClient.bootstrap();
389
+ })()
390
+ .catch((establishError) => {
391
+ logger('Failed to re-establish the identity-bound session', establishError);
392
+ })
393
+ .finally(() => {
394
+ tracker.inFlight = false;
395
+ });
396
+ },
397
+ [oxyServices, authStore, sessionClient, logger],
398
+ );
399
+
316
400
  // Projects `client.getState()` onto the exposed `sessions`/`activeSessionId`/
317
401
  // `user`. Sole authority for both locally-initiated mutations (switch/logout)
318
402
  // AND remotely-pushed `session_state` over the `device:<deviceId>` socket.
@@ -334,6 +418,13 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
334
418
  // Last-write-wins guard: capture the revision this projection is for, then
335
419
  // after the async profile fetch bail if a fresher state has landed.
336
420
  const capturedRevision = state.revision;
421
+ // Resolve the pin BEFORE the profile fetch (memoised: one storage + keychain
422
+ // read per boot) so every projection below is bound on the FIRST pass over a
423
+ // freshly applied state. Deferring it would let a sibling app's switch render
424
+ // once as this client's user before the pin corrected it. `null` for every
425
+ // account-mode provider, where each projection falls back to the device's
426
+ // active account exactly as before.
427
+ const pinnedAccountId = identity ? await identity.ensurePinnedAccountId() : null;
337
428
  const ids = accountIdsOf(state);
338
429
  let users: User[] = [];
339
430
  try {
@@ -346,16 +437,29 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
346
437
  if (!latest || latest.revision !== capturedRevision) {
347
438
  return;
348
439
  }
440
+ if (identity && (pinnedAccountId === null || activeSessionIdOf(latest, pinnedAccountId) === null)) {
441
+ // Either no verified pin, or the pinned account left this device's session
442
+ // set. Projecting anything here would mean projecting SOMEBODY ELSE's
443
+ // account, so leave the current projection untouched and re-derive the
444
+ // session from the local identity key instead.
445
+ reestablishIdentitySession(identity, latest.revision);
446
+ return;
447
+ }
349
448
  const usersById = new Map(users.map((resolvedUser) => [resolvedUser.id, resolvedUser]));
350
- updateSessions(deviceStateToClientSessions(latest, usersById));
351
- setActiveSessionId(activeSessionIdOf(latest));
352
- const activeUser = activeUserOf(latest, usersById);
449
+ updateSessions(deviceStateToClientSessions(latest, usersById, pinnedAccountId));
450
+ setActiveSessionId(activeSessionIdOf(latest, pinnedAccountId));
451
+ const activeUser = activeUserOf(latest, usersById, pinnedAccountId);
353
452
  if (activeUser) {
354
453
  loginSuccess(activeUser);
355
454
  }
356
- sessionClientHost.setCurrentAccountId(latest.activeAccountId);
455
+ // The host's current account feeds the socket handshake and the bearer's
456
+ // token-account comparisons, so it tracks the PIN — never the account another
457
+ // app switched the device to.
458
+ sessionClientHost.setCurrentAccountId(pinnedAccountId ?? latest.activeAccountId);
357
459
  }, [
460
+ identity,
358
461
  oxyServices,
462
+ reestablishIdentitySession,
359
463
  sessionClient,
360
464
  sessionClientHost,
361
465
  updateSessions,
@@ -397,6 +501,12 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
397
501
  logoutStore,
398
502
  setAuthState,
399
503
  logger,
504
+ ...(identity
505
+ ? {
506
+ identityBinding: identity.binding,
507
+ refreshPinnedAccountId: () => identity.refreshPinnedAccountId(),
508
+ }
509
+ : {}),
400
510
  });
401
511
 
402
512
  // "That wasn't me": repudiating a flagged sign-in IS revoking the device
@@ -483,14 +593,21 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
483
593
  // credential via `POST /session/device/token`) plus the proactive scheduler
484
594
  // that refreshes ~60s before expiry and on tab-focus. Replaces the deleted
485
595
  // services-local `inSessionTokenRefresh` module.
596
+ // An identity binding turns every re-mint pinned: it targets the pinned
597
+ // account instead of the device's active one, and its recovery arm becomes the
598
+ // PRIMARY-key identity sign-in rather than the cross-app shared keychain.
486
599
  useEffect(() => {
487
- const dispose = installAuthRefreshHandler({ oxy: oxyServices, store: authStore });
600
+ const dispose = installAuthRefreshHandler({
601
+ oxy: oxyServices,
602
+ store: authStore,
603
+ ...(identity ? { identity: identity.binding } : {}),
604
+ });
488
605
  const scheduler = startTokenRefreshScheduler(oxyServices);
489
606
  return () => {
490
607
  scheduler.dispose();
491
608
  dispose();
492
609
  };
493
- }, [oxyServices, authStore]);
610
+ }, [oxyServices, authStore, identity]);
494
611
 
495
612
  // ── Session commit funnel ────────────────────────────────────────────────
496
613
  // The single place a session becomes committed: plant tokens, persist the
@@ -571,21 +688,20 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
571
688
  markAuthResolved: markAuthResolvedRef.current,
572
689
  });
573
690
 
574
- if (options.activate && options.hubSync && hubSync && isWebBrowser()) {
575
- try {
576
- await syncHubAfterSignIn(oxyServices, { enabled: hubSync });
577
- } catch (hubError) {
578
- if (__DEV__) {
579
- loggerUtil.debug(
580
- 'Hub sync after sign-in failed (non-fatal)',
581
- { component: 'OxyContext', method: 'commitSession' },
582
- hubError as unknown,
583
- );
584
- }
585
- }
586
- }
691
+ // Post-sign-in hub sync a full-page redirect to auth.oxy.so. Gated HERE,
692
+ // inside the ONE commit funnel, so NO commit path (QR device flow,
693
+ // password/2FA through the account dialog, passkey, popup OAuth, cold
694
+ // boot) can bounce a `webAuthMode: 'popup'` tab to the IdP. See
695
+ // `maybeSyncHubAfterCommit` for the full gate list.
696
+ await maybeSyncHubAfterCommit({
697
+ activate: options.activate,
698
+ hubSyncRequested: options.hubSync === true,
699
+ hubSyncEnabled: hubSync,
700
+ webAuthMode,
701
+ syncHub: () => syncHubAfterSignIn(oxyServices, { enabled: hubSync }),
702
+ });
587
703
  },
588
- [oxyServices, authStore, updateSessions, setActiveSessionId, sessionClient, sessionClientHost, syncFromClient, loginSuccess, logger, hubSync],
704
+ [oxyServices, authStore, updateSessions, setActiveSessionId, sessionClient, sessionClientHost, syncFromClient, loginSuccess, logger, hubSync, webAuthMode],
589
705
  );
590
706
  const commitSessionRef = useRef(commitSession);
591
707
  commitSessionRef.current = commitSession;
@@ -638,6 +754,33 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
638
754
  [commitSession],
639
755
  );
640
756
 
757
+ // ── Web third-party OAuth sign-in ──────────────────────────────────────────
758
+ // The shared operation `OxySignInButton` (and any RP-owned button) delegates
759
+ // to, so no consumer ever writes popup listeners or a token exchange. The
760
+ // transport picks popup vs redirect from `webAuthMode`; both lanes land on the
761
+ // SAME completion path (`completeOAuthCode`) and the same commit funnel here.
762
+ //
763
+ // `hubSync: false` is deliberate: hub sync is a full-page redirect to
764
+ // auth.oxy.so, which would undo the one thing popup mode exists to guarantee —
765
+ // that the relying party never leaves its route. It matches how the redirect
766
+ // lane's return leg commits (see `runProviderColdBoot`).
767
+ const startWebOAuthSignInForContext = useCallback(
768
+ (options: StartWebOAuthSignInOptions): Promise<WebOAuthSignInResult> =>
769
+ startWebOAuthSignIn(
770
+ {
771
+ mode: webAuthMode,
772
+ oxyServices,
773
+ clientId,
774
+ authorizeBaseUrl,
775
+ identityBound: isIdentityBound,
776
+ commitSession: (input) =>
777
+ commitSessionRef.current(input, { activate: true, hubSync: false }),
778
+ },
779
+ options,
780
+ ),
781
+ [webAuthMode, oxyServices, clientId, authorizeBaseUrl, isIdentityBound],
782
+ );
783
+
641
784
  // ── Unified account dialog ─────────────────────────────────────────────────
642
785
  // The single account-chooser + sign-in surface. Built ONCE per provider mount
643
786
  // and bound to the live `oxyServices` + `sessionClient` + this provider's
@@ -679,8 +822,13 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
679
822
  accountDialogSurfaceRef.current?.dismiss();
680
823
  }, []);
681
824
 
825
+ // Never built while identity-bound: the dialog exists to CHOOSE an account,
826
+ // and an identity-bound client has exactly one — the owner of the local
827
+ // identity key. Leaving it `null` (the same shape consumers see with no
828
+ // provider mounted) keeps its QR/polling machinery off entirely instead of
829
+ // relying on UI-level filtering to hide it.
682
830
  const accountDialogControllerRef = useRef<AccountDialogController | null>(null);
683
- if (!accountDialogControllerRef.current) {
831
+ if (!isIdentityBound && !accountDialogControllerRef.current) {
684
832
  accountDialogControllerRef.current = createAccountDialogController({
685
833
  oxyServices,
686
834
  sessionClient,
@@ -714,11 +862,27 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
714
862
  // popup concept there, and off-origin passkey sign-in isn't reachable
715
863
  // (Commons owns the native flow).
716
864
  openPopup: isWebBrowser() ? openPasskeyHubPopup : undefined,
865
+ // Which surface a sign-in starts from — a FACT only this consumer can
866
+ // supply (headless core never touches a platform global). It decides
867
+ // whether automatic delivery may take the same-device Commons deep link;
868
+ // without it the controller stays `'unknown'` and that route never fires.
869
+ platform: resolveDeliveryPlatform(),
717
870
  });
718
871
  }
719
872
  const accountDialogController = accountDialogControllerRef.current;
720
873
 
721
874
  const openAccountDialog = useCallback((view?: AccountDialogView): void => {
875
+ if (isIdentityBound) {
876
+ // No controller was built, and there is nothing to choose: this app's user
877
+ // is fixed to the owner of the local identity key.
878
+ if (__DEV__) {
879
+ loggerUtil.warn(
880
+ 'openAccountDialog ignored: the session is identity-bound (sessionMode: "identity")',
881
+ { component: 'OxyContext', method: 'openAccountDialog' },
882
+ );
883
+ }
884
+ return;
885
+ }
722
886
  const nextView = view ?? 'accounts';
723
887
  accountDialogControllerRef.current?.setView(nextView);
724
888
 
@@ -762,7 +926,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
762
926
  }
763
927
  });
764
928
  setAccountDialogOpen(true);
765
- }, []);
929
+ }, [isIdentityBound]);
766
930
 
767
931
  const closeAccountDialog = useCallback((): void => {
768
932
  accountDialogControllerRef.current?.cancelSignIn();
@@ -867,24 +1031,46 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
867
1031
  // ── Cold boot ────────────────────────────────────────────────────────────
868
1032
  // Device-first session restore via `runProviderColdBoot` (see boot/runProviderColdBoot.ts).
869
1033
  const runColdBoot = useCallback(async (): Promise<void> => {
1034
+ // Resolve the pin BEFORE any boot lane runs: `getPinnedAccountId()` is a
1035
+ // SYNCHRONOUS read on `SessionClient`'s apply path, and a stale `null` there
1036
+ // would let a pinned client behave like an account-mode one for the first
1037
+ // state it applies (including re-electing the device's active account).
1038
+ if (identity) {
1039
+ await identity.ensurePinnedAccountId();
1040
+ }
870
1041
  await runProviderColdBoot({
871
1042
  oxyServices,
872
1043
  authStore,
873
1044
  clientId: clientIdProp,
874
1045
  authRedirectUri,
875
1046
  authorizeBaseUrl,
1047
+ sessionMode,
1048
+ identity: identity?.binding,
1049
+ // Popup mode forbids the boot's automatic full-page `prompt=none` restore:
1050
+ // a domain with no local credential resolves signed out instead of
1051
+ // navigating the tab to the IdP (see `allowsAutomaticIdpRedirect`).
1052
+ webAuthMode,
876
1053
  sessionClient,
877
1054
  syncDeviceCredentialToHost,
878
1055
  commitSession: (input, options) => commitSessionRef.current(input, options),
879
1056
  markAuthResolved: () => markAuthResolvedRef.current(),
880
1057
  setTokenReady,
881
1058
  });
1059
+ // The boot may have (re-)established the identity session or cleared a pin
1060
+ // whose key no longer exists, so re-resolve rather than trusting the memo
1061
+ // taken before it ran.
1062
+ if (identity) {
1063
+ await identity.refreshPinnedAccountId();
1064
+ }
882
1065
  }, [
883
1066
  oxyServices,
884
1067
  authStore,
885
1068
  clientIdProp,
886
1069
  authRedirectUri,
887
1070
  authorizeBaseUrl,
1071
+ sessionMode,
1072
+ identity,
1073
+ webAuthMode,
888
1074
  syncDeviceCredentialToHost,
889
1075
  sessionClient,
890
1076
  ]);
@@ -974,8 +1160,14 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
974
1160
  // Exposed `switchSession`: route through the server-authoritative
975
1161
  // `SessionClient`. Resolve the target account from the current device state,
976
1162
  // ask the server to switch, reproject, and return the now-active user.
1163
+ //
1164
+ // Identity-bound providers reject: this is the session-keyed twin of
1165
+ // `switchToAccount`, so leaving it live would be a bypass around the same rule.
977
1166
  const switchSessionForContext = useCallback(
978
1167
  async (sessionId: string): Promise<User> => {
1168
+ if (isIdentityBound) {
1169
+ throw new IdentityBoundSessionError('switchSession');
1170
+ }
979
1171
  const targetAccountId = sessionClient
980
1172
  .getState()
981
1173
  ?.accounts.find((account) => account.sessionId === sessionId)?.accountId;
@@ -990,7 +1182,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
990
1182
  }
991
1183
  return activeUser;
992
1184
  },
993
- [sessionClient, syncFromClient],
1185
+ [isIdentityBound, sessionClient, syncFromClient],
994
1186
  );
995
1187
 
996
1188
  // Thin passthroughs to the platform-agnostic KeyManager. NOTE: both now THROW
@@ -1021,6 +1213,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1021
1213
  isAuthenticated,
1022
1214
  tokenReady,
1023
1215
  initialized,
1216
+ identityBound: isIdentityBound,
1024
1217
  oxyServices,
1025
1218
  sessionClient,
1026
1219
  syncFromClient,
@@ -1046,6 +1239,8 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1046
1239
  isPrivateApiPending,
1047
1240
  isAuthResolved: authResolved,
1048
1241
  isStorageReady: storage !== null,
1242
+ sessionMode: isIdentityBound ? 'identity' : 'account',
1243
+ webAuthMode,
1049
1244
  error,
1050
1245
  currentLanguage,
1051
1246
  currentLanguages,
@@ -1061,6 +1256,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1061
1256
  removePasskey,
1062
1257
  revokeSuspiciousSignIn,
1063
1258
  handleWebSession,
1259
+ startWebOAuthSignIn: startWebOAuthSignInForContext,
1064
1260
  logout,
1065
1261
  logoutAll,
1066
1262
  switchSession: switchSessionForContext,
@@ -1100,6 +1296,8 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1100
1296
  isPrivateApiPending,
1101
1297
  authResolved,
1102
1298
  storage,
1299
+ isIdentityBound,
1300
+ webAuthMode,
1103
1301
  error,
1104
1302
  currentLanguage,
1105
1303
  currentLanguages,
@@ -1115,6 +1313,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1115
1313
  removePasskey,
1116
1314
  revokeSuspiciousSignIn,
1117
1315
  handleWebSession,
1316
+ startWebOAuthSignInForContext,
1118
1317
  logout,
1119
1318
  logoutAll,
1120
1319
  switchSessionForContext,
@@ -1172,6 +1371,8 @@ const LOADING_STATE: OxyContextState = {
1172
1371
  isPrivateApiPending: true,
1173
1372
  isAuthResolved: false,
1174
1373
  isStorageReady: false,
1374
+ sessionMode: 'account',
1375
+ webAuthMode: 'redirect',
1175
1376
  error: null,
1176
1377
  currentLanguage: 'en-US',
1177
1378
  currentLanguages: [],
@@ -1187,6 +1388,7 @@ const LOADING_STATE: OxyContextState = {
1187
1388
  removePasskey: () => rejectMissingProvider<void>(),
1188
1389
  revokeSuspiciousSignIn: () => rejectMissingProvider<void>(),
1189
1390
  handleWebSession: () => rejectMissingProvider<void>(),
1391
+ startWebOAuthSignIn: () => rejectMissingProvider<WebOAuthSignInResult>(),
1190
1392
  logout: () => rejectMissingProvider<void>(),
1191
1393
  logoutAll: () => rejectMissingProvider<void>(),
1192
1394
  switchSession: () => rejectMissingProvider<User>(),
@@ -1,8 +1,11 @@
1
1
  import type { User } from '@oxyhq/core';
2
2
  import { logger } from '@oxyhq/core';
3
+ import type { WebAuthMode } from '../../oauth/types';
3
4
  import {
4
5
  commitDeviceSetAndResolve,
6
+ maybeSyncHubAfterCommit,
5
7
  type CommitDeviceSetAndResolveDeps,
8
+ type MaybeSyncHubAfterCommitDeps,
6
9
  } from '../commitSessionFlow';
7
10
 
8
11
  // A resolved promise chain (addCurrentAccount -> start -> syncFromClient) settles
@@ -129,6 +132,19 @@ describe('commitDeviceSetAndResolve — cold boot (activate: false)', () => {
129
132
  expect(deps.markAuthResolved).toHaveBeenCalledTimes(1);
130
133
  });
131
134
 
135
+ it('falls back to the minimal commit-input user when getCurrentUser returns null', async () => {
136
+ const order: string[] = [];
137
+ const deps = buildDeps(order, {
138
+ getCurrentUser: jest.fn(async () => null),
139
+ });
140
+
141
+ await commitDeviceSetAndResolve(deps);
142
+ await flush();
143
+
144
+ expect(deps.loginSuccess).toHaveBeenCalledWith(makeUser('fallback'));
145
+ expect(deps.markAuthResolved).toHaveBeenCalledTimes(1);
146
+ });
147
+
132
148
  it('logs (never swallows) a failing background reconcile and still resolves', async () => {
133
149
  const warnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => undefined);
134
150
  const order: string[] = [];
@@ -178,6 +194,92 @@ describe('commitDeviceSetAndResolve — cold boot (activate: false)', () => {
178
194
  });
179
195
  });
180
196
 
197
+ /**
198
+ * Post-sign-in hub sync is a FULL-PAGE redirect to auth.oxy.so. It runs from the
199
+ * ONE commit funnel, so gating it here gates every commit path at once —
200
+ * `webAuthMode: 'popup'` must never bounce the tab, whichever path signed the
201
+ * user in (#691 phase 7).
202
+ */
203
+ describe('maybeSyncHubAfterCommit — webAuthMode transport gate (#691 phase 7)', () => {
204
+ const buildHubDeps = (
205
+ overrides: Partial<MaybeSyncHubAfterCommitDeps> = {},
206
+ ): MaybeSyncHubAfterCommitDeps => ({
207
+ activate: true,
208
+ hubSyncRequested: true,
209
+ hubSyncEnabled: true,
210
+ webAuthMode: 'redirect',
211
+ syncHub: jest.fn(async () => true),
212
+ ...overrides,
213
+ });
214
+
215
+ it("redirect mode: syncs the hub after a deliberate sign-in (unchanged)", async () => {
216
+ const deps = buildHubDeps();
217
+
218
+ await expect(maybeSyncHubAfterCommit(deps)).resolves.toBe(true);
219
+
220
+ expect(deps.syncHub).toHaveBeenCalledTimes(1);
221
+ });
222
+
223
+ it('popup mode: NEVER syncs the hub, even on an otherwise-eligible sign-in', async () => {
224
+ const deps = buildHubDeps({ webAuthMode: 'popup' });
225
+
226
+ await expect(maybeSyncHubAfterCommit(deps)).resolves.toBe(false);
227
+
228
+ expect(deps.syncHub).not.toHaveBeenCalled();
229
+ });
230
+
231
+ it('popup mode: no commit path can opt back in', async () => {
232
+ // Every commit path differs only in `activate` / `hubSyncRequested`; the
233
+ // transport gate outranks all of them.
234
+ for (const activate of [true, false]) {
235
+ for (const hubSyncRequested of [true, false]) {
236
+ const deps = buildHubDeps({ webAuthMode: 'popup', activate, hubSyncRequested });
237
+ await expect(maybeSyncHubAfterCommit(deps)).resolves.toBe(false);
238
+ expect(deps.syncHub).not.toHaveBeenCalled();
239
+ }
240
+ }
241
+ });
242
+
243
+ it.each(['popup', 'redirect'] as const)(
244
+ 'never syncs on a cold-boot restore, in either mode (%s)',
245
+ async (webAuthMode: WebAuthMode) => {
246
+ const deps = buildHubDeps({ webAuthMode, activate: false });
247
+
248
+ await expect(maybeSyncHubAfterCommit(deps)).resolves.toBe(false);
249
+
250
+ expect(deps.syncHub).not.toHaveBeenCalled();
251
+ },
252
+ );
253
+
254
+ it('redirect mode: honours the per-commit opt-out (account switch / popup OAuth lanes)', async () => {
255
+ const deps = buildHubDeps({ hubSyncRequested: false });
256
+
257
+ await expect(maybeSyncHubAfterCommit(deps)).resolves.toBe(false);
258
+
259
+ expect(deps.syncHub).not.toHaveBeenCalled();
260
+ });
261
+
262
+ it('redirect mode: honours the provider-level hubSync opt-out', async () => {
263
+ const deps = buildHubDeps({ hubSyncEnabled: false });
264
+
265
+ await expect(maybeSyncHubAfterCommit(deps)).resolves.toBe(false);
266
+
267
+ expect(deps.syncHub).not.toHaveBeenCalled();
268
+ });
269
+
270
+ it('redirect mode: a failing hub sync is non-fatal and never rejects', async () => {
271
+ const deps = buildHubDeps({
272
+ syncHub: jest.fn(async () => {
273
+ throw new Error('hub ticket failed');
274
+ }),
275
+ });
276
+
277
+ await expect(maybeSyncHubAfterCommit(deps)).resolves.toBe(false);
278
+
279
+ expect(deps.syncHub).toHaveBeenCalledTimes(1);
280
+ });
281
+ });
282
+
181
283
  describe('commitDeviceSetAndResolve — deliberate sign-in (activate: true)', () => {
182
284
  it('BLOCKS on the device-set reconcile before resolving auth (unchanged ordering)', async () => {
183
285
  const order: string[] = [];
@@ -1,5 +1,8 @@
1
1
  import type { User } from '@oxyhq/core';
2
2
  import { logger as loggerUtil } from '@oxyhq/core';
3
+ import { allowsAutomaticIdpRedirect } from '../oauth/legacyRedirectLanes';
4
+ import type { WebAuthMode } from '../oauth/types';
5
+ import { isWebBrowser } from '../utils/isWebBrowser';
3
6
  import { isUnauthorizedStatus } from './oxyContextHelpers';
4
7
 
5
8
  const LOG_CONTEXT = { component: 'OxyContext', method: 'commitSession' } as const;
@@ -98,6 +101,9 @@ export async function commitDeviceSetAndResolve(
98
101
  }
99
102
  fullUser = fallbackUser;
100
103
  }
104
+ if (!fullUser) {
105
+ fullUser = fallbackUser;
106
+ }
101
107
  if (fullUser) {
102
108
  loginSuccess(fullUser);
103
109
  onAuthStateChange?.(fullUser);
@@ -123,3 +129,61 @@ export async function commitDeviceSetAndResolve(
123
129
  await hydrateAndResolve();
124
130
  void reconcileDeviceSet().catch(logReconcileError);
125
131
  }
132
+
133
+ export interface MaybeSyncHubAfterCommitDeps {
134
+ /** Deliberate sign-in (`true`) vs. a cold-boot / background commit (`false`). */
135
+ activate: boolean;
136
+ /** Per-commit opt-in — `commitSession`'s `options.hubSync`. */
137
+ hubSyncRequested: boolean;
138
+ /** Provider-level opt-out — `OxyProviderProps.hubSync`. */
139
+ hubSyncEnabled: boolean;
140
+ /**
141
+ * The provider's web sign-in transport — `OxyProviderProps.webAuthMode`.
142
+ * A first-class input: `'popup'` forbids the redirect outright.
143
+ */
144
+ webAuthMode: WebAuthMode;
145
+ /**
146
+ * Starts the full-page hop to `auth.oxy.so/sync`; resolves `true` when the
147
+ * navigation was actually started.
148
+ */
149
+ syncHub: () => Promise<boolean>;
150
+ }
151
+
152
+ /**
153
+ * Post-sign-in hub sync: plant this device's credential on the IdP hub so OTHER
154
+ * official origins can silently restore a session on their next cold boot.
155
+ *
156
+ * The tail of the ONE commit funnel (`commitSession`), extracted so every gate
157
+ * on this legacy lane lives — and is tested — in one place. Returns whether the
158
+ * full-page redirect was started; a failure is non-fatal and never propagates
159
+ * (the sign-in itself already succeeded).
160
+ *
161
+ * Four gates, all of which must pass:
162
+ * 1. `activate` — only a deliberate sign-in syncs; a cold-boot restore never does.
163
+ * 2. `hubSyncRequested` — the in-place lanes (account switch, popup OAuth) opt out.
164
+ * 3. `hubSyncEnabled` — the provider-level `hubSync` prop.
165
+ * 4. `allowsAutomaticIdpRedirect(webAuthMode)` — hub sync REPLACES the document
166
+ * with `auth.oxy.so`, so `webAuthMode: 'popup'` forbids it: a redirect that
167
+ * bounces the tab defeats the entire point of the mode, no matter which
168
+ * commit path produced the session (issue #691, "Cold boot and cross-domain
169
+ * behavior"). Redirect mode — the default and the compatibility path — is
170
+ * unchanged, and this whole function disappears with it in phase 7b.
171
+ */
172
+ export async function maybeSyncHubAfterCommit(
173
+ deps: MaybeSyncHubAfterCommitDeps,
174
+ ): Promise<boolean> {
175
+ const { activate, hubSyncRequested, hubSyncEnabled, webAuthMode, syncHub } = deps;
176
+
177
+ if (!activate || !hubSyncRequested || !hubSyncEnabled) return false;
178
+ if (!allowsAutomaticIdpRedirect(webAuthMode)) return false;
179
+ if (!isWebBrowser()) return false;
180
+
181
+ try {
182
+ return await syncHub();
183
+ } catch (hubError) {
184
+ if (__DEV__) {
185
+ loggerUtil.debug('Hub sync after sign-in failed (non-fatal)', LOG_CONTEXT, hubError);
186
+ }
187
+ return false;
188
+ }
189
+ }