Skip to main content
Docs

Steps after sign-up/sign-in (after-auth flows)

You can configure your application to require users to complete specific actions after signing up or signing in. Clerk refers to these steps as after-auth flows. Once you have enabled an after-auth flow through the Clerk Dashboard, no further configuration or code is required. After-auth flows are embedded within the <SignIn /> and <SignUp> components out-of-the-box. Currently, you can't replace or move the built-in after-auth flows to custom pages, but we're working on adding this capability.

Available after-auth flows

The following after-auth flows are available:

How after-auth flows work

Once you have enabled an after-auth flow through the Clerk Dashboard, the sign-up/sign-in process looks like this:

  1. Users will use the <SignUp /> and/or <SignIn /> components to sign up or sign in. Once they have completed the authentication flow, such as entering their email address and password:
    • Their session will be in a pending state and their session will be treated as signed-out. This means that Clerk's authentication context will treat the user's session as signed-out and they will not be able to access protected content or routes.
    • They will be prompted to complete the after-auth flow.
  2. Once they have completed the after-auth flow, their session will move from pending to a signed-in state and they will be able to access protected content or routes.

pending sessions

A user's authentication state can be considered signed-in, signed-out, or pending. By default, pending sessions are treated as signed-out across Clerk's authentication context. This effects features that rely on the user's authentication state, like control components, the useAuth() hook, and server utilities that access the auth object, such as getAuth() or request.auth. However, you can set treatPendingAsSignedOut to false to treat pending sessions as signed-in. See the following sections for more information.

Control components

Control components manage authentication-related behaviors in your application. They handle tasks such as controlling content visibility based on user authentication status, managing loading states during authentication processes, and redirecting users to appropriate pages.

Control components accept a treatPendingAsSignedOut prop, which defaults to true. You can pass false to treat pending sessions as signed-in.

The following examples demonstrate how different control components respond to pending sessions.

The <Protect /> component protects content or even entire routes based on a user's authentication state. It will render its children if the user's state is signed-in. It accepts a fallback prop that will be rendered if the user's state is signed-out.

In the following example, if the user's state is pending, they will see the fallback content because, by default, pending sessions are treated as signed-out.

export default function Page() {
  return (
    <Protect fallback={<p>Signed-out and pending users can see this.</p>}>
      <p>Only signed-in users can see this.</p>
    </Protect>
  )
}

In the following example, if the user's state is pending, they will see the protected content because treatPendingAsSignedOut is set to false.

export default function Page() {
  return (
    <Protect
      treatPendingAsSignedOut={false}
      fallback={<p>Users that are signed-out can see this.</p>}
    >
      <p>Users that are signed-in or pending can see this.</p>
    </Protect>
  )
}

The <SignedOut /> component renders its children if the user's authentication state is signed-out.

In the following example, if the user's state is pending, they will see the content of the component because, by default, pending sessions are treated as signed-out.

export default function Page() {
  return (
    <SignedOut>
      <p>Users that are signed-out or pending will see this.</p>
    </SignedOut>
  )
}

In the following example, if the user's state is pending, they won't see the content of the component because treatPendingAsSignedOut is set to false.

export default function Page() {
  return (
    <SignedOut treatPendingAsSignedOut={false}>
      <p>Users that are signed-out will see this.</p>
    </SignedOut>
  )
}

The <SignedIn /> component renders its children if the user's authentication state is signed-in.

In the following example, if the user's state is pending, they won't see the content of the component because, by default, pending sessions are treated as signed-out.

export default function Page() {
  return (
    <SignedIn>
      <p>Users that are signed-in will see this.</p>
    </SignedIn>
  )
}

In the following example, if the user's state is pending, they will see the content of the component because treatPendingAsSignedOut is set to false.

export default function Page() {
  return (
    <SignedIn treatPendingAsSignedOut={false}>
      <p>Users that are signed-in or pending will see this.</p>
    </SignedIn>
  )
}

Other Clerk utilities

Once a user's session is in a pending state, Clerk's authentication context treats the session as signed-out by default. For example, the useAuth() hook and any helpers that access the , such as getAuth() or request.auth, will return null if the user has a pending session. However, most of these utilities accept a treatPendingAsSignedOut option that defaults to true. You can pass false to treat pending sessions as signed-in.

Example: Disable personal workspaces

For example, if you've enabled the disable personal workspaces after-auth flow in the Clerk Dashboard, your users will be required to select or create an organization after signing up or signing in. If they haven't, their session will be in a pending state. Any pages that are protected using Clerk's protection utilities will treat the user's session as signed-out.

For useAuth(), userId and isSignedIn will be null if the user has a pending session.

export default function Dashboard() {
  const { isSignedIn, userId } = useAuth()

  if (!userId) {
    return (
      <p>
        User has no session or a pending session. They either need to sign in, or they need to
        fulfill the after-auth requirements by selecting or creating an organization.
      </p>
    )
  }

  return (
    <p>
      <p>User has a valid session and {orgId} is defined</p>
    </p>
  )
}

For auth().userId, it would return null if the user has a pending session.

app/page.tsx
import { auth } from '@clerk/nextjs/server'

export default async function Page() {
  const { userId, orgId } = await auth()

  if (!userId) {
    return (
      <p>
        User has no session or a pending session. They either need to sign in, or they need to
        fulfill the after-auth requirements by selecting or creating an organization.
      </p>
    )
  }

  return (
    <p>
      <p>User has a valid session and {orgId} is defined</p>
    </p>
  )
}

For auth.protect(), signed-out users will be redirected to the sign-in page. So, pending users will be redirected to the sign-in page, where the <SignIn /> component will prompt them to fulfill the after-auth requirements, which in this case is selecting or creating an organization. Once finished, their session will move from pending to a signed-in state.

middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/forum(.*)'])

export default clerkMiddleware(async (auth, req) => {
  // pending users won't be able to access protected routes
  // and will be redirected to the sign-in page
  if (isProtectedRoute(req)) await auth.protect()
})

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for API routes
    '/(api|trpc)(.*)',
  ],
}

For auth.userId(), it would return null if the user has a pending session. In the following example, pending users will be redirected to the sign-in page, where the <SignIn /> component will prompt them to fulfill the after-auth requirements, which in this case is selecting or creating an organization. Once finished, their session will move from pending to a signed-in state.

app/middleware.ts
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'

const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/forum(.*)'])

export default clerkMiddleware(async (auth, req) => {
  const { userId, redirectToSignIn } = await auth()

  // pending users will not have a `userId`
  // and will be redirected to the sign-in page
  if (!userId && isProtectedRoute(req)) {
    return redirectToSignIn()
  }
})

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    // Always run for API routes
    '/(api|trpc)(.*)',
  ],
}

Feedback

What did you think of this content?

Last updated on