From 4f5378f7288f1f428a1d46e74daa915f1f1d8651 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 14 Jan 2025 09:34:23 -0300 Subject: [PATCH] Deprecate `useOAuth` in favor of `useSSO` --- .changeset/selfish-worms-switch.md | 4 +- packages/expo/src/hooks/index.ts | 3 +- packages/expo/src/hooks/useOAuth.ts | 119 ++++++++++++++++++++++++++++ packages/expo/src/hooks/useSso.ts | 20 ++--- 4 files changed, 133 insertions(+), 13 deletions(-) create mode 100644 packages/expo/src/hooks/useOAuth.ts diff --git a/.changeset/selfish-worms-switch.md b/.changeset/selfish-worms-switch.md index 75055f9080..3c6c5ce55e 100644 --- a/.changeset/selfish-worms-switch.md +++ b/.changeset/selfish-worms-switch.md @@ -4,5 +4,5 @@ Introduces support for SSO with SAML -- Rename `useOAuth` to `useSso` to support a wider range of protocols -- Update default redirect URI to from `oauth-native-callback` to `sso-native-callback` +- Introduce `useSSO` hook to support a wider range of SSO flow types +- Deprecated `useOAuth` in favor of new `useSSO` hook diff --git a/packages/expo/src/hooks/index.ts b/packages/expo/src/hooks/index.ts index 58aa8b943b..7b2b8313b3 100644 --- a/packages/expo/src/hooks/index.ts +++ b/packages/expo/src/hooks/index.ts @@ -10,5 +10,6 @@ export { useUser, } from '@clerk/clerk-react'; -export * from './useSso'; +export * from './useSSO'; +export * from './useOAuth'; export * from './useAuth'; diff --git a/packages/expo/src/hooks/useOAuth.ts b/packages/expo/src/hooks/useOAuth.ts new file mode 100644 index 0000000000..78cb793c24 --- /dev/null +++ b/packages/expo/src/hooks/useOAuth.ts @@ -0,0 +1,119 @@ +import { useSignIn, useSignUp } from '@clerk/clerk-react'; +import type { OAuthStrategy, SetActive, SignInResource, SignUpResource } from '@clerk/types'; +import * as AuthSession from 'expo-auth-session'; +import * as WebBrowser from 'expo-web-browser'; + +import { errorThrower } from '../utils/errors'; + +export type UseOAuthFlowParams = { + strategy: OAuthStrategy; + redirectUrl?: string; + unsafeMetadata?: SignUpUnsafeMetadata; +}; + +export type StartOAuthFlowParams = { + redirectUrl?: string; + unsafeMetadata?: SignUpUnsafeMetadata; +}; + +export type StartOAuthFlowReturnType = { + createdSessionId: string; + setActive?: SetActive; + signIn?: SignInResource; + signUp?: SignUpResource; + authSessionResult?: WebBrowser.WebBrowserAuthSessionResult; +}; + +/** + * @deprecated Use `useSSO` instead + */ +export function useOAuth(useOAuthParams: UseOAuthFlowParams) { + const { strategy } = useOAuthParams || {}; + if (!strategy) { + return errorThrower.throw('Missing oauth strategy'); + } + + const { signIn, setActive, isLoaded: isSignInLoaded } = useSignIn(); + const { signUp, isLoaded: isSignUpLoaded } = useSignUp(); + + async function startOAuthFlow(startOAuthFlowParams?: StartOAuthFlowParams): Promise { + if (!isSignInLoaded || !isSignUpLoaded) { + return { + createdSessionId: '', + signIn, + signUp, + setActive, + }; + } + + // Create a redirect url for the current platform and environment. + // + // This redirect URL needs to be whitelisted for your Clerk production instance via + // https://clerk.com/docs/reference/backend-api/tag/Redirect-URLs#operation/CreateRedirectURL + // + // For more information go to: + // https://docs.expo.dev/versions/latest/sdk/auth-session/#authsessionmakeredirecturi + const oauthRedirectUrl = + startOAuthFlowParams?.redirectUrl || + useOAuthParams.redirectUrl || + AuthSession.makeRedirectUri({ + path: 'oauth-native-callback', + }); + + await signIn.create({ strategy, redirectUrl: oauthRedirectUrl }); + + const { externalVerificationRedirectURL } = signIn.firstFactorVerification; + + const authSessionResult = await WebBrowser.openAuthSessionAsync( + // @ts-ignore + externalVerificationRedirectURL.toString(), + oauthRedirectUrl, + ); + + // @ts-expect-error + const { type, url } = authSessionResult || {}; + + // TODO: Check all the possible AuthSession results + // https://docs.expo.dev/versions/latest/sdk/auth-session/#returns-7 + if (type !== 'success') { + return { + authSessionResult, + createdSessionId: '', + setActive, + signIn, + signUp, + }; + } + + const params = new URL(url).searchParams; + + const rotatingTokenNonce = params.get('rotating_token_nonce') || ''; + await signIn.reload({ rotatingTokenNonce }); + + const { status, firstFactorVerification } = signIn; + + let createdSessionId = ''; + + if (status === 'complete') { + createdSessionId = signIn.createdSessionId!; + } else if (firstFactorVerification.status === 'transferable') { + await signUp.create({ + transfer: true, + unsafeMetadata: startOAuthFlowParams?.unsafeMetadata || useOAuthParams.unsafeMetadata, + }); + createdSessionId = signUp.createdSessionId || ''; + } + + return { + authSessionResult, + createdSessionId, + setActive, + signIn, + signUp, + }; + } + + return { + startOAuthFlow, + }; +} diff --git a/packages/expo/src/hooks/useSso.ts b/packages/expo/src/hooks/useSso.ts index 6d3eff43a6..2a1d2560e2 100644 --- a/packages/expo/src/hooks/useSso.ts +++ b/packages/expo/src/hooks/useSso.ts @@ -5,19 +5,19 @@ import * as WebBrowser from 'expo-web-browser'; import { errorThrower } from '../utils/errors'; -export type UseSsoParams = { +export type UseSSOParams = { strategy: OAuthStrategy | EnterpriseSSOStrategy; redirectUrl?: string; unsafeMetadata?: SignUpUnsafeMetadata; }; -export type StartSsoParams = { +export type StartSSOParams = { redirectUrl?: string; unsafeMetadata?: SignUpUnsafeMetadata; identifier?: string; }; -export type StartSsoFlowReturnType = { +export type StartSSOFlowReturnType = { createdSessionId: string; setActive?: SetActive; signIn?: SignInResource; @@ -25,8 +25,8 @@ export type StartSsoFlowReturnType = { authSessionResult?: WebBrowser.WebBrowserAuthSessionResult; }; -export function useSso(useSsoParams: UseSsoParams) { - const { strategy } = useSsoParams || {}; +export function useSSO(useSSOParams: UseSSOParams) { + const { strategy } = useSSOParams || {}; if (!strategy) { return errorThrower.throw('Missing strategy'); } @@ -34,7 +34,7 @@ export function useSso(useSsoParams: UseSsoParams) { const { signIn, setActive, isLoaded: isSignInLoaded } = useSignIn(); const { signUp, isLoaded: isSignUpLoaded } = useSignUp(); - async function startFlow(startSsoFlowParams?: StartSsoParams): Promise { + async function startFlow(startSSOFlowParams?: StartSSOParams): Promise { if (!isSignInLoaded || !isSignUpLoaded) { return { createdSessionId: '', @@ -52,13 +52,13 @@ export function useSso(useSsoParams: UseSsoParams) { // For more information go to: // https://docs.expo.dev/versions/latest/sdk/auth-session/#authsessionmakeredirecturi const oauthRedirectUrl = - startSsoFlowParams?.redirectUrl || - useSsoParams.redirectUrl || + startSSOFlowParams?.redirectUrl || + useSSOParams.redirectUrl || AuthSession.makeRedirectUri({ path: 'sso-native-callback', }); - await signIn.create({ strategy, redirectUrl: oauthRedirectUrl, identifier: startSsoFlowParams?.identifier }); + await signIn.create({ strategy, redirectUrl: oauthRedirectUrl, identifier: startSSOFlowParams?.identifier }); const { externalVerificationRedirectURL } = signIn.firstFactorVerification; @@ -100,7 +100,7 @@ export function useSso(useSsoParams: UseSsoParams) { } else if (firstFactorVerification.status === 'transferable') { await signUp.create({ transfer: true, - unsafeMetadata: startSsoFlowParams?.unsafeMetadata || useSsoParams.unsafeMetadata, + unsafeMetadata: startSSOFlowParams?.unsafeMetadata || useSSOParams.unsafeMetadata, }); createdSessionId = signUp.createdSessionId || ''; }