Auth & Identity8 min read

How to Integrate Descope with Your Headless CMS

Connect Descope to your headless CMS so auth roles, tenants, and gated content rules stay in sync the moment editors publish.

Published April 29, 2026
01Overview

What is Descope?

Descope is a customer identity and access management platform for adding authentication, authorization, and user lifecycle flows to apps. Teams use it for passkeys, magic links, OTP, social login, SSO, MFA, session handling, roles, tenants, and B2B user onboarding. It’s used by SaaS teams, marketplaces, partner portals, and internal platforms that need identity flows without building every auth screen and security control from scratch.


02The case for integration

Why integrate Descope with a headless CMS?

Auth rules usually start simple. A docs page is public, a pricing page is public, and a partner guide is private. Then you add enterprise-only docs, regional content, tenant-specific announcements, beta feature pages, and role-based training. If those access rules live in code while the content lives somewhere else, every content change becomes an engineering ticket.

Connecting Descope to a headless CMS lets editors control content and access metadata in one place, while Descope handles sign-in, sessions, roles, tenants, and MFA. For example, an editor can publish a partner-only guide with a required Descope role of partner_admin, and the app can check the user’s Descope token before serving that content. With Sanity, that access metadata is structured JSON in the Content Lake, so you don’t have to parse HTML or copy role names between tools. Webhooks can fire on publish, and Functions can run server-side sync logic without a separate queue or worker.

The alternative is usually brittle. Someone keeps a spreadsheet of role mappings, someone else hard-codes route guards, and nobody knows whether the content preview matches production auth behavior. The trade-off is that you need a clear boundary: Sanity should hold content access metadata, Descope should remain the authority for identity, sessions, and authentication policy. Don’t put secrets in content fields, and don’t trust client-side checks for protected content.


03Architecture

Architecture overview

A typical Descope and Sanity integration starts with structured documents in the Content Lake. You might model gatedPage, accessRole, accessPlan, organizationProfile, and memberProfile documents in Sanity Studio. Each document stores explicit fields such as requiredRoles, tenantId, planSlug, descopeRoleKey, or descopeLoginId. When an editor publishes or updates one of those documents, a Sanity webhook fires with the document ID and type. A Sanity Function or webhook handler validates the webhook secret, then uses GROQ to fetch exactly the fields Descope needs, including joins across references. For example, a memberProfile can resolve its related accessPlan and accessRole documents into Descope role keys and user custom attributes. That server-side handler calls Descope’s Management API through @descope/node-sdk using a Project ID and Management Key stored as environment variables. Depending on your model, it can update a Descope user’s custom attributes, assign roles, update tenant metadata, or keep permission names aligned with content access rules. The frontend then uses a Descope SDK to sign the user in, verifies the Descope session on the server, and queries Sanity for content that matches the user’s roles, tenant, or plan. The end user sees the right content, and a publish event can update the auth metadata path in seconds instead of waiting for a manual deploy.


04Use cases

Common use cases

🔐

Role-gated documentation

Let editors tag docs with Descope role keys such as customer_admin or partner_viewer, then serve only the pages a signed-in user can read.

🏢

Tenant-aware portals

Sync Sanity organization profiles, plan tiers, and portal settings into Descope tenant metadata for B2B SaaS workspaces.

🧑‍💼

User profile enrichment

Publish member profiles in Sanity Studio, then update Descope user custom attributes like locale, plan, region, and onboarding stage.

✉️

Passwordless onboarding pages

Connect invite-only onboarding content with Descope magic link or OTP flows so new users land on content matched to their role.


05Implementation

Step-by-step integration

  1. 1

    Set up Descope

    Create a Descope project, configure your authentication flow in the Descope Console, and copy your Project ID. Create a Management Key for server-side API calls, then install @descope/node-sdk for the sync layer and a frontend SDK such as @descope/react-sdk or @descope/nextjs-sdk for sign-in.

  2. 2

    Model access metadata in Sanity Studio

    Create schema fields that map content to identity rules. Common fields include descopeLoginId on memberProfile, descopeRoleKey on accessRole, tenantId on organizationProfile, requiredRoles on gatedPage, and planSlug on accessPlan. Keep these values typed and validated so editors don’t paste mismatched role names.

  3. 3

    Create a publish trigger

    Add a Sanity webhook or Sanity Function that runs when relevant documents are created, updated, published, or deleted. Include the document ID and type in the payload, sign the webhook, and keep the handler server-side so your Descope Management Key never reaches the browser.

  4. 4

    Fetch the exact fields with GROQ

    In the handler, use @sanity/client and a GROQ query to fetch only the fields Descope needs. Resolve references in the query, such as access roles attached to a member profile or a plan attached to an organization.

  5. 5

    Call Descope’s Management API

    Use @descope/node-sdk with your Project ID and Management Key to update users, roles, permissions, or tenants. Treat Descope as the source of truth for authentication and sessions, and treat Sanity as the source of truth for content access metadata.

  6. 6

    Test the frontend path

    Sign in as at least 2 test users with different roles. Publish a gated document in Sanity Studio, confirm the webhook ran, inspect the user or tenant in Descope, and verify that your frontend serves protected content only through a server-side check.



07Why Sanity

How Sanity + Descope works

Build your Descope integration on Sanity

Sanity gives you the structured content foundation, real-time event system, and flexible APIs to connect Descope roles, tenants, and user metadata to the content your teams publish.

Start building free →

08Comparison

CMS approaches to Descope

CapabilityTraditional CMSSanity
Role and permission metadataAccess rules often live in plugins, page settings, or custom code, which makes role names hard to validate across environments.Schema-as-code lets you model Descope role keys as reusable documents, validate values in Sanity Studio, and query them as structured JSON.
Real-time sync to DescopeTeams often rely on deploys, scheduled exports, or plugin hooks that vary by installation.Webhooks can trigger on publish, and Functions can run server-side sync logic for Descope without you operating separate infrastructure.
Tenant-aware contentTenant rules are usually added through custom tables or plugin-specific settings, which can be hard to reuse across apps.GROQ can join organization profiles, plans, required roles, and pages in one query before you update Descope tenant metadata or filter content.
Protected content deliveryThe page renderer often decides access, which can mix auth checks with presentation logic.Your app can verify the Descope session on the server, then query the Content Lake for only the content matching the user’s roles, tenant, or plan.
Editorial preview for gated pagesPreview may not match production auth rules, especially when access logic is hidden in plugins or templates.Presentation Tool and structured access fields let editors preview gated content while developers keep production Descope checks server-side.

09Next steps

Keep building

Explore related integrations to complete your content stack.

Ready to try Sanity?

See how Sanity's Content Operating System powers integrations with Descope and 200+ other tools.