@oxyhq/services 22.12.4 → 22.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -46
- package/lib/commonjs/index.js +18 -12
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/boot/runProviderColdBoot.js +51 -9
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/client.js +0 -13
- package/lib/commonjs/ui/client.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js +27 -4
- package/lib/commonjs/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +72 -1123
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/OxyConsentScreen.js +50 -12
- package/lib/commonjs/ui/components/OxyConsentScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +4 -4
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js +74 -40
- package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +527 -0
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -0
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js +90 -0
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js.map +1 -0
- package/lib/commonjs/ui/components/authChooser/SignInRequestView.js +144 -0
- package/lib/commonjs/ui/components/authChooser/SignInRequestView.js.map +1 -0
- package/lib/commonjs/ui/components/authChooser/SignUpView.js +188 -0
- package/lib/commonjs/ui/components/authChooser/SignUpView.js.map +1 -0
- package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js +63 -0
- package/lib/commonjs/ui/components/authChooser/TroubleDisclosure.js.map +1 -0
- package/lib/commonjs/ui/components/authChooser/primitives.js +169 -0
- package/lib/commonjs/ui/components/authChooser/primitives.js.map +1 -0
- package/lib/commonjs/ui/components/authChooser/requestSurfaces.js +149 -0
- package/lib/commonjs/ui/components/authChooser/requestSurfaces.js.map +1 -0
- package/lib/commonjs/ui/components/authChooser/signInProgress.js +54 -0
- package/lib/commonjs/ui/components/authChooser/signInProgress.js.map +1 -0
- package/lib/commonjs/ui/components/{oxyAuthChooserStyles.js → authChooser/styles.js} +19 -1
- package/lib/commonjs/ui/components/authChooser/styles.js.map +1 -0
- package/lib/commonjs/ui/components/authChooser/types.js +59 -0
- package/lib/commonjs/ui/components/authChooser/types.js.map +1 -0
- package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js +50 -0
- package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js.map +1 -0
- package/lib/commonjs/ui/context/OxyContext.js +205 -30
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/commitSessionFlow.js +46 -0
- package/lib/commonjs/ui/context/commitSessionFlow.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +15 -2
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/useOxyAccountGraph.js +13 -5
- package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +6 -1
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +1 -1
- package/lib/commonjs/ui/index.js +0 -13
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/oauth/browserAuthTransport.js +170 -0
- package/lib/commonjs/ui/oauth/browserAuthTransport.js.map +1 -0
- package/lib/commonjs/ui/oauth/completeOAuthCode.js +78 -0
- package/lib/commonjs/ui/oauth/completeOAuthCode.js.map +1 -0
- package/lib/commonjs/ui/oauth/legacyRedirectLanes.js +48 -0
- package/lib/commonjs/ui/oauth/legacyRedirectLanes.js.map +1 -0
- package/lib/commonjs/ui/oauth/oauthHandshake.js +53 -0
- package/lib/commonjs/ui/oauth/oauthHandshake.js.map +1 -0
- package/lib/commonjs/ui/oauth/oauthPopup.js +276 -0
- package/lib/commonjs/ui/oauth/oauthPopup.js.map +1 -0
- package/lib/commonjs/ui/oauth/oauthPopupMessages.js +141 -0
- package/lib/commonjs/ui/oauth/oauthPopupMessages.js.map +1 -0
- package/lib/commonjs/ui/oauth/types.js +2 -0
- package/lib/commonjs/ui/oauth/types.js.map +1 -0
- package/lib/commonjs/ui/server.js +2 -5
- package/lib/commonjs/ui/server.js.map +1 -1
- package/lib/commonjs/ui/session/authStore.js +6 -75
- package/lib/commonjs/ui/session/authStore.js.map +1 -1
- package/lib/commonjs/ui/session/createSessionClient.js +11 -3
- package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
- package/lib/commonjs/ui/session/identityBinding.js +95 -0
- package/lib/commonjs/ui/session/identityBinding.js.map +1 -0
- package/lib/commonjs/ui/session/index.js +13 -0
- package/lib/commonjs/ui/session/index.js.map +1 -1
- package/lib/commonjs/ui/session/nativeSecureStorage.js +90 -0
- package/lib/commonjs/ui/session/nativeSecureStorage.js.map +1 -0
- package/lib/commonjs/ui/session/tokenTransport.js +11 -1
- package/lib/commonjs/ui/session/tokenTransport.js.map +1 -1
- package/lib/commonjs/ui/utils/crossOriginRestore.js +5 -4
- package/lib/commonjs/ui/utils/crossOriginRestore.js.map +1 -1
- package/lib/commonjs/ui/utils/deliveryPlatform.js +58 -0
- package/lib/commonjs/ui/utils/deliveryPlatform.js.map +1 -0
- package/lib/commonjs/ui/utils/oauthReturn.js +72 -46
- package/lib/commonjs/ui/utils/oauthReturn.js.map +1 -1
- package/lib/module/index.js +9 -4
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/boot/runProviderColdBoot.js +51 -9
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/client.js +0 -1
- package/lib/module/ui/client.js.map +1 -1
- package/lib/module/ui/components/OxyAccountDialogScreen.js +27 -4
- package/lib/module/ui/components/OxyAccountDialogScreen.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +76 -1128
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/OxyConsentScreen.js +50 -12
- package/lib/module/ui/components/OxyConsentScreen.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +4 -4
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js +76 -42
- package/lib/module/ui/components/OxySignInButton.js.map +1 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js +523 -0
- package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -0
- package/lib/module/ui/components/authChooser/SignInEntryView.js +85 -0
- package/lib/module/ui/components/authChooser/SignInEntryView.js.map +1 -0
- package/lib/module/ui/components/authChooser/SignInRequestView.js +139 -0
- package/lib/module/ui/components/authChooser/SignInRequestView.js.map +1 -0
- package/lib/module/ui/components/authChooser/SignUpView.js +184 -0
- package/lib/module/ui/components/authChooser/SignUpView.js.map +1 -0
- package/lib/module/ui/components/authChooser/TroubleDisclosure.js +59 -0
- package/lib/module/ui/components/authChooser/TroubleDisclosure.js.map +1 -0
- package/lib/module/ui/components/authChooser/primitives.js +163 -0
- package/lib/module/ui/components/authChooser/primitives.js.map +1 -0
- package/lib/module/ui/components/authChooser/requestSurfaces.js +139 -0
- package/lib/module/ui/components/authChooser/requestSurfaces.js.map +1 -0
- package/lib/module/ui/components/authChooser/signInProgress.js +50 -0
- package/lib/module/ui/components/authChooser/signInProgress.js.map +1 -0
- package/lib/module/ui/components/{oxyAuthChooserStyles.js → authChooser/styles.js} +19 -1
- package/lib/module/ui/components/authChooser/styles.js.map +1 -0
- package/lib/module/ui/components/authChooser/types.js +55 -0
- package/lib/module/ui/components/authChooser/types.js.map +1 -0
- package/lib/module/ui/components/authChooser/useUsernameAvailability.js +46 -0
- package/lib/module/ui/components/authChooser/useUsernameAvailability.js.map +1 -0
- package/lib/module/ui/context/OxyContext.js +208 -33
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/commitSessionFlow.js +45 -0
- package/lib/module/ui/context/commitSessionFlow.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +15 -2
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/useOxyAccountGraph.js +13 -5
- package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/module/ui/hooks/useSwitchableAccounts.js +6 -1
- package/lib/module/ui/hooks/useSwitchableAccounts.js.map +1 -1
- package/lib/module/ui/index.js +0 -1
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/oauth/browserAuthTransport.js +167 -0
- package/lib/module/ui/oauth/browserAuthTransport.js.map +1 -0
- package/lib/module/ui/oauth/completeOAuthCode.js +75 -0
- package/lib/module/ui/oauth/completeOAuthCode.js.map +1 -0
- package/lib/module/ui/oauth/legacyRedirectLanes.js +44 -0
- package/lib/module/ui/oauth/legacyRedirectLanes.js.map +1 -0
- package/lib/module/ui/oauth/oauthHandshake.js +50 -0
- package/lib/module/ui/oauth/oauthHandshake.js.map +1 -0
- package/lib/module/ui/oauth/oauthPopup.js +268 -0
- package/lib/module/ui/oauth/oauthPopup.js.map +1 -0
- package/lib/module/ui/oauth/oauthPopupMessages.js +135 -0
- package/lib/module/ui/oauth/oauthPopupMessages.js.map +1 -0
- package/lib/module/ui/oauth/types.js +2 -0
- package/lib/module/ui/oauth/types.js.map +1 -0
- package/lib/module/ui/server.js +0 -2
- package/lib/module/ui/server.js.map +1 -1
- package/lib/module/ui/session/authStore.js +5 -75
- package/lib/module/ui/session/authStore.js.map +1 -1
- package/lib/module/ui/session/createSessionClient.js +11 -3
- package/lib/module/ui/session/createSessionClient.js.map +1 -1
- package/lib/module/ui/session/identityBinding.js +89 -0
- package/lib/module/ui/session/identityBinding.js.map +1 -0
- package/lib/module/ui/session/index.js +5 -2
- package/lib/module/ui/session/index.js.map +1 -1
- package/lib/module/ui/session/nativeSecureStorage.js +88 -0
- package/lib/module/ui/session/nativeSecureStorage.js.map +1 -0
- package/lib/module/ui/session/tokenTransport.js +11 -1
- package/lib/module/ui/session/tokenTransport.js.map +1 -1
- package/lib/module/ui/utils/crossOriginRestore.js +6 -5
- package/lib/module/ui/utils/crossOriginRestore.js.map +1 -1
- package/lib/module/ui/utils/deliveryPlatform.js +55 -0
- package/lib/module/ui/utils/deliveryPlatform.js.map +1 -0
- package/lib/module/ui/utils/oauthReturn.js +73 -47
- package/lib/module/ui/utils/oauthReturn.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts +34 -2
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts +0 -1
- 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 +21 -29
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyConsentScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts +17 -12
- package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts +38 -0
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts +32 -0
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/authChooser/SignInRequestView.d.ts +34 -0
- package/lib/typescript/commonjs/ui/components/authChooser/SignInRequestView.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/authChooser/SignUpView.d.ts +34 -0
- package/lib/typescript/commonjs/ui/components/authChooser/SignUpView.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/authChooser/TroubleDisclosure.d.ts +43 -0
- package/lib/typescript/commonjs/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts +55 -0
- package/lib/typescript/commonjs/ui/components/authChooser/primitives.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/authChooser/requestSurfaces.d.ts +54 -0
- package/lib/typescript/commonjs/ui/components/authChooser/requestSurfaces.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/authChooser/signInProgress.d.ts +26 -0
- package/lib/typescript/commonjs/ui/components/authChooser/signInProgress.d.ts.map +1 -0
- package/lib/typescript/{module/ui/components/oxyAuthChooserStyles.d.ts → commonjs/ui/components/authChooser/styles.d.ts} +19 -1
- package/lib/typescript/commonjs/ui/components/authChooser/styles.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +85 -0
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/authChooser/useUsernameAvailability.d.ts +16 -0
- package/lib/typescript/commonjs/ui/components/authChooser/useUsernameAvailability.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts +40 -0
- package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +11 -2
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +44 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +7 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts +0 -1
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/oauth/browserAuthTransport.d.ts +56 -0
- package/lib/typescript/commonjs/ui/oauth/browserAuthTransport.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/oauth/completeOAuthCode.d.ts +49 -0
- package/lib/typescript/commonjs/ui/oauth/completeOAuthCode.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/oauth/legacyRedirectLanes.d.ts +40 -0
- package/lib/typescript/commonjs/ui/oauth/legacyRedirectLanes.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/oauth/oauthHandshake.d.ts +50 -0
- package/lib/typescript/commonjs/ui/oauth/oauthHandshake.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/oauth/oauthPopup.d.ts +83 -0
- package/lib/typescript/commonjs/ui/oauth/oauthPopup.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/oauth/oauthPopupMessages.d.ts +67 -0
- package/lib/typescript/commonjs/ui/oauth/oauthPopupMessages.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/oauth/types.d.ts +176 -0
- package/lib/typescript/commonjs/ui/oauth/types.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/server.d.ts +0 -2
- package/lib/typescript/commonjs/ui/server.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/authStore.d.ts +3 -7
- package/lib/typescript/commonjs/ui/session/authStore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +8 -1
- package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/identityBinding.d.ts +66 -0
- package/lib/typescript/commonjs/ui/session/identityBinding.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/session/index.d.ts +6 -2
- package/lib/typescript/commonjs/ui/session/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/nativeSecureStorage.d.ts +25 -0
- package/lib/typescript/commonjs/ui/session/nativeSecureStorage.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts +8 -1
- package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +44 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/crossOriginRestore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/deliveryPlatform.d.ts +33 -0
- package/lib/typescript/commonjs/ui/utils/deliveryPlatform.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/utils/oauthReturn.d.ts +30 -16
- package/lib/typescript/commonjs/ui/utils/oauthReturn.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts +34 -2
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts +0 -1
- 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 +21 -29
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyConsentScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxySignInButton.d.ts +17 -12
- package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts +38 -0
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts +32 -0
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/authChooser/SignInRequestView.d.ts +34 -0
- package/lib/typescript/module/ui/components/authChooser/SignInRequestView.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/authChooser/SignUpView.d.ts +34 -0
- package/lib/typescript/module/ui/components/authChooser/SignUpView.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/authChooser/TroubleDisclosure.d.ts +43 -0
- package/lib/typescript/module/ui/components/authChooser/TroubleDisclosure.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts +55 -0
- package/lib/typescript/module/ui/components/authChooser/primitives.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/authChooser/requestSurfaces.d.ts +54 -0
- package/lib/typescript/module/ui/components/authChooser/requestSurfaces.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/authChooser/signInProgress.d.ts +26 -0
- package/lib/typescript/module/ui/components/authChooser/signInProgress.d.ts.map +1 -0
- package/lib/typescript/{commonjs/ui/components/oxyAuthChooserStyles.d.ts → module/ui/components/authChooser/styles.d.ts} +19 -1
- package/lib/typescript/module/ui/components/authChooser/styles.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/authChooser/types.d.ts +85 -0
- package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/authChooser/useUsernameAvailability.d.ts +16 -0
- package/lib/typescript/module/ui/components/authChooser/useUsernameAvailability.d.ts.map +1 -0
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/commitSessionFlow.d.ts +40 -0
- package/lib/typescript/module/ui/context/commitSessionFlow.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +11 -2
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +44 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +7 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts +0 -1
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/oauth/browserAuthTransport.d.ts +56 -0
- package/lib/typescript/module/ui/oauth/browserAuthTransport.d.ts.map +1 -0
- package/lib/typescript/module/ui/oauth/completeOAuthCode.d.ts +49 -0
- package/lib/typescript/module/ui/oauth/completeOAuthCode.d.ts.map +1 -0
- package/lib/typescript/module/ui/oauth/legacyRedirectLanes.d.ts +40 -0
- package/lib/typescript/module/ui/oauth/legacyRedirectLanes.d.ts.map +1 -0
- package/lib/typescript/module/ui/oauth/oauthHandshake.d.ts +50 -0
- package/lib/typescript/module/ui/oauth/oauthHandshake.d.ts.map +1 -0
- package/lib/typescript/module/ui/oauth/oauthPopup.d.ts +83 -0
- package/lib/typescript/module/ui/oauth/oauthPopup.d.ts.map +1 -0
- package/lib/typescript/module/ui/oauth/oauthPopupMessages.d.ts +67 -0
- package/lib/typescript/module/ui/oauth/oauthPopupMessages.d.ts.map +1 -0
- package/lib/typescript/module/ui/oauth/types.d.ts +176 -0
- package/lib/typescript/module/ui/oauth/types.d.ts.map +1 -0
- package/lib/typescript/module/ui/server.d.ts +0 -2
- package/lib/typescript/module/ui/server.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/authStore.d.ts +3 -7
- package/lib/typescript/module/ui/session/authStore.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/createSessionClient.d.ts +8 -1
- package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/identityBinding.d.ts +66 -0
- package/lib/typescript/module/ui/session/identityBinding.d.ts.map +1 -0
- package/lib/typescript/module/ui/session/index.d.ts +6 -2
- package/lib/typescript/module/ui/session/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/nativeSecureStorage.d.ts +25 -0
- package/lib/typescript/module/ui/session/nativeSecureStorage.d.ts.map +1 -0
- package/lib/typescript/module/ui/session/tokenTransport.d.ts +8 -1
- package/lib/typescript/module/ui/session/tokenTransport.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/navigation.d.ts +44 -0
- package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/crossOriginRestore.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/deliveryPlatform.d.ts +33 -0
- package/lib/typescript/module/ui/utils/deliveryPlatform.d.ts.map +1 -0
- package/lib/typescript/module/ui/utils/oauthReturn.d.ts +30 -16
- package/lib/typescript/module/ui/utils/oauthReturn.d.ts.map +1 -1
- package/package.json +6 -8
- package/src/index.ts +19 -5
- package/src/ui/boot/__tests__/coldBootWebAuthMode.test.ts +281 -0
- package/src/ui/boot/runProviderColdBoot.ts +83 -13
- package/src/ui/client.ts +0 -1
- package/src/ui/components/OxyAccountDialogScreen.tsx +33 -4
- package/src/ui/components/OxyAuthChooser.tsx +100 -1230
- package/src/ui/components/OxyConsentScreen.tsx +56 -13
- package/src/ui/components/OxyProvider.tsx +4 -4
- package/src/ui/components/OxySignInButton.tsx +119 -78
- package/src/ui/components/__tests__/OxyConsentScreen.test.tsx +27 -0
- package/src/ui/components/authChooser/AccountsMenuView.tsx +569 -0
- package/src/ui/components/authChooser/SignInEntryView.tsx +115 -0
- package/src/ui/components/authChooser/SignInRequestView.tsx +163 -0
- package/src/ui/components/authChooser/SignUpView.tsx +174 -0
- package/src/ui/components/authChooser/TroubleDisclosure.tsx +81 -0
- package/src/ui/components/authChooser/primitives.tsx +144 -0
- package/src/ui/components/authChooser/requestSurfaces.tsx +116 -0
- package/src/ui/components/authChooser/signInProgress.ts +54 -0
- package/src/ui/components/{oxyAuthChooserStyles.ts → authChooser/styles.ts} +18 -0
- package/src/ui/components/authChooser/types.ts +101 -0
- package/src/ui/components/authChooser/useUsernameAvailability.ts +60 -0
- package/src/ui/context/OxyContext.tsx +226 -24
- package/src/ui/context/__tests__/commitSessionFlow.test.ts +102 -0
- package/src/ui/context/commitSessionFlow.ts +64 -0
- package/src/ui/context/hooks/useAuthOperations.ts +26 -1
- package/src/ui/context/oxyContextTypes.ts +45 -1
- package/src/ui/context/useOxyAccountGraph.ts +19 -5
- package/src/ui/hooks/useSwitchableAccounts.ts +12 -1
- package/src/ui/index.ts +0 -1
- package/src/ui/oauth/__tests__/browserAuthTransport.test.ts +335 -0
- package/src/ui/oauth/__tests__/completeOAuthCode.test.ts +142 -0
- package/src/ui/oauth/__tests__/oauthPopup.test.ts +352 -0
- package/src/ui/oauth/__tests__/oauthPopupMessages.test.ts +189 -0
- package/src/ui/oauth/__tests__/sharedCompletionPath.test.ts +174 -0
- package/src/ui/oauth/browserAuthTransport.ts +193 -0
- package/src/ui/oauth/completeOAuthCode.ts +93 -0
- package/src/ui/oauth/legacyRedirectLanes.ts +43 -0
- package/src/ui/oauth/oauthHandshake.ts +74 -0
- package/src/ui/oauth/oauthPopup.ts +277 -0
- package/src/ui/oauth/oauthPopupMessages.ts +136 -0
- package/src/ui/oauth/types.ts +150 -0
- package/src/ui/server.ts +0 -2
- package/src/ui/session/__tests__/createSessionClient.test.ts +26 -0
- package/src/ui/session/__tests__/tokenTransport.test.ts +34 -0
- package/src/ui/session/authStore.ts +5 -89
- package/src/ui/session/createSessionClient.ts +15 -2
- package/src/ui/session/identityBinding.ts +116 -0
- package/src/ui/session/index.ts +6 -2
- package/src/ui/session/nativeSecureStorage.ts +98 -0
- package/src/ui/session/tokenTransport.ts +14 -1
- package/src/ui/types/navigation.ts +44 -1
- package/src/ui/utils/__tests__/crossOriginRestore.test.ts +17 -0
- package/src/ui/utils/crossOriginRestore.ts +9 -13
- package/src/ui/utils/deliveryPlatform.ts +65 -0
- package/src/ui/utils/oauthReturn.ts +79 -65
- package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-Black.ttf +0 -0
- package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-Bold.ttf +0 -0
- package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-ExtraBold.ttf +0 -0
- package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-Light.ttf +0 -0
- package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-Medium.ttf +0 -0
- package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-Regular.ttf +0 -0
- package/lib/commonjs/assets/assets/fonts/Inter/Inter_18pt-SemiBold.ttf +0 -0
- package/lib/commonjs/assets/fonts/Inter/Inter_18pt-Black.ttf +0 -0
- package/lib/commonjs/assets/fonts/Inter/Inter_18pt-Bold.ttf +0 -0
- package/lib/commonjs/assets/fonts/Inter/Inter_18pt-ExtraBold.ttf +0 -0
- package/lib/commonjs/assets/fonts/Inter/Inter_18pt-Light.ttf +0 -0
- package/lib/commonjs/assets/fonts/Inter/Inter_18pt-Medium.ttf +0 -0
- package/lib/commonjs/assets/fonts/Inter/Inter_18pt-Regular.ttf +0 -0
- package/lib/commonjs/assets/fonts/Inter/Inter_18pt-SemiBold.ttf +0 -0
- package/lib/commonjs/ui/components/FontLoader.js +0 -35
- package/lib/commonjs/ui/components/FontLoader.js.map +0 -1
- package/lib/commonjs/ui/components/FontLoader.native.js +0 -88
- package/lib/commonjs/ui/components/FontLoader.native.js.map +0 -1
- package/lib/commonjs/ui/components/oxyAuthChooserStyles.js.map +0 -1
- package/lib/module/assets/assets/fonts/Inter/Inter_18pt-Black.ttf +0 -0
- package/lib/module/assets/assets/fonts/Inter/Inter_18pt-Bold.ttf +0 -0
- package/lib/module/assets/assets/fonts/Inter/Inter_18pt-ExtraBold.ttf +0 -0
- package/lib/module/assets/assets/fonts/Inter/Inter_18pt-Light.ttf +0 -0
- package/lib/module/assets/assets/fonts/Inter/Inter_18pt-Medium.ttf +0 -0
- package/lib/module/assets/assets/fonts/Inter/Inter_18pt-Regular.ttf +0 -0
- package/lib/module/assets/assets/fonts/Inter/Inter_18pt-SemiBold.ttf +0 -0
- package/lib/module/assets/fonts/Inter/Inter_18pt-Black.ttf +0 -0
- package/lib/module/assets/fonts/Inter/Inter_18pt-Bold.ttf +0 -0
- package/lib/module/assets/fonts/Inter/Inter_18pt-ExtraBold.ttf +0 -0
- package/lib/module/assets/fonts/Inter/Inter_18pt-Light.ttf +0 -0
- package/lib/module/assets/fonts/Inter/Inter_18pt-Medium.ttf +0 -0
- package/lib/module/assets/fonts/Inter/Inter_18pt-Regular.ttf +0 -0
- package/lib/module/assets/fonts/Inter/Inter_18pt-SemiBold.ttf +0 -0
- package/lib/module/ui/components/FontLoader.js +0 -29
- package/lib/module/ui/components/FontLoader.js.map +0 -1
- package/lib/module/ui/components/FontLoader.native.js +0 -83
- package/lib/module/ui/components/FontLoader.native.js.map +0 -1
- package/lib/module/ui/components/oxyAuthChooserStyles.js.map +0 -1
- package/lib/typescript/commonjs/ui/components/FontLoader.d.ts +0 -24
- package/lib/typescript/commonjs/ui/components/FontLoader.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/components/FontLoader.native.d.ts +0 -13
- package/lib/typescript/commonjs/ui/components/FontLoader.native.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/FontLoader.d.ts +0 -24
- package/lib/typescript/module/ui/components/FontLoader.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/FontLoader.native.d.ts +0 -13
- package/lib/typescript/module/ui/components/FontLoader.native.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts.map +0 -1
- package/src/assets/fonts/Inter/Inter_18pt-Black.ttf +0 -0
- package/src/assets/fonts/Inter/Inter_18pt-Bold.ttf +0 -0
- package/src/assets/fonts/Inter/Inter_18pt-ExtraBold.ttf +0 -0
- package/src/assets/fonts/Inter/Inter_18pt-Light.ttf +0 -0
- package/src/assets/fonts/Inter/Inter_18pt-Medium.ttf +0 -0
- package/src/assets/fonts/Inter/Inter_18pt-Regular.ttf +0 -0
- package/src/assets/fonts/Inter/Inter_18pt-SemiBold.ttf +0 -0
- package/src/ui/components/FontLoader.native.tsx +0 -88
- package/src/ui/components/FontLoader.tsx +0 -27
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
|
-
import type { ApiError, AuthStateStore, SessionClient, User } from '@oxyhq/core';
|
|
2
|
+
import type { ApiError, AuthStateStore, IdentityBinding, SessionClient, User } from '@oxyhq/core';
|
|
3
3
|
import type { AuthState } from '../../stores/authStore';
|
|
4
4
|
import type { ClientSession, SessionLoginResponse } from '@oxyhq/core';
|
|
5
5
|
import { DeviceManager } from '@oxyhq/core';
|
|
@@ -43,6 +43,15 @@ export interface UseAuthOperationsOptions {
|
|
|
43
43
|
logoutStore: () => void;
|
|
44
44
|
setAuthState: (state: Partial<AuthState>) => void;
|
|
45
45
|
logger?: (message: string, error?: unknown) => void;
|
|
46
|
+
/**
|
|
47
|
+
* Identity-bound session (`sessionMode: 'identity'`). When set, a successful
|
|
48
|
+
* key sign-in writes the durable `{publicKey, accountId}` pin so pinned
|
|
49
|
+
* re-mints and projections bind immediately instead of waiting for a second
|
|
50
|
+
* `establishIdentitySession` round-trip.
|
|
51
|
+
*/
|
|
52
|
+
identityBinding?: IdentityBinding;
|
|
53
|
+
/** Refreshes the provider's memoised pinned account id after the pin lands. */
|
|
54
|
+
refreshPinnedAccountId?: () => Promise<string | null>;
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
export interface UseAuthOperationsResult {
|
|
@@ -97,6 +106,8 @@ export const useAuthOperations = ({
|
|
|
97
106
|
logoutStore,
|
|
98
107
|
setAuthState,
|
|
99
108
|
logger,
|
|
109
|
+
identityBinding,
|
|
110
|
+
refreshPinnedAccountId,
|
|
100
111
|
}: UseAuthOperationsOptions): UseAuthOperationsResult => {
|
|
101
112
|
/**
|
|
102
113
|
* Internal function to perform challenge-response sign in.
|
|
@@ -155,6 +166,18 @@ export const useAuthOperations = ({
|
|
|
155
166
|
}
|
|
156
167
|
}
|
|
157
168
|
|
|
169
|
+
if (identityBinding) {
|
|
170
|
+
try {
|
|
171
|
+
await identityBinding.pinStore.save({
|
|
172
|
+
publicKey: publicKey.toLowerCase(),
|
|
173
|
+
accountId: sessionResponse.user.id,
|
|
174
|
+
});
|
|
175
|
+
await refreshPinnedAccountId?.();
|
|
176
|
+
} catch (pinError) {
|
|
177
|
+
logger?.('Failed to persist identity pin after sign-in', pinError);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
158
181
|
// Register this recovered account+session into the server-authoritative
|
|
159
182
|
// device-session set AND make it active: `registerAndActivate()` adds via
|
|
160
183
|
// `POST /session/device/add` (identity derived from the bearer
|
|
@@ -237,6 +260,8 @@ export const useAuthOperations = ({
|
|
|
237
260
|
switchSession,
|
|
238
261
|
syncFromClient,
|
|
239
262
|
updateSessions,
|
|
263
|
+
identityBinding,
|
|
264
|
+
refreshPinnedAccountId,
|
|
240
265
|
],
|
|
241
266
|
);
|
|
242
267
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type { OxyServices, User, SessionLoginResponse, AccountNode, CreateAccountInput, ClientSession, AccountDialogController, AccountDialogView, ApiError, SessionClient } from '@oxyhq/core';
|
|
2
|
+
import type { OxyServices, User, SessionLoginResponse, AccountNode, CreateAccountInput, ClientSession, AccountDialogController, AccountDialogView, ApiError, SessionClient, SessionMode } from '@oxyhq/core';
|
|
3
3
|
import type { UseFollowHook } from '../hooks/useFollow.types';
|
|
4
4
|
import type { useLanguageManagement } from '../hooks/useLanguageManagement';
|
|
5
5
|
import type { RouteName } from '../navigation/routes';
|
|
6
|
+
import type { StartWebOAuthSignInOptions } from '../oauth/browserAuthTransport';
|
|
7
|
+
import type { WebAuthMode, WebOAuthSignInResult } from '../oauth/types';
|
|
6
8
|
|
|
7
9
|
export interface OxyContextState {
|
|
8
10
|
user: User | null;
|
|
@@ -26,6 +28,19 @@ export interface OxyContextState {
|
|
|
26
28
|
*/
|
|
27
29
|
isAuthResolved: boolean;
|
|
28
30
|
isStorageReady: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Who owns this provider's session (see `OxyProviderProps.sessionMode`).
|
|
33
|
+
*
|
|
34
|
+
* `'account'` — the device's active account; any app on the device can switch
|
|
35
|
+
* it and this client follows. `'identity'` — the owner of the local primary
|
|
36
|
+
* identity key, permanently: a sibling app's account switch never changes this
|
|
37
|
+
* client's user or bearer, and every account-graph mutation
|
|
38
|
+
* (`switchToAccount` / `switchSession`) rejects with `IdentityBoundSessionError`
|
|
39
|
+
* while `accounts` stays empty and the account dialog never opens.
|
|
40
|
+
*/
|
|
41
|
+
sessionMode: SessionMode;
|
|
42
|
+
/** How web third-party sign-in reaches the IdP (see `OxyProviderProps.webAuthMode`). */
|
|
43
|
+
webAuthMode: WebAuthMode;
|
|
29
44
|
error: string | null;
|
|
30
45
|
/** Active UI locale (`language-REGION`): the account's primary locale when signed in, else the guest/device locale. */
|
|
31
46
|
currentLanguage: string;
|
|
@@ -79,6 +94,24 @@ export interface OxyContextState {
|
|
|
79
94
|
revokeSuspiciousSignIn: () => Promise<void>;
|
|
80
95
|
handleWebSession: (session: SessionLoginResponse) => Promise<void>;
|
|
81
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Run a WEB third-party OAuth sign-in (authorization code + PKCE) end to end
|
|
99
|
+
* using this provider's `webAuthMode`, and commit the resulting session.
|
|
100
|
+
*
|
|
101
|
+
* This is the shared operation `OxySignInButton` delegates to; RPs that ship
|
|
102
|
+
* their own button call it directly and never implement popup listeners, a
|
|
103
|
+
* token exchange, or `state` validation themselves. Returns a typed result —
|
|
104
|
+
* closing the popup and running out of time are values, not exceptions.
|
|
105
|
+
*
|
|
106
|
+
* In popup mode the window MUST be opened during the user's click: either
|
|
107
|
+
* call this synchronously from the press handler (it opens one itself), or
|
|
108
|
+
* call `openOAuthPopup()` there and pass the handle as `popup`.
|
|
109
|
+
*
|
|
110
|
+
* No-op outside a browser and in `sessionMode: 'identity'`, both of which
|
|
111
|
+
* resolve to `{ status: 'unsupported' }`.
|
|
112
|
+
*/
|
|
113
|
+
startWebOAuthSignIn: (options: StartWebOAuthSignInOptions) => Promise<WebOAuthSignInResult>;
|
|
114
|
+
|
|
82
115
|
logout: (targetSessionId?: string) => Promise<void>;
|
|
83
116
|
logoutAll: () => Promise<void>;
|
|
84
117
|
switchSession: (sessionId: string) => Promise<User>;
|
|
@@ -133,6 +166,17 @@ export interface OxyContextProviderProps {
|
|
|
133
166
|
authorizeBaseUrl?: string;
|
|
134
167
|
storageKeyPrefix?: string;
|
|
135
168
|
clientId?: string;
|
|
169
|
+
/**
|
|
170
|
+
* Who owns this provider's session. See `OxyProviderProps.sessionMode` for the
|
|
171
|
+
* full contract.
|
|
172
|
+
* @default 'account'
|
|
173
|
+
*/
|
|
174
|
+
sessionMode?: SessionMode;
|
|
175
|
+
/**
|
|
176
|
+
* Transport for WEB third-party sign-in. See `OxyProviderProps.webAuthMode`.
|
|
177
|
+
* @default 'redirect'
|
|
178
|
+
*/
|
|
179
|
+
webAuthMode?: WebAuthMode;
|
|
136
180
|
/** Sync device credentials to auth.oxy.so after interactive sign-in. @default true */
|
|
137
181
|
hubSync?: boolean;
|
|
138
182
|
onAuthStateChange?: (user: User | null) => void;
|
|
@@ -6,12 +6,19 @@ import { useAuthStore } from '../stores/authStore';
|
|
|
6
6
|
import { isUnauthorizedStatus } from './oxyContextHelpers';
|
|
7
7
|
import { resetSessionScopedStores } from '../stores/resetSessionScopedStores';
|
|
8
8
|
import { ASSET_DOWNLOAD_URLS_QUERY_KEY } from '../hooks/useResolvedFileUrls';
|
|
9
|
+
import { IdentityBoundSessionError } from '../session';
|
|
9
10
|
import type { CommitInput } from './oxyContextTypes';
|
|
10
11
|
|
|
11
12
|
interface UseOxyAccountGraphParams {
|
|
12
13
|
isAuthenticated: boolean;
|
|
13
14
|
tokenReady: boolean;
|
|
14
15
|
initialized: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* `sessionMode: 'identity'`. The account graph is a switcher surface, and an
|
|
18
|
+
* identity-bound client authenticates as the owner of the local identity key —
|
|
19
|
+
* permanently. The graph is therefore never fetched and every switch rejects.
|
|
20
|
+
*/
|
|
21
|
+
identityBound: boolean;
|
|
15
22
|
oxyServices: OxyServices;
|
|
16
23
|
sessionClient: SessionClient;
|
|
17
24
|
syncFromClient: () => Promise<void>;
|
|
@@ -25,6 +32,7 @@ export function useOxyAccountGraph({
|
|
|
25
32
|
isAuthenticated,
|
|
26
33
|
tokenReady,
|
|
27
34
|
initialized,
|
|
35
|
+
identityBound,
|
|
28
36
|
oxyServices,
|
|
29
37
|
sessionClient,
|
|
30
38
|
syncFromClient,
|
|
@@ -36,7 +44,7 @@ export function useOxyAccountGraph({
|
|
|
36
44
|
const [accounts, setAccounts] = useState<AccountNode[]>([]);
|
|
37
45
|
|
|
38
46
|
const refreshAccounts = useCallback(async (): Promise<void> => {
|
|
39
|
-
if (!isAuthenticated || !tokenReady || !oxyServices.getAccessToken()) {
|
|
47
|
+
if (identityBound || !isAuthenticated || !tokenReady || !oxyServices.getAccessToken()) {
|
|
40
48
|
setAccounts([]);
|
|
41
49
|
return;
|
|
42
50
|
}
|
|
@@ -53,14 +61,14 @@ export function useOxyAccountGraph({
|
|
|
53
61
|
loggerUtil.debug('Failed to load accounts', { component: 'OxyContext' }, err as unknown);
|
|
54
62
|
}
|
|
55
63
|
}
|
|
56
|
-
}, [isAuthenticated, oxyServices, tokenReady, clearSessionStateRef]);
|
|
64
|
+
}, [identityBound, isAuthenticated, oxyServices, tokenReady, clearSessionStateRef]);
|
|
57
65
|
|
|
58
66
|
useEffect(() => {
|
|
59
|
-
if (isAuthenticated && initialized && tokenReady) {
|
|
67
|
+
if (!identityBound && isAuthenticated && initialized && tokenReady) {
|
|
60
68
|
refreshAccounts();
|
|
61
69
|
void accountDialogControllerRef.current?.refresh();
|
|
62
70
|
}
|
|
63
|
-
}, [isAuthenticated, initialized, tokenReady, refreshAccounts, accountDialogControllerRef]);
|
|
71
|
+
}, [identityBound, isAuthenticated, initialized, tokenReady, refreshAccounts, accountDialogControllerRef]);
|
|
64
72
|
|
|
65
73
|
const runPostAccountSwitchSideEffects = useCallback(async (): Promise<void> => {
|
|
66
74
|
resetSessionScopedStores();
|
|
@@ -74,6 +82,12 @@ export function useOxyAccountGraph({
|
|
|
74
82
|
|
|
75
83
|
const switchToAccount = useCallback(
|
|
76
84
|
async (accountId: string): Promise<void> => {
|
|
85
|
+
if (identityBound) {
|
|
86
|
+
// Loud rejection, never a silent no-op: a resolved promise here would
|
|
87
|
+
// read to the caller as a completed switch that simply left the user
|
|
88
|
+
// unchanged, which is exactly the confusion this mode must avoid.
|
|
89
|
+
throw new IdentityBoundSessionError('switchToAccount');
|
|
90
|
+
}
|
|
77
91
|
const deviceState = sessionClient.getState();
|
|
78
92
|
if (deviceState?.accounts.some((account) => account.accountId === accountId)) {
|
|
79
93
|
await sessionClient.switchAccount(accountId);
|
|
@@ -102,7 +116,7 @@ export function useOxyAccountGraph({
|
|
|
102
116
|
);
|
|
103
117
|
await runPostAccountSwitchSideEffects();
|
|
104
118
|
},
|
|
105
|
-
[oxyServices, sessionClient, syncFromClient, commitSession, runPostAccountSwitchSideEffects],
|
|
119
|
+
[identityBound, oxyServices, sessionClient, syncFromClient, commitSession, runPostAccountSwitchSideEffects],
|
|
106
120
|
);
|
|
107
121
|
|
|
108
122
|
const createAccount = useCallback(
|
|
@@ -22,7 +22,18 @@ const EMPTY_SNAPSHOT: AccountDialogSnapshot = {
|
|
|
22
22
|
loading: false,
|
|
23
23
|
error: null,
|
|
24
24
|
switchingAccountId: null,
|
|
25
|
-
signIn: {
|
|
25
|
+
signIn: {
|
|
26
|
+
phase: 'idle',
|
|
27
|
+
authorizeCode: null,
|
|
28
|
+
qrPayload: null,
|
|
29
|
+
expiresAt: null,
|
|
30
|
+
error: null,
|
|
31
|
+
route: null,
|
|
32
|
+
routeFailed: false,
|
|
33
|
+
pushSentAt: null,
|
|
34
|
+
openedAt: null,
|
|
35
|
+
progress: 'idle',
|
|
36
|
+
},
|
|
26
37
|
commonsAvailability: 'unknown',
|
|
27
38
|
};
|
|
28
39
|
|
package/src/ui/index.ts
CHANGED
|
@@ -26,7 +26,6 @@ export { RequireOxyAuth } from './components/RequireOxyAuth';
|
|
|
26
26
|
export type { RequireOxyAuthProps, RequireOxyAuthPrompt } from './components/RequireOxyAuth';
|
|
27
27
|
export { default as FollowButton } from './components/FollowButton';
|
|
28
28
|
export { default as OxyPayButton } from './components/OxyPayButton';
|
|
29
|
-
export { FontLoader, setupFonts } from './components/FontLoader';
|
|
30
29
|
export { default as ProfileButton } from './components/ProfileButton';
|
|
31
30
|
|
|
32
31
|
// Context + hooks
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
// The platform redirect is spied on so no real navigation happens and the
|
|
2
|
+
// authorize URL each lane hands to the browser can be asserted.
|
|
3
|
+
jest.mock('../../components/oauthNavigation', () => ({
|
|
4
|
+
redirectToAuthorize: jest.fn(),
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
import type { OxyServices } from '@oxyhq/core';
|
|
8
|
+
import {
|
|
9
|
+
computeCodeChallenge,
|
|
10
|
+
OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY,
|
|
11
|
+
OXY_OAUTH_STATE_STORAGE_KEY,
|
|
12
|
+
} from '@oxyhq/core';
|
|
13
|
+
import { redirectToAuthorize } from '../../components/oauthNavigation';
|
|
14
|
+
import {
|
|
15
|
+
startWebOAuthSignIn,
|
|
16
|
+
type WebOAuthTransportContext,
|
|
17
|
+
} from '../browserAuthTransport';
|
|
18
|
+
import { OXY_OAUTH_CODE_MESSAGE_TYPE, OXY_OAUTH_ERROR_MESSAGE_TYPE } from '../oauthPopupMessages';
|
|
19
|
+
import type { OAuthPopupHandle } from '../types';
|
|
20
|
+
|
|
21
|
+
const mockRedirect = redirectToAuthorize as jest.Mock;
|
|
22
|
+
|
|
23
|
+
const CLIENT_ID = 'oxy_dk_test';
|
|
24
|
+
const REDIRECT_URI = 'https://mention.earth';
|
|
25
|
+
const AUTHORIZE_BASE_URL = 'https://auth.oxy.so/authorize';
|
|
26
|
+
const IDP_ORIGIN = 'https://auth.oxy.so';
|
|
27
|
+
|
|
28
|
+
const SESSION = {
|
|
29
|
+
sessionId: 'sess-1',
|
|
30
|
+
deviceId: 'dev-1',
|
|
31
|
+
deviceSecret: 'secret-1',
|
|
32
|
+
accessToken: 'token-1',
|
|
33
|
+
expiresAt: '2099-01-01T00:00:00.000Z',
|
|
34
|
+
user: { id: 'user-1', username: 'nate' },
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
interface ControllablePopup extends OAuthPopupHandle {
|
|
38
|
+
close: jest.Mock;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function fakePopup(): ControllablePopup {
|
|
42
|
+
let closed = false;
|
|
43
|
+
return {
|
|
44
|
+
get closed() {
|
|
45
|
+
return closed;
|
|
46
|
+
},
|
|
47
|
+
close: jest.fn(() => {
|
|
48
|
+
closed = true;
|
|
49
|
+
}),
|
|
50
|
+
location: { href: '' },
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function makeContext(
|
|
55
|
+
overrides: Partial<WebOAuthTransportContext> = {},
|
|
56
|
+
): WebOAuthTransportContext & { exchangeOAuthCode: jest.Mock; commitSession: jest.Mock } {
|
|
57
|
+
const exchangeOAuthCode = jest.fn().mockResolvedValue(SESSION);
|
|
58
|
+
const commitSession = jest.fn().mockResolvedValue(undefined);
|
|
59
|
+
const base: WebOAuthTransportContext = {
|
|
60
|
+
mode: 'popup',
|
|
61
|
+
oxyServices: { exchangeOAuthCode } as unknown as OxyServices,
|
|
62
|
+
clientId: CLIENT_ID,
|
|
63
|
+
authorizeBaseUrl: AUTHORIZE_BASE_URL,
|
|
64
|
+
identityBound: false,
|
|
65
|
+
commitSession,
|
|
66
|
+
};
|
|
67
|
+
return { ...base, ...overrides, exchangeOAuthCode, commitSession };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Wait for the transport to finish preparing PKCE and navigate the popup. */
|
|
71
|
+
async function waitForAuthorizeUrl(popup: OAuthPopupHandle): Promise<URL> {
|
|
72
|
+
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
73
|
+
if (popup.location.href) return new URL(popup.location.href);
|
|
74
|
+
await new Promise((resolve) => {
|
|
75
|
+
setTimeout(resolve, 5);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
throw new Error('the popup was never navigated to the authorize URL');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function dispatchFromPopup(data: unknown, source: unknown, origin = IDP_ORIGIN): void {
|
|
82
|
+
const event = new Event('message');
|
|
83
|
+
Object.assign(event, { data, origin, source });
|
|
84
|
+
window.dispatchEvent(event);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
describe('startWebOAuthSignIn', () => {
|
|
88
|
+
beforeEach(() => {
|
|
89
|
+
mockRedirect.mockClear();
|
|
90
|
+
globalThis.sessionStorage?.clear();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
afterEach(() => {
|
|
94
|
+
jest.restoreAllMocks();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('popup mode', () => {
|
|
98
|
+
it('signs in without navigating the relying party', async () => {
|
|
99
|
+
const context = makeContext();
|
|
100
|
+
const popup = fakePopup();
|
|
101
|
+
|
|
102
|
+
const pending = startWebOAuthSignIn(context, { redirectUri: REDIRECT_URI, popup });
|
|
103
|
+
const authorizeUrl = await waitForAuthorizeUrl(popup);
|
|
104
|
+
|
|
105
|
+
// The IdP is asked for the popup channel, with the standard PKCE binding.
|
|
106
|
+
expect(authorizeUrl.origin).toBe(IDP_ORIGIN);
|
|
107
|
+
expect(authorizeUrl.searchParams.get('response_mode')).toBe('web_message');
|
|
108
|
+
expect(authorizeUrl.searchParams.get('client_id')).toBe(CLIENT_ID);
|
|
109
|
+
expect(authorizeUrl.searchParams.get('redirect_uri')).toBe(REDIRECT_URI);
|
|
110
|
+
expect(authorizeUrl.searchParams.get('code_challenge_method')).toBe('S256');
|
|
111
|
+
|
|
112
|
+
const state = authorizeUrl.searchParams.get('state');
|
|
113
|
+
dispatchFromPopup(
|
|
114
|
+
{ type: OXY_OAUTH_CODE_MESSAGE_TYPE, code: 'code-1', state },
|
|
115
|
+
popup,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
await expect(pending).resolves.toEqual({ status: 'signed-in' });
|
|
119
|
+
expect(mockRedirect).not.toHaveBeenCalled();
|
|
120
|
+
|
|
121
|
+
// The verifier the main window replays is the one behind the challenge it
|
|
122
|
+
// sent — the popup never saw either.
|
|
123
|
+
const exchangeArgs = context.exchangeOAuthCode.mock.calls[0][0];
|
|
124
|
+
expect(exchangeArgs.code).toBe('code-1');
|
|
125
|
+
expect(exchangeArgs.redirectUri).toBe(REDIRECT_URI);
|
|
126
|
+
await expect(computeCodeChallenge(exchangeArgs.codeVerifier)).resolves.toBe(
|
|
127
|
+
authorizeUrl.searchParams.get('code_challenge'),
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
expect(context.commitSession).toHaveBeenCalledTimes(1);
|
|
131
|
+
// Nothing about the popup handshake is left in storage.
|
|
132
|
+
expect(sessionStorage.getItem(OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY)).toBeNull();
|
|
133
|
+
expect(sessionStorage.getItem(OXY_OAUTH_STATE_STORAGE_KEY)).toBeNull();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('opens the window itself when the caller did not pre-open one', async () => {
|
|
137
|
+
const context = makeContext();
|
|
138
|
+
const popup = fakePopup();
|
|
139
|
+
const openSpy = jest.spyOn(window, 'open').mockReturnValue(popup as unknown as Window);
|
|
140
|
+
|
|
141
|
+
const pending = startWebOAuthSignIn(context, { redirectUri: REDIRECT_URI });
|
|
142
|
+
const authorizeUrl = await waitForAuthorizeUrl(popup);
|
|
143
|
+
dispatchFromPopup(
|
|
144
|
+
{
|
|
145
|
+
type: OXY_OAUTH_CODE_MESSAGE_TYPE,
|
|
146
|
+
code: 'code-1',
|
|
147
|
+
state: authorizeUrl.searchParams.get('state'),
|
|
148
|
+
},
|
|
149
|
+
popup,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
await expect(pending).resolves.toEqual({ status: 'signed-in' });
|
|
153
|
+
expect(openSpy).toHaveBeenCalledTimes(1);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('falls back to a full-page redirect when the popup was blocked', async () => {
|
|
157
|
+
const context = makeContext();
|
|
158
|
+
|
|
159
|
+
const result = await startWebOAuthSignIn(context, {
|
|
160
|
+
redirectUri: REDIRECT_URI,
|
|
161
|
+
popup: null,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
expect(result).toEqual({ status: 'redirecting', via: 'popup-blocked' });
|
|
165
|
+
expect(mockRedirect).toHaveBeenCalledTimes(1);
|
|
166
|
+
// The fallback is a plain redirect request — no popup channel.
|
|
167
|
+
const url = new URL(mockRedirect.mock.calls[0][0]);
|
|
168
|
+
expect(url.searchParams.get('response_mode')).toBeNull();
|
|
169
|
+
// …and its handshake survives the navigation.
|
|
170
|
+
expect(sessionStorage.getItem(OXY_OAUTH_STATE_STORAGE_KEY)).toBe(
|
|
171
|
+
url.searchParams.get('state'),
|
|
172
|
+
);
|
|
173
|
+
expect(sessionStorage.getItem(OXY_OAUTH_CODE_VERIFIER_STORAGE_KEY)).toBeTruthy();
|
|
174
|
+
expect(context.exchangeOAuthCode).not.toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('falls back to a full-page redirect when the popup cannot be navigated', async () => {
|
|
178
|
+
const context = makeContext();
|
|
179
|
+
const deadPopup: OAuthPopupHandle = {
|
|
180
|
+
closed: false,
|
|
181
|
+
close: jest.fn(),
|
|
182
|
+
get location(): { href: string } {
|
|
183
|
+
throw new Error('window closed');
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const result = await startWebOAuthSignIn(context, {
|
|
188
|
+
redirectUri: REDIRECT_URI,
|
|
189
|
+
popup: deadPopup,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(result).toEqual({ status: 'redirecting', via: 'popup-navigation-failed' });
|
|
193
|
+
expect(mockRedirect).toHaveBeenCalledTimes(1);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('surfaces a typed IdP error and closes the window', async () => {
|
|
197
|
+
const context = makeContext();
|
|
198
|
+
const popup = fakePopup();
|
|
199
|
+
|
|
200
|
+
const pending = startWebOAuthSignIn(context, { redirectUri: REDIRECT_URI, popup });
|
|
201
|
+
const authorizeUrl = await waitForAuthorizeUrl(popup);
|
|
202
|
+
dispatchFromPopup(
|
|
203
|
+
{
|
|
204
|
+
type: OXY_OAUTH_ERROR_MESSAGE_TYPE,
|
|
205
|
+
error: 'access_denied',
|
|
206
|
+
errorDescription: 'user declined',
|
|
207
|
+
state: authorizeUrl.searchParams.get('state'),
|
|
208
|
+
},
|
|
209
|
+
popup,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
await expect(pending).resolves.toEqual({
|
|
213
|
+
status: 'failed',
|
|
214
|
+
reason: 'idp-error',
|
|
215
|
+
description: 'user declined',
|
|
216
|
+
});
|
|
217
|
+
expect(context.exchangeOAuthCode).not.toHaveBeenCalled();
|
|
218
|
+
expect(popup.close).toHaveBeenCalled();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('refuses a result carrying the wrong state', async () => {
|
|
222
|
+
const context = makeContext();
|
|
223
|
+
const popup = fakePopup();
|
|
224
|
+
|
|
225
|
+
const pending = startWebOAuthSignIn(context, { redirectUri: REDIRECT_URI, popup });
|
|
226
|
+
await waitForAuthorizeUrl(popup);
|
|
227
|
+
dispatchFromPopup(
|
|
228
|
+
{ type: OXY_OAUTH_CODE_MESSAGE_TYPE, code: 'code-1', state: 'forged-state' },
|
|
229
|
+
popup,
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
await expect(pending).resolves.toEqual({ status: 'failed', reason: 'state-mismatch' });
|
|
233
|
+
expect(context.exchangeOAuthCode).not.toHaveBeenCalled();
|
|
234
|
+
expect(context.commitSession).not.toHaveBeenCalled();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('reports a timeout when the popup never answers', async () => {
|
|
238
|
+
const context = makeContext();
|
|
239
|
+
const popup = fakePopup();
|
|
240
|
+
|
|
241
|
+
const result = await startWebOAuthSignIn(context, {
|
|
242
|
+
redirectUri: REDIRECT_URI,
|
|
243
|
+
popup,
|
|
244
|
+
timeoutMs: 20,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
expect(result).toEqual({ status: 'timed-out' });
|
|
248
|
+
expect(popup.close).toHaveBeenCalled();
|
|
249
|
+
expect(context.exchangeOAuthCode).not.toHaveBeenCalled();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('reports a failed exchange', async () => {
|
|
253
|
+
const context = makeContext();
|
|
254
|
+
context.exchangeOAuthCode.mockRejectedValue(new Error('invalid_grant'));
|
|
255
|
+
const popup = fakePopup();
|
|
256
|
+
|
|
257
|
+
const pending = startWebOAuthSignIn(context, { redirectUri: REDIRECT_URI, popup });
|
|
258
|
+
const authorizeUrl = await waitForAuthorizeUrl(popup);
|
|
259
|
+
dispatchFromPopup(
|
|
260
|
+
{
|
|
261
|
+
type: OXY_OAUTH_CODE_MESSAGE_TYPE,
|
|
262
|
+
code: 'code-1',
|
|
263
|
+
state: authorizeUrl.searchParams.get('state'),
|
|
264
|
+
},
|
|
265
|
+
popup,
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
await expect(pending).resolves.toEqual({ status: 'failed', reason: 'exchange-failed' });
|
|
269
|
+
expect(context.commitSession).not.toHaveBeenCalled();
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
describe('redirect mode', () => {
|
|
274
|
+
it('hands the top-level document to the IdP and persists the handshake', async () => {
|
|
275
|
+
const context = makeContext({ mode: 'redirect' });
|
|
276
|
+
|
|
277
|
+
const result = await startWebOAuthSignIn(context, { redirectUri: REDIRECT_URI });
|
|
278
|
+
|
|
279
|
+
expect(result).toEqual({ status: 'redirecting', via: 'redirect-mode' });
|
|
280
|
+
expect(mockRedirect).toHaveBeenCalledTimes(1);
|
|
281
|
+
const url = new URL(mockRedirect.mock.calls[0][0]);
|
|
282
|
+
expect(url.searchParams.get('response_mode')).toBeNull();
|
|
283
|
+
expect(url.searchParams.get('client_id')).toBe(CLIENT_ID);
|
|
284
|
+
expect(sessionStorage.getItem(OXY_OAUTH_STATE_STORAGE_KEY)).toBe(
|
|
285
|
+
url.searchParams.get('state'),
|
|
286
|
+
);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('never opens a window, even if one was handed to it', async () => {
|
|
290
|
+
const context = makeContext({ mode: 'redirect' });
|
|
291
|
+
const popup = fakePopup();
|
|
292
|
+
const openSpy = jest.spyOn(window, 'open');
|
|
293
|
+
|
|
294
|
+
await startWebOAuthSignIn(context, { redirectUri: REDIRECT_URI, popup });
|
|
295
|
+
|
|
296
|
+
expect(openSpy).not.toHaveBeenCalled();
|
|
297
|
+
expect(popup.close).toHaveBeenCalledTimes(1);
|
|
298
|
+
expect(popup.location.href).toBe('');
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('aborts instead of redirecting when the handshake cannot be persisted', async () => {
|
|
302
|
+
const context = makeContext({ mode: 'redirect' });
|
|
303
|
+
jest.spyOn(Storage.prototype, 'setItem').mockImplementation(() => {
|
|
304
|
+
throw new Error('storage is disabled');
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const result = await startWebOAuthSignIn(context, { redirectUri: REDIRECT_URI });
|
|
308
|
+
|
|
309
|
+
expect(result).toEqual({ status: 'failed', reason: 'handshake-storage' });
|
|
310
|
+
expect(mockRedirect).not.toHaveBeenCalled();
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
describe('inapplicable configurations', () => {
|
|
315
|
+
it('refuses an identity-bound provider and closes any popup it was given', async () => {
|
|
316
|
+
const context = makeContext({ identityBound: true });
|
|
317
|
+
const popup = fakePopup();
|
|
318
|
+
|
|
319
|
+
const result = await startWebOAuthSignIn(context, { redirectUri: REDIRECT_URI, popup });
|
|
320
|
+
|
|
321
|
+
expect(result).toEqual({ status: 'unsupported', reason: 'identity-bound' });
|
|
322
|
+
expect(popup.close).toHaveBeenCalledTimes(1);
|
|
323
|
+
expect(mockRedirect).not.toHaveBeenCalled();
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('refuses a provider with no clientId', async () => {
|
|
327
|
+
const context = makeContext({ clientId: null });
|
|
328
|
+
|
|
329
|
+
const result = await startWebOAuthSignIn(context, { redirectUri: REDIRECT_URI });
|
|
330
|
+
|
|
331
|
+
expect(result).toEqual({ status: 'unsupported', reason: 'missing-client-id' });
|
|
332
|
+
expect(mockRedirect).not.toHaveBeenCalled();
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
});
|