From 2e34970eed9cc1a1bc55a1e05e0a76011a8fe17c Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Wed, 15 Jan 2025 22:23:00 -0300 Subject: [PATCH 01/25] Add bare draft for `useSSO` docs --- docs/references/expo/use-sso.mdx | 68 ++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 docs/references/expo/use-sso.mdx diff --git a/docs/references/expo/use-sso.mdx b/docs/references/expo/use-sso.mdx new file mode 100644 index 0000000000..c62a12c808 --- /dev/null +++ b/docs/references/expo/use-sso.mdx @@ -0,0 +1,68 @@ +--- +title: '`useSSO()`' +description: Clerk's useSSO() hook is used to create a new SSO flow. +--- + +The `useSSO()` hook is used to create a new SSO flow. It can be used in both web and native apps. + +## Parameters + + + - `strategy` + - [`OAuthStrategy`](/docs/references/javascript/types/oauth#o-auth-strategy) + - [`EnterpriseSSOStrategy`](/docs/references/javascript/types/oauth#o-auth-strategy) + + The strategy corresponding to the SSO provider. For example: `oauth_facebook`, `oauth_github`, `enterprise_sso`, etc. + + --- + + - `redirectUrl?` + - `string` + + The URL or path to redirect to after the SSO flow is complete. + + --- + + - `unsafeMetadata?` + - [`SignUpUnsafeMetadata`](/docs/references/javascript/types/metadata#sign-up-unsafe-metadata) + + Unsafe metadata to be passed to the SSO provider. + + +## Returns + +The `useSSO()` hook returns the `startFlow()` method, which you can use to initiate the SSO flow. + +The `startFlow()` method has the following signature: + +```ts +const startFlow: ( + startSSOFlowParams?: StartSSOFlowParams, +) => Promise +``` + +It accepts the following parameters (`StartSSOFlowParams`): + + + - `redirectUrl?` + - `string` + + The URL or path to redirect to after the OAuth flow is complete. + + --- + + - `unsafeMetadata?` + - [`SignUpUnsafeMetadata`](/docs/references/javascript/types/metadata#sign-up-unsafe-metadata) + + Unsafe metadata to be passed to the OAuth provider. + + --- + - `identifier?` + - string + + The [identifier](/docs/authentication/configuration/sign-up-sign-in-options#identifier) of the user. + + +## How to use the `useSSO()` hook + + From 1e71a1a2808488a9ae6effba39c6acfc9b34cdd0 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Wed, 15 Jan 2025 22:44:00 -0300 Subject: [PATCH 02/25] Update snippets --- docs/_partials/expo/oauth-custom-flow.mdx | 58 -------- docs/references/expo/use-sso.mdx | 166 +++++++++++++++++++--- 2 files changed, 146 insertions(+), 78 deletions(-) delete mode 100644 docs/_partials/expo/oauth-custom-flow.mdx diff --git a/docs/_partials/expo/oauth-custom-flow.mdx b/docs/_partials/expo/oauth-custom-flow.mdx deleted file mode 100644 index 81f09bee6c..0000000000 --- a/docs/_partials/expo/oauth-custom-flow.mdx +++ /dev/null @@ -1,58 +0,0 @@ -The following example demonstrates how to create a custom OAuth sign-in flow for [Google accounts](/docs/authentication/social-connections/google). - -```tsx {{ filename: 'app/(auth)/sign-in.tsx', collapsible: true }} -import React from 'react' -import * as WebBrowser from 'expo-web-browser' -import { Text, View, Button } from 'react-native' -import { Link } from 'expo-router' -import { useOAuth } from '@clerk/clerk-expo' -import * as Linking from 'expo-linking' - -export const useWarmUpBrowser = () => { - React.useEffect(() => { - // Warm up the android browser to improve UX - // https://docs.expo.dev/guides/authentication/#improving-user-experience - void WebBrowser.warmUpAsync() - return () => { - void WebBrowser.coolDownAsync() - } - }, []) -} - -WebBrowser.maybeCompleteAuthSession() - -export default function Page() { - useWarmUpBrowser() - - const { startOAuthFlow } = useOAuth({ strategy: 'oauth_google' }) - - const onPress = React.useCallback(async () => { - try { - const { createdSessionId, signIn, signUp, setActive } = await startOAuthFlow({ - redirectUrl: Linking.createURL('/dashboard', { scheme: 'myapp' }), - }) - - // If sign in was successful, set the active session - if (createdSessionId) { - setActive!({ session: createdSessionId }) - } else { - // Use signIn or signUp returned from startOAuthFlow - // for next steps, such as MFA - } - } catch (err) { - // See https://clerk.com/docs/custom-flows/error-handling - // for more info on error handling - console.error(JSON.stringify(err, null, 2)) - } - }, []) - - return ( - - - Home - - ) } ``` - ```jsx {{ filename: 'app/sign-up/sso-callback/page.tsx' }} + ```jsx {{ filename: 'app/sign-in/sso-callback/page.tsx' }} import { AuthenticateWithRedirectCallback } from '@clerk/nextjs' export default function Page() { @@ -78,10 +80,16 @@ The following example shows two files: ``` + + + + ## Enterprise account transfer flows +{/* TODO(Laura): I believe this is built in with Expo's `useSSO()` hook, so I don't think we need to add Expo example here. Is this correct? */} + It is common for users who are authenticating with an enterprise account to use a sign-in button when they mean to sign-up, and vice versa. For those cases, the `SignIn` and `SignUp` objects have a `transferable` status that indicates whether the user can be transferred to the other flow. **If you would like to keep your sign-in and sign-up flows completely separate, then you can skip this section.** @@ -90,7 +98,7 @@ The following example demonstrates how to handle these cases in your sign-in flo - ```tsx {{ filename: 'app/sign-in/[[...sign-in]]/page.tsx', collapsible: true }} + ```tsx {{ filename: 'app/sign-in/page.tsx', collapsible: true }} 'use client' import * as React from 'react' @@ -111,7 +119,7 @@ The following example demonstrates how to handle these cases in your sign-in flo .authenticateWithRedirect({ identifier: email, strategy: 'enterprise_sso', - redirectUrl: '/sign-up/sso-callback', + redirectUrl: '/sign-in/sso-callback', redirectUrlComplete: '/', }) .then((res) => { diff --git a/docs/custom-flows/oauth-connections.mdx b/docs/custom-flows/oauth-connections.mdx index 3456fe07f6..880d9a6462 100644 --- a/docs/custom-flows/oauth-connections.mdx +++ b/docs/custom-flows/oauth-connections.mdx @@ -39,11 +39,21 @@ When using OAuth, the sign-up and sign-in flows are equivalent. if (!signIn) return null const signInWith = (strategy: OAuthStrategy) => { - return signIn.authenticateWithRedirect({ - strategy, - redirectUrl: '/sign-up/sso-callback', - redirectUrlComplete: '/', - }) + return signIn + .authenticateWithRedirect({ + strategy, + redirectUrl: '/sign-up/sso-callback', + redirectUrlComplete: '/', + }) + .then((res) => { + console.log(res) + }) + .catch((err: any) => { + // See https://clerk.com/docs/custom-flows/error-handling + // for more info on error handling + console.log(err.errors) + console.error(err, null, 2) + }) } // Render a button for each supported OAuth provider @@ -70,26 +80,6 @@ When using OAuth, the sign-up and sign-in flows are equivalent. - ### Before you start - - Install `expo-linking` to handle linking. - - - ```bash {{ filename: 'terminal' }} - npm install expo-linking - ``` - - ```bash {{ filename: 'terminal' }} - yarn add expo-linking - ``` - - ```bash {{ filename: 'terminal' }} - pnpm add expo-linking - ``` - - - ### Build the flow - diff --git a/docs/references/expo/overview.mdx b/docs/references/expo/overview.mdx index cdf3aa3bc5..36a05e22c0 100644 --- a/docs/references/expo/overview.mdx +++ b/docs/references/expo/overview.mdx @@ -11,20 +11,23 @@ The Expo SDK gives you access to the following resources: ### Clerk hooks {{ id: 'hooks' }} -The following hooks are available for both **native** and **web** apps: +The Expo SDK provides the following hooks: -- All React hooks are available. See [the React docs](/docs/references/react/overview){{ target: '_blank' }} for more information. - [`useSSO()`](/docs/references/expo/use-sso) - [`useLocalCredentials()`](/docs/references/expo/use-local-credentials) +Because the Expo SDK is built on top of the Clerk React SDK, you can use the hooks that the React SDK provides. These hooks include access to the [`Clerk`](/docs/references/javascript/clerk/clerk) object, [`User` object](/docs/references/javascript/user/user), [`Organization` object](/docs/references/javascript/organization/organization), and a set of useful helper methods for signing in and signing up. + + + ### Clerk components {{ id: 'components' }} - **Native** apps: - - [``](/docs/components/control/clerk-loaded){{ target: '_blank' }} - - [``](/docs/components/control/clerk-loading){{ target: '_blank' }} - - [``](/docs/components/control/signed-in){{ target: '_blank' }} - - [``](/docs/components/control/signed-out){{ target: '_blank' }} - - [``](/docs/components/protect){{ target: '_blank' }} + - [``](/docs/components/control/clerk-loaded) + - [``](/docs/components/control/clerk-loading) + - [``](/docs/components/control/signed-in) + - [``](/docs/components/control/signed-out) + - [``](/docs/components/protect) - **Web** apps: - All Clerk components are available. See [the component docs](/docs/components/overview) for more information. diff --git a/docs/references/expo/use-oauth.mdx b/docs/references/expo/use-oauth.mdx index 9fb23a157e..b0f3650d0b 100644 --- a/docs/references/expo/use-oauth.mdx +++ b/docs/references/expo/use-oauth.mdx @@ -1,5 +1,5 @@ --- -title: useOAuth() +title: useOAuth() (deprecated) description: Clerk's useOAuth() hook is used to create a new OAuth flow. --- @@ -61,4 +61,61 @@ It accepts the following parameters (`StartOAuthFlowParams`): ## How to use the `useOAuth()` hook - +The following example demonstrates how to create a custom OAuth sign-in flow for [Google accounts](/docs/authentication/social-connections/google). + +```tsx {{ filename: 'app/(auth)/sign-in.tsx', collapsible: true }} +import React from 'react' +import * as WebBrowser from 'expo-web-browser' +import { Text, View, Button } from 'react-native' +import { Link } from 'expo-router' +import { useOAuth } from '@clerk/clerk-expo' +import * as Linking from 'expo-linking' + +export const useWarmUpBrowser = () => { + React.useEffect(() => { + // Warm up the android browser to improve UX + // https://docs.expo.dev/guides/authentication/#improving-user-experience + void WebBrowser.warmUpAsync() + return () => { + void WebBrowser.coolDownAsync() + } + }, []) +} + +WebBrowser.maybeCompleteAuthSession() + +export default function Page() { + useWarmUpBrowser() + + const { startOAuthFlow } = useOAuth({ strategy: 'oauth_google' }) + + const onPress = React.useCallback(async () => { + try { + const { createdSessionId, signIn, signUp, setActive } = await startOAuthFlow({ + redirectUrl: Linking.createURL('/dashboard', { scheme: 'myapp' }), + }) + + // If sign in was successful, set the active session + if (createdSessionId) { + setActive!({ session: createdSessionId }) + } else { + // Use signIn or signUp returned from startOAuthFlow + // for next steps, such as MFA + } + } catch (err) { + // See https://clerk.com/docs/custom-flows/error-handling + // for more info on error handling + console.error(JSON.stringify(err, null, 2)) + } + }, []) + + return ( + + + Home + + - - ) - } - ``` - - - -[sign-in-redirect]: /docs/references/javascript/sign-in/authenticate-with#authenticate-with-redirect - -[sign-up-redirect]: /docs/references/javascript/sign-up/authenticate-with#authenticate-with-redirect diff --git a/docs/custom-flows/oauth-connections.mdx b/docs/custom-flows/oauth-connections.mdx index 880d9a6462..c6454ab04d 100644 --- a/docs/custom-flows/oauth-connections.mdx +++ b/docs/custom-flows/oauth-connections.mdx @@ -7,23 +7,13 @@ description: Learn how to use the Clerk API to build a custom sign-up and sign-i ## Before you start -You must configure your application instance through the Clerk Dashboard for the social connection(s) that you want to use, as described at [the top of the OAuth guide](/docs/authentication/social-connections/oauth#configuration). +You must configure your application instance through the Clerk Dashboard for the social connection(s) that you want to use. Visit [the appropriate guide for your platform](/docs/authentication/social-connections/overview) to learn how to configure your instance. ## Create the sign-up and sign-in flow -When using OAuth, the sign-up and sign-in flows are equivalent. - - A successful OAuth flow consists of the following steps: - - 1. Start the OAuth flow by calling [`SignIn.authenticateWithRedirect(params)`](/docs/references/javascript/sign-in/authenticate-with#authenticate-with-redirect) or [`SignUp.authenticateWithRedirect(params)`](/docs/references/javascript/sign-up/authenticate-with#authenticate-with-redirect). Both of these methods require a `redirectUrl` param, which is the URL that the browser will be redirected to once the user authenticates with the OAuth provider. - 1. Create a route at the URL that the `redirectUrl` param points to. The following example names this route `/sso-callback`. This route should call the [`Clerk.handleRedirectCallback()`](/docs/references/javascript/clerk/handle-navigation#handle-redirect-callback) method or simply render the prebuilt [``](/docs/components/control/authenticate-with-callback) component. - - The following example shows two files: - - 1. The sign-in page where the user can start the OAuth flow. - 1. The SSO callback page where the OAuth flow is completed. + ```tsx {{ filename: 'app/sign-in/page.tsx' }} @@ -70,8 +60,8 @@ When using OAuth, the sign-up and sign-in flows are equivalent. import { AuthenticateWithRedirectCallback } from '@clerk/nextjs' export default function SSOCallback() { - // Handle the redirect flow by rendering the - // prebuilt AuthenticateWithRedirectCallback component. + // Handle the redirect flow by calling the Clerk.handleRedirectCallback() method + // or rendering the prebuilt component. // This is the final step in the custom OAuth flow. return } @@ -148,108 +138,3 @@ When using OAuth, the sign-up and sign-in flows are equivalent. ``` - -## OAuth account transfer flows - -It is common for users who are authenticating with OAuth to use a sign-in button when they mean to sign-up, and vice versa. For those cases, the `SignIn` and `SignUp` objects have a `transferable` status that indicates whether the user can be transferred to the other flow. - -**If you would like to keep your sign-in and sign-up flows completely separate, then you can skip this section.** - -The following example demonstrates how to handle these cases in your sign-in flow. To apply the same logic to the sign-up flow, simply replace `signIn.authenticateWithRedirect()` with `signUp.authenticateWithRedirect()` in your code. - - - - ```tsx {{ filename: 'app/sign-in/page.tsx', collapsible: true }} - 'use client' - - import * as React from 'react' - import { OAuthStrategy } from '@clerk/types' - import { useSignIn, useSignUp } from '@clerk/nextjs' - - export default function OauthSignIn() { - const { signIn } = useSignIn() - const { signUp, setActive } = useSignUp() - - if (!signIn || !signUp) return null - - const signInWith = (strategy: OAuthStrategy) => { - return signIn.authenticateWithRedirect({ - strategy, - redirectUrl: '/sign-up/sso-callback', - redirectUrlComplete: '/', - }) - } - - async function handleSignIn(strategy: OAuthStrategy) { - if (!signIn || !signUp) return null - - // If the user has an account in your application, but does not yet - // have an OAuth account connected to it, you can transfer the OAuth - // account to the existing user account. - const userExistsButNeedsToSignIn = - signUp.verifications.externalAccount.status === 'transferable' && - signUp.verifications.externalAccount.error?.code === 'external_account_exists' - - if (userExistsButNeedsToSignIn) { - const res = await signIn.create({ transfer: true }) - - if (res.status === 'complete') { - setActive({ - session: res.createdSessionId, - }) - } - } - - // If the user has an OAuth account but does not yet - // have an account in your app, you can create an account - // for them using the OAuth information. - const userNeedsToBeCreated = signIn.firstFactorVerification.status === 'transferable' - - if (userNeedsToBeCreated) { - const res = await signUp.create({ - transfer: true, - }) - - if (res.status === 'complete') { - setActive({ - session: res.createdSessionId, - }) - } - } else { - // If the user has an account in your application - // and has an OAuth account connected to it, you can sign them in. - signInWith(strategy) - } - } - - // Render a button for each supported OAuth provider - // you want to add to your app. This example uses only Google. - return ( -
- -
- ) - } - ``` -
- - - ```swift {{ filename: 'OAuthView.swift', collapsible: true }} - // If a user attempted to sign in but doesn't have an account, - // Clerk will try to handle the transfer to sign up for you. - // If that's not possible (i.e. maybe signing up requires a CAPTCHA Token), - // authenticateWithRedirect() will return the original sign in to you - // so you can manually perform the transfer to sign up. - - // Check if the sign in needs to be transferred to a sign up - if let signIn = externalAuthResult?.signIn, - signIn.firstFactorVerification?.status == .transferable { - // Create a new sign up with the strategy `.transfer` - let signUp = try await SignUp.create( - strategy: .transfer, - captchaToken: "TOKEN" - ) - } - ``` - -
diff --git a/docs/references/expo/use-sso.mdx b/docs/references/expo/use-sso.mdx index 9606fae5b0..26964b5938 100644 --- a/docs/references/expo/use-sso.mdx +++ b/docs/references/expo/use-sso.mdx @@ -23,7 +23,7 @@ function startSSOFlow(startSSOFlowParams: StartSSOFlowParams): Promise - `strategy` - - `'oauth_'` | `'enterprise_sso'` + - [`OAuthStrategy`](/docs/references/javascript/types/sso#oauth-strategy) | [`EnterpriseSSOStrategy`](/docs/references/javascript/types/sso#enterprise-sso-strategy) The strategy to use for authentication. The following strategies are supported: @@ -86,10 +86,7 @@ function startSSOFlow(startSSOFlowParams: StartSSOFlowParams): Promise - -### With Enterprise SSO - - +- [OAuth custom flow](/docs/references/expo/oauth-custom-flow) +- [Enterprise SSO custom flow](/docs/references/expo/enterprise-sso-custom-flow) From 5a8a7f9b7cd37f3fb227eb840a8324ff99f69888 Mon Sep 17 00:00:00 2001 From: Laura Beatris <48022589+LauraBeatris@users.noreply.github.com> Date: Tue, 4 Feb 2025 18:59:30 -0300 Subject: [PATCH 24/25] Add reference for `authSessionResult` --- docs/references/expo/use-sso.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/references/expo/use-sso.mdx b/docs/references/expo/use-sso.mdx index 26964b5938..763ec8df30 100644 --- a/docs/references/expo/use-sso.mdx +++ b/docs/references/expo/use-sso.mdx @@ -60,7 +60,7 @@ function startSSOFlow(startSSOFlowParams: StartSSOFlowParams): Promise Date: Tue, 4 Feb 2025 19:07:05 -0300 Subject: [PATCH 25/25] Fix linting and formatting --- docs/_partials/expo/oauth-custom-flow.mdx | 39 +++++++++++------------ docs/references/expo/use-sso.mdx | 4 +-- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/docs/_partials/expo/oauth-custom-flow.mdx b/docs/_partials/expo/oauth-custom-flow.mdx index 98d2d44682..242c7840dc 100644 --- a/docs/_partials/expo/oauth-custom-flow.mdx +++ b/docs/_partials/expo/oauth-custom-flow.mdx @@ -8,43 +8,42 @@ The following example: - If authentication is not successful, you can handle the missing requirements, such as MFA, using the [`signIn`](/docs/references/javascript/sign-in/sign-in) or [`signUp`](/docs/references/javascript/sign-up/sign-up) object returned from `startSSOFlow()`, depending on if the user is signing in or signing up. These objects include properties, like `status`, that can be used to determine the next steps. See the respective linked references for more information. ```tsx {{ filename: 'app/(auth)/sign-in.tsx', collapsible: true }} -import React, { useCallback, useEffect } from "react"; -import * as WebBrowser from "expo-web-browser"; -import { useSSO } from "@clerk/clerk-expo"; -import { View, Button } from "react-native"; +import React, { useCallback, useEffect } from 'react' +import * as WebBrowser from 'expo-web-browser' +import { useSSO } from '@clerk/clerk-expo' +import { View, Button } from 'react-native' export const useWarmUpBrowser = () => { useEffect(() => { // Preloads the browser for Android devices to reduce authentication load time // See: https://docs.expo.dev/guides/authentication/#improving-user-experience - void WebBrowser.warmUpAsync(); + void WebBrowser.warmUpAsync() return () => { // Cleanup: closes browser when component unmounts - void WebBrowser.coolDownAsync(); - }; - }, []); -}; + void WebBrowser.coolDownAsync() + } + }, []) +} // Handle any pending authentication sessions -WebBrowser.maybeCompleteAuthSession(); +WebBrowser.maybeCompleteAuthSession() export default function Page() { - useWarmUpBrowser(); + useWarmUpBrowser() // Use the `useSSO()` hook to access the `startSSOFlow()` method - const { startSSOFlow } = useSSO(); + const { startSSOFlow } = useSSO() const onPress = useCallback(async () => { try { // Start the authentication process by calling `startSSOFlow()` - const { createdSessionId, setActive, signIn, signUp } = - await startSSOFlow({ - strategy: "oauth_google", - }); + const { createdSessionId, setActive, signIn, signUp } = await startSSOFlow({ + strategy: 'oauth_google', + }) // If sign in was successful, set the active session if (createdSessionId) { - setActive!({ session: createdSessionId }); + setActive!({ session: createdSessionId }) } else { // If there is no `createdSessionId`, // there are missing requirements, such as MFA @@ -54,14 +53,14 @@ export default function Page() { } catch (err) { // See https://clerk.com/docs/custom-flows/error-handling // for more info on error handling - console.error(JSON.stringify(err, null, 2)); + console.error(JSON.stringify(err, null, 2)) } - }, []); + }, []) return (