Implement a Unified E2E Membership Management Experience with Stigg

Published on December 5, 2024
Learning GoalLearn how to implement dynamic entitlements in SaaS applications with Auth0 and Stigg.

In this lab, you will learn how to deliver a unified end-to-end membership management experience using the Stigg's React components.

The Auth0 and Stigg integration connects an environment in Stigg to your Auth0 tenant by creating:

  • One Action in your Auth0 tenant Post Login flow to handle Stigg customer provisioning from Auth0 users/organizations and send login events to Stigg.
  • An "Active Users" metered feature in Stigg for tracking MAUs based on Auth0 logins.

Prerequisites

To begin integrating Auth0 into your Stigg project, there are a few requirements that you'll need to set up before getting started:

  • A Stigg account with an environment you want to integrate with. To create an account, visit Stigg website and click "Try Stigg" to get started.
  • A new Auth0 tenant. Using a new Auth0 tenant for this sample application ensures you don't encounter any conflicts due to existing configuration in an existing tenant. You can read more about how to set one up here.
  • Node.js v20 or later is required to run the bootstrapping process.

You can sign up for a free Auth0 account at https://auth0.com/signup.

Once you sign in, Auth0 takes you to the Auth0 Dashboard, where you can configure and manage the authentication services for your applications.

Set Up the Stigg Integration

To set up the integration, follow these steps:

  1. Log in to your Stigg account and select the Stigg environment to which this integration will have access. Note: The integration is limited to one environment per Auth0 tenant.
  2. Navigate to "Integrations" -> "Apps" -> "Auth0"
  3. You'll need to create a new Auth0 Machine-to-Machine Application and authorize Stigg to access the Auth0 Management API. Learn how to do it here.
  4. Under the integration Auth0 tenant step, enter your Auth0 Tenant ID and Region.
  5. Enter the Client ID and Client Secret of the application you've created in step 3.
Initial setup
  1. Click "Connect".
  2. Select and confirm the Auth0 application you want to integrate with Stigg.
  3. When selecting the Auth0 application, confirm the type of users Stigg will handle. This will be automatically preselected based on the Organization Behavior of the chosen Auth0 application:
    1. Individuals (B2C): Every individual user will be associated with a customer in Stigg. For example, Spotify associates every listener with a customer record to manage subscriptions and billing.
    2. Business users (B2B): Every organization will be associated with a customer in Stigg. For example, Slack connects each company using its platform to a customer account to manage workspace-wide billing and services.
    3. Both: Individual users and organizations will be associated with customers in Stigg. For example, OpenAI offers products like ChatGPT to individual users while also providing businesses with API access to integrate AI into their applications.
  1. Click "Continue"
  2. Select the initial subscription plan to be assigned automatically when this integration provisions new customers. If you haven't created a Product and Plan yet, you can learn how to do it by referring to the Docs.
Stigg_Integration
  1. Click "Complete setup"

The integration is now active. You can confirm the setup by navigating to your tenant's Post Login actions within Auth0 (Select Actions > Triggers > "post-login"), where you will find a new custom action at the end of the flow.

Auth0 Action overview

After authentication, Stigg creates an action in the Auth0 tenant at the end of the Post Login flow.

The action handles both customer and subscription provisioning, usage metering, and enforcement of active users' limits belonging to an organization. It operates in the Post-Login flow of the Auth0 tenant and automates critical aspects of user management.

Visit the "Auth0 Action" section in the "Introducing the Auth0 + Stigg Integration" blog to learn more about the Auth0 Action that Stigg creates in your Auth0 tenant for the integration.

Set Up the B2B SaaS App Template

After you have successfully integrated Auth0 with your Stigg environment, you can add feature gating and monetization capabilities in your application using the Stigg's both Node SDK and React SDKs.

For this demo, you're going to use a simple B2B SaaS app template (learn more here). Follow the next steps to clone and set up the project.

Clone and Install Dependencies

Start by cloning the auth0-b2b-saas-starter project into your local machine:

COMMAND
git clone https://github.com/auth0-developer-hub/auth0-b2b-saas-starter.git

Make the project directory your current working directory:

COMMAND
cd auth0-b2b-saas-starter

Next, install the project dependencies:

COMMAND
npm install

Install the Auth0 CLI

This project uses the Auth0 CLI to make setting up your tenant a lot easier, by scripting away as much manual work as possible. If you want to familiarize yourself with the Auth0 CLI, read the Auth0 CLI Basics lab.

There are different ways to install the Auth0 CLI, depending on your operating system.

macOS

You can install the Auth0 CLI using Homebrew, package manager for macOS:

COMMAND
brew tap auth0/auth0-cli && brew install auth0

Windows

You can install the Auth0 CLI using Scoop, a command-line installer for Windows.

First, add the bucket:

COMMAND
scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git

Then, install the application:

COMMAND
scoop install auth0

Linux

You can install the Auth0 CLI using cURL, a command line tool and library for transferring data with URLs.

Download the binary to ./auth0:

COMMAND
curl -sSfL https://raw.githubusercontent.com/auth0/auth0-cli/main/install.sh | sh -s -- -b .

If you want to run the binary from any directory, move the installation to a place in your $PATH. For example:

COMMAND
sudo mv ./auth0 /usr/local/bin

Test the Auth0 CLI installation

Run the following command to show in the terminal the version of your Auth0 CLI installation:

COMMAND
auth0 --version

You should see the following output:

COMMAND
auth0 version <version-number> <hash>
You have the Auth0 CLI up and running.

Now, log in by entering the following command and following the instructions to choose a specific tenant to authenticate with:

COMMAND
auth0 login --scopes "update:tenant_settings,create:connections,create:client_grants,create:email_templates,update:guardian_factors"

This will take you through a flow that will securely retrieve a Management API token for your Auth0 tenant.

At the Authorize App step, be sure to select the same NEW tenant that you created in the previous steps. Whatever you choose during this step will be the tenant that will be bootstrapped in the next steps, so it's important to make sure it's a newly created tenant without existing configuration.

Bootstrap the Auth0 Tenant

This step will create and update entities in your Auth0 tenant. The provided script will use the Auth0 CLI to provision the resources required for this sample application:

  • Creating the appropriate clients (called Applications in Auth0)
  • Creating admin and member roles,
  • Creating actions for setting roles and security policies
  • Creating email and login templates
  • Enabling MFA factors
Only run the following command on a newly created tenant to avoid changing existing configuration or introducing conflicting elements to your existing Auth0 tenants.

Run the following command to execute the bootstrap script:

COMMAND
npm run auth0:bootstrap

The bootstrap script will create a .env.local file containing the environment variables at the root of your project directory when it completes successfully.

Run the Sample Application

Execute the following command to run the development server:

COMMAND
npm run dev

You can now visit http://localhost:3000/ to access the application.

You can proceed to interact with the app as if you were a user: create an account, navigate to the settings, explore the identity capabilities.

Guard the SSO Tab Behind an Entitlement

Now that we've decided that the SSO capability will be treated as a premium feature in our app, we need to display only the SSO tab based on a feature entitlement so only customers with this entitlement can use it.

First, we'll need to create the "SSO" capability as a Boolean feature in Stigg (learn how to create a Boolean feature here) so we can reference it in our app's code.

Let's start by installing and initializing the Stigg Node SDK.

COMMAND
npm install @stigg/node-server-sdk

You'll need to get your environment's Server API Key from the Account Settings page.
You can store it in the .env.local file for local development.

Now, we'll need to initialize the Node SDK and provide the API key. Create a file lib/stigg.ts and copy this content:

lib/stigg.ts
import { Stigg } from "@stigg/node-server-sdk"
export const stiggClient = Stigg.initialize({
apiKey: process.env.STIGG_SERVER_API_KEY,
})
export async function waitForStiggInit() {
try {
await stiggClient.waitForInitialization()
} catch (error) {
console.error("Stigg failed to initialize", error)
}
}

Then, update the app/layout.tsx file with the following code to ensure Stigg is initialized:

app/layout.tsx
// ...
import { waitForStiggInit } from "@/lib/stigg"
// ...
export default async function RootLayout({
}: Readonly<{
children: React.ReactNode
}>) {
// initialize Stigg before rendering the app, this blocks only once
await waitForStiggInit()
// ...
}

For convenience, we'll add a small helper module that exposes a checkAccess method:

lib/entitlements.ts
import { Claims } from "@auth0/nextjs-auth0"
import { stiggClient } from "@/lib/stigg"
export const features = {
sso: "feature-sso"
// ...
}
export type Feature = keyof typeof features
function getCustomerId(user: Claims) {
return user.org_id
}
export async function checkAccess(user: Claims, feature: Feature) {
const customerId = getCustomerId(user)
const featureId = features[feature]
const { hasAccess } = await stiggClient.getBooleanEntitlement({
customerId,
featureId,
})
return hasAccess
}

Using our checkAccess helper function, we can add an entitlement check to ensure only authorized organizations can access the SSO tab. Since this is a Next.js project and the SSO page is an asynchronous React Server Component, the check executes on the server side:

app/dashboard/organization/sso/page.tsx
// ...
import { checkAccess } from "@/lib/entitlements"
// ...
export default async function SSO() {
const session = await appClient.getSession()
const hasAccess = await checkAccess(session!.user, "sso")
// ...
return (
<div className="space-y-2">
<PageHeader
title="Single Sign-On"
description="Configure SSO for your organization."
/>
{!hasAccess ? (
<NoAccess />
) : (
<ConnectionsList ... />
)}
</div>
)
}

Now, if a user tries to access the SSO tab when their organization is missing the SSO feature entitlement, the user will see the <NoAccess/> component instead of the actual tab's content.

All the code changes above can be found here.

Add the Billing Tab Using Stigg's React Components

To let users manage their subscriptions, view their invoices, and track their usage, we'll introduce a self-service customer portal by embedding Stigg's CustomerPortal, Paywall, and Checkout components using Stigg's React SDK.

Let's start by installing and initializing the Stigg React SDK:

COMMAND
npm install @stigg/react-sdk

You'll need to get your environment's Client API Key from the Account Settings page.

You can store it in the .env.local file for local development.

Now, we'll need to initialize StiggProvider and provide the API key:

components/stigg-provider.tsx
"use client"
import * as React from "react"
import { StiggProvider as ReactStiggProvider } from "@stigg/react-sdk"
import { providerTheme } from "@/components/stigg-theme"
type StiggProviderProps = {
customerId?: string | undefined
customerToken?: string | undefined
children: React.ReactNode
}
export function StiggProvider({
children,
customerId,
customerToken,
}: StiggProviderProps) {
return (
<ReactStiggProvider
theme={providerTheme}
apiKey={process.env.NEXT_PUBLIC_STIGG_CLIENT_API_KEY}
customerId={customerId}
customerToken={customerToken}
>
{children}
</ReactStiggProvider>
)
}

Our next step is to import and render the CustomerPortal component. It's essential to render the CustomerPortal component beneath our StiggProvider in the component tree, as shown in this example:

app/dashboard/organization/billing/customer-portal.tsx
"use client"
import * as React from "react"
import { CustomerPortal as StiggCustomerPortal } from "@stigg/react-sdk"
// ...
import { StiggProvider } from "@/components/stigg-provider"
import { Paywall } from "@/app/dashboard/organization/billing/paywall"
// ...
type CustomerPortalProps = {
customerId: string
customerToken: string
}
export function CustomerPortal({
customerId,
customerToken,
}: CustomerPortalProps) {
return (
<div>
<StiggProvider customerId={customerId} customerToken={customerToken}>
<StiggCustomerPortal
paywallComponent={<Paywall />}
...
/>
</StiggProvider>
</div>
)
}

Let's add the Billing tab so our users will be able to access the customer portal:

app/dashboard/organization/layout.tsx
// ...
const sidebarNavItems = [
// ...
{
title: "Billing",
href: "/dashboard/organization/billing",
},
]
// ...
We recommend enforcing client-side security by limiting client API token access to specific customers. This can be achieved by generating a customer token (SHA-256) from the customer ID and signing a secret on the server side. You can learn more about this approach here.
app/dashboard/organization/billing/page.tsx
import { createHmac } from "crypto"
import { appClient } from "@/lib/auth0"
import { PageHeader } from "@/components/page-header"
import { CustomerPortal } from "./customer-portal"
function generateCustomerToken(customerId: string) {
const secret = process.env.STIGG_CUSTOMER_TOKEN_SIGNING_SECRET || ""
const signature = createHmac("sha256", secret)
.update(customerId)
.digest("hex")
return `HMAC-SHA256 ${customerId}:${signature}`
}
export default async function Billing() {
const session = await appClient.getSession()
const customerId = session!.user.org_id
const customerToken = generateCustomerToken(customerId)
return (
<div className="space-y-2">
<PageHeader
title="Billing"
description="Manage your organization's billing settings."
/>
<CustomerPortal customerId={customerId} customerToken={customerToken} />
</div>
)
}

Having the self-service customer portal in place, we complete the purchase experience by including the Paywall and the Checkout components to handle subscription upgrades, downgrades, and payments.

Import the Checkout component:

app/dashboard/organization/billing/checkout.tsx
import * as React from "react"
import { Plan, Checkout as StiggCheckout } from "@stigg/react-sdk"
// ...
type CheckoutProps = {
plan: Plan
onCheckout: () => Promise<void>
}
export function Checkout({ plan, onCheckout }: CheckoutProps) {
return (
<StiggCheckout
...
planId={plan.id}
onCheckoutCompleted={async ({ success, error }) => {
if (success) {
await onCheckout()
} else {
console.error(error)
}
}}
/>
)
}

Paywall component, the Checkout will be rendered instead when a plan is selected by the user:

app/dashboard/organization/billing/paywall.tsx
import * as React from "react"
import { useState } from "react"
import {
Plan,
Paywall as StiggPaywall,
useStiggContext,
} from "@stigg/react-sdk"
import { Checkout } from "@/app/dashboard/organization/billing/checkout"
export function Paywall() {
const { refreshData } = useStiggContext()
const [selectedPlan, setSelectedPlan] = useState<Plan | undefined>()
return (
<>
{selectedPlan ? (
<Checkout
plan={selectedPlan}
onCheckout={async () => {
await refreshData()
setSelectedPlan(undefined)
}}
/>
) : (
<StiggPaywall onPlanSelected={({ plan }) => setSelectedPlan(plan)}/>
)}
</>
)

Test the Integration

We have successfully integrated all the three components within our demo app, and this is how our Billing tab should look like when running the app:

App demo image

All the code changes above and the styles applied for the dark theme can be found in this commit.

Source code

The full source code for the sample app is available in this GitHub repo and can serve as a starting template for your next Auth0 + Stigg project.

Live demo: https://stigg-saastart-app.vercel.app/