@oxyhq/services 10.2.0 → 10.2.2

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 (246) hide show
  1. package/README.md +9 -13
  2. package/lib/commonjs/index.js +10 -0
  3. package/lib/commonjs/index.js.map +1 -1
  4. package/lib/commonjs/ui/components/AccountMenu.js +297 -226
  5. package/lib/commonjs/ui/components/AccountMenu.js.map +1 -1
  6. package/lib/commonjs/ui/components/AccountMenuButton.js.map +1 -1
  7. package/lib/commonjs/ui/components/FollowButton.js +3 -1
  8. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  9. package/lib/commonjs/ui/components/OxySignInButton.js +1 -1
  10. package/lib/commonjs/ui/components/SignInModal.js +11 -12
  11. package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
  12. package/lib/commonjs/ui/components/accountMenuRows.js +18 -30
  13. package/lib/commonjs/ui/components/accountMenuRows.js.map +1 -1
  14. package/lib/commonjs/ui/context/OxyContext.js +57 -78
  15. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  16. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +7 -13
  17. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  18. package/lib/commonjs/ui/hooks/useAuth.js +10 -40
  19. package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
  20. package/lib/commonjs/ui/hooks/useDeviceAccounts.js +285 -0
  21. package/lib/commonjs/ui/hooks/useDeviceAccounts.js.map +1 -0
  22. package/lib/commonjs/ui/hooks/useFollow.js +21 -7
  23. package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
  24. package/lib/commonjs/ui/hooks/useSessionManagement.js +5 -6
  25. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
  26. package/lib/commonjs/ui/hooks/useSessionSocket.js +4 -5
  27. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
  28. package/lib/commonjs/ui/hooks/useWebSSO.js +1 -1
  29. package/lib/commonjs/ui/navigation/routes.js +7 -7
  30. package/lib/commonjs/ui/navigation/routes.js.map +1 -1
  31. package/lib/commonjs/ui/screens/OxyAuthScreen.js +6 -7
  32. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
  33. package/lib/commonjs/ui/screens/ProfileScreen.js +18 -20
  34. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  35. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +4 -4
  36. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/{karma/KarmaAboutScreen.js → trust/TrustAboutScreen.js} +11 -11
  38. package/lib/commonjs/ui/screens/trust/TrustAboutScreen.js.map +1 -0
  39. package/lib/commonjs/ui/screens/{karma/KarmaCenterScreen.js → trust/TrustCenterScreen.js} +91 -41
  40. package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -0
  41. package/lib/commonjs/ui/screens/{karma/KarmaFAQScreen.js → trust/TrustFAQScreen.js} +11 -11
  42. package/lib/commonjs/ui/screens/{karma/KarmaFAQScreen.js.map → trust/TrustFAQScreen.js.map} +1 -1
  43. package/lib/commonjs/ui/screens/{karma/KarmaLeaderboardScreen.js → trust/TrustLeaderboardScreen.js} +63 -42
  44. package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -0
  45. package/lib/commonjs/ui/screens/{karma/KarmaRewardsScreen.js → trust/TrustRewardsScreen.js} +54 -54
  46. package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js.map +1 -0
  47. package/lib/commonjs/ui/screens/{karma/KarmaRulesScreen.js → trust/TrustRulesScreen.js} +45 -16
  48. package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js.map +1 -0
  49. package/lib/commonjs/ui/screens/trust/trustTier.js +23 -0
  50. package/lib/commonjs/ui/screens/trust/trustTier.js.map +1 -0
  51. package/lib/commonjs/utils/deviceFlowSignIn.js +12 -10
  52. package/lib/commonjs/utils/deviceFlowSignIn.js.map +1 -1
  53. package/lib/module/index.js +3 -0
  54. package/lib/module/index.js.map +1 -1
  55. package/lib/module/ui/components/AccountMenu.js +297 -226
  56. package/lib/module/ui/components/AccountMenu.js.map +1 -1
  57. package/lib/module/ui/components/AccountMenuButton.js.map +1 -1
  58. package/lib/module/ui/components/FollowButton.js +3 -1
  59. package/lib/module/ui/components/FollowButton.js.map +1 -1
  60. package/lib/module/ui/components/OxySignInButton.js +1 -1
  61. package/lib/module/ui/components/SignInModal.js +11 -12
  62. package/lib/module/ui/components/SignInModal.js.map +1 -1
  63. package/lib/module/ui/components/accountMenuRows.js +18 -30
  64. package/lib/module/ui/components/accountMenuRows.js.map +1 -1
  65. package/lib/module/ui/context/OxyContext.js +58 -79
  66. package/lib/module/ui/context/OxyContext.js.map +1 -1
  67. package/lib/module/ui/context/hooks/useAuthOperations.js +7 -13
  68. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  69. package/lib/module/ui/hooks/useAuth.js +10 -40
  70. package/lib/module/ui/hooks/useAuth.js.map +1 -1
  71. package/lib/module/ui/hooks/useDeviceAccounts.js +281 -0
  72. package/lib/module/ui/hooks/useDeviceAccounts.js.map +1 -0
  73. package/lib/module/ui/hooks/useFollow.js +21 -7
  74. package/lib/module/ui/hooks/useFollow.js.map +1 -1
  75. package/lib/module/ui/hooks/useSessionManagement.js +5 -6
  76. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
  77. package/lib/module/ui/hooks/useSessionSocket.js +4 -5
  78. package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
  79. package/lib/module/ui/hooks/useWebSSO.js +1 -1
  80. package/lib/module/ui/navigation/routes.js +7 -7
  81. package/lib/module/ui/navigation/routes.js.map +1 -1
  82. package/lib/module/ui/screens/OxyAuthScreen.js +6 -7
  83. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
  84. package/lib/module/ui/screens/ProfileScreen.js +18 -20
  85. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  86. package/lib/module/ui/screens/WelcomeNewUserScreen.js +4 -4
  87. package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  88. package/lib/module/ui/screens/{karma/KarmaAboutScreen.js → trust/TrustAboutScreen.js} +11 -11
  89. package/lib/module/ui/screens/trust/TrustAboutScreen.js.map +1 -0
  90. package/lib/module/ui/screens/{karma/KarmaCenterScreen.js → trust/TrustCenterScreen.js} +92 -42
  91. package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -0
  92. package/lib/module/ui/screens/{karma/KarmaFAQScreen.js → trust/TrustFAQScreen.js} +11 -11
  93. package/lib/module/ui/screens/{karma/KarmaFAQScreen.js.map → trust/TrustFAQScreen.js.map} +1 -1
  94. package/lib/module/ui/screens/{karma/KarmaLeaderboardScreen.js → trust/TrustLeaderboardScreen.js} +63 -42
  95. package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -0
  96. package/lib/module/ui/screens/{karma/KarmaRewardsScreen.js → trust/TrustRewardsScreen.js} +54 -54
  97. package/lib/module/ui/screens/trust/TrustRewardsScreen.js.map +1 -0
  98. package/lib/module/ui/screens/{karma/KarmaRulesScreen.js → trust/TrustRulesScreen.js} +45 -16
  99. package/lib/module/ui/screens/trust/TrustRulesScreen.js.map +1 -0
  100. package/lib/module/ui/screens/trust/trustTier.js +19 -0
  101. package/lib/module/ui/screens/trust/trustTier.js.map +1 -0
  102. package/lib/module/utils/deviceFlowSignIn.js +13 -10
  103. package/lib/module/utils/deviceFlowSignIn.js.map +1 -1
  104. package/lib/typescript/commonjs/index.d.ts +3 -1
  105. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts +30 -10
  107. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts +1 -1
  109. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts +19 -12
  111. package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +3 -3
  113. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  114. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +1 -7
  116. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts +3 -7
  118. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts +133 -0
  120. package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts.map +1 -0
  121. package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts +1 -1
  122. package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/ui/hooks/useSessionSocket.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts +1 -1
  126. package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
  127. package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts.map +1 -1
  128. package/lib/typescript/commonjs/ui/screens/ProfileScreen.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/ui/screens/trust/TrustAboutScreen.d.ts +5 -0
  130. package/lib/typescript/commonjs/ui/screens/{karma/KarmaAboutScreen.d.ts.map → trust/TrustAboutScreen.d.ts.map} +1 -1
  131. package/lib/typescript/commonjs/ui/screens/trust/TrustCenterScreen.d.ts +5 -0
  132. package/lib/typescript/commonjs/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -0
  133. package/lib/typescript/{module/ui/screens/karma/KarmaFAQScreen.d.ts → commonjs/ui/screens/trust/TrustFAQScreen.d.ts} +1 -1
  134. package/lib/typescript/commonjs/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts +5 -0
  136. package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -0
  137. package/lib/typescript/commonjs/ui/screens/trust/TrustRewardsScreen.d.ts +5 -0
  138. package/lib/typescript/commonjs/ui/screens/{karma/KarmaRewardsScreen.d.ts.map → trust/TrustRewardsScreen.d.ts.map} +1 -1
  139. package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts +5 -0
  140. package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -0
  141. package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts +9 -0
  142. package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts.map +1 -0
  143. package/lib/typescript/commonjs/ui/types/navigation.d.ts +1 -1
  144. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +11 -9
  145. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +1 -1
  146. package/lib/typescript/module/index.d.ts +3 -1
  147. package/lib/typescript/module/index.d.ts.map +1 -1
  148. package/lib/typescript/module/ui/components/AccountMenu.d.ts +30 -10
  149. package/lib/typescript/module/ui/components/AccountMenu.d.ts.map +1 -1
  150. package/lib/typescript/module/ui/components/SignInModal.d.ts +1 -1
  151. package/lib/typescript/module/ui/components/SignInModal.d.ts.map +1 -1
  152. package/lib/typescript/module/ui/components/accountMenuRows.d.ts +19 -12
  153. package/lib/typescript/module/ui/components/accountMenuRows.d.ts.map +1 -1
  154. package/lib/typescript/module/ui/context/OxyContext.d.ts +3 -3
  155. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  156. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  157. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +1 -7
  158. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  159. package/lib/typescript/module/ui/hooks/useAuth.d.ts +3 -7
  160. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
  161. package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts +133 -0
  162. package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts.map +1 -0
  163. package/lib/typescript/module/ui/hooks/useFollow.d.ts +1 -1
  164. package/lib/typescript/module/ui/hooks/useFollow.d.ts.map +1 -1
  165. package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
  166. package/lib/typescript/module/ui/hooks/useSessionSocket.d.ts.map +1 -1
  167. package/lib/typescript/module/ui/hooks/useWebSSO.d.ts +1 -1
  168. package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
  169. package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts.map +1 -1
  170. package/lib/typescript/module/ui/screens/ProfileScreen.d.ts.map +1 -1
  171. package/lib/typescript/module/ui/screens/trust/TrustAboutScreen.d.ts +5 -0
  172. package/lib/typescript/module/ui/screens/{karma/KarmaAboutScreen.d.ts.map → trust/TrustAboutScreen.d.ts.map} +1 -1
  173. package/lib/typescript/module/ui/screens/trust/TrustCenterScreen.d.ts +5 -0
  174. package/lib/typescript/module/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -0
  175. package/lib/typescript/{commonjs/ui/screens/karma/KarmaFAQScreen.d.ts → module/ui/screens/trust/TrustFAQScreen.d.ts} +1 -1
  176. package/lib/typescript/module/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -0
  177. package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts +5 -0
  178. package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -0
  179. package/lib/typescript/module/ui/screens/trust/TrustRewardsScreen.d.ts +5 -0
  180. package/lib/typescript/module/ui/screens/{karma/KarmaRewardsScreen.d.ts.map → trust/TrustRewardsScreen.d.ts.map} +1 -1
  181. package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts +5 -0
  182. package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -0
  183. package/lib/typescript/module/ui/screens/trust/trustTier.d.ts +9 -0
  184. package/lib/typescript/module/ui/screens/trust/trustTier.d.ts.map +1 -0
  185. package/lib/typescript/module/ui/types/navigation.d.ts +1 -1
  186. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +11 -9
  187. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +1 -1
  188. package/package.json +2 -2
  189. package/src/index.ts +10 -1
  190. package/src/ui/components/AccountMenu.tsx +311 -253
  191. package/src/ui/components/AccountMenuButton.tsx +2 -2
  192. package/src/ui/components/FollowButton.tsx +2 -2
  193. package/src/ui/components/OxySignInButton.tsx +1 -1
  194. package/src/ui/components/SignInModal.tsx +13 -14
  195. package/src/ui/components/accountMenuRows.ts +28 -40
  196. package/src/ui/context/OxyContext.tsx +71 -74
  197. package/src/ui/context/hooks/useAuthOperations.ts +7 -13
  198. package/src/ui/hooks/useAuth.ts +12 -49
  199. package/src/ui/hooks/useDeviceAccounts.ts +348 -0
  200. package/src/ui/hooks/useFollow.ts +16 -8
  201. package/src/ui/hooks/useSessionManagement.ts +5 -14
  202. package/src/ui/hooks/useSessionSocket.ts +4 -5
  203. package/src/ui/hooks/useWebSSO.ts +1 -1
  204. package/src/ui/navigation/routes.ts +13 -13
  205. package/src/ui/screens/OxyAuthScreen.tsx +6 -7
  206. package/src/ui/screens/ProfileScreen.tsx +15 -17
  207. package/src/ui/screens/WelcomeNewUserScreen.tsx +2 -2
  208. package/src/ui/screens/{karma/KarmaAboutScreen.tsx → trust/TrustAboutScreen.tsx} +15 -15
  209. package/src/ui/screens/{karma/KarmaCenterScreen.tsx → trust/TrustCenterScreen.tsx} +87 -41
  210. package/src/ui/screens/{karma/KarmaFAQScreen.tsx → trust/TrustFAQScreen.tsx} +10 -10
  211. package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +101 -0
  212. package/src/ui/screens/{karma/KarmaRewardsScreen.tsx → trust/TrustRewardsScreen.tsx} +54 -54
  213. package/src/ui/screens/{karma/KarmaRulesScreen.tsx → trust/TrustRulesScreen.tsx} +27 -13
  214. package/src/ui/screens/trust/trustTier.ts +20 -0
  215. package/src/ui/types/navigation.ts +1 -2
  216. package/src/utils/__tests__/deviceFlowSignIn.test.ts +2 -3
  217. package/src/utils/deviceFlowSignIn.ts +18 -12
  218. package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js.map +0 -1
  219. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +0 -1
  220. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +0 -1
  221. package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js.map +0 -1
  222. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +0 -1
  223. package/lib/module/ui/screens/karma/KarmaAboutScreen.js.map +0 -1
  224. package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +0 -1
  225. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +0 -1
  226. package/lib/module/ui/screens/karma/KarmaRewardsScreen.js.map +0 -1
  227. package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +0 -1
  228. package/lib/typescript/commonjs/ui/screens/karma/KarmaAboutScreen.d.ts +0 -5
  229. package/lib/typescript/commonjs/ui/screens/karma/KarmaCenterScreen.d.ts +0 -5
  230. package/lib/typescript/commonjs/ui/screens/karma/KarmaCenterScreen.d.ts.map +0 -1
  231. package/lib/typescript/commonjs/ui/screens/karma/KarmaFAQScreen.d.ts.map +0 -1
  232. package/lib/typescript/commonjs/ui/screens/karma/KarmaLeaderboardScreen.d.ts +0 -5
  233. package/lib/typescript/commonjs/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +0 -1
  234. package/lib/typescript/commonjs/ui/screens/karma/KarmaRewardsScreen.d.ts +0 -5
  235. package/lib/typescript/commonjs/ui/screens/karma/KarmaRulesScreen.d.ts +0 -5
  236. package/lib/typescript/commonjs/ui/screens/karma/KarmaRulesScreen.d.ts.map +0 -1
  237. package/lib/typescript/module/ui/screens/karma/KarmaAboutScreen.d.ts +0 -5
  238. package/lib/typescript/module/ui/screens/karma/KarmaCenterScreen.d.ts +0 -5
  239. package/lib/typescript/module/ui/screens/karma/KarmaCenterScreen.d.ts.map +0 -1
  240. package/lib/typescript/module/ui/screens/karma/KarmaFAQScreen.d.ts.map +0 -1
  241. package/lib/typescript/module/ui/screens/karma/KarmaLeaderboardScreen.d.ts +0 -5
  242. package/lib/typescript/module/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +0 -1
  243. package/lib/typescript/module/ui/screens/karma/KarmaRewardsScreen.d.ts +0 -5
  244. package/lib/typescript/module/ui/screens/karma/KarmaRulesScreen.d.ts +0 -5
  245. package/lib/typescript/module/ui/screens/karma/KarmaRulesScreen.d.ts.map +0 -1
  246. package/src/ui/screens/karma/KarmaLeaderboardScreen.tsx +0 -88
@@ -3,7 +3,7 @@ import { useCallback, useRef, useState } from 'react';
3
3
  import { TouchableOpacity, StyleSheet, Platform, type LayoutChangeEvent } from 'react-native';
4
4
  import { getAccountDisplayName } from '@oxyhq/core';
5
5
  import Avatar from './Avatar';
6
- import AccountMenu from './AccountMenu';
6
+ import AccountMenu, { type AccountMenuAnchor } from './AccountMenu';
7
7
  import { useOxy } from '../context/OxyContext';
8
8
  import { useI18n } from '../hooks/useI18n';
9
9
 
@@ -33,7 +33,7 @@ const AccountMenuButton: React.FC<AccountMenuButtonProps> = ({
33
33
  const { user, oxyServices, isAuthenticated } = useOxy();
34
34
  const { t, locale } = useI18n();
35
35
  const [open, setOpen] = useState(false);
36
- const [anchor, setAnchor] = useState<{ top: number; right: number } | null>(null);
36
+ const [anchor, setAnchor] = useState<AccountMenuAnchor | null>(null);
37
37
  const triggerRef = useRef<React.ComponentRef<typeof TouchableOpacity>>(null);
38
38
 
39
39
  const measureAnchor = useCallback(() => {
@@ -108,12 +108,12 @@ const FollowButtonInner = memo(function FollowButtonInner({
108
108
  });
109
109
 
110
110
  const FollowButton: React.FC<FollowButtonProps> = (props) => {
111
- const { oxyServices, isAuthenticated, user: currentUser } = useOxy();
111
+ const { oxyServices, isAuthenticated, isAuthResolved, isTokenReady, user: currentUser } = useOxy();
112
112
 
113
113
  const currentUserId = currentUser?.id ? String(currentUser.id).trim() : '';
114
114
  const targetUserId = props.userId ? String(props.userId).trim() : '';
115
115
 
116
- if (!isAuthenticated || !targetUserId || (currentUserId && currentUserId === targetUserId)) {
116
+ if (!isAuthResolved || !isTokenReady || !isAuthenticated || !targetUserId || (currentUserId && currentUserId === targetUserId)) {
117
117
  return null;
118
118
  }
119
119
 
@@ -100,7 +100,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
100
100
  }, []);
101
101
 
102
102
  // Handle button press
103
- // - Web: legacy full-screen Modal (dialog UX fits desktop / browser)
103
+ // - Web: full-screen modal (dialog UX fits desktop / browser)
104
104
  // - Native: bottom sheet (sheet UX fits iOS/Android)
105
105
  const handlePress = useCallback(() => {
106
106
  if (onPress) {
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * A semi-transparent full-screen modal that displays:
5
5
  * - QR code for scanning with Oxy Accounts app
6
- * - Button to open Oxy Auth popup
6
+ * - Button to open the Oxy Auth approval page
7
7
  *
8
8
  * Animates with fade-in effect.
9
9
  */
@@ -143,12 +143,11 @@ const SignInModal: React.FC = () => {
143
143
  // this is the device-flow equivalent of OAuth's code-for-token
144
144
  // exchange (RFC 8628 §3.4).
145
145
  //
146
- // Without that exchange the SDK has no bearer token and every
147
- // subsequent call (including `switchSession` -> `getTokenBySession`)
148
- // would 401 against the C1-hardened API. Once `claimSessionByToken`
149
- // plants the tokens in the HttpService, the rest of the session
150
- // wiring (state, persistence, language preference) flows through
151
- // the normal `switchSession` path.
146
+ // Without that exchange the SDK has no bearer token and the app never
147
+ // becomes authenticated even though the session is authorized server-side.
148
+ // Once `claimSessionByToken` plants the tokens in the HttpService, the rest
149
+ // of the session wiring (state, persistence, language preference) flows
150
+ // through the normal `switchSession` path.
152
151
  const handleAuthSuccess = useCallback(async (sessionId: string, sessionToken: string) => {
153
152
  if (isProcessingRef.current) return;
154
153
  isProcessingRef.current = true;
@@ -327,8 +326,8 @@ const SignInModal: React.FC = () => {
327
326
  return `oxyauth://${authSession.sessionToken}`;
328
327
  };
329
328
 
330
- // Open Oxy Auth popup
331
- const handleOpenAuthPopup = useCallback(async () => {
329
+ // Open Oxy Auth approval page for this device-flow session.
330
+ const handleOpenAuthApproval = useCallback(async () => {
332
331
  if (!authSession) return;
333
332
 
334
333
  const baseURL = oxyServices.getBaseURL();
@@ -354,7 +353,7 @@ const SignInModal: React.FC = () => {
354
353
  webUrl.searchParams.set('token', authSession.sessionToken);
355
354
 
356
355
  if (Platform.OS === 'web') {
357
- // Open popup window on web
356
+ // Open a separate approval window on web for the device-flow token.
358
357
  const width = 500;
359
358
  const height = 650;
360
359
  const screenWidth = window.screen?.width ?? width;
@@ -364,8 +363,8 @@ const SignInModal: React.FC = () => {
364
363
 
365
364
  window.open(
366
365
  webUrl.toString(),
367
- 'oxy-auth-popup',
368
- `width=${width},height=${height},left=${left},top=${top},popup=1`
366
+ 'oxy-auth-approval',
367
+ `width=${width},height=${height},left=${left},top=${top}`
369
368
  );
370
369
  } else {
371
370
  // Open in browser on native
@@ -449,9 +448,9 @@ const SignInModal: React.FC = () => {
449
448
  <View style={[styles.divider, { backgroundColor: 'rgba(255,255,255,0.3)' }]} />
450
449
  </View>
451
450
 
452
- {/* Open Auth Popup Button */}
451
+ {/* Open approval window */}
453
452
  <Button
454
- onPress={handleOpenAuthPopup}
453
+ onPress={handleOpenAuthApproval}
455
454
  icon={<OxyLogo variant="icon" size={20} fillColor={theme.colors.card} />}
456
455
  >
457
456
  Open Oxy Auth
@@ -1,60 +1,48 @@
1
- import type { User, ClientSession } from '@oxyhq/core';
2
- import { getAccountDisplayName, getAccountFallbackHandle } from '@oxyhq/core';
1
+ import type { DeviceAccount, DeviceAccountUser } from '../hooks/useDeviceAccounts';
3
2
 
4
3
  export interface AccountRow {
5
4
  sessionId: string;
5
+ /** Device-local refresh-cookie slot index (web silent-switch). */
6
+ authuser?: number;
6
7
  isActive: boolean;
7
8
  displayName: string;
8
9
  secondary: string | null;
9
10
  avatarUri?: string;
10
- user: User | null;
11
+ user: DeviceAccountUser | null;
11
12
  }
12
13
 
13
14
  export interface BuildAccountRowsInput {
14
- sessions: ClientSession[] | null | undefined;
15
- activeSessionId: string | null | undefined;
16
- user: User | null | undefined;
17
- locale: string;
18
- getAvatarUrl: (avatarId: string) => string;
15
+ /**
16
+ * Per-device account entries from {@link useDeviceAccounts}. Each entry
17
+ * already carries real per-account `displayName` / `email` / `avatarUrl` /
18
+ * `color`, so EVERY row (not just the active one) renders full identity.
19
+ */
20
+ accounts: DeviceAccount[];
19
21
  }
20
22
 
21
23
  /**
22
24
  * Pure builder for `AccountMenu` rows. Extracted so the multi-account display
23
25
  * logic can be unit-tested without rendering React Native.
24
26
  *
25
- * Each `sessions[i]` becomes one row. Only the row matching `activeSessionId`
26
- * carries the loaded `user` payload the others are placeholders shown by
27
- * fallback handle. This mirrors how `OxyContext` only hydrates one user at a
28
- * time.
27
+ * Maps each {@link DeviceAccount} (sourced from `useDeviceAccounts()`, which
28
+ * hydrates EVERY account with real name/email/avatar/color from the shared
29
+ * apex `refresh-all` path or the local fallback) into an `AccountRow`.
30
+ *
31
+ * `secondary` is the account's real email when present; otherwise it falls
32
+ * back to the `@handle` line. A missing email is NEVER synthesized into a fake
33
+ * `username@oxy.so` — the device-account layer already resolved `email` to the
34
+ * real value or the `@handle` fallback.
29
35
  */
30
36
  export function buildAccountRows({
31
- sessions,
32
- activeSessionId,
33
- user,
34
- locale,
35
- getAvatarUrl,
37
+ accounts,
36
38
  }: BuildAccountRowsInput): AccountRow[] {
37
- return (sessions ?? []).map((session: ClientSession) => {
38
- const isActive = session.sessionId === activeSessionId;
39
- const candidate: Partial<User> | null = isActive ? user ?? null : null;
40
- const displayName = getAccountDisplayName(
41
- candidate ?? { username: undefined },
42
- locale,
43
- );
44
- const handle = getAccountFallbackHandle(candidate ?? { username: undefined });
45
- const secondary = (candidate?.email)
46
- ?? (handle && candidate?.username ? `@${handle}` : handle)
47
- ?? null;
48
- const avatarUri = candidate?.avatar
49
- ? getAvatarUrl(candidate.avatar)
50
- : undefined;
51
- return {
52
- sessionId: session.sessionId,
53
- isActive,
54
- displayName,
55
- secondary,
56
- avatarUri,
57
- user: isActive ? user ?? null : null,
58
- };
59
- });
39
+ return accounts.map((account: DeviceAccount): AccountRow => ({
40
+ sessionId: account.sessionId,
41
+ authuser: account.authuser,
42
+ isActive: account.isCurrent,
43
+ displayName: account.displayName,
44
+ secondary: account.email,
45
+ avatarUri: account.avatarUrl,
46
+ user: account.user,
47
+ }));
60
48
  }
@@ -4,6 +4,7 @@ import {
4
4
  useCallback,
5
5
  useContext,
6
6
  useEffect,
7
+ useLayoutEffect,
7
8
  useMemo,
8
9
  useRef,
9
10
  useState,
@@ -89,10 +90,10 @@ export interface OxyContextState {
89
90
  signIn: (publicKey: string, deviceName?: string) => Promise<User>;
90
91
 
91
92
  /**
92
- * Handle session from popup authentication
93
- * Updates auth state, persists session to storage
93
+ * Handle a session returned by web SSO.
94
+ * Updates auth state, persists session metadata to storage.
94
95
  */
95
- handlePopupSession: (session: SessionLoginResponse) => Promise<void>;
96
+ handleWebSession: (session: SessionLoginResponse) => Promise<void>;
96
97
 
97
98
  // Session management
98
99
  logout: (targetSessionId?: string) => Promise<void>;
@@ -206,7 +207,7 @@ function silentColdBootKey(oxyServices: OxyServices): string {
206
207
  * iframe never posts a message, so the full wait would be dead latency in front
207
208
  * of the terminal `/sso` bounce. `silentSignIn` already fails fast on a load
208
209
  * error via `iframe.onerror`; this caps the no-message case. 2.5s is well above
209
- * a same-origin iframe handshake yet a fraction of the legacy 5s default.
210
+ * a same-origin iframe handshake without blocking cold boot for several seconds.
210
211
  */
211
212
  const SILENT_IFRAME_TIMEOUT = 2500;
212
213
 
@@ -287,6 +288,12 @@ function isSameSiteIdP(idpOrigin: string): boolean {
287
288
  return idpHostname === pageApex || idpHostname.endsWith(`.${pageApex}`);
288
289
  }
289
290
 
291
+ function isOnSsoCallbackPath(): boolean {
292
+ return isWebBrowser() && window.location.pathname === SSO_CALLBACK_PATH;
293
+ }
294
+
295
+ const useBrowserLayoutEffect = typeof document !== 'undefined' ? useLayoutEffect : useEffect;
296
+
290
297
  let cachedUseFollowHook: UseFollowHook | null = null;
291
298
 
292
299
  const loadUseFollowHook = (): UseFollowHook => {
@@ -387,6 +394,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
387
394
  const [authResolved, setAuthResolved] = useState(false);
388
395
  const authResolvedRef = useRef(false);
389
396
  const [initialized, setInitialized] = useState(false);
397
+ const [ssoCallbackIntercepting, setSsoCallbackIntercepting] = useState(false);
390
398
  const setAuthState = useAuthStore.setState;
391
399
 
392
400
  // Keep the shared `oxyClient` singleton's token store in lockstep with the
@@ -403,8 +411,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
403
411
  // plumbing and regardless of which auth code path fired.
404
412
  //
405
413
  // When the app passed the singleton itself as `oxyServices` (Mention's
406
- // pattern), `oxyServices === oxyClient`, so we skip the redundant self-write
407
- // and the subscription is a no-op mirror — fully backward compatible.
414
+ // pattern), `oxyServices === oxyClient`, so we skip the redundant self-write.
408
415
  useEffect(() => {
409
416
  if (oxyServices === oxyClient) {
410
417
  return;
@@ -635,7 +642,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
635
642
  // init. Callers MUST invoke this BEFORE any work that can trigger a route
636
643
  // navigation (`onAuthStateChange`) — navigation can interrupt a still-pending
637
644
  // async write, which is exactly what once left `session_ids` empty after a
638
- // successful sign-in. Shared by the FedCM/popup path and the cold-boot
645
+ // successful sign-in. Shared by the FedCM/SSO path and the cold-boot
639
646
  // refresh-cookie restore so both land the same durable record.
640
647
  const persistSessionDurably = useCallback(async (sessionId: string): Promise<void> => {
641
648
  const readyStorage = await getReadyStorage();
@@ -663,7 +670,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
663
670
  // Idempotent and monotonic via `authResolvedRef`: the first call wins and the
664
671
  // setters fire at most once, so the restore `finally` backstop becomes a no-op
665
672
  // once a commit site has already marked resolution. Called from EVERY place a
666
- // user is actually committed (the FedCM/iframe/redirect/SSO path
673
+ // user is actually committed (the FedCM/iframe/SSO path
667
674
  // `handleWebSSOSession`, the cookie-restore path, and the stored-session path)
668
675
  // so the common reload case unblocks the loading gate without sitting behind
669
676
  // the remaining (now-skipped) cold-boot steps.
@@ -691,10 +698,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
691
698
  // Calls `oxyServices.refreshAllSessions()` → `POST /auth/refresh-all` with
692
699
  // `credentials: 'include'`. The server rotates every device-local
693
700
  // `oxy_rt_${authuser}` cookie in parallel and returns one entry per valid
694
- // account (Google-style multi-account). On an older server that lacks the
695
- // multi-account endpoint, the SDK transparently falls back to the legacy
696
- // `/auth/refresh` single-account path and wraps the result in the same
697
- // shape, so this caller doesn't branch.
701
+ // account (Google-style multi-account).
698
702
  //
699
703
  // Active-account selection: the persisted `oxy_active_authuser` slot index
700
704
  // wins when it matches a returned account; otherwise the lowest `authuser`
@@ -787,23 +791,31 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
787
791
  // Native (and offline) stored-session restore — the ONLY restore path that
788
792
  // runs on React Native, and the web fallback when no cross-domain step won.
789
793
  //
790
- // Verbatim-extracted from the previous `restoreSessionsFromStorage` body: it
791
- // reads the durable `session_ids` / `active_session_id` slots, validates each
792
- // stored session in parallel (bearer `validateSession`), and switches to the
793
- // stored active session via the session-management `switchSession`. This body
794
- // is platform-agnostic and gated by NO `enabled()` predicate so it runs on
795
- // every platform — on native it is reached unconditionally (every web-only
796
- // step ahead of it is disabled by `isWebBrowser()`), so native restore is
797
- // exactly this and nothing else (no FedCM / iframe / refresh-all /
798
- // handleAuthCallback).
794
+ // Stored-session restore. Web uses this only as a fast local winner after
795
+ // URL-return handling; native uses it as the durable SecureStore path. Native
796
+ // first plants the shared access token from KeyManager, then validates the
797
+ // stored session ids with the bearer already in memory.
799
798
  const restoreStoredSession = useCallback(async (): Promise<boolean> => {
800
799
  if (!storage) {
801
800
  return false;
802
801
  }
803
802
 
804
803
  const storedSessionIdsJson = await storage.getItem(storageKeys.sessionIds);
805
- const storedSessionIds: string[] = storedSessionIdsJson ? JSON.parse(storedSessionIdsJson) : [];
806
- const storedActiveSessionId = await storage.getItem(storageKeys.activeSessionId);
804
+ const storedSessionIdsFromStorage: string[] = storedSessionIdsJson ? JSON.parse(storedSessionIdsJson) : [];
805
+ let storedActiveSessionId = await storage.getItem(storageKeys.activeSessionId);
806
+
807
+ const nativeSharedSession = !isWebBrowser()
808
+ ? await KeyManager.getSharedSession().catch(() => null)
809
+ : null;
810
+ if (nativeSharedSession?.accessToken) {
811
+ oxyServices.setTokens(nativeSharedSession.accessToken);
812
+ storedActiveSessionId = storedActiveSessionId ?? nativeSharedSession.sessionId;
813
+ }
814
+
815
+ const storedSessionIds = Array.from(new Set([
816
+ ...storedSessionIdsFromStorage,
817
+ ...(nativeSharedSession?.sessionId ? [nativeSharedSession.sessionId] : []),
818
+ ]));
807
819
 
808
820
  let validSessions: ClientSession[] = [];
809
821
 
@@ -964,7 +976,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
964
976
  // web-only step is gated by `isWebBrowser()`, so on native ONLY
965
977
  // `stored-session` runs.
966
978
  //
967
- // Order (web): redirect callback → SSO return → stored session → FedCM silent
979
+ // Order (web): SSO return → stored session → FedCM silent
968
980
  // (central) → silent iframe (per-apex, the durable reload path) → cookie
969
981
  // restore → SSO bounce (terminal).
970
982
  //
@@ -972,8 +984,8 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
972
984
  // (`fedcm-silent`, `silent-iframe`, `cookie-restore`). On a normal reload the
973
985
  // local bearer validates in one round-trip and wins, so `runColdBoot`
974
986
  // short-circuits and never sits through those probes' timeouts (the prior
975
- // serial sum was a ~20-30s stall). `redirect` and `sso-return` MUST stay
976
- // first — they consume the URL fragment before anything can strip it. On a
987
+ // serial sum was a ~20-30s stall). `sso-return` MUST stay first — it consumes
988
+ // the URL fragment before anything can strip it. On a
977
989
  // first visit with no local session, `stored-session` skips and the
978
990
  // cross-domain fallback chain (fedcm → iframe → cookie → sso-bounce) runs
979
991
  // exactly as before; the per-apex silent iframe still restores a durable
@@ -1006,26 +1018,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1006
1018
  const outcome = await runColdBoot<true>({
1007
1019
  steps: [
1008
1020
  {
1009
- // 0) Redirect callback wins: a popup/redirect sign-in just landed
1010
- // back on this page with `access_token`/`session_id` query params.
1011
- // `handleAuthCallback` plants the token but returns a PLACEHOLDER
1012
- // user (empty id), so we hydrate the REAL user via `getCurrentUser`
1013
- // and commit through `handleWebSSOSession` before claiming a
1014
- // session — never expose a placeholder user (R4).
1015
- id: 'redirect',
1016
- enabled: () => isWebBrowser(),
1017
- run: async () => {
1018
- const callbackSession = oxyServices.handleAuthCallback?.();
1019
- if (!callbackSession || !commitWebSession) {
1020
- return { kind: 'skip' };
1021
- }
1022
- const fullUser = await oxyServices.getCurrentUser();
1023
- await commitWebSession({ ...callbackSession, user: fullUser });
1024
- return { kind: 'session', session: true };
1025
- },
1026
- },
1027
- {
1028
- // 1) Central SSO return: we are landing back from an `auth.oxy.so/sso`
1021
+ // 0) Central SSO return: we are landing back from an `auth.oxy.so/sso`
1029
1022
  // bounce with the result in the URL fragment. Parse it, validate the
1030
1023
  // CSRF state, exchange the opaque code, and commit. On any non-ok
1031
1024
  // outcome `runSsoReturn` sets the per-origin NO_SESSION flag so the
@@ -1048,8 +1041,8 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1048
1041
  // normal reload the local bearer validates in one round-trip and
1049
1042
  // wins; `runColdBoot` then short-circuits and never even evaluates
1050
1043
  // the slow no-redirect probes that would otherwise time out (the
1051
- // ~20-30s serial stall). The `redirect` and `sso-return` steps stay
1052
- // AHEAD of this one — they must consume the URL fragment before any
1044
+ // ~20-30s serial stall). The `sso-return` step stays AHEAD of this
1045
+ // one — it must consume the URL fragment before any
1053
1046
  // later step (or anything else) strips it. On a first visit with no
1054
1047
  // local session this step skips and the cross-domain fallback chain
1055
1048
  // (fedcm → iframe → cookie → sso-bounce) runs exactly as before.
@@ -1308,13 +1301,12 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1308
1301
  // +not-found screen before the storage-gated cold-boot `sso-return` step gets
1309
1302
  // a chance to strip the fragment and restore the real destination.
1310
1303
  //
1311
- // This effect fires the SAME `runSsoReturn` kernel the instant we mount ON the
1312
- // callback path, BEFORE the cold boot (which awaits storage init). It restores
1313
- // the pre-bounce destination (and, on `ok`, commits the exchanged session via
1314
- // `handleWebSSOSession`) immediately, so the router re-syncs off the callback
1315
- // path and never lingers on it. Because the SDK owns this interception
1316
- // entirely, NO app needs a `/__oxy/sso-callback` route — it works identically
1317
- // across every consumer with zero per-app code.
1304
+ // This effect fires the SAME `runSsoReturn` kernel the instant we hydrate ON
1305
+ // the callback path, BEFORE the cold boot (which awaits storage init). The
1306
+ // first render intentionally matches the app/router's static HTML; the
1307
+ // browser layout effect then hides the internal route and consumes the
1308
+ // callback before the first visible paint. That keeps SSR/SSG hydration stable
1309
+ // while still ensuring no app needs a `/__oxy/sso-callback` route.
1318
1310
  //
1319
1311
  // It is purely ADDITIVE. The later cold-boot `sso-return` step stays as
1320
1312
  // defense-in-depth for the non-callback-path case; `consumeSsoReturn` strips
@@ -1330,13 +1322,13 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1330
1322
  // already wired when this fires at eager-mount time. If for any reason it were
1331
1323
  // not yet set, the later cold-boot `sso-return` step would commit it — but the
1332
1324
  // ref IS set during render, so the eager `ok` commit works.
1333
- useEffect(() => {
1334
- if (!isWebBrowser()) {
1335
- return;
1336
- }
1337
- if (window.location.pathname !== SSO_CALLBACK_PATH) {
1325
+ useBrowserLayoutEffect(() => {
1326
+ if (!isOnSsoCallbackPath()) {
1327
+ setSsoCallbackIntercepting(false);
1338
1328
  return;
1339
1329
  }
1330
+ let mounted = true;
1331
+ setSsoCallbackIntercepting(true);
1340
1332
  runSsoReturnRef.current().catch((error) => {
1341
1333
  if (__DEV__) {
1342
1334
  loggerUtil.debug(
@@ -1345,11 +1337,19 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1345
1337
  error,
1346
1338
  );
1347
1339
  }
1340
+ }).finally(() => {
1341
+ if (mounted) {
1342
+ setSsoCallbackIntercepting(false);
1343
+ }
1348
1344
  });
1345
+
1346
+ return () => {
1347
+ mounted = false;
1348
+ };
1349
1349
  }, []);
1350
1350
 
1351
- // Web SSO: Automatically check for cross-domain session on web platforms
1352
- // Also used for popup auth - updates all state and persists session
1351
+ // Web SSO: automatically check for cross-domain session on web platforms.
1352
+ // Updates all state and persists session metadata.
1353
1353
  const handleWebSSOSession = useCallback(async (session: SessionLoginResponse) => {
1354
1354
  if (!session?.user || !session?.sessionId) {
1355
1355
  if (__DEV__) {
@@ -1358,12 +1358,10 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1358
1358
  return;
1359
1359
  }
1360
1360
 
1361
- // Set the access token on the HTTP client before updating UI state
1362
- if (session.accessToken) {
1363
- oxyServices.httpService.setTokens(session.accessToken);
1364
- } else {
1365
- await oxyServices.getTokenBySession(session.sessionId);
1361
+ if (!session.accessToken) {
1362
+ throw new Error('Session response did not include an access token');
1366
1363
  }
1364
+ oxyServices.httpService.setTokens(session.accessToken);
1367
1365
 
1368
1366
  const clientSession = {
1369
1367
  sessionId: session.sessionId,
@@ -1405,14 +1403,14 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1405
1403
  fullUser = session.user as unknown as User;
1406
1404
  }
1407
1405
  loginSuccess(fullUser);
1408
- // A session is now committed (FedCM silent / per-apex iframe / redirect /
1409
- // SSO-return / popup all funnel through here) — unblock the auth-resolution
1406
+ // A session is now committed (FedCM silent / per-apex iframe /
1407
+ // SSO-return all funnel through here) — unblock the auth-resolution
1410
1408
  // gate immediately, ahead of the cold-boot chain returning (idempotent).
1411
1409
  markAuthResolvedRef.current();
1412
1410
  onAuthStateChange?.(fullUser);
1413
1411
  }, [oxyServices, updateSessions, setActiveSessionId, loginSuccess, onAuthStateChange, persistSessionDurably]);
1414
1412
 
1415
- // Expose `handleWebSSOSession` to the cold-boot FedCM/iframe/redirect steps,
1413
+ // Expose `handleWebSSOSession` to the cold-boot FedCM/iframe/SSO steps,
1416
1414
  // which reference it through a ref because they are declared above this
1417
1415
  // callback. Assigned synchronously on every render so the ref is populated
1418
1416
  // before the cold-boot effect (gated on `storage`/`initialized`) can fire.
@@ -1670,7 +1668,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1670
1668
  hasIdentity,
1671
1669
  getPublicKey,
1672
1670
  signIn,
1673
- handlePopupSession: handleWebSSOSession,
1671
+ handleWebSession: handleWebSSOSession,
1674
1672
  logout,
1675
1673
  logoutAll,
1676
1674
  switchSession: switchSessionForContext,
@@ -1735,7 +1733,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1735
1733
 
1736
1734
  return (
1737
1735
  <OxyContext.Provider value={contextValue}>
1738
- {children}
1736
+ {ssoCallbackIntercepting ? null : children}
1739
1737
  </OxyContext.Provider>
1740
1738
  );
1741
1739
  };
@@ -1777,7 +1775,7 @@ const LOADING_STATE: OxyContextState = {
1777
1775
  hasIdentity: () => Promise.resolve(false),
1778
1776
  getPublicKey: () => Promise.resolve(null),
1779
1777
  signIn: () => rejectMissingProvider<User>(),
1780
- handlePopupSession: () => rejectMissingProvider<void>(),
1778
+ handleWebSession: () => rejectMissingProvider<void>(),
1781
1779
  logout: () => rejectMissingProvider<void>(),
1782
1780
  logoutAll: () => rejectMissingProvider<void>(),
1783
1781
  switchSession: () => rejectMissingProvider<User>(),
@@ -1809,4 +1807,3 @@ export const useOxy = (): OxyContextState => {
1809
1807
  };
1810
1808
 
1811
1809
  export default OxyContext;
1812
-
@@ -184,12 +184,9 @@ export const useAuthOperations = ({
184
184
  // Verify and create session. `verifyChallenge` plants the first
185
185
  // access token (and refresh token) from the `/auth/verify` response
186
186
  // body internally — mirroring `claimSessionByToken` — so the client is
187
- // authenticated as soon as this resolves. We deliberately do NOT fall
188
- // back to the bearer-protected `GET /session/token/:sessionId` (C1
189
- // hardening): for a brand-new identity with no bearer yet that route
190
- // 401s, which previously broke the entire new-identity onboarding
191
- // flow. A token-less verify response simply leaves the client without
192
- // a bearer here rather than triggering that 401.
187
+ // authenticated as soon as this resolves. Session IDs are not public
188
+ // token-minting credentials; a token-less verify response simply leaves
189
+ // the client without a bearer here.
193
190
  sessionResponse = await oxyServices.verifyChallenge(
194
191
  publicKey,
195
192
  challenge,
@@ -302,11 +299,8 @@ export const useAuthOperations = ({
302
299
 
303
300
  try {
304
301
  const sessionToLogout = targetSessionId || activeSessionId;
305
- // Web multi-account: when the target session carries an `authuser`
306
- // slot index it is backed by an httpOnly `oxy_rt_${n}` cookie. Use
307
- // the cookie-cleared logout endpoint so the server can `Set-Cookie`
308
- // an immediate expiry alongside revoking the family. Native and
309
- // legacy sessions (no `authuser` plumbed yet) fall through to the
302
+ // Web multi-account sessions carry an `authuser` slot index backed by
303
+ // an httpOnly `oxy_rt_${n}` cookie. Native sessions fall through to the
310
304
  // bearer-protected endpoint.
311
305
  const targetSession = sessionsRef.current.find((s) => s.sessionId === sessionToLogout);
312
306
  const targetAuthuser = targetSession?.authuser;
@@ -378,8 +372,8 @@ export const useAuthOperations = ({
378
372
  // - Web: "Sign out of all accounts" = sign out every device-local
379
373
  // account on THIS device. The cookie endpoint is the only path
380
374
  // that can `Set-Cookie` an immediate expiry on every
381
- // `oxy_rt_${n}` slot (plus legacy `oxy_rt`) AND revoke every
382
- // presented family server-side. The bearer-protected
375
+ // `oxy_rt_${n}` slots AND revoke every presented family server-side.
376
+ // The bearer-protected
383
377
  // `logoutAllSessions(activeSessionId)` would only revoke the
384
378
  // active user's sessions across devices and leave sibling
385
379
  // accounts' cookies sitting on this device — wrong UX for the