@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
@@ -5,105 +5,21 @@
5
5
  * (`deviceId` + `deviceSecret`) per origin (web) / per device (native), and the
6
6
  * SDK re-mints the access token from that credential on cold boot. `@oxyhq/core`
7
7
  * owns the store shape + logic (`createWebAuthStateStore` /
8
- * `createNativeAuthStateStore`); this module only supplies the platform storage
8
+ * `createNativeAuthStateStore`); this module only selects the platform storage
9
9
  * seam:
10
10
  *
11
11
  * - web → `createWebAuthStateStore()` (localStorage, in-memory fallback).
12
12
  * - native → `createNativeAuthStateStore(secureKV)` for the SESSION blob over
13
- * `expo-secure-store` (AsyncStorage fallback).
14
- *
15
- * `expo-secure-store` is loaded via a runtime-computed dynamic import (the same
16
- * optional-native-module pattern `OxyProvider` uses for netinfo / keyboard
17
- * controller), so the web bundle never pulls it and a device without it falls
18
- * back to AsyncStorage rather than crashing.
13
+ * the shared `createNativeSecureKeyValueStorage()` adapter
14
+ * (`expo-secure-store`, AsyncStorage fallback).
19
15
  */
20
16
  import {
21
17
  createWebAuthStateStore,
22
18
  createNativeAuthStateStore,
23
19
  type AuthStateStore,
24
- type NativeKeyValueStorage,
25
20
  } from '@oxyhq/core';
26
- import { createPlatformStorage, isReactNative } from '../utils/storageHelpers';
27
-
28
- // Variable indirection so Metro's static analyzer never traces expo-secure-store
29
- // into the web bundle; the module is native-only and optional.
30
- const SECURE_STORE_MODULE = 'expo-secure-store';
31
-
32
- interface SecureStoreLike {
33
- getItemAsync(key: string): Promise<string | null>;
34
- setItemAsync(key: string, value: string): Promise<void>;
35
- deleteItemAsync(key: string): Promise<void>;
36
- }
37
-
38
- let secureStorePromise: Promise<SecureStoreLike | null> | null = null;
39
-
40
- /**
41
- * Resolve the `expo-secure-store` module once (memoised), or `null` when it is
42
- * unavailable — callers then fall back to AsyncStorage. Never throws.
43
- */
44
- function loadSecureStore(): Promise<SecureStoreLike | null> {
45
- if (!secureStorePromise) {
46
- const moduleName = SECURE_STORE_MODULE;
47
- secureStorePromise = import(moduleName)
48
- .then((mod: Partial<SecureStoreLike>) => {
49
- if (
50
- typeof mod.getItemAsync === 'function' &&
51
- typeof mod.setItemAsync === 'function' &&
52
- typeof mod.deleteItemAsync === 'function'
53
- ) {
54
- return {
55
- getItemAsync: mod.getItemAsync.bind(mod),
56
- setItemAsync: mod.setItemAsync.bind(mod),
57
- deleteItemAsync: mod.deleteItemAsync.bind(mod),
58
- } satisfies SecureStoreLike;
59
- }
60
- return null;
61
- })
62
- .catch(() => null);
63
- }
64
- return secureStorePromise;
65
- }
66
-
67
- /**
68
- * A native `NativeKeyValueStorage` that prefers `expo-secure-store` (encrypted
69
- * at rest) and falls back to AsyncStorage when SecureStore is not installed.
70
- * The SecureStore module is resolved lazily on first access so construction
71
- * stays synchronous.
72
- */
73
- function createNativeSecureKeyValueStorage(): NativeKeyValueStorage {
74
- let asyncStorage: Awaited<ReturnType<typeof createPlatformStorage>> | null = null;
75
- const getAsyncStorage = async () => {
76
- if (!asyncStorage) {
77
- asyncStorage = await createPlatformStorage();
78
- }
79
- return asyncStorage;
80
- };
81
- return {
82
- getItem: async (key) => {
83
- const secure = await loadSecureStore();
84
- if (secure) {
85
- return secure.getItemAsync(key);
86
- }
87
- return (await getAsyncStorage()).getItem(key);
88
- },
89
- setItem: async (key, value) => {
90
- const secure = await loadSecureStore();
91
- if (secure) {
92
- await secure.setItemAsync(key, value);
93
- return;
94
- }
95
- await (await getAsyncStorage()).setItem(key, value);
96
- },
97
- removeItem: async (key) => {
98
- const secure = await loadSecureStore();
99
- if (secure) {
100
- await secure.deleteItemAsync(key);
101
- return;
102
- }
103
- await (await getAsyncStorage()).removeItem(key);
104
- },
105
- };
106
- }
21
+ import { isReactNative } from '../utils/storageHelpers';
22
+ import { createNativeSecureKeyValueStorage } from './nativeSecureStorage';
107
23
 
108
24
  /**
109
25
  * Build the platform {@link AuthStateStore} for this runtime.
@@ -33,16 +33,29 @@ import { createTokenTransport } from './tokenTransport';
33
33
  *
34
34
  * The realtime socket uses bearer when authenticated, or deviceId+deviceSecret
35
35
  * when signed out (after the one-shot join redirect).
36
+ *
37
+ * `getPinnedAccountId` reports the account an IDENTITY-BOUND provider
38
+ * (`sessionMode: 'identity'`) is pinned to, and `null` for every account-mode
39
+ * provider — which is exactly the value `SessionClient` and the transport read
40
+ * when the option is absent, so account mode is unchanged. While it reports an
41
+ * account, the client tracks device state truthfully but never re-binds its
42
+ * bearer to the device's mutable active account.
36
43
  */
37
44
  export function createSessionClient(
38
45
  oxyServices: OxyServices,
39
46
  onUnauthenticated?: (origin: SessionStateOrigin) => void,
47
+ getPinnedAccountId?: () => string | null,
40
48
  ): {
41
49
  client: SessionClient;
42
50
  host: ReturnType<typeof createSessionClientHost>;
43
51
  } {
44
52
  const host = createSessionClientHost(oxyServices);
45
- const transport = createTokenTransport(oxyServices);
46
- const client = new SessionClient(host, { transport, socketFactory: io, onUnauthenticated });
53
+ const transport = createTokenTransport(oxyServices, getPinnedAccountId);
54
+ const client = new SessionClient(host, {
55
+ transport,
56
+ socketFactory: io,
57
+ onUnauthenticated,
58
+ getPinnedAccountId,
59
+ });
47
60
  return { client, host };
48
61
  }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Identity-bound sessions — the PLATFORM half of `sessionMode: 'identity'`.
3
+ *
4
+ * `@oxyhq/core` owns everything platform-agnostic about an identity-bound
5
+ * session: the pin store shapes, `resolveIdentityPin`,
6
+ * `establishIdentitySession`, and the pinned cold-boot / re-mint lanes. What it
7
+ * deliberately leaves out is WHICH pin store a given runtime should use, so this
8
+ * module supplies it — web `localStorage` vs the same native SecureStore adapter
9
+ * the device credential already persists through — and adds the one piece of
10
+ * state the provider needs on top: a memoised, SYNCHRONOUSLY readable pinned
11
+ * account id.
12
+ *
13
+ * Why the memo matters: `SessionClient.getPinnedAccountId` is a synchronous read
14
+ * on the socket/apply path, while resolving the pin is asynchronous (a storage
15
+ * read plus a keychain read, through `resolveIdentityPin`). The provider
16
+ * resolves it before the cold boot and after every (re-)established identity
17
+ * session; every read after that is free.
18
+ */
19
+ import {
20
+ createNativeIdentityPinStore,
21
+ createWebIdentityPinStore,
22
+ resolveIdentityPin,
23
+ type IdentityBinding,
24
+ } from '@oxyhq/core';
25
+ import { isReactNative } from '../utils/storageHelpers';
26
+ import { createNativeSecureKeyValueStorage } from './nativeSecureStorage';
27
+
28
+ /**
29
+ * The provider-scoped identity binding: the core {@link IdentityBinding} handed
30
+ * to the cold boot and the re-mint handler, plus the resolved pin the projection
31
+ * and `SessionClient` bind to.
32
+ */
33
+ export interface IdentitySessionBinding {
34
+ /** Handed to `runSessionColdBoot` and `installAuthRefreshHandler`. */
35
+ readonly binding: IdentityBinding;
36
+ /**
37
+ * The last resolved pinned account id, read synchronously. `null` until the
38
+ * first resolve lands, and after a resolve that found no usable pin.
39
+ */
40
+ getPinnedAccountId(): string | null;
41
+ /**
42
+ * Resolve the pin, reusing the memo once a pinned account is known.
43
+ * Concurrent callers share one in-flight resolve.
44
+ */
45
+ ensurePinnedAccountId(): Promise<string | null>;
46
+ /**
47
+ * Drop the memo and re-resolve. Used after a lane that may have rewritten or
48
+ * cleared the pin (the cold boot, or a re-established identity session).
49
+ */
50
+ refreshPinnedAccountId(): Promise<string | null>;
51
+ }
52
+
53
+ /**
54
+ * Thrown by every account-graph mutation while the provider runs in
55
+ * `sessionMode: 'identity'`.
56
+ *
57
+ * An identity-bound app's user is the owner of the local identity key — fixed,
58
+ * for the lifetime of that key. Switching it is not a temporarily-unavailable
59
+ * operation that could be retried, it is not part of this mode's contract at
60
+ * all, so the surfaces reject loudly rather than resolving to a no-op that would
61
+ * read to the caller as a successful switch.
62
+ */
63
+ export class IdentityBoundSessionError extends Error {
64
+ override readonly name = 'IdentityBoundSessionError';
65
+ readonly code = 'identity_bound_session';
66
+ constructor(readonly operation: string) {
67
+ super(
68
+ `"${operation}" is unavailable while the session is identity-bound (sessionMode: "identity"): this app authenticates as the owner of the local identity key and cannot switch accounts.`,
69
+ );
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Build the identity binding for this runtime. Called ONCE per provider mount,
75
+ * and ONLY when `sessionMode: 'identity'` — account-mode providers never
76
+ * construct a pin store.
77
+ */
78
+ export function createIdentitySessionBinding(): IdentitySessionBinding {
79
+ const pinStore = isReactNative()
80
+ ? createNativeIdentityPinStore(createNativeSecureKeyValueStorage())
81
+ : createWebIdentityPinStore();
82
+ const binding: IdentityBinding = { pinStore };
83
+
84
+ // Only a RESOLVED (non-null) pin is memoised. A `null` outcome is either "not
85
+ // pinned yet" or an indeterminate key read (locked keychain), and caching
86
+ // either one would leave this client permanently unpinned for the rest of the
87
+ // process — the exact state in which it would start following the device's
88
+ // mutable active account.
89
+ let pinnedAccountId: string | null = null;
90
+ let inFlight: Promise<string | null> | null = null;
91
+
92
+ const resolve = (): Promise<string | null> => {
93
+ if (!inFlight) {
94
+ inFlight = resolveIdentityPin(binding)
95
+ .then((pin) => {
96
+ pinnedAccountId = pin?.accountId ?? null;
97
+ return pinnedAccountId;
98
+ })
99
+ .finally(() => {
100
+ inFlight = null;
101
+ });
102
+ }
103
+ return inFlight;
104
+ };
105
+
106
+ return {
107
+ binding,
108
+ getPinnedAccountId: () => pinnedAccountId,
109
+ ensurePinnedAccountId: () =>
110
+ pinnedAccountId !== null ? Promise.resolve(pinnedAccountId) : resolve(),
111
+ refreshPinnedAccountId: () => {
112
+ pinnedAccountId = null;
113
+ return resolve();
114
+ },
115
+ };
116
+ }
@@ -4,13 +4,17 @@
4
4
  * `@oxyhq/core` owns the platform-agnostic `SessionClient`, its host adapter,
5
5
  * and the pure `DeviceSessionState → services` projection helpers; those are
6
6
  * re-exported straight from core here so `OxyContext` has one import site.
7
- * `@oxyhq/services` supplies only the two platform pieces: the thin
7
+ * `@oxyhq/services` supplies only the platform pieces: the thin
8
8
  * `createSessionClient` factory (injects socket.io-client `io` + the
9
- * device-first token transport) and the `AuthStateStore` factory.
9
+ * device-first token transport), the `AuthStateStore` factory, and — for
10
+ * `sessionMode: 'identity'` — the identity binding (platform pin store + the
11
+ * memoised pinned account id).
10
12
  */
11
13
  export { createSessionClient } from './createSessionClient';
12
14
  export { createTokenTransport } from './tokenTransport';
13
15
  export { createPlatformAuthStateStore } from './authStore';
16
+ export { createIdentitySessionBinding, IdentityBoundSessionError } from './identityBinding';
17
+ export type { IdentitySessionBinding } from './identityBinding';
14
18
  export {
15
19
  createSessionClientHost,
16
20
  accountIdsOf,
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The native secure key/value seam every durable `@oxyhq/core` store in this
3
+ * package is built on.
4
+ *
5
+ * `@oxyhq/core` owns the store LOGIC (`createNativeAuthStateStore` for the
6
+ * zero-cookie device credential, `createNativeIdentityPinStore` for the identity
7
+ * pin) and takes its persistence as an injected `NativeKeyValueStorage`. This
8
+ * module supplies that injection ONCE, so those stores can never drift onto
9
+ * different backings — a device credential in SecureStore and a pin in
10
+ * AsyncStorage would resolve differently after a keystore reset.
11
+ *
12
+ * `expo-secure-store` is loaded via a runtime-computed dynamic import (the same
13
+ * optional-native-module pattern `OxyProvider` uses for netinfo / keyboard
14
+ * controller), so the web bundle never pulls it and a device without it falls
15
+ * back to AsyncStorage rather than crashing.
16
+ */
17
+ import type { NativeKeyValueStorage } from '@oxyhq/core';
18
+ import { createPlatformStorage } from '../utils/storageHelpers';
19
+
20
+ // Variable indirection so Metro's static analyzer never traces expo-secure-store
21
+ // into the web bundle; the module is native-only and optional.
22
+ const SECURE_STORE_MODULE = 'expo-secure-store';
23
+
24
+ interface SecureStoreLike {
25
+ getItemAsync(key: string): Promise<string | null>;
26
+ setItemAsync(key: string, value: string): Promise<void>;
27
+ deleteItemAsync(key: string): Promise<void>;
28
+ }
29
+
30
+ let secureStorePromise: Promise<SecureStoreLike | null> | null = null;
31
+
32
+ /**
33
+ * Resolve the `expo-secure-store` module once (memoised), or `null` when it is
34
+ * unavailable — callers then fall back to AsyncStorage. Never throws.
35
+ */
36
+ function loadSecureStore(): Promise<SecureStoreLike | null> {
37
+ if (!secureStorePromise) {
38
+ const moduleName = SECURE_STORE_MODULE;
39
+ secureStorePromise = import(moduleName)
40
+ .then((mod: Partial<SecureStoreLike>) => {
41
+ if (
42
+ typeof mod.getItemAsync === 'function' &&
43
+ typeof mod.setItemAsync === 'function' &&
44
+ typeof mod.deleteItemAsync === 'function'
45
+ ) {
46
+ return {
47
+ getItemAsync: mod.getItemAsync.bind(mod),
48
+ setItemAsync: mod.setItemAsync.bind(mod),
49
+ deleteItemAsync: mod.deleteItemAsync.bind(mod),
50
+ } satisfies SecureStoreLike;
51
+ }
52
+ return null;
53
+ })
54
+ .catch(() => null);
55
+ }
56
+ return secureStorePromise;
57
+ }
58
+
59
+ /**
60
+ * A native `NativeKeyValueStorage` that prefers `expo-secure-store` (encrypted
61
+ * at rest) and falls back to AsyncStorage when SecureStore is not installed.
62
+ * The SecureStore module is resolved lazily on first access so construction
63
+ * stays synchronous.
64
+ */
65
+ export function createNativeSecureKeyValueStorage(): NativeKeyValueStorage {
66
+ let asyncStorage: Awaited<ReturnType<typeof createPlatformStorage>> | null = null;
67
+ const getAsyncStorage = async () => {
68
+ if (!asyncStorage) {
69
+ asyncStorage = await createPlatformStorage();
70
+ }
71
+ return asyncStorage;
72
+ };
73
+ return {
74
+ getItem: async (key) => {
75
+ const secure = await loadSecureStore();
76
+ if (secure) {
77
+ return secure.getItemAsync(key);
78
+ }
79
+ return (await getAsyncStorage()).getItem(key);
80
+ },
81
+ setItem: async (key, value) => {
82
+ const secure = await loadSecureStore();
83
+ if (secure) {
84
+ await secure.setItemAsync(key, value);
85
+ return;
86
+ }
87
+ await (await getAsyncStorage()).setItem(key, value);
88
+ },
89
+ removeItem: async (key) => {
90
+ const secure = await loadSecureStore();
91
+ if (secure) {
92
+ await secure.deleteItemAsync(key);
93
+ return;
94
+ }
95
+ await (await getAsyncStorage()).removeItem(key);
96
+ },
97
+ };
98
+ }
@@ -23,10 +23,23 @@ import type { DeviceSessionState } from '@oxyhq/contracts';
23
23
  *
24
24
  * A failure is logged and swallowed: this method must never throw out (it runs
25
25
  * inside the socket state handler).
26
+ *
27
+ * `getPinnedAccountId` is the identity-bound escape hatch. Converging the bearer
28
+ * on `state.activeAccountId` is precisely what an identity-bound client must
29
+ * never do — its token is minted for the PINNED account by the cold boot /
30
+ * re-mint lane. `SessionClient` already bypasses the transport entirely while
31
+ * pinned; this guard is the services-side half of that contract, so no future
32
+ * wiring can reintroduce the convergence from this end.
26
33
  */
27
- export function createTokenTransport(oxyServices: OxyServices): TokenTransport {
34
+ export function createTokenTransport(
35
+ oxyServices: OxyServices,
36
+ getPinnedAccountId?: () => string | null,
37
+ ): TokenTransport {
28
38
  return {
29
39
  async ensureActiveToken(state: DeviceSessionState): Promise<void> {
40
+ if ((getPinnedAccountId?.() ?? null) !== null) {
41
+ return;
42
+ }
30
43
  try {
31
44
  // Skip the mint ONLY when the planted bearer already identifies the
32
45
  // active account. `getCurrentUserId()` decodes the bearer's `userId`/`id`
@@ -2,7 +2,8 @@ import type { ReactNode, RefObject } from 'react';
2
2
  import type { QueryClient } from '@tanstack/react-query';
3
3
  import type { RouteName } from '../navigation/routes';
4
4
  import type { User } from '@oxyhq/core';
5
- import type { ClientSession } from '@oxyhq/core';
5
+ import type { ClientSession, SessionMode } from '@oxyhq/core';
6
+ import type { WebAuthMode } from '../oauth/types';
6
7
 
7
8
  export interface StepController {
8
9
  canGoBack: () => boolean;
@@ -80,6 +81,48 @@ export interface OxyProviderProps {
80
81
  * so a local/staging deployment targets its own IdP instead of production.
81
82
  */
82
83
  authorizeBaseUrl?: string;
84
+ /**
85
+ * Who owns this app's session.
86
+ *
87
+ * - `'account'` (default) — the device's ACTIVE account. Every ordinary Oxy
88
+ * app: any app sharing the device's `DeviceSession` can switch the active
89
+ * account and this one follows, sign-in surfaces and the account switcher
90
+ * are available, and `accounts` lists the account graph.
91
+ * - `'identity'` — the owner of this device's PRIMARY identity key, for as
92
+ * long as that key exists. The provider pins its authenticated user AND
93
+ * its bearer to the account that key authenticates as, so an account
94
+ * switch made by any sibling app changes the shared device state but never
95
+ * this app's user or token. Built for the identity vault (Commons).
96
+ *
97
+ * In `'identity'` mode the account-graph surfaces are disabled at the source
98
+ * rather than hidden in the UI: `accounts` stays empty and is never fetched,
99
+ * `switchToAccount` / `switchSession` reject with `IdentityBoundSessionError`,
100
+ * `accountDialogController` is `null` and `openAccountDialog()` does nothing,
101
+ * and the two web OAuth cold-boot lanes (authorization-code return + silent
102
+ * cross-origin restore) are skipped — all of them commit whichever account
103
+ * the IdP resolves, which is not necessarily the local key's owner.
104
+ *
105
+ * Read once at mount, like `baseURL` / `oxyServices`: changing it on a
106
+ * mounted provider does not re-bind the session.
107
+ * @default 'account'
108
+ */
109
+ sessionMode?: SessionMode;
110
+ /**
111
+ * How a WEB third-party "Sign in with Oxy" hands the user to the IdP.
112
+ *
113
+ * - `'redirect'` (default) — a full-page navigation to `auth.oxy.so`, which
114
+ * returns to the registered `redirect_uri` with `?code=`. The tab unmounts
115
+ * and remounts, so route and unsaved state are lost.
116
+ * - `'popup'` — a small `auth.oxy.so` window opened from the user's click.
117
+ * The app stays MOUNTED and becomes authenticated without a reload; the
118
+ * IdP delivers only the authorization code + `state` back to the opener
119
+ * via `postMessage` (never a token, device secret, or PKCE verifier). A
120
+ * blocked popup falls back to the redirect automatically.
121
+ *
122
+ * Native is unaffected — it always uses an in-app auth session.
123
+ * @default 'redirect'
124
+ */
125
+ webAuthMode?: WebAuthMode;
83
126
  queryClient?: QueryClient;
84
127
  /** Sync device credentials to auth.oxy.so after interactive sign-in. @default true */
85
128
  hubSync?: boolean;
@@ -25,6 +25,7 @@ import {
25
25
  isLoopbackOrigin,
26
26
  isOfficialWebOrigin,
27
27
  isIdpHubOrigin,
28
+ OXY_OAUTH_REDIRECT_URI_STORAGE_KEY,
28
29
  } from '@oxyhq/core';
29
30
  import { redirectToAuthorize } from '../../components/oauthNavigation';
30
31
  import {
@@ -139,5 +140,21 @@ describe('crossOriginRestore', () => {
139
140
  expect(params.get('client_id')).toBe('oxy_dk_test');
140
141
  expect(params.get('prompt')).toBe('none');
141
142
  });
143
+
144
+ it('persists the path-qualified redirect_uri in the handshake for token exchange', async () => {
145
+ makeOriginEligible();
146
+ const redirectUri = 'https://app.example/oauth/callback';
147
+ const redirected = await maybeStartSilentOAuthRestore({
148
+ oxyServices: OXY_SERVICES_STUB,
149
+ clientId: 'oxy_dk_test',
150
+ redirectUri,
151
+ });
152
+ expect(redirected).toBe(true);
153
+ expect(globalThis.sessionStorage?.getItem(OXY_OAUTH_REDIRECT_URI_STORAGE_KEY)).toBe(
154
+ redirectUri,
155
+ );
156
+ const url = mockRedirect.mock.calls[0][0] as string;
157
+ expect(new URL(url).searchParams.get('redirect_uri')).toBe(redirectUri);
158
+ });
142
159
  });
143
160
  });
@@ -8,12 +8,13 @@ import {
8
8
  isOfficialWebOrigin,
9
9
  OXY_CROSS_ORIGIN_RESTORE_ATTEMPTED_KEY,
10
10
  OXY_SILENT_OAUTH_ATTEMPTED_KEY,
11
+ clearOAuthHandshake,
11
12
  persistOAuthHandshake,
12
- consumeOAuthReturnPath,
13
- normalizeOAuthRedirectUri,
13
+ canonicalizeOAuthRedirectUri,
14
14
  logger,
15
15
  } from '@oxyhq/core';
16
16
  import { isWebBrowser } from './isWebBrowser';
17
+ import { replaceUrlAfterOAuthReturn } from './oauthReturn';
17
18
  import { redirectToAuthorize } from '../components/oauthNavigation';
18
19
 
19
20
  function sessionStore(): Storage | undefined {
@@ -108,13 +109,12 @@ export async function maybeStartSilentOAuthRestore(
108
109
  try {
109
110
  const { codeVerifier, codeChallenge } = await generatePkcePair();
110
111
  const state = await generateOAuthState();
111
- if (!persistOAuthHandshake(state, codeVerifier)) {
112
- return false;
113
- }
114
-
115
- const redirectUri = normalizeOAuthRedirectUri(
112
+ const redirectUri = canonicalizeOAuthRedirectUri(
116
113
  opts.redirectUri ?? location.origin,
117
114
  );
115
+ if (!persistOAuthHandshake(state, codeVerifier, redirectUri)) {
116
+ return false;
117
+ }
118
118
 
119
119
  const authorizeUrl = buildOAuthAuthorizeUrl({
120
120
  authorizeBaseUrl: opts.authorizeBaseUrl,
@@ -161,12 +161,8 @@ export function consumeSilentOAuthError(): 'login_required' | 'consent_required'
161
161
  // Silent restore failed, but the visitor still asked for a specific page.
162
162
  // Put them back on it rather than leaving them on the bare origin the IdP
163
163
  // redirected to.
164
- const returnPath = consumeOAuthReturnPath();
165
- history.replaceState(
166
- history.state,
167
- '',
168
- returnPath ?? `${url.pathname}${url.search}${url.hash}`,
169
- );
164
+ replaceUrlAfterOAuthReturn(`${url.pathname}${url.search}${url.hash}`);
170
165
  }
166
+ clearOAuthHandshake();
171
167
  return error;
172
168
  }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Classify the surface a "Sign in with Oxy" request is initiated from, for
3
+ * `AccountDialogController`'s `platform` option (issue #691, Phase 4/5).
4
+ *
5
+ * `@oxyhq/core` deliberately refuses to do this itself — it never touches a
6
+ * platform global — so the consumer that OWNS the environment classifies it and
7
+ * hands the verdict in. The verdict feeds `selectCommonsDelivery`, where it
8
+ * decides one thing only: whether this device may take the `'open-commons'`
9
+ * deep-link route. Getting it wrong in the "mobile" direction is the costly
10
+ * error (a custom-scheme navigation that does not resolve is a dead end with no
11
+ * automatic way back), so every ambiguous signal resolves AWAY from `'mobile'`.
12
+ *
13
+ * No user-agent string is parsed. The two signals used are declarative and
14
+ * standardized:
15
+ * 1. `navigator.userAgentData.mobile` — the User-Agent Client Hints boolean.
16
+ * Authoritative where implemented (Chromium), in both directions.
17
+ * 2. The PRIMARY pointer (`(pointer: coarse)`) plus a touch digitizer.
18
+ * A phone/tablet reports a coarse primary pointer; a touchscreen laptop
19
+ * still reports its mouse as primary, so it is not misclassified.
20
+ */
21
+
22
+ import type { CommonsDeliveryPlatform } from '@oxyhq/core';
23
+ import { isWebBrowser } from './isWebBrowser';
24
+
25
+ /**
26
+ * The `navigator.userAgentData` slice this module reads. Declared locally
27
+ * because `NavigatorUAData` is not in TypeScript's DOM lib yet; only the one
28
+ * field consumed here is modelled, and it stays optional so an implementation
29
+ * without it is simply "no answer" rather than a type lie.
30
+ */
31
+ interface NavigatorUserAgentData {
32
+ readonly mobile?: boolean;
33
+ }
34
+
35
+ type NavigatorWithUserAgentData = Navigator & {
36
+ readonly userAgentData?: NavigatorUserAgentData;
37
+ };
38
+
39
+ /**
40
+ * Which surface this app is running on, as `selectCommonsDelivery` models it.
41
+ *
42
+ * - Native (Expo/RN) is always `'mobile'`: it is an Oxy app installed on a
43
+ * phone/tablet, exactly the case where a verified Commons link can resolve
44
+ * on this very device.
45
+ * - A web browser is classified from the signals above.
46
+ * - Anything else — a browser-like environment with neither signal — is
47
+ * `'unknown'`, a first-class value that never opts into the deep link.
48
+ */
49
+ export function resolveDeliveryPlatform(): CommonsDeliveryPlatform {
50
+ if (!isWebBrowser()) return 'mobile';
51
+
52
+ const nav: NavigatorWithUserAgentData | undefined =
53
+ typeof navigator === 'undefined' ? undefined : navigator;
54
+
55
+ const uaMobile = nav?.userAgentData?.mobile;
56
+ if (typeof uaMobile === 'boolean') return uaMobile ? 'mobile' : 'desktop';
57
+
58
+ if (typeof window !== 'undefined' && typeof window.matchMedia === 'function') {
59
+ const coarsePrimaryPointer = window.matchMedia('(pointer: coarse)').matches;
60
+ const hasTouchDigitizer = (nav?.maxTouchPoints ?? 0) > 0;
61
+ return coarsePrimaryPointer && hasTouchDigitizer ? 'mobile' : 'desktop';
62
+ }
63
+
64
+ return 'unknown';
65
+ }