clerk-orgs

Organisations Clerk pour le B2B SaaS - créer des applications multi-tenant avec org

npx skills add https://github.com/clerk/skills --skill clerk-orgs

Organisations (B2B SaaS)

STOP — Prérequis du Tableau de bord uniquement. Les Organisations doivent être activées dans le Tableau de bord Clerk avant que toute API, hook ou composant lié à org fonctionne. Ouvrez Tableau de bord → Paramètres Organisations et activez Organisations. Choisissez délibérément le mode Adhésion : Membership required (par défaut depuis 2025-08-22) achemine les utilisateurs connectés via la tâche choose-organization et désactive les comptes personnels, tandis que Membership optional maintient les comptes personnels disponibles pour la coexistence B2C + B2B. Choisissez optional si vous avez besoin d'abonnements personnels aux côtés d'abonnements org.

Version : Cette skill cible les SDKs actuels (@clerk/nextjs v7+, @clerk/react v6+ — Core 3). Les différences Core 2 sont notées en ligne avec des appels > **Core 2 ONLY (skip if current SDK):** — consultez la skill clerk pour le tableau de version complet.

Démarrage rapide

  1. Activez OrganisationsTableau de bord → Paramètres Organisations. Choisissez Membership required (B2B uniquement) ou Membership optional (B2C + B2B). Tableau de bord uniquement ; pas de chemin CLI.
  2. Créez une org — via <OrganizationSwitcher />, <CreateOrganization />, ou programmatiquement avec clerkClient().organizations.createOrganization().
  3. Protégez les routes — lisez orgId / orgSlug depuis auth() et contrôlez avec has({ role }) ou has({ permission }).
  4. Gérez les membres — envoyez des invitations via l'API Backend ou l'onglet <OrganizationProfile /> intégré.
  5. Limitez l'adhésion — définissez maxAllowedMemberships à la création de l'org ou choisissez un Plan de facturation limité en sièges (voir skill clerk-billing).

De quoi avez-vous besoin ?

Tâche Référence
Catalogue de permissions système, rôles personnalisés, ensembles de rôles references/roles-permissions.md
Cycle de vie des invitations (créer, lister, révoquer, UI intégrée) references/invitations.md
Configuration SSO Enterprise, accès aux champs de fournisseur, vérification de domaine references/enterprise-sso.md
Adaptations Next.js pour les orgs (middleware rôle/permission, invariants slug, écritures scoped orgId) references/nextjs-patterns.md

Références

Référence Description
references/roles-permissions.md Rôles par défaut + personnalisés, catalogue des Permissions Système, nommage des permissions
references/invitations.md API Backend pour les invitations + UI intégrée
references/enterprise-sso.md SAML/OIDC par org, vérification de domaine, accès correct aux champs
references/nextjs-patterns.md Adaptations Next.js spécifiques aux orgs. Pour les patterns Next.js génériques, voir la skill clerk-nextjs-patterns.

Raccourcis du Tableau de bord

Action URL
Activez Organisations + mode Adhésion https://dashboard.clerk.com/last-active?path=organizations-settings
Gérez les rôles + permissions https://dashboard.clerk.com/last-active?path=organizations-settings/roles
Créez/modifiez une organisation https://dashboard.clerk.com/last-active?path=organizations
Webhooks pour les événements org https://dashboard.clerk.com/last-active?path=webhooks

Documentation

Patterns clés

Les exemples utilisent @clerk/nextjs par défaut. Pour d'autres frameworks, remplacez l'import par @clerk/react (Vite/CRA), @clerk/astro/components, @clerk/vue, @clerk/expo, @clerk/react-router, ou @clerk/tanstack-react-start — les APIs au niveau des features (has(), orgId, <OrganizationSwitcher />, <Show>) sont identiques dans les SDKs. Les patterns spécifiques au framework (middleware, redirects) se trouvent dans references/nextjs-patterns.md.

1. Lire l'Organisation depuis Auth

Accès côté serveur à l'organisation active :

import { auth } from '@clerk/nextjs/server'

const { orgId, orgSlug, orgRole } = await auth()
if (!orgId) {
  // l'utilisateur n'a pas d'org active — soit aucune, soit en train de consulter le Compte Personnel
}

auth() est spécifique à Next.js. Accesseurs côté serveur équivalents par SDK : auth(event) (Nuxt via event.context.auth()), context.locals.auth() (Astro), getAuth(req) (Express, après clerkMiddleware()). Côté client : useAuth() (SDKs basés sur React) ou composables (Vue/Nuxt). Tous retournent la même forme orgId / orgSlug / orgRole.

2. Routes dynamiques avec Slug Org

Le pattern route-par-org fonctionne dans tout framework supportant les routes dynamiques basées sur fichiers. Exemple Next.js :

app/orgs/[slug]/page.tsx
app/orgs/[slug]/settings/page.tsx

Vérifiez toujours que le slug d'URL correspond au slug d'org actif — sinon les utilisateurs peuvent accéder à /orgs/other-org/... avec un orgSlug obsolète dans leur session :

export default async function OrgPage({ params }: { params: { slug: string } }) {
  const { orgSlug } = await auth()
  if (orgSlug !== params.slug) {
    redirect('/dashboard')  // ou quel que soit votre flux « pas d'accès »
  }
  return <div>Bienvenue chez {orgSlug}</div>
}

3. Contrôle d'accès basé sur les rôles

const { has } = await auth()

if (!has({ role: 'org:admin' })) {
  return <div>Accès administrateur requis</div>
}

Les vérifications de permission utilisent la même surface has() :

if (!has({ permission: 'org:sys_memberships:manage' })) {
  redirect('/unauthorized')
}

Convention de nommage des permissions. Les Permissions Système commencent par org:sys_ ; les Permissions personnalisées utilisent org:<resource>:<action>. Le catalogue complet des Permissions Système se trouve dans references/roles-permissions.md — la liste courte est :

  • org:sys_memberships:{read, manage}
  • org:sys_profile:{manage, delete}
  • org:sys_domains:{read, manage}
  • org:sys_billing:{read, manage}

N'inventez PAS de noms comme org:create, org:manage_members, org:update_metadata — ce ne sont pas de vrais slugs de permission. Consultez references/roles-permissions.md pour les rôles personnalisés et le tableau des permissions.

4. Rendu conditionnel avec <Show>

import { Show } from '@clerk/nextjs'

<Show when={{ role: 'org:admin' }}>
  <AdminPanel />
</Show>

<Show when={{ permission: 'org:sys_memberships:manage' }}>
  <MembersTab />
</Show>

Core 2 ONLY (skip if current SDK): Utilisez <Protect role="org:admin"> / <Protect permission="..."> à la place de <Show>. <Show> a remplacé à la fois <Protect> et <SignedIn>/<SignedOut> en Core 3.

Syntaxe de template Astro pour le même composant (importé depuis @clerk/astro/components) :

<Show when={{ role: 'org:admin' }}>
  <AdminPanel />
</Show>

5. OrganizationSwitcher

import { OrganizationSwitcher } from '@clerk/nextjs'

<OrganizationSwitcher
  hidePersonal
  afterCreateOrganizationUrl="/orgs/:slug/dashboard"
  afterSelectOrganizationUrl="/orgs/:slug/dashboard"
/>

Props clés :

  • hidePersonal: boolean — masquez l'option Compte Personnel. Par défaut false. Passez true pour les apps B2B uniquement.
  • afterCreateOrganizationUrl, afterSelectOrganizationUrl, afterLeaveOrganizationUrl, afterSelectPersonalUrl — hooks de navigation. :slug est substitué à l'exécution.
  • createOrganizationMode, organizationProfileMode'modal' | 'navigation' (par défaut 'modal').

La liste complète des props se trouve dans la référence du composant.

6. Tâche de session — Choisir une Organisation

Quand Membership required est activé (par défaut), les utilisateurs sans org sont acheminés via une tâche de session choose-organization après la connexion. Clerk gère cela automatiquement dans <SignIn />, mais vous pouvez héberger l'UI vous-même :

import { ClerkProvider } from '@clerk/nextjs'

<ClerkProvider taskUrls={{ 'choose-organization': '/session-tasks/choose-organization' }}>
  {children}
</ClerkProvider>
// app/session-tasks/choose-organization/page.tsx
import { TaskChooseOrganization } from '@clerk/nextjs'

export default function Page() {
  return <TaskChooseOrganization redirectUrlComplete="/dashboard" />
}

TaskChooseOrganization est livré en tant que composant importé dans les SDKs basés sur React (@clerk/nextjs, @clerk/react, @clerk/react-router, @clerk/tanstack-react-start). Pour le SDK Frontend JS (@clerk/clerk-js), l'équivalent est clerk.mountTaskChooseOrganization(node) / clerk.unmountTaskChooseOrganization(node).

Core 2 ONLY (skip if current SDK): Les tâches de session ne sont pas disponibles. Forcez une sélection d'org à la connexion en redirigeant vers une page qui affiche <OrganizationSwitcher hidePersonal />.

Rôles par défaut + Permissions Système

Rôle Signification par défaut
org:admin Accès complet — toutes les Permissions Système, peut gérer org + adhésions
org:member Permissions lecture membres + lecture facturation uniquement

Vous pouvez créer jusqu'à 10 rôles personnalisés par instance dans Tableau de bord → Organisations → Rôles & Permissions. Rôle-par-org est contrôlé via Ensembles de rôles — consultez references/roles-permissions.md pour le modèle complet (rôles personnalisés, paramètres rôle Créateur/Par défaut, ensembles de rôles, et catalogue des Permissions Système).

Vérifications de facturation

has() supporte aussi les vérifications de plan et de feature quand Clerk Billing est activé :

const { has } = await auth()

has({ plan: 'gold' })        // plan d'abonnement
has({ feature: 'widgets' })  // droit de feature

Core 2 ONLY (skip if current SDK): has() ne supporte que role et permission. Les vérifications de facturation ne sont pas disponibles.

Consultez clerk-billing pour la surface de Facturation complète et le modèle de plan limité en sièges.

SSO Enterprise

SAML/OIDC par org. Configuré dans Tableau de bord → Configurer → Connexions Enterprise (ou par org : Organisations → sélectionnez org → Connexions SSO). La connexion SSO possède son domaine directement ; aucun Domaine Vérifié séparé n'est requis (et les deux features s'excluent mutuellement sur le même domaine). Jointure automatique à la première connexion SSO utilise JIT Provisioning, pas les Domaines Vérifiés. Fait clé : le champ provider se trouve sur enterpriseConnection, pas directement sur enterpriseAccounts[0]. Consultez references/enterprise-sso.md pour le flux complet et l'accès correct aux champs.

// Nom de stratégie pour SSO Enterprise (Core 3)
strategy: 'enterprise_sso'

Core 2 ONLY (skip if current SDK): Utilise strategy: 'saml' et user.samlAccounts à la place de user.enterpriseAccounts.

Pièges

maxAllowedMemberships limite les sièges

const clerk = await clerkClient()
await clerk.organizations.createOrganization({
  name: 'Acme Corp',
  createdBy: userId,
  maxAllowedMemberships: 10,
})

// Mettez à jour plus tard :
await clerk.organizations.updateOrganization(orgId, {
  maxAllowedMemberships: 25,
})

Pour les limites de sièges basées sur les niveaux liées à un abonnement, utilisez un Plan de facturation limité en sièges (voir clerk-billing).

La facturation contrôle les Permissions au niveau Feature

Quand Clerk Billing est activé, has({ permission: 'org:posts:edit' }) retourne false si la Feature associée à cette permission n'est pas incluse dans le Plan actif de l'organisation — même si l'utilisateur a la Permission assignée via son rôle. Assurez-vous que la Feature est attachée au Plan actif dans Tableau de bord → Facturation → Plans → Features.

Les mises à jour de métadonnées REMPLACENT, ne fusionnent pas

updateOrganization({ publicMetadata }) remplace toutes les métadonnées publiques. Lisez d'abord, étalez, puis écrivez :

const org = await clerk.organizations.getOrganization({ organizationId: orgId })
await clerk.organizations.updateOrganization(orgId, {
  publicMetadata: { ...org.publicMetadata, newField: 'value' },
})

S'applique identiquement à privateMetadata et aux métadonnées utilisateur via clerkClient.users.updateUser.

Signatures d'erreur (diagnostiquez rapidement)

La plupart des défaillances « liées à org » sont des configuration, pas du code. Ne modifiez pas les composants avant de vérifier ceux-ci :

Erreur / symptôme Cause racine Correction
orgId / orgSlug est undefined pour un utilisateur connecté Organisations non activées pour cette instance, OU l'utilisateur n'a pas d'org active (compte personnel) Activez dans Tableau de bord → Organisations ; vérifiez le mode Adhésion ; affichez <OrganizationSwitcher />
has({ permission: 'org:manage_members' }) toujours false Utilisation d'un slug de permission inventé Utilisez org:sys_memberships:manage (voir le catalogue roles-permissions.md)
has({ role }) retourne false mais l'utilisateur ressemble à un administrateur Jeton de session obsolète après changement de rôle Re-connectez-vous, ou rafraîchissez la session : await clerk.session?.reload()
has({ permission }) false même avec le rôle assigné Feature non attachée au Plan actif (la facturation contrôle les permissions) Tableau de bord → Facturation → Plans → attachez Feature
<OrganizationSwitcher /> n'affiche pas « Compte Personnel » Mode Membership required activé (par défaut depuis 22 août 2025) Tableau de bord → Paramètres Organisations → Membership optional
TaskChooseOrganization lance « cannot render when a user doesn't have current session tasks » Affiché en dehors d'un contexte de tâche de session choose-organization Enveloppez dans une route de tâche de session choose-organization uniquement ; n'affichez pas sans conditions
enterpriseAccounts[0].provider est undefined Accès à provider au mauvais niveau d'imbrication Utilisez user.enterpriseAccounts[0].enterpriseConnection?.provider

Pattern d'autorisation (Exemple complet)

Composant serveur protégeant une page d'admin scoped par slug :

import { auth } from '@clerk/nextjs/server'
import { redirect } from 'next/navigation'

export default async function AdminPage({ params }: { params: { slug: string } }) {
  const { orgSlug, has } = await auth()

  if (orgSlug !== params.slug) redirect('/dashboard')
  if (!has({ role: 'org:admin' })) redirect(`/orgs/${orgSlug}`)

  return <div>Paramètres d'administration pour {orgSlug}</div>
}

Pour la protection au niveau middleware (Next.js), consultez references/nextjs-patterns.md.

Invitations (forme courte)

Envoyez depuis une action serveur ou un gestionnaire de route :

import { clerkClient, auth } from '@clerk/nextjs/server'

export async function inviteMember(organizationId: string, emailAddress: string, role: string) {
  const { userId, has } = await auth()

  if (!userId) throw new Error('Not signed in')
  if (!has({ permission: 'org:sys_memberships:manage' })) {
    throw new Error('Not authorized to invite members')
  }

  const clerk = await clerkClient()
  return clerk.organizations.createOrganizationInvitation({
    organizationId,
    inviterUserId: userId,       // requis selon l'API Backend
    emailAddress,
    role,                        // p.ex. 'org:admin' ou 'org:member'
    redirectUrl: 'https://yourapp.com/accept-invite',
  })
}

Le cycle de vie complet (lister, révoquer, créer en masse, UI <OrganizationProfile /> intégrée) se trouve dans references/invitations.md.

Flux de travail

  1. Activez — Organisations + mode Adhésion dans le Tableau de bord
  2. Créez une org — via composant UI ou API Backend
  3. Invitez des membres — API Backend ou UI intégrée, avec inviterUserId
  4. Contrôlez l'accèshas({ role }) / has({ permission }) avec les noms canoniques org:sys_*
  5. Scoped les routesorgSlug === params.slug sur chaque page protégée
  6. Changez d'org<OrganizationSwitcher /> gère tout le flux

Voir aussi

  • clerk-setup — Installation initiale de Clerk
  • clerk-billing — Plans limités en sièges, facturation par plan, has({ plan }) / has({ feature })
  • clerk-webhooks — Synchronisez les événements org à votre base de données (organization.created, organizationMembership.*)
  • clerk-backend-api — Référence API Backend complète
  • clerk-nextjs-patterns — Middleware spécifique au framework, actions serveur, caching

Skills similaires