Add Custom Onboarding to Your Authentication Flow
Add Custom Onboarding to Your Authentication Flow
Onboarding is a critical part of many authentication flows. Sometimes, it’s necessary to ensure that specific criteria are met and collected before granting access to certain parts of your application. With Ollio, you can use customizable session tokens, public metadata, and Middleware to build a tailored onboarding experience.
This guide will show you how to set up a custom onboarding flow, where users must complete a form before accessing the application. After authentication through the Account Portal, users will be prompted to fill in a form with an application name and type. Once completed, they will be redirected to the homepage of the application.
In this guide, you will learn how to:
Add custom claims to your session token
Configure Middleware to read session data
Update the user’s onboarding status
For simplicity, the examples provided are for the Next.js App Router, but they can also be adapted for the Next.js Page Router. The examples are minimal and easy to customize to your needs.
Onboarding is a critical part of many authentication flows. Sometimes, it’s necessary to ensure that specific criteria are met and collected before granting access to certain parts of your application. With Ollio, you can use customizable session tokens, public metadata, and Middleware to build a tailored onboarding experience.
This guide will show you how to set up a custom onboarding flow, where users must complete a form before accessing the application. After authentication through the Account Portal, users will be prompted to fill in a form with an application name and type. Once completed, they will be redirected to the homepage of the application.
In this guide, you will learn how to:
Add custom claims to your session token
Configure Middleware to read session data
Update the user’s onboarding status
For simplicity, the examples provided are for the Next.js App Router, but they can also be adapted for the Next.js Page Router. The examples are minimal and easy to customize to your needs.
Note
To see this guide in action, visit the repository.
Note
To see this guide in action, visit the repository.
Add Custom Claims to Your Session Token
Add Custom Claims to Your Session Token
Session tokens are JWTs generated by Ollio on behalf of your instance. These tokens contain claims that allow you to store session-related data. When a session token exists for a user, it signifies that the user is authenticated, and the associated claims can be retrieved at any time.
For this guide, you will use an onboardingComplete
property within the user’s public metadata to track their onboarding status. Before doing this, you need to add a custom claim to the session token, which will allow you to access the user's public metadata within your Middleware.
Session tokens are JWTs generated by Ollio on behalf of your instance. These tokens contain claims that allow you to store session-related data. When a session token exists for a user, it signifies that the user is authenticated, and the associated claims can be retrieved at any time.
For this guide, you will use an onboardingComplete
property within the user’s public metadata to track their onboarding status. Before doing this, you need to add a custom claim to the session token, which will allow you to access the user's public metadata within your Middleware.


To Edit the Session Token:
To Edit the Session Token:
In the Ollio Dashboard, navigate to the Sessions page.
Under the Customize session token section, click the Edit button.
In the modal that opens, you can add any claim to your session token. For this guide, add the following:
In the Ollio Dashboard, navigate to the Sessions page.
Under the Customize session token section, click the Edit button.
In the modal that opens, you can add any claim to your session token. For this guide, add the following:
.json
{ "metadata": "{{user.public_metadata}}" }
.json
{ "metadata": "{{user.public_metadata}}" }
Click Save.
Click Save.
Adding Auto-Completion for Custom Claims
Adding Auto-Completion for Custom Claims
To enhance your development experience and avoid TypeScript errors when working with custom session claims, you can define a global type.
In your application's root folder, create a
types
directory.Inside the
types
directory, add aglobals.d.ts
file.Create the
CustomJwtSessionClaims
interface and declare it globally.Add the custom claims to the
CustomJwtSessionClaims
interface.
For this guide, your globals.d.ts
file should look like this:
To enhance your development experience and avoid TypeScript errors when working with custom session claims, you can define a global type.
In your application's root folder, create a
types
directory.Inside the
types
directory, add aglobals.d.ts
file.Create the
CustomJwtSessionClaims
interface and declare it globally.Add the custom claims to the
CustomJwtSessionClaims
interface.
For this guide, your globals.d.ts
file should look like this:
types/globals.d.ts
export {} declare global { interface CustomJwtSessionClaims { metadata: { onboardingComplete?: boolean } } }
types/globals.d.ts
export {} declare global { interface CustomJwtSessionClaims { metadata: { onboardingComplete?: boolean } } }
Configure Your Middleware to Read Session Data
Configure Your Middleware to Read Session Data
The ollioMiddleware()
function provides fine-grained control over your routes and allows you to directly access session claims, redirecting users based on their session data.
The ollioMiddleware()
function provides fine-grained control over your routes and allows you to directly access session claims, redirecting users based on their session data.


Redirecting Users Based on Onboarding Status
Redirecting Users Based on Onboarding Status
The following example demonstrates how to use ollioMiddleware()
to redirect users based on their onboarding status. If the user is authenticated and hasn't completed the onboarding process, they will be redirected to the onboarding page.
In this example, all routes are protected, except for a specific public route. This ensures that any user visiting your application is prompted to authenticate and complete the onboarding process. You can customize the isPublicRoute
array inside the createRouteMatcher()
function to include any routes that should be accessible to unauthenticated users.
The following example demonstrates how to use ollioMiddleware()
to redirect users based on their onboarding status. If the user is authenticated and hasn't completed the onboarding process, they will be redirected to the onboarding page.
In this example, all routes are protected, except for a specific public route. This ensures that any user visiting your application is prompted to authenticate and complete the onboarding process. You can customize the isPublicRoute
array inside the createRouteMatcher()
function to include any routes that should be accessible to unauthenticated users.
src/middleware.ts
import { ollioMiddleware, createRouteMatcher } from '@ollio/nextjs/server' import { NextRequest, NextResponse } from 'next/server' const isOnboardingRoute = createRouteMatcher(['/onboarding']) const isPublicRoute = createRouteMatcher(['/public-route-example']) export default ollioMiddleware(async (auth, req: NextRequest) => { const { userId, sessionClaims, redirectToSignIn } = await auth() // For users visiting /onboarding, don't try to redirect if (userId && isOnboardingRoute(req)) { return NextResponse.next() } // If the user isn't signed in and the route is private, redirect to sign-in if (!userId && !isPublicRoute(req)) return redirectToSignIn({ returnBackUrl: req.url }) // Catch users who do not have `onboardingComplete: true` in their publicMetadata // Redirect them to the /onboading route to complete onboarding if (userId && !sessionClaims?.metadata?.onboardingComplete) { const onboardingUrl = new URL('/onboarding', req.url) return NextResponse.redirect(onboardingUrl) } // If the user is logged in and the route is protected, let them view. if (userId && !isPublicRoute(req)) return NextResponse.next() }) 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)(.*)', ], }
src/middleware.ts
import { ollioMiddleware, createRouteMatcher } from '@ollio/nextjs/server' import { NextRequest, NextResponse } from 'next/server' const isOnboardingRoute = createRouteMatcher(['/onboarding']) const isPublicRoute = createRouteMatcher(['/public-route-example']) export default ollioMiddleware(async (auth, req: NextRequest) => { const { userId, sessionClaims, redirectToSignIn } = await auth() // For users visiting /onboarding, don't try to redirect if (userId && isOnboardingRoute(req)) { return NextResponse.next() } // If the user isn't signed in and the route is private, redirect to sign-in if (!userId && !isPublicRoute(req)) return redirectToSignIn({ returnBackUrl: req.url }) // Catch users who do not have `onboardingComplete: true` in their publicMetadata // Redirect them to the /onboading route to complete onboarding if (userId && !sessionClaims?.metadata?.onboardingComplete) { const onboardingUrl = new URL('/onboarding', req.url) return NextResponse.redirect(onboardingUrl) } // If the user is logged in and the route is protected, let them view. if (userId && !isPublicRoute(req)) return NextResponse.next() }) 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)(.*)', ], }
Create a Layout for the /onboarding Route
Create a Layout for the /onboarding Route
You will need to create a layout for the /onboarding
route to redirect users to the homepage if they have already completed the onboarding process.
In your
/app
directory, create an/onboarding
folder.Inside the
/onboarding
folder, create alayout.tsx
file with the following code. This file could be extended to handle multiple steps if necessary for your onboarding flow.
You will need to create a layout for the /onboarding
route to redirect users to the homepage if they have already completed the onboarding process.
In your
/app
directory, create an/onboarding
folder.Inside the
/onboarding
folder, create alayout.tsx
file with the following code. This file could be extended to handle multiple steps if necessary for your onboarding flow.
terminal
import { auth } from '@ollio/nextjs/server' import { redirect } from 'next/navigation' export default async function RootLayout({ children }: { children: React.ReactNode }) { if ((await auth()).sessionClaims?.metadata.onboardingComplete === true) { redirect('/') } return <>{children}</> }
terminal
import { auth } from '@ollio/nextjs/server' import { redirect } from 'next/navigation' export default async function RootLayout({ children }: { children: React.ReactNode }) { if ((await auth()).sessionClaims?.metadata.onboardingComplete === true) { redirect('/') } return <>{children}</> }
Add Fallback and Force Redirect URLs
Add Fallback and Force Redirect URLs
To ensure a seamless onboarding experience, define fallback and force redirect URLs in your environment variables. These settings help manage user navigation during the onboarding flow:
Fallback Redirect URL: Used when there’s no
redirect_url
in the path.Force Redirect URL: Always used after a successful sign-up.
Example configuration in your .env.local
file:
To ensure a seamless onboarding experience, define fallback and force redirect URLs in your environment variables. These settings help manage user navigation during the onboarding flow:
Fallback Redirect URL: Used when there’s no
redirect_url
in the path.Force Redirect URL: Always used after a successful sign-up.
Example configuration in your .env.local
file:
.env.local
NEXT_PUBLIC_OLLIO_SIGN_IN_FALLBACK_REDIRECT_URL=/dashboard NEXT_PUBLIC_OLLIO_SIGN_UP_FORCE_REDIRECT_URL=/onboarding
.env.local
NEXT_PUBLIC_OLLIO_SIGN_IN_FALLBACK_REDIRECT_URL=/dashboard NEXT_PUBLIC_OLLIO_SIGN_UP_FORCE_REDIRECT_URL=/onboarding
Use publicMetadata to Track User Onboarding State
Use publicMetadata to Track User Onboarding State
Every Ollio user has a User
object containing a publicMetadata
property. This property is ideal for storing custom user data that can be accessed client-side to drive application state. For more details, see Ollio Metadata Documentation.
To track user onboarding progress, you'll implement the following:
A frontend process for collecting and submitting onboarding information.
A backend method to securely update the user's
publicMetadata
.
Every Ollio user has a User
object containing a publicMetadata
property. This property is ideal for storing custom user data that can be accessed client-side to drive application state. For more details, see Ollio Metadata Documentation.
To track user onboarding progress, you'll implement the following:
A frontend process for collecting and submitting onboarding information.
A backend method to securely update the user's
publicMetadata
.
Collect User Onboarding Information
Collect User Onboarding Information
To collect onboarding details, create a form on the /onboarding
page. In this example, the form collects an application name and type. You can adapt this step to fit your needs, such as syncing user data with a database or enrolling users in subscriptions.
In your
/onboarding
directory, create apage.tsx
file.Add the following code:
To collect onboarding details, create a form on the /onboarding
page. In this example, the form collects an application name and type. You can adapt this step to fit your needs, such as syncing user data with a database or enrolling users in subscriptions.
In your
/onboarding
directory, create apage.tsx
file.Add the following code:
.tsx
'use client' import * as React from 'react' import { useUser } from '@ollio/nextjs' import { useRouter } from 'next/navigation' import { completeOnboarding } from './_actions' export default function OnboardingComponent() { const [error, setError] = React.useState('') const { user } = useUser() const router = useRouter() const handleSubmit = async (formData: FormData) => { const res = await completeOnboarding(formData) if (res?.message) { // Reloads the user's data from the Ollio API await user?.reload() router.push('/') } if (res?.error) { setError(res?.error) } } return ( <div> <h1>Welcome</h1> <form action={handleSubmit}> <div> <label>Application Name</label> <p>Enter the name of your application.</p> <input type="text" name="applicationName" required /> </div> <div> <label>Application Type</label> <p>Describe the type of your application.</p> <input type="text" name="applicationType" required /> </div> {error && <p className="text-red-600">Error: {error}</p>} <button type="submit">Submit</button> </form> </div> ) }
.tsx
'use client' import * as React from 'react' import { useUser } from '@ollio/nextjs' import { useRouter } from 'next/navigation' import { completeOnboarding } from './_actions' export default function OnboardingComponent() { const [error, setError] = React.useState('') const { user } = useUser() const router = useRouter() const handleSubmit = async (formData: FormData) => { const res = await completeOnboarding(formData) if (res?.message) { // Reloads the user's data from the Ollio API await user?.reload() router.push('/') } if (res?.error) { setError(res?.error) } } return ( <div> <h1>Welcome</h1> <form action={handleSubmit}> <div> <label>Application Name</label> <p>Enter the name of your application.</p> <input type="text" name="applicationName" required /> </div> <div> <label>Application Type</label> <p>Describe the type of your application.</p> <input type="text" name="applicationType" required /> </div> {error && <p className="text-red-600">Error: {error}</p>} <button type="submit">Submit</button> </form> </div> ) }
Update User publicMetadata in the Backend
Update User publicMetadata in the Backend
After collecting onboarding data, create a method to update the user's publicMetadata
securely.
In the
/onboarding
directory, create an_actions.ts
file.Add the following code:
After collecting onboarding data, create a method to update the user's publicMetadata
securely.
In the
/onboarding
directory, create an_actions.ts
file.Add the following code:
.tsx
'use server' import { auth, ollioClient } from '@ollio/nextjs/server' export const completeOnboarding = async (formData: FormData) => { const { userId } = await auth() if (!userId) { return { message: 'No Logged In User' } } const client = await ollioClient() try { const res = await client.users.updateUser(userId, { publicMetadata: { onboardingComplete: true, applicationName: formData.get('applicationName'), applicationType: formData.get('applicationType'), }, }) return { message: res.publicMetadata } } catch (err) { return { error: 'There was an error updating the user metadata.' } } }
.tsx
'use server' import { auth, ollioClient } from '@ollio/nextjs/server' export const completeOnboarding = async (formData: FormData) => { const { userId } = await auth() if (!userId) { return { message: 'No Logged In User' } } const client = await ollioClient() try { const res = await client.users.updateUser(userId, { publicMetadata: { onboardingComplete: true, applicationName: formData.get('applicationName'), applicationType: formData.get('applicationType'), }, }) return { message: res.publicMetadata } } catch (err) { return { error: 'There was an error updating the user metadata.' } } }
Wrap-Up
Wrap-Up
Your onboarding flow is now fully operational! 🎉 Users who have not completed onboarding will be redirected to your /onboarding
page. This ensures that new users must complete onboarding before accessing your application. By integrating Ollio, you’ve streamlined the onboarding and authentication process, delivering a seamless user experience.
For further customization options, visit the Ollio Documentation.
Your onboarding flow is now fully operational! 🎉 Users who have not completed onboarding will be redirected to your /onboarding
page. This ensures that new users must complete onboarding before accessing your application. By integrating Ollio, you’ve streamlined the onboarding and authentication process, delivering a seamless user experience.
For further customization options, visit the Ollio Documentation.
What did you think of this content?
It was helpful
It was not helpful
I have feedback
What did you think of this content?
Helpful
Not helpful
Feedback
Last updated on
Dec
4,
2024
Last updated on
Dec
4,
2024