Skip to main content
Docs

Build a custom flow for authenticating with enterprise connections

Warning

This guide is for users who want to build a custom user interface using the Clerk API. To use a prebuilt UI, use the Account Portal pages or prebuilt components.

Before you start

You must configure your application instance through the Clerk Dashboard for the enterprise connection(s) that you want to use. Visit the appropriate guide for your platform to learn how to configure your instance.

Create the sign-up and sign-in flow

The following example will both sign up and sign in users, eliminating the need for a separate sign-up page. However, if you want to have separate sign-up and sign-in pages, the sign-up and sign-in flows are equivalent, meaning that all you have to do is swap out the SignIn object for the SignUp object using the useSignUp() hook.

The following example:

  1. Accesses the SignInJavaScript Icon object using the useSignIn() hook.
  2. Starts the authentication process by calling SignIn.sso(params)JavaScript Icon. This method requires the following params:
    • redirectUrl: The URL that the browser will be redirected to once the user authenticates with the identity provider if no additional requirements are needed, and a session has been created
    • redirectCallbackUrl: The URL that the browser will be redirected to once the user authenticates with the identity provider if additional requirements are needed
  3. Creates a route at the URL that the redirectCallbackUrl param points to. The following example re-uses the /sign-in route, which should be written to handle when a sign-in attempt is in a non-complete status such as needs_second_factor.
app/sign-in/page.tsx
'use client'

import * as React from 'react'
import { useSignIn } from '@clerk/nextjs'

export default function Page() {
  const { signIn, fetchStatus } = useSignIn()

  const signInWithEnterpriseSSO = async (formData: FormData) => {
    const email = formData.get('email') as string

    await signIn.sso({
      identifier: email,
      strategy: 'enterprise_sso',
      // The URL that the user will be redirected to if additional requirements are needed
      redirectCallbackUrl: '/sign-in',
      redirectUrl: '/',
    })
  }

  return (
    <form action={signInWithEnterpriseSSO}>
      <input id="email" type="email" name="email" placeholder="Enter email address" />
      <button type="submit" disabled={fetchStatus === 'fetching'}>
        Sign in with Enterprise SSO
      </button>
    </form>
  )
}

The following example will both sign up and sign in users, eliminating the need for a separate sign-up page.

The following example:

  1. Uses the useSSO()Expo Icon hook to access the startSSOFlow() method.
  2. Calls the startSSOFlow() method with the strategy param set to enterprise_sso and the identifier param set to the user's email address that they provided. The optional redirect_url param is also set in order to redirect the user once they finish the authentication flow.
    • If authentication is successful, the setActive() method is called to set the active session with the new createdSessionId.
    • If authentication is not successful, you can handle the missing requirements, such as MFA, using the signInJavaScript Icon or signUpJavaScript Icon 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.
app/(auth)/sign-in.tsx
import React, { useEffect, useState } from 'react'
import * as WebBrowser from 'expo-web-browser'
import * as AuthSession from 'expo-auth-session'
import { useSSO } from '@clerk/clerk-expo'
import { View, Button, TextInput, Platform } 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
    if (Platform.OS !== 'android') return
    void WebBrowser.warmUpAsync()
    return () => {
      // Cleanup: closes browser when component unmounts
      void WebBrowser.coolDownAsync()
    }
  }, [])
}

// Handle any pending authentication sessions
WebBrowser.maybeCompleteAuthSession()

export default function Page() {
  useWarmUpBrowser()

  const [email, setEmail] = useState('')

  // Use the `useSSO()` hook to access the `startSSOFlow()` method
  const { startSSOFlow } = useSSO()

  const onPress = async () => {
    try {
      // Start the authentication process by calling `startSSOFlow()`
      const { createdSessionId, setActive, signIn, signUp } = await startSSOFlow({
        strategy: 'enterprise_sso',
        identifier: email,
        // For web, defaults to current path
        // For native, you must pass a scheme, like AuthSession.makeRedirectUri({ scheme, path })
        // For more info, see https://docs.expo.dev/versions/latest/sdk/auth-session/#authsessionmakeredirecturioptions
        redirectUrl: AuthSession.makeRedirectUri(),
      })

      // If sign in was successful, set the active session
      if (createdSessionId) {
        setActive!({
          session: createdSessionId,
          navigate: async ({ session }) => {
            if (session?.currentTask) {
              // Check for tasks and navigate to custom UI to help users resolve them
              // See https://clerk.com/docs/guides/development/custom-flows/overview#session-tasks
              console.log(session?.currentTask)
              return
            }

            router.push('/')
          },
        })
      } else {
        // If there is no `createdSessionId`,
        // there are missing requirements, such as MFA
        // Use the `signIn` or `signUp` returned from `startSSOFlow`
        // to handle next steps
      }
    } catch (err) {
      // See https://clerk.com/docs/guides/development/custom-flows/error-handling
      // for more info on error handling
      console.error(JSON.stringify(err, null, 2))
    }
  }

  return (
    <View>
      <TextInput
        value={email}
        onChangeText={setEmail}
        placeholder="Enter email"
        placeholderTextColor="#666666"
      />
      <Button title="Sign in with SAML" onPress={onPress} />
    </View>
  )
}

Feedback

What did you think of this content?

Last updated on