@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.
- package/README.md +9 -13
- package/lib/commonjs/index.js +10 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/AccountMenu.js +297 -226
- package/lib/commonjs/ui/components/AccountMenu.js.map +1 -1
- package/lib/commonjs/ui/components/AccountMenuButton.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +3 -1
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js +1 -1
- package/lib/commonjs/ui/components/SignInModal.js +11 -12
- package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
- package/lib/commonjs/ui/components/accountMenuRows.js +18 -30
- package/lib/commonjs/ui/components/accountMenuRows.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +57 -78
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +7 -13
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAuth.js +10 -40
- package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
- package/lib/commonjs/ui/hooks/useDeviceAccounts.js +285 -0
- package/lib/commonjs/ui/hooks/useDeviceAccounts.js.map +1 -0
- package/lib/commonjs/ui/hooks/useFollow.js +21 -7
- package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +5 -6
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +4 -5
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/commonjs/ui/hooks/useWebSSO.js +1 -1
- package/lib/commonjs/ui/navigation/routes.js +7 -7
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/screens/OxyAuthScreen.js +6 -7
- package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js +18 -20
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +4 -4
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/{karma/KarmaAboutScreen.js → trust/TrustAboutScreen.js} +11 -11
- package/lib/commonjs/ui/screens/trust/TrustAboutScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/{karma/KarmaCenterScreen.js → trust/TrustCenterScreen.js} +91 -41
- package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/{karma/KarmaFAQScreen.js → trust/TrustFAQScreen.js} +11 -11
- package/lib/commonjs/ui/screens/{karma/KarmaFAQScreen.js.map → trust/TrustFAQScreen.js.map} +1 -1
- package/lib/commonjs/ui/screens/{karma/KarmaLeaderboardScreen.js → trust/TrustLeaderboardScreen.js} +63 -42
- package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/{karma/KarmaRewardsScreen.js → trust/TrustRewardsScreen.js} +54 -54
- package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/{karma/KarmaRulesScreen.js → trust/TrustRulesScreen.js} +45 -16
- package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/trust/trustTier.js +23 -0
- package/lib/commonjs/ui/screens/trust/trustTier.js.map +1 -0
- package/lib/commonjs/utils/deviceFlowSignIn.js +12 -10
- package/lib/commonjs/utils/deviceFlowSignIn.js.map +1 -1
- package/lib/module/index.js +3 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/AccountMenu.js +297 -226
- package/lib/module/ui/components/AccountMenu.js.map +1 -1
- package/lib/module/ui/components/AccountMenuButton.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +3 -1
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js +1 -1
- package/lib/module/ui/components/SignInModal.js +11 -12
- package/lib/module/ui/components/SignInModal.js.map +1 -1
- package/lib/module/ui/components/accountMenuRows.js +18 -30
- package/lib/module/ui/components/accountMenuRows.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +58 -79
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +7 -13
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/hooks/useAuth.js +10 -40
- package/lib/module/ui/hooks/useAuth.js.map +1 -1
- package/lib/module/ui/hooks/useDeviceAccounts.js +281 -0
- package/lib/module/ui/hooks/useDeviceAccounts.js.map +1 -0
- package/lib/module/ui/hooks/useFollow.js +21 -7
- package/lib/module/ui/hooks/useFollow.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +5 -6
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/hooks/useSessionSocket.js +4 -5
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/module/ui/hooks/useWebSSO.js +1 -1
- package/lib/module/ui/navigation/routes.js +7 -7
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/screens/OxyAuthScreen.js +6 -7
- package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js +18 -20
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +4 -4
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/screens/{karma/KarmaAboutScreen.js → trust/TrustAboutScreen.js} +11 -11
- package/lib/module/ui/screens/trust/TrustAboutScreen.js.map +1 -0
- package/lib/module/ui/screens/{karma/KarmaCenterScreen.js → trust/TrustCenterScreen.js} +92 -42
- package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -0
- package/lib/module/ui/screens/{karma/KarmaFAQScreen.js → trust/TrustFAQScreen.js} +11 -11
- package/lib/module/ui/screens/{karma/KarmaFAQScreen.js.map → trust/TrustFAQScreen.js.map} +1 -1
- package/lib/module/ui/screens/{karma/KarmaLeaderboardScreen.js → trust/TrustLeaderboardScreen.js} +63 -42
- package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -0
- package/lib/module/ui/screens/{karma/KarmaRewardsScreen.js → trust/TrustRewardsScreen.js} +54 -54
- package/lib/module/ui/screens/trust/TrustRewardsScreen.js.map +1 -0
- package/lib/module/ui/screens/{karma/KarmaRulesScreen.js → trust/TrustRulesScreen.js} +45 -16
- package/lib/module/ui/screens/trust/TrustRulesScreen.js.map +1 -0
- package/lib/module/ui/screens/trust/trustTier.js +19 -0
- package/lib/module/ui/screens/trust/trustTier.js.map +1 -0
- package/lib/module/utils/deviceFlowSignIn.js +13 -10
- package/lib/module/utils/deviceFlowSignIn.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +3 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts +30 -10
- package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SignInModal.d.ts +1 -1
- package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts +19 -12
- package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +3 -3
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +1 -7
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts +3 -7
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts +133 -0
- package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts +1 -1
- package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustAboutScreen.d.ts +5 -0
- package/lib/typescript/commonjs/ui/screens/{karma/KarmaAboutScreen.d.ts.map → trust/TrustAboutScreen.d.ts.map} +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustCenterScreen.d.ts +5 -0
- package/lib/typescript/commonjs/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -0
- package/lib/typescript/{module/ui/screens/karma/KarmaFAQScreen.d.ts → commonjs/ui/screens/trust/TrustFAQScreen.d.ts} +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts +5 -0
- package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/trust/TrustRewardsScreen.d.ts +5 -0
- package/lib/typescript/commonjs/ui/screens/{karma/KarmaRewardsScreen.d.ts.map → trust/TrustRewardsScreen.d.ts.map} +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts +5 -0
- package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts +9 -0
- package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +1 -1
- package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +11 -9
- package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +3 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/AccountMenu.d.ts +30 -10
- package/lib/typescript/module/ui/components/AccountMenu.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SignInModal.d.ts +1 -1
- package/lib/typescript/module/ui/components/SignInModal.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/accountMenuRows.d.ts +19 -12
- package/lib/typescript/module/ui/components/accountMenuRows.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts +3 -3
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +1 -7
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAuth.d.ts +3 -7
- package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts +133 -0
- package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useFollow.d.ts +1 -1
- package/lib/typescript/module/ui/hooks/useFollow.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useWebSSO.d.ts +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustAboutScreen.d.ts +5 -0
- package/lib/typescript/module/ui/screens/{karma/KarmaAboutScreen.d.ts.map → trust/TrustAboutScreen.d.ts.map} +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustCenterScreen.d.ts +5 -0
- package/lib/typescript/module/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -0
- package/lib/typescript/{commonjs/ui/screens/karma/KarmaFAQScreen.d.ts → module/ui/screens/trust/TrustFAQScreen.d.ts} +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts +5 -0
- package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/trust/TrustRewardsScreen.d.ts +5 -0
- package/lib/typescript/module/ui/screens/{karma/KarmaRewardsScreen.d.ts.map → trust/TrustRewardsScreen.d.ts.map} +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts +5 -0
- package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/trust/trustTier.d.ts +9 -0
- package/lib/typescript/module/ui/screens/trust/trustTier.d.ts.map +1 -0
- package/lib/typescript/module/ui/types/navigation.d.ts +1 -1
- package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +11 -9
- package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +10 -1
- package/src/ui/components/AccountMenu.tsx +311 -253
- package/src/ui/components/AccountMenuButton.tsx +2 -2
- package/src/ui/components/FollowButton.tsx +2 -2
- package/src/ui/components/OxySignInButton.tsx +1 -1
- package/src/ui/components/SignInModal.tsx +13 -14
- package/src/ui/components/accountMenuRows.ts +28 -40
- package/src/ui/context/OxyContext.tsx +71 -74
- package/src/ui/context/hooks/useAuthOperations.ts +7 -13
- package/src/ui/hooks/useAuth.ts +12 -49
- package/src/ui/hooks/useDeviceAccounts.ts +348 -0
- package/src/ui/hooks/useFollow.ts +16 -8
- package/src/ui/hooks/useSessionManagement.ts +5 -14
- package/src/ui/hooks/useSessionSocket.ts +4 -5
- package/src/ui/hooks/useWebSSO.ts +1 -1
- package/src/ui/navigation/routes.ts +13 -13
- package/src/ui/screens/OxyAuthScreen.tsx +6 -7
- package/src/ui/screens/ProfileScreen.tsx +15 -17
- package/src/ui/screens/WelcomeNewUserScreen.tsx +2 -2
- package/src/ui/screens/{karma/KarmaAboutScreen.tsx → trust/TrustAboutScreen.tsx} +15 -15
- package/src/ui/screens/{karma/KarmaCenterScreen.tsx → trust/TrustCenterScreen.tsx} +87 -41
- package/src/ui/screens/{karma/KarmaFAQScreen.tsx → trust/TrustFAQScreen.tsx} +10 -10
- package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +101 -0
- package/src/ui/screens/{karma/KarmaRewardsScreen.tsx → trust/TrustRewardsScreen.tsx} +54 -54
- package/src/ui/screens/{karma/KarmaRulesScreen.tsx → trust/TrustRulesScreen.tsx} +27 -13
- package/src/ui/screens/trust/trustTier.ts +20 -0
- package/src/ui/types/navigation.ts +1 -2
- package/src/utils/__tests__/deviceFlowSignIn.test.ts +2 -3
- package/src/utils/deviceFlowSignIn.ts +18 -12
- package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js.map +0 -1
- package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +0 -1
- package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +0 -1
- package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js.map +0 -1
- package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +0 -1
- package/lib/module/ui/screens/karma/KarmaAboutScreen.js.map +0 -1
- package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +0 -1
- package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +0 -1
- package/lib/module/ui/screens/karma/KarmaRewardsScreen.js.map +0 -1
- package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +0 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaAboutScreen.d.ts +0 -5
- package/lib/typescript/commonjs/ui/screens/karma/KarmaCenterScreen.d.ts +0 -5
- package/lib/typescript/commonjs/ui/screens/karma/KarmaCenterScreen.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaFAQScreen.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaLeaderboardScreen.d.ts +0 -5
- package/lib/typescript/commonjs/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaRewardsScreen.d.ts +0 -5
- package/lib/typescript/commonjs/ui/screens/karma/KarmaRulesScreen.d.ts +0 -5
- package/lib/typescript/commonjs/ui/screens/karma/KarmaRulesScreen.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/karma/KarmaAboutScreen.d.ts +0 -5
- package/lib/typescript/module/ui/screens/karma/KarmaCenterScreen.d.ts +0 -5
- package/lib/typescript/module/ui/screens/karma/KarmaCenterScreen.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/karma/KarmaFAQScreen.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/karma/KarmaLeaderboardScreen.d.ts +0 -5
- package/lib/typescript/module/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/karma/KarmaRewardsScreen.d.ts +0 -5
- package/lib/typescript/module/ui/screens/karma/KarmaRulesScreen.d.ts +0 -5
- package/lib/typescript/module/ui/screens/karma/KarmaRulesScreen.d.ts.map +0 -1
- 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<
|
|
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:
|
|
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
|
|
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
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
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
|
|
331
|
-
const
|
|
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
|
|
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-
|
|
368
|
-
`width=${width},height=${height},left=${left},top=${top}
|
|
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
|
|
451
|
+
{/* Open approval window */}
|
|
453
452
|
<Button
|
|
454
|
-
onPress={
|
|
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 {
|
|
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:
|
|
11
|
+
user: DeviceAccountUser | null;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export interface BuildAccountRowsInput {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
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
|
-
|
|
32
|
-
activeSessionId,
|
|
33
|
-
user,
|
|
34
|
-
locale,
|
|
35
|
-
getAvatarUrl,
|
|
37
|
+
accounts,
|
|
36
38
|
}: BuildAccountRowsInput): AccountRow[] {
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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/
|
|
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).
|
|
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
|
-
//
|
|
791
|
-
//
|
|
792
|
-
//
|
|
793
|
-
// stored
|
|
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
|
|
806
|
-
|
|
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):
|
|
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). `
|
|
976
|
-
//
|
|
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)
|
|
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 `
|
|
1052
|
-
//
|
|
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
|
|
1312
|
-
// callback path, BEFORE the cold boot (which awaits storage init).
|
|
1313
|
-
//
|
|
1314
|
-
//
|
|
1315
|
-
//
|
|
1316
|
-
//
|
|
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
|
-
|
|
1334
|
-
if (!
|
|
1335
|
-
|
|
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:
|
|
1352
|
-
//
|
|
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
|
-
|
|
1362
|
-
|
|
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 /
|
|
1409
|
-
// SSO-return
|
|
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/
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
188
|
-
//
|
|
189
|
-
//
|
|
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
|
|
306
|
-
//
|
|
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}`
|
|
382
|
-
//
|
|
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
|