@oxyhq/services 27.1.3 → 29.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -661
- package/NOTICE +16 -0
- package/README.md +67 -2
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionModule.kt +178 -0
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionProvider.kt +99 -0
- package/android/src/main/java/so/oxy/devicesession/OxyDeviceSessionStore.kt +115 -0
- package/expo-module.config.json +1 -0
- package/lib/commonjs/index.js +78 -16
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/boot/runProviderColdBoot.js +7 -0
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/client.js +12 -7
- package/lib/commonjs/ui/client.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js +10 -27
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +132 -44
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +4 -2
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/SurfaceScreen.js +31 -3
- package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +209 -84
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js +25 -7
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/primitives.js +35 -22
- package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/signInProgress.js +5 -0
- package/lib/commonjs/ui/components/authChooser/signInProgress.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/types.js +11 -0
- package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +257 -306
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +27 -37
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/oxyContextHelpers.js +0 -29
- package/lib/commonjs/ui/context/oxyContextHelpers.js.map +1 -1
- package/lib/commonjs/ui/context/useOxyAccountGraph.js +6 -17
- package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js +42 -0
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js.map +1 -0
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +0 -19
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/useDeviceSwitcher.js +57 -0
- package/lib/commonjs/ui/hooks/useDeviceSwitcher.js.map +1 -0
- package/lib/commonjs/ui/hooks/useI18n.js +22 -3
- package/lib/commonjs/ui/hooks/useI18n.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +46 -153
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/index.js +12 -7
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/navigation/routes.js +85 -73
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/navigation/surfaces.js +8 -7
- package/lib/commonjs/ui/navigation/surfaces.js.map +1 -1
- package/lib/commonjs/ui/runtime/createOxyRuntime.js +433 -0
- package/lib/commonjs/ui/runtime/createOxyRuntime.js.map +1 -0
- package/lib/commonjs/ui/runtime/index.js +63 -0
- package/lib/commonjs/ui/runtime/index.js.map +1 -0
- package/lib/commonjs/ui/runtime/runtimeContext.js +83 -0
- package/lib/commonjs/ui/runtime/runtimeContext.js.map +1 -0
- package/lib/commonjs/ui/runtime/types.js +6 -0
- package/lib/commonjs/ui/runtime/types.js.map +1 -0
- package/lib/commonjs/ui/runtime/useRuntimeSnapshot.js +57 -0
- package/lib/commonjs/ui/runtime/useRuntimeSnapshot.js.map +1 -0
- package/lib/commonjs/ui/screens/AccountMembersScreen.js +3 -4
- package/lib/commonjs/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/CreateAccountScreen.js +3 -12
- package/lib/commonjs/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js +6 -5
- package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js +4 -4
- package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js +1 -1
- package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js +4 -2
- package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js.map +1 -1
- package/lib/commonjs/ui/server.js +1 -2
- package/lib/commonjs/ui/server.js.map +1 -1
- package/lib/commonjs/ui/session/authStore.js +45 -9
- package/lib/commonjs/ui/session/authStore.js.map +1 -1
- package/lib/commonjs/ui/session/sharedDeviceCredentialStore.js +306 -0
- package/lib/commonjs/ui/session/sharedDeviceCredentialStore.js.map +1 -0
- package/lib/commonjs/ui/stores/authStore.js +154 -55
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/commonjs/ui/stores/followTargetStore.js +27 -0
- package/lib/commonjs/ui/stores/followTargetStore.js.map +1 -1
- package/lib/commonjs/ui/utils/avatarUtils.js +2 -36
- package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
- package/lib/module/index.js +20 -9
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/boot/runProviderColdBoot.js +7 -0
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/client.js +1 -2
- package/lib/module/ui/client.js.map +1 -1
- package/lib/module/ui/components/OxyAccountDialogScreen.js +10 -27
- package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +134 -44
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +5 -3
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/SurfaceScreen.js +33 -5
- package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js +210 -85
- package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignInEntryView.js +25 -7
- package/lib/module/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/module/ui/components/authChooser/primitives.js +35 -22
- package/lib/module/ui/components/authChooser/primitives.js.map +1 -1
- package/lib/module/ui/components/authChooser/signInProgress.js +5 -0
- package/lib/module/ui/components/authChooser/signInProgress.js.map +1 -1
- package/lib/module/ui/components/authChooser/types.js +11 -0
- package/lib/module/ui/components/authChooser/types.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +251 -303
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +27 -37
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/oxyContextHelpers.js +0 -28
- package/lib/module/ui/context/oxyContextHelpers.js.map +1 -1
- package/lib/module/ui/context/useOxyAccountGraph.js +6 -17
- package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/module/ui/hooks/accountDialogSnapshot.js +38 -0
- package/lib/module/ui/hooks/accountDialogSnapshot.js.map +1 -0
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +0 -19
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/module/ui/hooks/useDeviceSwitcher.js +53 -0
- package/lib/module/ui/hooks/useDeviceSwitcher.js.map +1 -0
- package/lib/module/ui/hooks/useI18n.js +24 -4
- package/lib/module/ui/hooks/useI18n.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +47 -155
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/index.js +1 -2
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/navigation/routes.js +84 -72
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/navigation/surfaces.js +6 -6
- package/lib/module/ui/navigation/surfaces.js.map +1 -1
- package/lib/module/ui/runtime/createOxyRuntime.js +430 -0
- package/lib/module/ui/runtime/createOxyRuntime.js.map +1 -0
- package/lib/module/ui/runtime/index.js +15 -0
- package/lib/module/ui/runtime/index.js.map +1 -0
- package/lib/module/ui/runtime/runtimeContext.js +75 -0
- package/lib/module/ui/runtime/runtimeContext.js.map +1 -0
- package/lib/module/ui/runtime/types.js +4 -0
- package/lib/module/ui/runtime/types.js.map +1 -0
- package/lib/module/ui/runtime/useRuntimeSnapshot.js +52 -0
- package/lib/module/ui/runtime/useRuntimeSnapshot.js.map +1 -0
- package/lib/module/ui/screens/AccountMembersScreen.js +4 -5
- package/lib/module/ui/screens/AccountMembersScreen.js.map +1 -1
- package/lib/module/ui/screens/CreateAccountScreen.js +4 -13
- package/lib/module/ui/screens/CreateAccountScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustCenterScreen.js +6 -5
- package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js +5 -5
- package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -1
- package/lib/module/ui/screens/trust/TrustRewardsScreen.js +1 -1
- package/lib/module/ui/screens/trust/TrustRulesScreen.js +4 -2
- package/lib/module/ui/screens/trust/TrustRulesScreen.js.map +1 -1
- package/lib/module/ui/server.js +0 -1
- package/lib/module/ui/server.js.map +1 -1
- package/lib/module/ui/session/authStore.js +46 -11
- package/lib/module/ui/session/authStore.js.map +1 -1
- package/lib/module/ui/session/sharedDeviceCredentialStore.js +302 -0
- package/lib/module/ui/session/sharedDeviceCredentialStore.js.map +1 -0
- package/lib/module/ui/stores/authStore.js +154 -55
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/module/ui/stores/followTargetStore.js +25 -0
- package/lib/module/ui/stores/followTargetStore.js.map +1 -1
- package/lib/module/ui/utils/avatarUtils.js +2 -35
- package/lib/module/ui/utils/avatarUtils.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +6 -4
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts +1 -2
- package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts +11 -4
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts +3 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts +18 -6
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/signInProgress.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +33 -1
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +35 -4
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +8 -12
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextHelpers.d.ts +0 -2
- package/lib/typescript/commonjs/ui/context/oxyContextHelpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +9 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +1 -3
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts +15 -0
- package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useDeviceSwitcher.d.ts +49 -0
- package/lib/typescript/commonjs/ui/hooks/useDeviceSwitcher.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts +13 -0
- package/lib/typescript/commonjs/ui/hooks/useI18n.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts +23 -18
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts +1 -2
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts +15 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/surfaces.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/runtime/createOxyRuntime.d.ts +144 -0
- package/lib/typescript/commonjs/ui/runtime/createOxyRuntime.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/index.d.ts +16 -0
- package/lib/typescript/commonjs/ui/runtime/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/runtimeContext.d.ts +43 -0
- package/lib/typescript/commonjs/ui/runtime/runtimeContext.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/types.d.ts +89 -0
- package/lib/typescript/commonjs/ui/runtime/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/runtime/useRuntimeSnapshot.d.ts +23 -0
- package/lib/typescript/commonjs/ui/runtime/useRuntimeSnapshot.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/AccountMembersScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/CreateAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/server.d.ts +0 -1
- package/lib/typescript/commonjs/ui/server.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/authStore.d.ts +60 -9
- package/lib/typescript/commonjs/ui/session/authStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/sharedDeviceCredentialStore.d.ts +32 -0
- package/lib/typescript/commonjs/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/stores/authStore.d.ts +49 -5
- package/lib/typescript/commonjs/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/stores/followTargetStore.d.ts +8 -1
- package/lib/typescript/commonjs/ui/stores/followTargetStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +26 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts +2 -10
- package/lib/typescript/commonjs/ui/utils/avatarUtils.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +6 -4
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts +1 -2
- package/lib/typescript/module/ui/client.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAccountDialogScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts +11 -4
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts +3 -1
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts +18 -6
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/signInProgress.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/types.d.ts +33 -1
- package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts +35 -4
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +8 -12
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextHelpers.d.ts +0 -2
- package/lib/typescript/module/ui/context/oxyContextHelpers.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +9 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +1 -3
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts +15 -0
- package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useDeviceSwitcher.d.ts +49 -0
- package/lib/typescript/module/ui/hooks/useDeviceSwitcher.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useI18n.d.ts +13 -0
- package/lib/typescript/module/ui/hooks/useI18n.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts +23 -18
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts +1 -2
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts +15 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/surfaces.d.ts.map +1 -1
- package/lib/typescript/module/ui/runtime/createOxyRuntime.d.ts +144 -0
- package/lib/typescript/module/ui/runtime/createOxyRuntime.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/index.d.ts +16 -0
- package/lib/typescript/module/ui/runtime/index.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/runtimeContext.d.ts +43 -0
- package/lib/typescript/module/ui/runtime/runtimeContext.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/types.d.ts +89 -0
- package/lib/typescript/module/ui/runtime/types.d.ts.map +1 -0
- package/lib/typescript/module/ui/runtime/useRuntimeSnapshot.d.ts +23 -0
- package/lib/typescript/module/ui/runtime/useRuntimeSnapshot.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/AccountMembersScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/CreateAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/server.d.ts +0 -1
- package/lib/typescript/module/ui/server.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/authStore.d.ts +60 -9
- package/lib/typescript/module/ui/session/authStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/sharedDeviceCredentialStore.d.ts +32 -0
- package/lib/typescript/module/ui/session/sharedDeviceCredentialStore.d.ts.map +1 -0
- package/lib/typescript/module/ui/stores/authStore.d.ts +49 -5
- package/lib/typescript/module/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/stores/followTargetStore.d.ts +8 -1
- package/lib/typescript/module/ui/stores/followTargetStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/navigation.d.ts +26 -0
- package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/avatarUtils.d.ts +2 -10
- package/lib/typescript/module/ui/utils/avatarUtils.d.ts.map +1 -1
- package/package.json +8 -8
- package/plugins/withSharedDeviceSessionProvider.js +102 -0
- package/plugins/withSharedDeviceSessionReader.js +57 -0
- package/src/__tests__/esmSourcePurity.test.ts +61 -0
- package/src/index.ts +36 -9
- package/src/ui/boot/runProviderColdBoot.ts +7 -0
- package/src/ui/client.ts +1 -2
- package/src/ui/components/OxyAccountDialogScreen.tsx +10 -29
- package/src/ui/components/OxyAuthChooser.tsx +149 -50
- package/src/ui/components/OxyProvider.tsx +7 -5
- package/src/ui/components/SurfaceScreen.tsx +28 -4
- package/src/ui/components/__tests__/followTargetButton.test.ts +39 -0
- package/src/ui/components/authChooser/AccountsMenuView.tsx +246 -96
- package/src/ui/components/authChooser/SignInEntryView.tsx +31 -8
- package/src/ui/components/authChooser/primitives.tsx +37 -23
- package/src/ui/components/authChooser/signInProgress.ts +4 -0
- package/src/ui/components/authChooser/types.ts +34 -1
- package/src/ui/context/OxyContext.tsx +269 -324
- package/src/ui/context/hooks/useAuthOperations.ts +32 -35
- package/src/ui/context/oxyContextHelpers.ts +0 -30
- package/src/ui/context/oxyContextTypes.ts +9 -1
- package/src/ui/context/useOxyAccountGraph.ts +5 -19
- package/src/ui/hooks/accountDialogSnapshot.ts +37 -0
- package/src/ui/hooks/mutations/useAccountMutations.ts +0 -19
- package/src/ui/hooks/queries/__tests__/userCache.test.ts +12 -1
- package/src/ui/hooks/useDeviceSwitcher.ts +110 -0
- package/src/ui/hooks/useI18n.ts +23 -3
- package/src/ui/hooks/useSessionManagement.ts +62 -211
- package/src/ui/index.ts +1 -2
- package/src/ui/navigation/routes.ts +85 -75
- package/src/ui/navigation/surfaces.ts +6 -9
- package/src/ui/runtime/createOxyRuntime.ts +623 -0
- package/src/ui/runtime/index.ts +34 -0
- package/src/ui/runtime/runtimeContext.tsx +111 -0
- package/src/ui/runtime/types.ts +110 -0
- package/src/ui/runtime/useRuntimeSnapshot.ts +52 -0
- package/src/ui/screens/AccountMembersScreen.tsx +6 -5
- package/src/ui/screens/CreateAccountScreen.tsx +3 -16
- package/src/ui/screens/trust/TrustCenterScreen.tsx +7 -7
- package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +4 -5
- package/src/ui/screens/trust/TrustRewardsScreen.tsx +1 -1
- package/src/ui/screens/trust/TrustRulesScreen.tsx +3 -2
- package/src/ui/server.ts +0 -1
- package/src/ui/session/__tests__/authStore.test.ts +90 -0
- package/src/ui/session/__tests__/sharedDeviceCredentialStore.test.ts +289 -0
- package/src/ui/session/authStore.ts +78 -9
- package/src/ui/session/sharedDeviceCredentialStore.ts +318 -0
- package/src/ui/stores/authStore.ts +171 -43
- package/src/ui/stores/followTargetStore.ts +24 -1
- package/src/ui/types/navigation.ts +26 -0
- package/src/ui/utils/avatarUtils.ts +2 -37
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +0 -64
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +0 -1
- package/lib/commonjs/ui/screens/trust/trustTier.js +0 -23
- package/lib/commonjs/ui/screens/trust/trustTier.js.map +0 -1
- package/lib/commonjs/ui/stores/accountStore.js +0 -221
- package/lib/commonjs/ui/stores/accountStore.js.map +0 -1
- package/lib/module/ui/hooks/useSwitchableAccounts.js +0 -60
- package/lib/module/ui/hooks/useSwitchableAccounts.js.map +0 -1
- package/lib/module/ui/screens/trust/trustTier.js +0 -19
- package/lib/module/ui/screens/trust/trustTier.js.map +0 -1
- package/lib/module/ui/stores/accountStore.js +0 -213
- package/lib/module/ui/stores/accountStore.js.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +0 -23
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts +0 -9
- package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/stores/accountStore.d.ts +0 -27
- package/lib/typescript/commonjs/ui/stores/accountStore.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +0 -23
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/trust/trustTier.d.ts +0 -9
- package/lib/typescript/module/ui/screens/trust/trustTier.d.ts.map +0 -1
- package/lib/typescript/module/ui/stores/accountStore.d.ts +0 -27
- package/lib/typescript/module/ui/stores/accountStore.d.ts.map +0 -1
- package/src/ui/hooks/useSwitchableAccounts.ts +0 -75
- package/src/ui/screens/trust/trustTier.ts +0 -20
- package/src/ui/stores/accountStore.ts +0 -264
package/NOTICE
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
============================================================================
|
|
2
|
+
NOTICE
|
|
3
|
+
@oxyhq/services
|
|
4
|
+
Copyright (c) 2025-present The Oxy Collective, Inc.
|
|
5
|
+
============================================================================
|
|
6
|
+
|
|
7
|
+
This product is licensed under the Apache License, Version 2.0. See the
|
|
8
|
+
LICENSE file in this directory.
|
|
9
|
+
|
|
10
|
+
----------------------------------------------------------------------------
|
|
11
|
+
THIRD PARTY COMPONENTS
|
|
12
|
+
----------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
This package contains no third party source. Its dependencies are resolved
|
|
15
|
+
and installed by the package manager and remain governed by their own
|
|
16
|
+
licenses.
|
package/README.md
CHANGED
|
@@ -50,6 +50,69 @@ This package (`@oxyhq/services`) is the **single UI SDK** for every React surfac
|
|
|
50
50
|
|
|
51
51
|
See [PLATFORM_GUIDE.md](./PLATFORM_GUIDE.md) for the complete architecture guide.
|
|
52
52
|
|
|
53
|
+
### This package does not declare `sideEffects: false`
|
|
54
|
+
|
|
55
|
+
It used to, and the declaration was **wrong**. Three modules in the build have
|
|
56
|
+
top-level side effects, one of them the entry itself:
|
|
57
|
+
|
|
58
|
+
| Module | Effect |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `lib/module/index.js` | `setPlatformOS(Platform.OS)` — configures `@oxyhq/core` |
|
|
61
|
+
| `lib/module/ui/navigation/surfaceBackBridge.js` | `installEscapeHandler()` — installs a global key handler |
|
|
62
|
+
| `lib/module/ui/components/SettingsIcon.js` | assigns `displayName` |
|
|
63
|
+
|
|
64
|
+
Removing a false declaration would be worth doing on its own. It also fixed a
|
|
65
|
+
production outage, and that is the part worth remembering, because the symptom
|
|
66
|
+
looked nothing like a `package.json` field.
|
|
67
|
+
|
|
68
|
+
`auth.oxy.so/authorize` rendered a blank page with React error #130 ("element
|
|
69
|
+
type is undefined") for every request that reached `OxySignInRequestSurface`,
|
|
70
|
+
which blocked every OIDC login into Allo ([#784]). Source and dev server were
|
|
71
|
+
fine; only the production bundle was broken. Two facts combined:
|
|
72
|
+
|
|
73
|
+
1. **The UI graph was one large import cycle.** `ui/context/OxyContext` reaches
|
|
74
|
+
`ui/navigation/bottomSheetManager` → `navigation/routes`, `routes` reached
|
|
75
|
+
every screen (through `require()` calls it made for Metro's benefit), and
|
|
76
|
+
every screen reaches back into the context — a 67-module strongly connected
|
|
77
|
+
component that included `OxySignInRequestSurface`. A bundler cannot
|
|
78
|
+
concatenate a cycle, so it emits each member as a lazily-initialized wrapper
|
|
79
|
+
whose exported bindings are `undefined` until the initializer runs. Modules
|
|
80
|
+
outside the cycle, `OxyConsentScreen` among them, are emitted inline and were
|
|
81
|
+
never affected — which is exactly why only some screens broke.
|
|
82
|
+
2. **`sideEffects: false` told the bundler that running those initializers was
|
|
83
|
+
unobservable**, so rolldown-vite omitted the initializer call at the
|
|
84
|
+
consuming import site in `packages/auth` while still emitting the reference.
|
|
85
|
+
The binding was therefore never assigned, and React was handed `undefined`.
|
|
86
|
+
|
|
87
|
+
That cycle was the deeper defect, and it has since been cut down. `routes.ts`
|
|
88
|
+
registers screens as `lazy(() => import(...))` rather than `require()`, so the
|
|
89
|
+
registry no longer drags all 40 screens into the context's component; the two
|
|
90
|
+
remaining `require()` sites became static imports read at call time. Measured on
|
|
91
|
+
the built ESM output, the largest strongly connected component went from **67
|
|
92
|
+
modules to 2**, and `OxySignInRequestSurface` is no longer inside any cycle.
|
|
93
|
+
What is left is two mutual pairs — `surfaces` ↔ `SurfaceScreen` and
|
|
94
|
+
`OxyContext` ↔ `useFollow` — each of which only ever reads the other at call or
|
|
95
|
+
render time.
|
|
96
|
+
|
|
97
|
+
Cycles still exist, so do not re-add `sideEffects` in any form. A narrowed,
|
|
98
|
+
truthful list would leave every component still in a cycle exactly as exposed,
|
|
99
|
+
because none of *them* has a top-level side effect either. The cost of not
|
|
100
|
+
declaring it was measured at the time on the IdP's own production bundle, which
|
|
101
|
+
is the heaviest consumer of this package on the web: **5,483.86 kB → 5,489.99 kB
|
|
102
|
+
raw (+0.11%)** and **1,457.31 kB → 1,458.66 kB gzip (+0.09%)**.
|
|
103
|
+
|
|
104
|
+
**No module in this package's source may call `require()`.** The build is ESM,
|
|
105
|
+
and a surviving `require()` puts bundlers back into CommonJS interop — the same
|
|
106
|
+
lazily-initialized wrappers, reachable the same silent way. To defer a read, use
|
|
107
|
+
a thunk over a static import; to defer a *load*, use `import()`.
|
|
108
|
+
`src/__tests__/esmSourcePurity.test.ts` enforces this.
|
|
109
|
+
|
|
110
|
+
`packages/auth/lib/__tests__/authorize-surface-bundle.test.ts` builds the IdP's
|
|
111
|
+
real production bundle and renders the surface out of it, so a regression fails
|
|
112
|
+
in CI with React's own message instead of on auth.oxy.so.
|
|
113
|
+
|
|
114
|
+
[#784]: https://github.com/OxyHQ/OxyHQServices/issues/784
|
|
115
|
+
|
|
53
116
|
## Installation
|
|
54
117
|
|
|
55
118
|
```bash
|
|
@@ -618,7 +681,7 @@ const metadata = getLanguageMetadata('es-ES');
|
|
|
618
681
|
|
|
619
682
|
### Common Issues
|
|
620
683
|
|
|
621
|
-
#### 1. "useOxy
|
|
684
|
+
#### 1. "useOxy() was called outside <OxyProvider>"
|
|
622
685
|
|
|
623
686
|
**Solution**: Wrap your app with `OxyProvider`
|
|
624
687
|
|
|
@@ -785,4 +848,6 @@ Comprehensive documentation is available in the `/docs` directory:
|
|
|
785
848
|
|
|
786
849
|
## License
|
|
787
850
|
|
|
788
|
-
This
|
|
851
|
+
This package is licensed under the Apache License, Version 2.0, (c) The Oxy Collective, Inc. See the [LICENSE](LICENSE) and [NOTICE](NOTICE) files for details.
|
|
852
|
+
|
|
853
|
+
Versions published before `28.0.0` were AGPL-3.0-only and stay that way; a licence change binds future versions only.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
package so.oxy.devicesession
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.net.Uri
|
|
5
|
+
import android.os.Bundle
|
|
6
|
+
import expo.modules.kotlin.exception.Exceptions
|
|
7
|
+
import expo.modules.kotlin.modules.Module
|
|
8
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* JS bridge for the shared DeviceSession credential.
|
|
12
|
+
*
|
|
13
|
+
* - `read` resolves the credential this DEVICE holds, from the sibling providers
|
|
14
|
+
* first and this app's own store second (see [readShared] for why that order).
|
|
15
|
+
* - `write` publishes into this app's own store only, and reports whether a
|
|
16
|
+
* read-back confirmed it.
|
|
17
|
+
* - `clear` drops this app's copy.
|
|
18
|
+
*
|
|
19
|
+
* ## `read` returns a STATUS, not a nullable value
|
|
20
|
+
*
|
|
21
|
+
* The three answers "here it is", "there is none" and "I could not tell" are
|
|
22
|
+
* genuinely different, and only the second one authorises the JS side to seed the
|
|
23
|
+
* slot. A nullable return would merge the last two, and the merged value reads as
|
|
24
|
+
* "fresh device" — which is exactly how a locked or broken keystore ends up
|
|
25
|
+
* overwriting a live session. So every path here reports its status explicitly,
|
|
26
|
+
* and never degrades a failure into an absence.
|
|
27
|
+
*
|
|
28
|
+
* Return shapes:
|
|
29
|
+
* ```
|
|
30
|
+
* { "status": "present", "deviceId": "...", "deviceSecret": "..." }
|
|
31
|
+
* { "status": "absent" }
|
|
32
|
+
* { "status": "unavailable", "reason": "<ExceptionClassName>" }
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* Plain scalars in, a plain `Map` out — never an Expo `Record`. This package
|
|
36
|
+
* ships as SOURCE that each consuming app compiles, so the annotation processing
|
|
37
|
+
* a `Record` needs to become introspectable does not necessarily run in the
|
|
38
|
+
* consumer's build; the one `Record` this package ever had failed to convert on a
|
|
39
|
+
* real device and stored nothing, silently. See the long note in
|
|
40
|
+
* `so.oxy.session.OxyBackgroundSessionModule` before reaching for one here.
|
|
41
|
+
*/
|
|
42
|
+
class OxyDeviceSessionModule : Module() {
|
|
43
|
+
private val context: Context
|
|
44
|
+
get() = appContext.reactContext ?: throw Exceptions.ReactContextLost()
|
|
45
|
+
|
|
46
|
+
override fun definition() = ModuleDefinition {
|
|
47
|
+
Name("OxyDeviceSession")
|
|
48
|
+
|
|
49
|
+
AsyncFunction("read") {
|
|
50
|
+
readShared()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
AsyncFunction("write") { deviceId: String, deviceSecret: String ->
|
|
54
|
+
if (deviceId.isEmpty() || deviceSecret.isEmpty()) {
|
|
55
|
+
return@AsyncFunction false
|
|
56
|
+
}
|
|
57
|
+
OxyDeviceSessionStore.write(context, deviceId, deviceSecret)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
AsyncFunction("clear") {
|
|
61
|
+
OxyDeviceSessionStore.clear(context)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Providers FIRST, this app's own store second.
|
|
67
|
+
*
|
|
68
|
+
* Under `android:sharedUserId="so.oxy.shared"` every Oxy app sees one data
|
|
69
|
+
* directory, so for a UID member the local file IS the group's file and the
|
|
70
|
+
* order changes nothing. It matters for a same-signature app OUTSIDE the UID:
|
|
71
|
+
* there the local file is a private mirror of whatever that app itself last
|
|
72
|
+
* wrote, so reading it first would shadow the group's real credential forever.
|
|
73
|
+
*
|
|
74
|
+
* `unavailable` is sticky across the whole sweep: if ANY source could not be
|
|
75
|
+
* read and none produced a credential, the answer is `unavailable`, not
|
|
76
|
+
* `absent`. The pessimistic merge is the point — the optimistic one authorises
|
|
77
|
+
* a write.
|
|
78
|
+
*/
|
|
79
|
+
private fun readShared(): Map<String, String> {
|
|
80
|
+
var unavailableReason: String? = null
|
|
81
|
+
|
|
82
|
+
for (authority in PROVIDER_AUTHORITIES) {
|
|
83
|
+
when (val read = callProvider(authority)) {
|
|
84
|
+
is DeviceSessionRead.Present -> return present(read)
|
|
85
|
+
is DeviceSessionRead.Unavailable -> unavailableReason = unavailableReason ?: read.reason
|
|
86
|
+
// Absent, or no provider there at all (not installed, refused, threw) —
|
|
87
|
+
// neither is evidence about the other sources, so keep looking.
|
|
88
|
+
else -> Unit
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
when (val local = OxyDeviceSessionStore.read(context)) {
|
|
93
|
+
is DeviceSessionRead.Present -> return present(local)
|
|
94
|
+
is DeviceSessionRead.Unavailable -> unavailableReason = unavailableReason ?: local.reason
|
|
95
|
+
is DeviceSessionRead.Absent -> Unit
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
val reason = unavailableReason
|
|
99
|
+
return if (reason != null) {
|
|
100
|
+
mapOf(
|
|
101
|
+
OxyDeviceSessionProvider.KEY_STATUS to OxyDeviceSessionProvider.STATUS_UNAVAILABLE,
|
|
102
|
+
OxyDeviceSessionProvider.KEY_REASON to reason,
|
|
103
|
+
)
|
|
104
|
+
} else {
|
|
105
|
+
mapOf(OxyDeviceSessionProvider.KEY_STATUS to OxyDeviceSessionProvider.STATUS_ABSENT)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private fun present(read: DeviceSessionRead.Present): Map<String, String> = mapOf(
|
|
110
|
+
OxyDeviceSessionProvider.KEY_STATUS to OxyDeviceSessionProvider.STATUS_PRESENT,
|
|
111
|
+
OxyDeviceSessionStore.KEY_DEVICE_ID to read.deviceId,
|
|
112
|
+
OxyDeviceSessionStore.KEY_DEVICE_SECRET to read.deviceSecret,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Call one sibling provider. `null` means "nothing to learn from this one" —
|
|
117
|
+
* the app is not installed, package visibility hid it, the permission was
|
|
118
|
+
* refused, or the call threw. A provider that answered but could not read its
|
|
119
|
+
* own store returns [DeviceSessionRead.Unavailable], which the sweep keeps.
|
|
120
|
+
*/
|
|
121
|
+
private fun callProvider(authority: String): DeviceSessionRead? = runCatching {
|
|
122
|
+
val uri = Uri.parse("content://$authority")
|
|
123
|
+
val bundle: Bundle = context.contentResolver.call(
|
|
124
|
+
uri,
|
|
125
|
+
OxyDeviceSessionProvider.METHOD_READ,
|
|
126
|
+
null,
|
|
127
|
+
null,
|
|
128
|
+
) ?: return@runCatching null
|
|
129
|
+
|
|
130
|
+
when (bundle.getString(OxyDeviceSessionProvider.KEY_STATUS)) {
|
|
131
|
+
OxyDeviceSessionProvider.STATUS_PRESENT -> {
|
|
132
|
+
val deviceId = bundle.getString(OxyDeviceSessionStore.KEY_DEVICE_ID)
|
|
133
|
+
val deviceSecret = bundle.getString(OxyDeviceSessionStore.KEY_DEVICE_SECRET)
|
|
134
|
+
if (deviceId.isNullOrEmpty() || deviceSecret.isNullOrEmpty()) {
|
|
135
|
+
// A `present` verdict with an incomplete payload is a broken peer, not
|
|
136
|
+
// an empty device.
|
|
137
|
+
DeviceSessionRead.Unavailable("IncompleteProviderPayload")
|
|
138
|
+
} else {
|
|
139
|
+
DeviceSessionRead.Present(deviceId, deviceSecret)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
OxyDeviceSessionProvider.STATUS_ABSENT -> DeviceSessionRead.Absent
|
|
143
|
+
OxyDeviceSessionProvider.STATUS_UNAVAILABLE ->
|
|
144
|
+
DeviceSessionRead.Unavailable(
|
|
145
|
+
bundle.getString(OxyDeviceSessionProvider.KEY_REASON) ?: "PeerUnavailable",
|
|
146
|
+
)
|
|
147
|
+
// An older sibling that predates this protocol. It said something we do not
|
|
148
|
+
// understand, so we have learned nothing — never read that as "absent".
|
|
149
|
+
else -> DeviceSessionRead.Unavailable("UnrecognisedProviderStatus")
|
|
150
|
+
}
|
|
151
|
+
}.getOrNull()
|
|
152
|
+
|
|
153
|
+
companion object {
|
|
154
|
+
/**
|
|
155
|
+
* Sibling authorities to sweep, in order. Each official Oxy Android app hosts
|
|
156
|
+
* the provider at `${applicationId}.devicesession` via the
|
|
157
|
+
* `withSharedDeviceSession` config plugin, and both the prod and `.dev`
|
|
158
|
+
* variants are listed so a developer build can join a device too.
|
|
159
|
+
*
|
|
160
|
+
* MAINTAINED LIST, and the constraint on it is specific: every entry must be
|
|
161
|
+
* an app inside the `so.oxy.shared` UID. UID members all serve ONE file, so
|
|
162
|
+
* the sweep is deterministic no matter which of them answers first. Adding an
|
|
163
|
+
* app that is same-signature but NOT in the UID would put a second,
|
|
164
|
+
* independent copy of "the shared credential" into the sweep and make the
|
|
165
|
+
* winner depend on list order.
|
|
166
|
+
*
|
|
167
|
+
* The same authorities must also be added to the `<queries>` block in
|
|
168
|
+
* `withSharedDeviceSession.js`, or Android 11+ package-visibility filtering
|
|
169
|
+
* hides them from `ContentResolver.call` and the sweep silently finds nothing.
|
|
170
|
+
*/
|
|
171
|
+
private val PROVIDER_AUTHORITIES = listOf(
|
|
172
|
+
"so.oxy.accounts.devicesession",
|
|
173
|
+
"so.oxy.accounts.dev.devicesession",
|
|
174
|
+
"so.oxy.commons.devicesession",
|
|
175
|
+
"so.oxy.commons.dev.devicesession",
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
package so.oxy.devicesession
|
|
2
|
+
|
|
3
|
+
import android.content.ContentProvider
|
|
4
|
+
import android.content.ContentValues
|
|
5
|
+
import android.content.Context
|
|
6
|
+
import android.content.pm.PackageManager
|
|
7
|
+
import android.database.Cursor
|
|
8
|
+
import android.net.Uri
|
|
9
|
+
import android.os.Bundle
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Cross-process READ surface for the shared DeviceSession credential, declared
|
|
13
|
+
* per-app by the `withSharedDeviceSession` config plugin at authority
|
|
14
|
+
* `${applicationId}.devicesession` behind a `signature`-level permission.
|
|
15
|
+
*
|
|
16
|
+
* The signature check below is belt-and-suspenders on top of that permission:
|
|
17
|
+
* even if the manifest gate were ever misconfigured, a differently-signed caller
|
|
18
|
+
* still gets nothing. Same-signature is the whole trust boundary — an
|
|
19
|
+
* incorrectly-signed app cannot read this slot, and there is no other way in.
|
|
20
|
+
*
|
|
21
|
+
* READ-ONLY across the process boundary, deliberately. A caller may join the
|
|
22
|
+
* device's session; it may not seed or overwrite another app's. Writes happen
|
|
23
|
+
* only in-process via [OxyDeviceSessionModule], against this app's own store.
|
|
24
|
+
*
|
|
25
|
+
* What crosses the boundary is a session credential — never the identity
|
|
26
|
+
* keypair, which lives behind `so.oxy.identity.OxyIdentityProvider` under a
|
|
27
|
+
* different permission. That separation is the point.
|
|
28
|
+
*
|
|
29
|
+
* All standard CRUD operations are no-ops; this provider exists solely for the
|
|
30
|
+
* `call()` channel.
|
|
31
|
+
*/
|
|
32
|
+
class OxyDeviceSessionProvider : ContentProvider() {
|
|
33
|
+
override fun onCreate(): Boolean = true
|
|
34
|
+
|
|
35
|
+
override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
|
|
36
|
+
if (method != METHOD_READ) return null
|
|
37
|
+
val ctx = context ?: return null
|
|
38
|
+
if (!callerSignatureMatches(ctx)) return null
|
|
39
|
+
|
|
40
|
+
return when (val read = OxyDeviceSessionStore.read(ctx)) {
|
|
41
|
+
is DeviceSessionRead.Present -> Bundle().apply {
|
|
42
|
+
putString(KEY_STATUS, STATUS_PRESENT)
|
|
43
|
+
putString(OxyDeviceSessionStore.KEY_DEVICE_ID, read.deviceId)
|
|
44
|
+
putString(OxyDeviceSessionStore.KEY_DEVICE_SECRET, read.deviceSecret)
|
|
45
|
+
}
|
|
46
|
+
is DeviceSessionRead.Absent -> Bundle().apply { putString(KEY_STATUS, STATUS_ABSENT) }
|
|
47
|
+
// Reported, not swallowed. A null here would be indistinguishable from
|
|
48
|
+
// "this app has no credential", and the caller would go on to treat the
|
|
49
|
+
// device as fresh.
|
|
50
|
+
is DeviceSessionRead.Unavailable -> Bundle().apply {
|
|
51
|
+
putString(KEY_STATUS, STATUS_UNAVAILABLE)
|
|
52
|
+
putString(KEY_REASON, read.reason)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* True when the calling package shares this app's signing certificate. Uses
|
|
59
|
+
* `checkSignatures` (deprecated but still the simplest correct cross-package
|
|
60
|
+
* signature comparison; returns SIGNATURE_MATCH only for same-cert apps).
|
|
61
|
+
*/
|
|
62
|
+
private fun callerSignatureMatches(ctx: Context): Boolean {
|
|
63
|
+
val caller = callingPackage ?: return false
|
|
64
|
+
return runCatching {
|
|
65
|
+
@Suppress("DEPRECATION")
|
|
66
|
+
ctx.packageManager.checkSignatures(caller, ctx.packageName) == PackageManager.SIGNATURE_MATCH
|
|
67
|
+
}.getOrDefault(false)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
override fun query(
|
|
71
|
+
uri: Uri,
|
|
72
|
+
projection: Array<out String>?,
|
|
73
|
+
selection: String?,
|
|
74
|
+
selectionArgs: Array<out String>?,
|
|
75
|
+
sortOrder: String?
|
|
76
|
+
): Cursor? = null
|
|
77
|
+
|
|
78
|
+
override fun getType(uri: Uri): String? = null
|
|
79
|
+
|
|
80
|
+
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
|
|
81
|
+
|
|
82
|
+
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int = 0
|
|
83
|
+
|
|
84
|
+
override fun update(
|
|
85
|
+
uri: Uri,
|
|
86
|
+
values: ContentValues?,
|
|
87
|
+
selection: String?,
|
|
88
|
+
selectionArgs: Array<out String>?
|
|
89
|
+
): Int = 0
|
|
90
|
+
|
|
91
|
+
companion object {
|
|
92
|
+
const val METHOD_READ = "read"
|
|
93
|
+
const val KEY_STATUS = "status"
|
|
94
|
+
const val KEY_REASON = "reason"
|
|
95
|
+
const val STATUS_PRESENT = "present"
|
|
96
|
+
const val STATUS_ABSENT = "absent"
|
|
97
|
+
const val STATUS_UNAVAILABLE = "unavailable"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
package so.oxy.devicesession
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.SharedPreferences
|
|
5
|
+
import so.oxy.storage.OxyEncryptedPrefs
|
|
6
|
+
import so.oxy.storage.RecoveryPolicy
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* What a read of the shared slot found.
|
|
10
|
+
*
|
|
11
|
+
* THREE outcomes, and the difference between the last two is the entire safety
|
|
12
|
+
* property of this file. An EMPTY slot authorises a write (seed the device
|
|
13
|
+
* session); a slot that could not be READ must authorise nothing, because it may
|
|
14
|
+
* hold a live session belonging to someone still signed in. Collapsing them —
|
|
15
|
+
* which a plain nullable return would do — is how a locked or broken keystore
|
|
16
|
+
* gets mistaken for a fresh device.
|
|
17
|
+
*/
|
|
18
|
+
internal sealed interface DeviceSessionRead {
|
|
19
|
+
data class Present(val deviceId: String, val deviceSecret: String) : DeviceSessionRead
|
|
20
|
+
object Absent : DeviceSessionRead
|
|
21
|
+
/** [reason] is an exception CLASS NAME only — never a message, never a value. */
|
|
22
|
+
data class Unavailable(val reason: String) : DeviceSessionRead
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The cross-app DeviceSession credential — `deviceId` + `deviceSecret`, and
|
|
27
|
+
* nothing else.
|
|
28
|
+
*
|
|
29
|
+
* This is NOT the identity keypair. `so.oxy.identity.OxyIdentityStore` holds the
|
|
30
|
+
* self-custody private key that signs identity approvals and cannot be
|
|
31
|
+
* re-created; this file holds an ordinary session credential the server can
|
|
32
|
+
* revoke and any signed-in app can re-publish. They are separate files behind
|
|
33
|
+
* separate providers with separate permissions precisely so an app that only
|
|
34
|
+
* needs a session is never handed the key.
|
|
35
|
+
*
|
|
36
|
+
* Under `android:sharedUserId="so.oxy.shared"` every Oxy app sees ONE data
|
|
37
|
+
* directory, so this file is literally shared between them — that is the primary
|
|
38
|
+
* transport, and [OxyDeviceSessionProvider] extends the same bytes to a
|
|
39
|
+
* same-signature app outside the UID.
|
|
40
|
+
*/
|
|
41
|
+
internal object OxyDeviceSessionStore {
|
|
42
|
+
/**
|
|
43
|
+
* Global (not package-suffixed) on purpose — the opposite choice from
|
|
44
|
+
* `oxy_background_session`, and for the opposite reason. That store is
|
|
45
|
+
* per-app precisely so one app cannot touch another's widget credential. This
|
|
46
|
+
* one EXISTS to be one value shared by every app in the UID; scoping it per
|
|
47
|
+
* package would give each app its own copy and defeat the whole point.
|
|
48
|
+
*/
|
|
49
|
+
const val PREFS_NAME = "oxy_shared_device_session"
|
|
50
|
+
const val KEY_DEVICE_ID = "deviceId"
|
|
51
|
+
const val KEY_DEVICE_SECRET = "deviceSecret"
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* [RecoveryPolicy.RebuildFileOnly] — this store must NEVER escalate to a
|
|
55
|
+
* UID-shared master-key reset.
|
|
56
|
+
*
|
|
57
|
+
* What it holds is DERIVED: every signed-in app re-publishes the credential
|
|
58
|
+
* from its own durable copy, so losing this file costs at most one interactive
|
|
59
|
+
* sign-in on a device that has no other Oxy app installed. A master-key reset
|
|
60
|
+
* would wipe every other Oxy store sharing the alias — including the
|
|
61
|
+
* self-custody identity keypair, which is IRREPLACEABLE. Trading someone's
|
|
62
|
+
* unrecoverable identity to save a credential we can simply re-publish is never
|
|
63
|
+
* the right trade, so the escalation is not merely discouraged here, it is
|
|
64
|
+
* unreachable.
|
|
65
|
+
*
|
|
66
|
+
* Consequence worth stating: a stage-1 heal WIPES this file. That surfaces as
|
|
67
|
+
* `Absent`, which is honest — after the wipe the slot really is empty, and the
|
|
68
|
+
* next successful mint in any app re-seeds it.
|
|
69
|
+
*/
|
|
70
|
+
private fun prefs(context: Context): SharedPreferences =
|
|
71
|
+
OxyEncryptedPrefs.open(context, PREFS_NAME, RecoveryPolicy.RebuildFileOnly)
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Read the credential. A thrown open (the keyset is unreadable and the policy
|
|
75
|
+
* above refuses to escalate) is reported as [DeviceSessionRead.Unavailable] —
|
|
76
|
+
* never as absent.
|
|
77
|
+
*/
|
|
78
|
+
fun read(context: Context): DeviceSessionRead =
|
|
79
|
+
runCatching {
|
|
80
|
+
val p = prefs(context)
|
|
81
|
+
val deviceId = p.getString(KEY_DEVICE_ID, null)
|
|
82
|
+
val deviceSecret = p.getString(KEY_DEVICE_SECRET, null)
|
|
83
|
+
if (deviceId.isNullOrEmpty() || deviceSecret.isNullOrEmpty()) {
|
|
84
|
+
DeviceSessionRead.Absent
|
|
85
|
+
} else {
|
|
86
|
+
DeviceSessionRead.Present(deviceId, deviceSecret)
|
|
87
|
+
}
|
|
88
|
+
}.getOrElse { DeviceSessionRead.Unavailable(it.javaClass.simpleName) }
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Replace the credential, returning whether a read-back confirmed it landed.
|
|
92
|
+
*
|
|
93
|
+
* The read-back is the contract JS relies on: a fresh install adopts whatever
|
|
94
|
+
* is in this slot, so publishing a value that did not actually persist would
|
|
95
|
+
* send it into a mint that can never succeed.
|
|
96
|
+
*
|
|
97
|
+
* `commit()` (synchronous) so a cross-process reader firing immediately after
|
|
98
|
+
* the write sees the flushed value.
|
|
99
|
+
*/
|
|
100
|
+
fun write(context: Context, deviceId: String, deviceSecret: String): Boolean =
|
|
101
|
+
runCatching {
|
|
102
|
+
val p = prefs(context)
|
|
103
|
+
p.edit()
|
|
104
|
+
.putString(KEY_DEVICE_ID, deviceId)
|
|
105
|
+
.putString(KEY_DEVICE_SECRET, deviceSecret)
|
|
106
|
+
.commit()
|
|
107
|
+
p.getString(KEY_DEVICE_ID, null) == deviceId &&
|
|
108
|
+
p.getString(KEY_DEVICE_SECRET, null) == deviceSecret
|
|
109
|
+
}.getOrDefault(false)
|
|
110
|
+
|
|
111
|
+
/** Drop the shared credential. Best-effort; safe to call when already empty. */
|
|
112
|
+
fun clear(context: Context) {
|
|
113
|
+
runCatching { prefs(context).edit().clear().commit() }
|
|
114
|
+
}
|
|
115
|
+
}
|