@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
@@ -76,13 +76,20 @@ export interface OxyConsentScreenProps {
76
76
  /**
77
77
  * Friendly-label i18n keys for the scopes the platform issues. Standard OIDC
78
78
  * scopes (`openid` / `profile` / `email` / `offline_access`) and the Oxy scope
79
- * set both map to a curated `consent.scopes.*` sentence. Unknown scopes fall
80
- * back to the raw scope string so the user always sees something concrete.
79
+ * set including the `<resource>:read` ids the API actually issues
80
+ * (`profile:read`, `email:read`) map to a curated `consent.scopes.*` sentence
81
+ * that already ships in `@oxyhq/core` for all 11 locales. This is the SAME
82
+ * vocabulary the Commons approval screen uses
83
+ * (`packages/commons/lib/commons-signin/scope-summary.ts`), so one permission
84
+ * reads identically wherever it is granted. Unknown scopes fall back to the raw
85
+ * scope string so the user always sees something concrete.
81
86
  */
82
- const SCOPE_LABEL_KEYS: Record<string, string> = {
87
+ const SCOPE_LABEL_KEYS: Readonly<Record<string, string>> = {
83
88
  openid: 'consent.scopes.openid',
84
89
  profile: 'consent.scopes.profile',
90
+ 'profile:read': 'consent.scopes.profile',
85
91
  email: 'consent.scopes.email',
92
+ 'email:read': 'consent.scopes.email',
86
93
  offline_access: 'consent.scopes.offlineAccess',
87
94
  'user:read': 'consent.scopes.userRead',
88
95
  'files:read': 'consent.scopes.filesRead',
@@ -96,9 +103,47 @@ const SCOPE_LABEL_KEYS: Record<string, string> = {
96
103
 
97
104
  type Translate = ReturnType<typeof useI18n>['t'];
98
105
 
99
- function scopeLabel(scope: string, t: Translate): string {
100
- const key = SCOPE_LABEL_KEYS[scope];
101
- return key ? t(key) : scope;
106
+ /** One permission row of the "Permissions requested" list. */
107
+ interface PermissionRow {
108
+ /** Stable list key / testID suffix — the first scope that produced this row. */
109
+ scope: string;
110
+ /** Shared `consent.scopes.*` key, absent when this build has no sentence for the scope. */
111
+ labelKey?: string;
112
+ }
113
+
114
+ /**
115
+ * Resolve the requested scopes into the rows the user actually reads.
116
+ *
117
+ * De-duplicates by RESOLVED MEANING rather than by raw string — `profile` and
118
+ * `profile:read` are one permission and must not render as two identical rows —
119
+ * while preserving the server's ordering. An unknown scope is never dropped: it
120
+ * keeps its own row and renders verbatim, so a permission this build has no
121
+ * sentence for is still shown to the person granting it.
122
+ */
123
+ function resolvePermissionRows(scopes: readonly string[]): PermissionRow[] {
124
+ const rows: PermissionRow[] = [];
125
+ const seen = new Set<string>();
126
+
127
+ for (const raw of scopes) {
128
+ // The caller resolves scopes from the network; a non-string entry must not
129
+ // take the whole consent surface down with a `.trim()` on it.
130
+ if (typeof raw !== 'string') continue;
131
+ const scope = raw.trim();
132
+ if (!scope) continue;
133
+
134
+ const labelKey = SCOPE_LABEL_KEYS[scope];
135
+ const identity = labelKey ?? `raw:${scope}`;
136
+ if (seen.has(identity)) continue;
137
+ seen.add(identity);
138
+
139
+ rows.push(labelKey ? { scope, labelKey } : { scope });
140
+ }
141
+
142
+ return rows;
143
+ }
144
+
145
+ function permissionRowLabel(row: PermissionRow, t: Translate): string {
146
+ return row.labelKey ? t(row.labelKey) : row.scope;
102
147
  }
103
148
 
104
149
  /** A tappable legal/website link that opens the URL in the platform browser. */
@@ -150,9 +195,7 @@ export function OxyConsentScreen({
150
195
  const theme = useTheme();
151
196
  const { t } = useI18n();
152
197
  const appName = application.name;
153
- // Scopes key the permission rows — a duplicate in the request would mean
154
- // duplicate React keys and redundant rows.
155
- const uniqueScopes = [...new Set(scopes)];
198
+ const permissionRows = resolvePermissionRows(scopes);
156
199
 
157
200
  const provenanceLabel = application.isOfficial
158
201
  ? t('consent.provenance.official')
@@ -217,12 +260,12 @@ export function OxyConsentScreen({
217
260
  {t('consent.permissions.title')}
218
261
  </Text>
219
262
  <View style={[styles.card, { borderColor: theme.colors.border, backgroundColor: theme.colors.card }]}>
220
- {uniqueScopes.length > 0 ? (
221
- uniqueScopes.map((scope) => (
222
- <View key={scope} testID={`consent-scope-${scope}`} style={styles.row}>
263
+ {permissionRows.length > 0 ? (
264
+ permissionRows.map((row) => (
265
+ <View key={row.scope} testID={`consent-scope-${row.scope}`} style={styles.row}>
223
266
  <View style={[styles.bullet, { backgroundColor: theme.colors.primary }]} />
224
267
  <Text style={[styles.rowText, { color: theme.colors.text }]}>
225
- {scopeLabel(scope, t)}
268
+ {permissionRowLabel(row, t)}
226
269
  </Text>
227
270
  </View>
228
271
  ))
@@ -12,7 +12,6 @@ import { logger as loggerUtil } from '@oxyhq/core';
12
12
  import { RequireOxyAuth } from './RequireOxyAuth';
13
13
  import { attachQueryPersistence, createQueryClient } from '../hooks/queryClient';
14
14
  import { createPlatformStorage, type StorageInterface } from '../utils/storageHelpers';
15
- import { setupFonts } from './FontLoader';
16
15
 
17
16
  /**
18
17
  * Background color shown for the brief window between mount and the
@@ -40,9 +39,6 @@ const bootStyles = StyleSheet.create({
40
39
  },
41
40
  });
42
41
 
43
- // Initialize fonts automatically
44
- setupFonts();
45
-
46
42
  // Detect if running on web
47
43
  const isWeb = Platform.OS === 'web';
48
44
 
@@ -95,6 +91,8 @@ const OxyProvider: FC<OxyProviderProps> = ({
95
91
  authWebUrl,
96
92
  authRedirectUri,
97
93
  authorizeBaseUrl,
94
+ sessionMode = 'account',
95
+ webAuthMode = 'redirect',
98
96
  queryClient: providedQueryClient,
99
97
  requireAuth = 'off',
100
98
  hubSync = true,
@@ -293,6 +291,8 @@ const OxyProvider: FC<OxyProviderProps> = ({
293
291
  authorizeBaseUrl={authorizeBaseUrl}
294
292
  storageKeyPrefix={storageKeyPrefix}
295
293
  clientId={clientId}
294
+ sessionMode={sessionMode}
295
+ webAuthMode={webAuthMode}
296
296
  hubSync={hubSync}
297
297
  onAuthStateChange={onAuthStateChange as OxyContextProviderProps['onAuthStateChange']}
298
298
  >
@@ -6,9 +6,6 @@ import {
6
6
  generatePkcePair,
7
7
  generateOAuthState,
8
8
  buildOAuthAuthorizeUrl,
9
- OXY_OAUTH_STATE_STORAGE_KEY,
10
- OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY,
11
- persistOAuthHandshake,
12
9
  type PublicApplication,
13
10
  } from '@oxyhq/core';
14
11
  import { useAuthStore } from '../stores/authStore';
@@ -16,15 +13,18 @@ import { useShallow } from 'zustand/react/shallow';
16
13
  import { useTheme } from '@oxyhq/bloom/theme';
17
14
  import { Button, type ButtonVariant } from '@oxyhq/bloom/button';
18
15
  import { useOxy } from '../context/OxyContext';
16
+ import { useI18n } from '../hooks/useI18n';
19
17
  import { LogoIcon } from './logo/LogoIcon';
20
18
  import { subscribeToAccountDialog } from '../navigation/accountDialogManager';
21
- import { redirectToAuthorize, openAuthorizeUrlNative } from './oauthNavigation';
19
+ import { openAuthorizeUrlNative } from './oauthNavigation';
20
+ import { closeOAuthPopup, openOAuthPopup } from '../oauth/oauthPopup';
21
+ import type { OAuthPopupHandle } from '../oauth/types';
22
22
 
23
23
  /**
24
24
  * The OAuth handshake surfaced to a NATIVE third-party RP via
25
25
  * {@link OxySignInButtonProps.onOAuthResult} so it can finish the code exchange
26
- * (`POST /auth/oauth/token`). Web RPs read the same `state` / `code_verifier`
27
- * back from `sessionStorage` across the redirect and do not need this callback.
26
+ * (`POST /auth/oauth/token`). Web sign-in is handled entirely by the SDK's web
27
+ * transport (`useOxy().startWebOAuthSignIn`) and never uses this callback.
28
28
  */
29
29
  export interface OxyOAuthResult {
30
30
  /** Deep-link URL the native auth session returned to (`?code=…&state=…`), or `null` if unobserved. */
@@ -35,17 +35,6 @@ export interface OxyOAuthResult {
35
35
  codeVerifier: string;
36
36
  }
37
37
 
38
- /**
39
- * Persist the OAuth CSRF `state` + PKCE `code_verifier` for the RP callback.
40
- * Returns `false` when the handshake could not be stored — no `sessionStorage`
41
- * (SSR / non-browser host) or a write that threw (`SecurityError` /
42
- * `QuotaExceededError`, e.g. Safari private mode) — so the caller aborts the
43
- * flow cleanly rather than redirect to a callback that cannot validate `state`.
44
- */
45
- function persistOAuthHandshakeForButton(state: string, codeVerifier: string): boolean {
46
- return persistOAuthHandshake(state, codeVerifier);
47
- }
48
-
49
38
  export interface OxySignInButtonProps {
50
39
  /**
51
40
  * Controls the appearance of the button
@@ -70,8 +59,12 @@ export interface OxySignInButtonProps {
70
59
  textStyle?: StyleProp<TextStyle>;
71
60
 
72
61
  /**
73
- * Custom button text
74
- * @default 'Sign in with Oxy'
62
+ * Overrides the button label. When omitted the button renders the ONE
63
+ * primary relying-party action, localized: `accountSwitcher.continueWithOxy`
64
+ * ("Continue with Oxy" in English). "Sign in with Oxy" remains the name of
65
+ * the MECHANISM — only this surface's label is "Continue with Oxy".
66
+ *
67
+ * @default localized `accountSwitcher.continueWithOxy`
75
68
  */
76
69
  text?: string;
77
70
 
@@ -90,19 +83,20 @@ export interface OxySignInButtonProps {
90
83
  /**
91
84
  * Exact registered redirect URI the OAuth authorization code is returned to.
92
85
  * REQUIRED only for third-party (`type: 'third_party'`) applications, which
93
- * sign in via an OAuth + PKCE redirect to `auth.oxy.so`. First-party /
94
- * official apps open the in-app dialog and ignore this prop. If a third-party
95
- * app resolves without it, the button logs an error and does nothing (it will
96
- * not invent a redirect URI).
86
+ * sign in via OAuth + PKCE against `auth.oxy.so`. First-party / official apps
87
+ * open the in-app dialog and ignore this prop. If a third-party app resolves
88
+ * without it, the button logs an error and does nothing (it will not invent a
89
+ * redirect URI).
97
90
  */
98
91
  oauthRedirectUri?: string;
99
92
 
100
93
  /**
101
94
  * Native only: receives the OAuth handshake after a third-party auth session
102
- * so the RP can finish the token exchange. On web the handshake is read back
103
- * from `sessionStorage` across the full-page redirect, so this is not used
104
- * there. A native third-party sign-in with NO `onOAuthResult` handler cannot
105
- * complete (the `state` + `code_verifier` are lost) and logs a warning.
95
+ * so the RP can finish the token exchange. On web the SDK completes the flow
96
+ * itself the popup transport keeps the handshake in memory, and the
97
+ * redirect transport reads it back from `sessionStorage` so this is never
98
+ * called there. A native third-party sign-in with NO `onOAuthResult` handler
99
+ * cannot complete (the `state` + `code_verifier` are lost) and logs a warning.
106
100
  *
107
101
  * @example
108
102
  * ```tsx
@@ -148,14 +142,15 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
148
142
  onPress,
149
143
  style,
150
144
  textStyle,
151
- text = 'Sign in with Oxy',
145
+ text,
152
146
  disabled = false,
153
147
  showWhenAuthenticated = false,
154
148
  oauthRedirectUri,
155
149
  onOAuthResult,
156
150
  }) => {
157
151
  const theme = useTheme();
158
- const { openAccountDialog, oxyServices, clientId } = useOxy();
152
+ const { t } = useI18n();
153
+ const { openAccountDialog, oxyServices, clientId, webAuthMode, startWebOAuthSignIn } = useOxy();
159
154
  const { isAuthenticated, isLoading } = useAuthStore(
160
155
  useShallow((state) => ({ isAuthenticated: state.isAuthenticated, isLoading: state.isLoading }))
161
156
  );
@@ -180,6 +175,12 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
180
175
  // redirects, so block a second concurrent press from racing the sessionStorage
181
176
  // handshake against a different PKCE pair.
182
177
  const routingRef = useRef(false);
178
+ // Popup mode only opens a window for applications that could actually need
179
+ // one: `oauthRedirectUri` is required for (and only for) third-party OAuth,
180
+ // so an official app never flashes a popup open and closed while its
181
+ // in-app dialog loads.
182
+ const shouldPreOpenPopup =
183
+ Platform.OS === 'web' && webAuthMode === 'popup' && Boolean(oauthRedirectUri);
183
184
 
184
185
  const resolvePublicApplication = useCallback((): Promise<PublicApplication> | null => {
185
186
  if (!clientId) return null;
@@ -204,44 +205,58 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
204
205
  openAccountDialog('signin');
205
206
  }, [openAccountDialog]);
206
207
 
207
- // Third-party surface: an OAuth 2.0 authorization-code + PKCE redirect to
208
+ // Third-party surface: an OAuth 2.0 authorization-code + PKCE flow against
208
209
  // auth.oxy.so. No FedCM, no SSO bounce, no Oxy session cookies.
210
+ //
211
+ // Web delegates to the ONE shared transport (`startWebOAuthSignIn`), which
212
+ // owns popup-vs-redirect selection, the popup lifecycle, the code exchange,
213
+ // and the session commit. Native keeps its in-app auth session and hands the
214
+ // handshake to the RP, which owns that exchange.
209
215
  const startThirdPartyOAuth = useCallback(
210
- async (app: PublicApplication): Promise<void> => {
216
+ async (app: PublicApplication, popup: OAuthPopupHandle | null): Promise<void> => {
211
217
  if (!clientId) {
218
+ closeOAuthPopup(popup);
212
219
  startOfficialSignIn();
213
220
  return;
214
221
  }
215
222
  if (!oauthRedirectUri) {
223
+ closeOAuthPopup(popup);
216
224
  logger.error(
217
- 'OxySignInButton: a third_party application requires the `oauthRedirectUri` prop to start the OAuth redirect; sign-in aborted',
225
+ 'OxySignInButton: a third_party application requires the `oauthRedirectUri` prop to start the OAuth flow; sign-in aborted',
218
226
  undefined,
219
227
  { component: 'OxySignInButton', clientId, application: app.name },
220
228
  );
221
229
  return;
222
230
  }
223
231
 
224
- const [pkce, state] = await Promise.all([generatePkcePair(), generateOAuthState()]);
225
- const authorizeUrl = buildOAuthAuthorizeUrl({
226
- clientId,
227
- redirectUri: oauthRedirectUri,
228
- state,
229
- codeChallenge: pkce.codeChallenge,
230
- });
231
-
232
232
  if (Platform.OS === 'web') {
233
- // Persist the handshake for the RP callback, then hand the
234
- // top-level document to the IdP. Without storage the callback
235
- // cannot validate `state`, so abort cleanly rather than redirect.
236
- if (!persistOAuthHandshakeForButton(state, pkce.codeVerifier)) {
237
- return;
233
+ const result = await startWebOAuthSignIn({
234
+ redirectUri: oauthRedirectUri,
235
+ // In popup mode the window was claimed during the press (see
236
+ // `handlePress`); `null` means the browser blocked it and the
237
+ // transport falls back to a redirect. `undefined` (redirect
238
+ // mode) lets the transport decide for itself.
239
+ ...(shouldPreOpenPopup ? { popup } : {}),
240
+ });
241
+ if (result.status === 'failed') {
242
+ logger.warn(
243
+ `OxySignInButton: web sign-in failed (${result.reason})`,
244
+ { component: 'OxySignInButton', application: app.name },
245
+ result.description,
246
+ );
238
247
  }
239
- redirectToAuthorize(authorizeUrl);
240
248
  return;
241
249
  }
242
250
 
243
251
  // Native: open the in-app auth session, then hand the handshake to the
244
252
  // RP so it can complete the token exchange from its deep-link callback.
253
+ const [pkce, state] = await Promise.all([generatePkcePair(), generateOAuthState()]);
254
+ const authorizeUrl = buildOAuthAuthorizeUrl({
255
+ clientId,
256
+ redirectUri: oauthRedirectUri,
257
+ state,
258
+ codeChallenge: pkce.codeChallenge,
259
+ });
245
260
  const { redirectUrl } = await openAuthorizeUrlNative(authorizeUrl, oauthRedirectUri);
246
261
  if (onOAuthResult) {
247
262
  onOAuthResult({ redirectUrl, state, codeVerifier: pkce.codeVerifier });
@@ -252,51 +267,73 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
252
267
  { component: 'OxySignInButton', application: app.name },
253
268
  );
254
269
  },
255
- [clientId, oauthRedirectUri, onOAuthResult, startOfficialSignIn],
270
+ [
271
+ clientId,
272
+ oauthRedirectUri,
273
+ onOAuthResult,
274
+ startOfficialSignIn,
275
+ startWebOAuthSignIn,
276
+ shouldPreOpenPopup,
277
+ ],
256
278
  );
257
279
 
258
- // Resolve the Application once, then route: third-party → OAuth redirect;
259
- // first-party / official / unresolved → the in-app dialog. Resolution failure
260
- // NEVER breaks an official app's sign-in — it falls back to the dialog.
261
- const routeSignIn = useCallback(async (): Promise<void> => {
262
- if (routingRef.current) return;
263
- routingRef.current = true;
264
- try {
265
- const resolving = resolvePublicApplication();
266
- if (!resolving) {
267
- startOfficialSignIn();
280
+ // Resolve the Application once, then route: third-party → OAuth; first-party
281
+ // / official / unresolved → the in-app dialog. Resolution failure NEVER
282
+ // breaks an official app's sign-in — it falls back to the dialog. Every path
283
+ // that does NOT reach the OAuth lane closes the pre-opened popup, so a
284
+ // mis-routed press can never leave an empty window on screen.
285
+ const routeSignIn = useCallback(
286
+ async (popup: OAuthPopupHandle | null): Promise<void> => {
287
+ if (routingRef.current) {
288
+ closeOAuthPopup(popup);
268
289
  return;
269
290
  }
270
- let app: PublicApplication;
291
+ routingRef.current = true;
271
292
  try {
272
- app = await resolving;
273
- } catch (error) {
274
- logger.warn(
275
- 'OxySignInButton: could not resolve the application; opening the sign-in dialog',
276
- { component: 'OxySignInButton', clientId },
277
- error,
278
- );
293
+ const resolving = resolvePublicApplication();
294
+ if (!resolving) {
295
+ closeOAuthPopup(popup);
296
+ startOfficialSignIn();
297
+ return;
298
+ }
299
+ let app: PublicApplication;
300
+ try {
301
+ app = await resolving;
302
+ } catch (error) {
303
+ closeOAuthPopup(popup);
304
+ logger.warn(
305
+ 'OxySignInButton: could not resolve the application; opening the sign-in dialog',
306
+ { component: 'OxySignInButton', clientId },
307
+ error,
308
+ );
309
+ startOfficialSignIn();
310
+ return;
311
+ }
312
+ if (app.type === 'third_party' && !app.isOfficial) {
313
+ await startThirdPartyOAuth(app, popup);
314
+ return;
315
+ }
316
+ closeOAuthPopup(popup);
279
317
  startOfficialSignIn();
280
- return;
281
- }
282
- if (app.type === 'third_party' && !app.isOfficial) {
283
- await startThirdPartyOAuth(app);
284
- return;
318
+ } finally {
319
+ routingRef.current = false;
285
320
  }
286
- startOfficialSignIn();
287
- } finally {
288
- routingRef.current = false;
289
- }
290
- }, [resolvePublicApplication, startOfficialSignIn, startThirdPartyOAuth, clientId]);
321
+ },
322
+ [resolvePublicApplication, startOfficialSignIn, startThirdPartyOAuth, clientId],
323
+ );
291
324
 
292
325
  // Defer to a caller-supplied handler, otherwise route by application type.
326
+ //
327
+ // The popup is opened HERE, synchronously, before the async application
328
+ // resolve: `window.open` only survives while the click is still being
329
+ // handled, so a window opened after that first `await` is silently blocked.
293
330
  const handlePress = useCallback(() => {
294
331
  if (onPress) {
295
332
  onPress();
296
333
  return;
297
334
  }
298
- void routeSignIn();
299
- }, [onPress, routeSignIn]);
335
+ void routeSignIn(shouldPreOpenPopup ? openOAuthPopup() : null);
336
+ }, [onPress, routeSignIn, shouldPreOpenPopup]);
300
337
 
301
338
  // Don't show the button if already authenticated (unless explicitly overridden)
302
339
  if (isAuthenticated && !showWhenAuthenticated) return null;
@@ -314,6 +351,10 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
314
351
  const logoColor = isContained ? '#ffffff' : theme.colors.primary;
315
352
  const logoLetterColor = isContained ? theme.colors.primary : '#ffffff';
316
353
 
354
+ // The relying party surfaces exactly ONE primary action, and it reads
355
+ // "Continue with Oxy" (issue #691). A caller-supplied `text` still wins.
356
+ const label = text ?? t('accountSwitcher.continueWithOxy');
357
+
317
358
  return (
318
359
  <Button
319
360
  variant={buttonVariant}
@@ -330,7 +371,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
330
371
  />
331
372
  }
332
373
  >
333
- {isLoading || isModalOpen ? 'Signing in...' : text}
374
+ {isLoading || isModalOpen ? t('signin.status.signingIn') : label}
334
375
  </Button>
335
376
  );
336
377
  };
@@ -43,6 +43,33 @@ describe('OxyConsentScreen', () => {
43
43
  expect(getByTestId('consent-scope-custom:thing').textContent).toContain('custom:thing');
44
44
  });
45
45
 
46
+ it('renders human labels for the scope ids the API actually issues (profile:read / email:read)', () => {
47
+ const { getByTestId } = renderScreen({ scopes: ['profile:read', 'email:read'] });
48
+
49
+ const profileRow = getByTestId('consent-scope-profile:read');
50
+ const emailRow = getByTestId('consent-scope-email:read');
51
+ expect(profileRow.textContent).toContain('Read your basic profile');
52
+ expect(emailRow.textContent).toContain('Read your email address');
53
+ // The raw scope id never reaches the person deciding what to grant.
54
+ expect(profileRow.textContent).not.toContain('profile:read');
55
+ expect(emailRow.textContent).not.toContain('email:read');
56
+ });
57
+
58
+ it('collapses scopes that resolve to the same permission into a single row', () => {
59
+ const { getByTestId, queryByTestId } = renderScreen({ scopes: ['profile', 'profile:read'] });
60
+
61
+ // First scope wins the row; the synonym does not add a duplicate sentence.
62
+ expect(getByTestId('consent-scope-profile').textContent).toContain('Read your basic profile');
63
+ expect(queryByTestId('consent-scope-profile:read')).toBeNull();
64
+ });
65
+
66
+ it('keeps an unknown scope alongside known ones instead of dropping the permission', () => {
67
+ const { getByTestId } = renderScreen({ scopes: ['profile:read', 'acme:teleport'] });
68
+
69
+ expect(getByTestId('consent-scope-profile:read').textContent).toContain('Read your basic profile');
70
+ expect(getByTestId('consent-scope-acme:teleport').textContent).toContain('acme:teleport');
71
+ });
72
+
46
73
  it('shows the basic-permissions fallback when no scopes are requested', () => {
47
74
  const { getByTestId, queryByTestId } = renderScreen({ scopes: [] });
48
75
  expect(getByTestId('consent-scope-basic').textContent).toContain('Sign you in and read your basic profile');