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:
- 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.
- Their session will be in a
- Once they have completed the after-auth flow, their session will move from
pending
to asigned-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.
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.
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.
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
Last updated on