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

When using Enterprise SSO, the sign-in and sign-up flows are equivalent. A successful Enterprise SSO flow consists of the following steps:

  1. Start the Enterprise SSO flow by calling SignIn.authenticateWithRedirect(params) or SignUp.authenticateWithRedirect(params). 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 identity provider.
  2. Create a route at the URL that the redirectUrl param points to. The following example names this route /sso-callback. This route should either call the Clerk.handleRedirectCallback() method or render the prebuilt <AuthenticateWithRedirectCallback/> component.

The following example shows two files:

  1. The sign-in page where the user can start the Enterprise SSO flow.
  2. The SSO callback page where the flow is completed.
app/sign-in/page.tsx
'use client'

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

export default function Page() {
  const { signIn } = useSignIn()
  const { signUp } = useSignUp()

  if (!signIn || !signUp) return null

  const signInWithEnterpriseSSO = (e: React.FormEvent) => {
    e.preventDefault()

    const email = (e.target as HTMLFormElement).email.value

    signIn
      .authenticateWithRedirect({
        identifier: email,
        strategy: 'enterprise_sso',
        redirectUrl: '/sso-callback',
        redirectUrlComplete: '/',
      })
      .then((res) => {
        console.log(res)
      })
      .catch((err: any) => {
        console.log(err.errors)
        console.error(err, null, 2)
      })
  }

  return (
    <form onSubmit={(e) => signInWithEnterpriseSSO(e)}>
      <input type="email" name="email" placeholder="Enter email address" />
      <button>Sign in with Enterprise SSO</button>
    </form>
  )
}
app/sign-up/sso-callback/page.tsx
import { AuthenticateWithRedirectCallback } from '@clerk/nextjs'

export default function Page() {
  // Handle the redirect flow by rendering the
  // prebuilt AuthenticateWithRedirectCallback component.
  // This is the final step in the custom Enterprise SSO flow.
  return <AuthenticateWithRedirectCallback />
}

Enterprise account transfer flows

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.

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.

app/sign-in/[[...sign-in]]/page.tsx
'use client'

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

export default function Page() {
  const { signIn } = useSignIn()
  const { signUp, setActive } = useSignUp()

  if (!signIn || !signUp) return null

  const signInWithEnterpriseSSO = (e: React.FormEvent) => {
    e.preventDefault()

    const email = (e.target as HTMLFormElement).email.value

    signIn
      .authenticateWithRedirect({
        identifier: email,
        strategy: 'enterprise_sso',
        redirectUrl: '/sso-callback',
        redirectUrlComplete: '/',
      })
      .then((res) => {
        console.log(res)
      })
      .catch((err: any) => {
        console.log(err.errors)
        console.error(err, null, 2)
      })
  }

  async function handleSignIn(e: React.FormEvent) {
    if (!signIn || !signUp) return null

    // If the user has an account in your application, but does not yet
    // have a enterprise account connected to it, you can transfer the enterprise
    // 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 a enterprise account but does not yet
    // have an account in your app, you can create an account
    // for them using the enterprise account's 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 enterprise account connected to it, you can sign them in.
      signInWithEnterpriseSSO(e)
    }
  }

  return (
    <form onSubmit={(e) => handleSignIn(e)}>
      <input type="email" name="email" placeholder="Enter email address" />
      <button>Sign in with Enterprise SSO</button>
    </form>
  )
}

Feedback

What did you think of this content?

Last updated on