diff --git a/docs/authentication/enterprise-connections/authentication-flows.mdx b/docs/authentication/enterprise-connections/authentication-flows.mdx index 05a1d583d6..ebd0005f87 100644 --- a/docs/authentication/enterprise-connections/authentication-flows.mdx +++ b/docs/authentication/enterprise-connections/authentication-flows.mdx @@ -3,11 +3,7 @@ title: Enterprise SSO authentication flows description: Learn about the Enterprise SSO authentication flows. --- -Clerk offers the following types of Enterprise SSO connections: [EASIE](#easie), [SAML](#saml), and [OIDC](#oidc). - -## EASIE - -EASIE connections support the authentication flows described at [easie.dev](https://easie.dev). +Clerk offers the following types of Enterprise SSO connections: [SAML](#saml), [OIDC](#oidc), and [EASIE](#easie). ## SAML @@ -60,3 +56,7 @@ To mitigate the risks associated with IdP-initiated flows, Clerk implements seve ## OIDC Clerk supports Enterprise SSO via the OpenID Connect (OIDC) protocol, either through [EASIE](#easie) or by [integrating with any OIDC-compatible provider](/docs/authentication/enterprise-connections/oidc/custom-provider). + +### EASIE + +EASIE connections support the authentication flows described at [easie.dev](https://easie.dev). diff --git a/docs/components/organization/organization-list.mdx b/docs/components/organization/organization-list.mdx index 9566924e3c..66b45541aa 100644 --- a/docs/components/organization/organization-list.mdx +++ b/docs/components/organization/organization-list.mdx @@ -3,68 +3,68 @@ title: '`` component' description: Clerk's component is used to display organization related memberships, invitations, and suggestions for the user. --- -![The \ component is used to display organization related memberships, invitations, and suggestions for the user.](/docs/images/ui-components/organization-list.png){{ style: { maxWidth: '460px' } }} +![The \ component displays organization-related memberships and automatic invitations and suggestions for the user.](/docs/images/ui-components/organization-list.png){{ style: { maxWidth: '460px' } }} -The `` component is used to display organization related memberships, [invitations, and suggestions](/docs/organizations/overview#automatic-invitations-and-suggestions) for the user. +The `` component displays organization-related memberships and automatic [invitations](/docs/organizations/verified-domains#automatic-invitations) and [suggestions](/docs/organizations/verified-domains#automatic-suggestions) for the user. ## Properties -All props are optional. +The `` component accepts the following properties, all of which are **optional**: - - `hidePersonal` - - `boolean` + - `afterCreateOrganizationUrl` + - ((org: [Organization][org-ref]) => string) | string - By default, users can switch between organization and their personal account. This option controls whether `` will include the user's personal account in the organization list. Setting this to `false` will hide the personal account entry, and users will only be able to switch between organizations. Defaults to `false`. + The full URL or path to navigate to after creating a new organization. --- - - `skipInvitationScreen` - - `boolean | undefined` + - `afterSelectOrganizationUrl` + - ((org: [Organization][org-ref]) => string) | string - Hides the screen for sending invitations after an organization is created. When left undefined Clerk will automatically hide the screen if the number of max allowed members is equal to 1. Defaults to `false`. + The full URL or path to navigate to after selecting an organization. Defaults to `undefined`. --- - - `appearance` - - [Appearance](/docs/customization/overview) | undefined + - `afterSelectPersonalUrl` + - ((org: [Organization][org-ref]) => string) | string - Optional object to style your components. Will only affect [Clerk components](/docs/components/overview) and not [Account Portal](/docs/customization/account-portal/overview) pages. + The full URL or path to navigate to after selecting the personal account. Defaults to `undefined`. --- - - `afterCreateOrganizationUrl` - - ((org: [Organization][org-ref]) => string) | string + - `appearance` + - [Appearance](/docs/customization/overview) | undefined - Full URL or path to navigate to after creating a new organization. + Optional object to style your components. Will only affect [Clerk components](/docs/components/overview) and not [Account Portal](/docs/customization/account-portal/overview) pages. --- - - `afterSelectOrganizationUrl` - - ((org: [Organization][org-ref]) => string) | string + - `fallback?` + - `ReactNode` - Full URL or path to navigate to after selecting an organization. Defaults to `undefined`. + An optional element to be rendered while the component is mounting. --- - - `afterSelectPersonalUrl` - - ((org: [Organization][org-ref]) => string) | string + - `hidePersonal` + - `boolean` - Full URL or path to navigate to after selecting the personal account. Defaults to `undefined`. + A boolean that controls whether `` will include the user's personal account in the organization list. Setting this to `true` will hide the personal account option, and users will only be able to switch between organizations. Defaults to `false`. --- - `hideSlug` - `boolean` - Hides the optional slug field in the organization creation screen. + A boolean that controls whether the optional slug field in the organization creation screen is hidden. Defaults to `false`. --- - - `fallback?` - - `ReactNode` + - `skipInvitationScreen` + - `boolean | undefined` - An optional element to be rendered while the component is mounting. + A boolean that controls whether the screen for sending invitations after an organization is created is hidden. When `undefined`, Clerk will automatically hide the screen if the number of max allowed members is equal to 1. Defaults to `false`. ## Usage with frameworks @@ -170,103 +170,105 @@ All props are optional. -## Usage with JavaScript + + ## Usage with JavaScript -The following methods available on an instance of the [`Clerk`](/docs/references/javascript/clerk/clerk) class are used to render and control the `` component: + The following methods available on an instance of the [`Clerk`](/docs/references/javascript/clerk/clerk) class are used to render and control the `` component: -- [`mountOrganizationList()`](#mount-organization-list) -- [`unmountOrganizationList()`](#unmount-organization-list) + - [`mountOrganizationList()`](#mount-organization-list) + - [`unmountOrganizationList()`](#unmount-organization-list) -The following examples assume that you have followed the [quickstart](/docs/quickstarts/javascript) in order to add Clerk to your JavaScript application. + The following examples assume that you have followed the [quickstart](/docs/quickstarts/javascript) in order to add Clerk to your JavaScript application. -## `mountOrganizationList()` + ## `mountOrganizationList()` -Render the `` component to an HTML `
` element. + Render the `` component to an HTML `
` element. -```typescript -function mountOrganizationList(node: HTMLDivElement, props?: OrganizationListProps): void -``` + ```typescript + function mountOrganizationList(node: HTMLDivElement, props?: OrganizationListProps): void + ``` -### `mountOrganizationList()` params + ### `mountOrganizationList()` params - - - `node` - - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) + + - `node` + - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) - The `
` element used to render in the `` component + The `
` element used to render in the `` component - --- + --- - - `props?` - - [`OrganizationListProps`](#properties) + - `props?` + - [`OrganizationListProps`](#properties) - The properties to pass to the `` component - + The properties to pass to the `` component + -### `mountOrganizationList()` usage + ### `mountOrganizationList()` usage -```js {{ filename: 'main.js', mark: [15] }} -import { Clerk } from '@clerk/clerk-js' + ```js {{ filename: 'main.js', mark: [15] }} + import { Clerk } from '@clerk/clerk-js' -// Initialize Clerk with your Clerk Publishable Key -const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + // Initialize Clerk with your Clerk Publishable Key + const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY -const clerk = new Clerk(clerkPubKey) -await clerk.load() + const clerk = new Clerk(clerkPubKey) + await clerk.load() -document.getElementById('app').innerHTML = ` -
-` + document.getElementById('app').innerHTML = ` +
+ ` -const orgListDiv = document.getElementById('organization-list') + const orgListDiv = document.getElementById('organization-list') -clerk.mountOrganizationList(orgListDiv) -``` + clerk.mountOrganizationList(orgListDiv) + ``` -## `unmountOrganizationList()` + ## `unmountOrganizationList()` -Unmount and run cleanup on an existing `` component instance. + Unmount and run cleanup on an existing `` component instance. -```typescript -function unmountOrganizationList(node: HTMLDivElement): void -``` + ```typescript + function unmountOrganizationList(node: HTMLDivElement): void + ``` -### `unmountOrganizationList()` params + ### `unmountOrganizationList()` params - - - `node` - - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) + + - `node` + - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) - The container `
` element with a rendered `` component instance - + The container `
` element with a rendered `` component instance + -### `unmountOrganizationList()` usage + ### `unmountOrganizationList()` usage -```js {{ filename: 'main.js', mark: [19] }} -import { Clerk } from '@clerk/clerk-js' + ```js {{ filename: 'main.js', mark: [19] }} + import { Clerk } from '@clerk/clerk-js' -// Initialize Clerk with your Clerk Publishable Key -const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + // Initialize Clerk with your Clerk Publishable Key + const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY -const clerk = new Clerk(clerkPubKey) -await clerk.load() + const clerk = new Clerk(clerkPubKey) + await clerk.load() -document.getElementById('app').innerHTML = ` -
-` + document.getElementById('app').innerHTML = ` +
+ ` -const orgListDiv = document.getElementById('organization-list') + const orgListDiv = document.getElementById('organization-list') -clerk.mountOrganizationList(orgListDiv) + clerk.mountOrganizationList(orgListDiv) -// ... + // ... -clerk.unmountOrganizationList(orgListDiv) -``` + clerk.unmountOrganizationList(orgListDiv) + ``` + ## Force organizations -If you would like to prohibit people from using their personal accounts and force them to be part of an organization, the `hidePersonal` property forces your user to join or create an organization in order to continue. For more information on how to hide Personal Accounts and force organizations, see the [dedicated guide](/docs/organizations/force-organizations). +If you would like to prohibit people from using their personal accounts and force them to be part of an organization, the `hidePersonal` property forces your user to join or create an organization in order to continue. For more information on how to hide personal accounts and force organizations, see the [dedicated guide](/docs/organizations/force-organizations). ```tsx {{ filename: 'organization-portal.tsx' }} export default function OrganizationListPage() { diff --git a/docs/components/organization/organization-profile.mdx b/docs/components/organization/organization-profile.mdx index 53d1894eed..a79e2d55de 100644 --- a/docs/components/organization/organization-profile.mdx +++ b/docs/components/organization/organization-profile.mdx @@ -3,9 +3,9 @@ title: '`` component' description: Clerk's component is used to render a beautiful, full-featured organization management UI that allows users to manage their organization profile and security settings. --- -![The \ component renders a full-featured organization management UI that allows users to manage their organization profile and security settings.](/docs/images/ui-components/organization-profile.png) +![The \ component allows users to manage their organization membership and security settings.](/docs/images/ui-components/organization-profile.png) -The `` component is used to render a beautiful, full-featured organization management UI that allows users to manage their organization profile and security settings. +The `` component allows users to manage their organization membership and security settings. This component's **General** tab displays the organization's information and the **Leave organization** button. Admins will be able to see the **Update profile** button, **Verified domains** section, and **Delete organization** button. @@ -13,7 +13,7 @@ The **Members** tab shows the organization's members along with their join dates ## Properties -All props are optional. +The `` component accepts the following properties, all of which are **optional**: - `appearance` @@ -26,35 +26,35 @@ All props are optional. - `afterLeaveOrganizationUrl` - `string` - Full URL or path to navigate to after leaving an organization. + The full URL or path to navigate to after leaving an organization. --- - - `routing` - - `'hash' | 'path'` + - `customPages` + - `CustomPages[]` - The [routing](/docs/how-clerk-works/routing) strategy for your pages. Defaults to `'path'` for frameworks that handle routing, such as Next.js and Remix. Defaults to `hash` for all other SDK's, such as React. + An array of custom pages to add to the organization profile. Only available for the [JavaScript SDK](/docs/references/javascript/overview). To add custom pages with React-based SDK's, see the [dedicated guide](/docs/customization/organization-profile). --- - - `path` - - `string` + - `fallback?` + - `ReactNode` - The path where the component is mounted on when `routing` is set to `path`. It is ignored in hash-based routing. For example: `/organization-profile`. + An optional element to be rendered while the component is mounting. --- - - `customPages` - - `CustomPages[]` + - `path` + - `string` - An array of custom pages to add to the organization profile. Only available for the [JavaScript SDK](/docs/references/javascript/overview). To add custom pages with React-based SDK's, see the [dedicated guide](/docs/customization/organization-profile). + The path where the component is mounted on when `routing` is set to `path`. It is ignored in hash- and virtual-based routing.
For example: `/organization-profile`. --- - - `fallback?` - - `ReactNode` + - `routing` + - `'hash' | 'path'` - An optional element to be rendered while the component is mounting. + The [routing](/docs/how-clerk-works/routing) strategy for your pages.
Defaults to `'path'` for frameworks that handle routing, such as Next.js and Remix. Defaults to `hash` for all other SDK's, such as React.
## Usage with frameworks @@ -130,166 +130,168 @@ All props are optional. -## Usage with JavaScript + + ## Usage with JavaScript -The following methods available on an instance of the [`Clerk`](/docs/references/javascript/clerk/clerk) class are used to render and control the `` component: + The following methods available on an instance of the [`Clerk`](/docs/references/javascript/clerk/clerk) class are used to render and control the `` component: -- [`mountOrganizationProfile()`](#mount-organization-profile) -- [`unmountOrganizationProfile()`](#unmount-organization-profile) -- [`openOrganizationProfile()`](#open-organization-profile) -- [`closeOrganizationProfile()`](#close-organization-profile) + - [`mountOrganizationProfile()`](#mount-organization-profile) + - [`unmountOrganizationProfile()`](#unmount-organization-profile) + - [`openOrganizationProfile()`](#open-organization-profile) + - [`closeOrganizationProfile()`](#close-organization-profile) -The following examples assume that you have followed the [quickstart](/docs/quickstarts/javascript) in order to add Clerk to your JavaScript application. + The following examples assume that you have followed the [quickstart](/docs/quickstarts/javascript) in order to add Clerk to your JavaScript application. -### mountOrganizationProfile() + ### mountOrganizationProfile() -Render the `` component to an HTML `
` element. + Render the `` component to an HTML `
` element. -```typescript -function mountOrganizationProfile(node: HTMLDivElement, props?: OrganizationProfileProps): void -``` + ```typescript + function mountOrganizationProfile(node: HTMLDivElement, props?: OrganizationProfileProps): void + ``` -#### `mountOrganizationProfile()` params + #### `mountOrganizationProfile()` params - - - `node` - - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) + + - `node` + - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) - The `
` element used to render in the `` component + The `
` element used to render in the `` component - --- + --- - - `props?` - - [`OrganizationProfileProps`](#properties) + - `props?` + - [`OrganizationProfileProps`](#properties) - The properties to pass to the `` component - + The properties to pass to the `` component + -#### `mountOrganizationProfile()` usage + #### `mountOrganizationProfile()` usage -```js {{ filename: 'main.js', mark: [15] }} -import { Clerk } from '@clerk/clerk-js' + ```js {{ filename: 'main.js', mark: [15] }} + import { Clerk } from '@clerk/clerk-js' -// Initialize Clerk with your Clerk Publishable Key -const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + // Initialize Clerk with your Clerk Publishable Key + const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY -const clerk = new Clerk(clerkPubKey) -await clerk.load() + const clerk = new Clerk(clerkPubKey) + await clerk.load() -document.getElementById('app').innerHTML = ` -
-` + document.getElementById('app').innerHTML = ` +
+ ` -const orgProfileDiv = document.getElementById('organization-profile') + const orgProfileDiv = document.getElementById('organization-profile') -clerk.mountOrganizationProfile(orgProfileDiv) -``` + clerk.mountOrganizationProfile(orgProfileDiv) + ``` -### unmountOrganizationProfile() + ### unmountOrganizationProfile() -Unmount and run cleanup on an existing `` component instance. + Unmount and run cleanup on an existing `` component instance. -```typescript -function unmountOrganizationProfile(node: HTMLDivElement): void -``` + ```typescript + function unmountOrganizationProfile(node: HTMLDivElement): void + ``` -#### `unmountOrganizationProfile()` params + #### `unmountOrganizationProfile()` params - - - `node` - - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) + + - `node` + - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) - The container `
` element with a rendered `` component instance. - + The container `
` element with a rendered `` component instance. + -#### `unmountOrganizationProfile()` usage + #### `unmountOrganizationProfile()` usage -```js {{ filename: 'main.js', mark: [19] }} -import { Clerk } from '@clerk/clerk-js' + ```js {{ filename: 'main.js', mark: [19] }} + import { Clerk } from '@clerk/clerk-js' -// Initialize Clerk with your Clerk Publishable Key -const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + // Initialize Clerk with your Clerk Publishable Key + const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY -const clerk = new Clerk(clerkPubKey) -await clerk.load() + const clerk = new Clerk(clerkPubKey) + await clerk.load() -document.getElementById('app').innerHTML = ` -
-` + document.getElementById('app').innerHTML = ` +
+ ` -const orgProfileDiv = document.getElementById('organization-profile') + const orgProfileDiv = document.getElementById('organization-profile') -clerk.mountOrganizationProfile(orgProfileDiv) + clerk.mountOrganizationProfile(orgProfileDiv) -// ... + // ... -clerk.unmountOrganizationProfile(orgProfileDiv) -``` + clerk.unmountOrganizationProfile(orgProfileDiv) + ``` -### `openOrganizationProfile()` + ### `openOrganizationProfile()` -Opens the `` component as an overlay at the root of your HTML `body` element. + Opens the `` component as an overlay at the root of your HTML `body` element. -```typescript -function openOrganizationProfile(props?: OrganizationProfileProps): void -``` + ```typescript + function openOrganizationProfile(props?: OrganizationProfileProps): void + ``` -#### `openOrganizationProfile()` params + #### `openOrganizationProfile()` params - - - `props?` - - [`OrganizationProfileProps`](#properties) + + - `props?` + - [`OrganizationProfileProps`](#properties) - The properties to pass to the `` component - + The properties to pass to the `` component + -#### `openOrganizationProfile()` usage + #### `openOrganizationProfile()` usage -```js {{ filename: 'main.js', mark: [15] }} -import { Clerk } from '@clerk/clerk-js' + ```js {{ filename: 'main.js', mark: [15] }} + import { Clerk } from '@clerk/clerk-js' -// Initialize Clerk with your Clerk Publishable Key -const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + // Initialize Clerk with your Clerk Publishable Key + const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY -const clerk = new Clerk(clerkPubKey) -await clerk.load() + const clerk = new Clerk(clerkPubKey) + await clerk.load() -document.getElementById('app').innerHTML = ` -
-` + document.getElementById('app').innerHTML = ` +
+ ` -const orgProfileDiv = document.getElementById('organization-profile') + const orgProfileDiv = document.getElementById('organization-profile') -clerk.openOrganizationProfile(orgProfileDiv) -``` + clerk.openOrganizationProfile(orgProfileDiv) + ``` -### `closeOrganizationProfile()` + ### `closeOrganizationProfile()` -Closes the organization profile overlay. + Closes the organization profile overlay. -```typescript -function closeOrganizationProfile(): void -``` + ```typescript + function closeOrganizationProfile(): void + ``` -#### `closeOrganizationProfile()` usage + #### `closeOrganizationProfile()` usage -```js {{ filename: 'main.js', mark: [15] }} -import { Clerk } from '@clerk/clerk-js' + ```js {{ filename: 'main.js', mark: [15] }} + import { Clerk } from '@clerk/clerk-js' -// Initialize Clerk with your Clerk Publishable Key -const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + // Initialize Clerk with your Clerk Publishable Key + const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY -const clerk = new Clerk(clerkPubKey) -await clerk.load() + const clerk = new Clerk(clerkPubKey) + await clerk.load() -document.getElementById('app').innerHTML = ` -
-` + document.getElementById('app').innerHTML = ` +
+ ` -const orgProfileDiv = document.getElementById('organization-profile') + const orgProfileDiv = document.getElementById('organization-profile') -clerk.closeOrganizationProfile(orgProfileDiv) -``` + clerk.closeOrganizationProfile(orgProfileDiv) + ``` + ## Customization diff --git a/docs/components/organization/organization-switcher.mdx b/docs/components/organization/organization-switcher.mdx index 8aee87aef7..5553e736f0 100644 --- a/docs/components/organization/organization-switcher.mdx +++ b/docs/components/organization/organization-switcher.mdx @@ -3,107 +3,105 @@ title: '`` component' description: Clerk's component is used to enable the ability to switch between available organizations the user may be part of in your application. --- -![The \ component is used to enable the ability to switch between available organizations the user may be part of in your application.](/docs/images/ui-components/organization-switcher.png){{ style: { maxWidth: '436px' } }} +![The \ component allows a user to switch between their account types - their personal account and their joined organizations.](/docs/images/ui-components/organization-switcher.png){{ style: { maxWidth: '436px' } }} -The `` component allows a user to switch between their account types - their personal account and their joined organizations. This component is useful for applications that have a multi-tenant architecture, where users can be part of multiple organizations. - -This component will show notifications to the user if they have organization [invitations](/docs/organizations/overview#organization-invitations) or [suggestions](/docs/organizations/overview#suggestions). Admins will be able to see notifications for [requests](/docs/organizations/overview#membership-requests) to join an organization. +The `` component allows a user to switch between their account types - their personal account and their joined organizations. This component is useful for applications that have a multi-tenant architecture, where users can be part of multiple organizations. It handles all organization-related flows, including full organization management for admins. Learn more about [organizations](/docs/organizations/overview). If you would like to learn how to hide a user's personal account in order to enforce an organization-centric application, see the [dedicated guide](/docs/organizations/force-organizations). -## `` properties +## Properties -All props below are optional. +The `` component accepts the following properties, all of which are **optional**: - `afterCreateOrganizationUrl` - `string` - Full URL or path to navigate to after creating a new organization. + The full URL or path to navigate to after creating a new organization. --- - - `appearance` - - [Appearance](/docs/customization/overview) | undefined + - `afterLeaveOrganizationUrl` + - `string` - Optional object to style your components. Will only affect [Clerk components](/docs/components/overview) and not [Account Portal](/docs/customization/account-portal/overview) pages. + The full URL or path to navigate to after the user leaves the currently active organization. --- - - `createOrganizationUrl` + - `afterSelectOrganizationUrl` - `string` - Full URL or path where the [``][createorg-ref] component is mounted. + The full URL or path to navigate to after a successful organization switch. --- - - `organizationProfileUrl` - - `string` + - `appearance` + - [Appearance](/docs/customization/overview) | undefined - Full URL or path where the [``][orgprofile-ref] component is mounted. + Optional object to style your components. Will only affect [Clerk components](/docs/components/overview) and not [Account Portal](/docs/customization/account-portal/overview) pages. --- - `createOrganizationMode` - `'modal' | 'navigation'` - Controls whether clicking the "Create organization" button will cause the [``][createorg-ref] component to open as a modal, or if the browser will navigate to the `createOrganizationUrl` where `` is mounted as a page. Defaults to: `'modal'`. + A boolean that controls whether clicking the "Create organization" button will cause the [``][createorg-ref] component to open as a modal, or if the browser will navigate to the `createOrganizationUrl` where `` is mounted as a page. Defaults to: `'modal'`. --- - - `organizationProfileMode` - - `'modal' | 'navigation'` + - `createOrganizationUrl` + - `string` - Controls whether clicking the **Manage organization** button will cause the [``][orgprofile-ref] component to open as a modal, or if the browser will navigate to the `organizationProfileUrl` where `` is mounted as a page. Defaults to: `'modal'`. + The full URL or path where the ``][createorg-ref] component is mounted. --- - - `afterLeaveOrganizationUrl` - - `string` + - `defaultOpen` + - `boolean` - Full URL or path to navigate to after the user leaves the currently active organization. + A boolean that controls the default state of the `` component. --- - - `afterSelectOrganizationUrl` - - `string` + - `fallback?` + - `ReactNode` - Full URL or path to navigate to after a successful organization switch. + An optional element to be rendered while the component is mounting. --- - `hidePersonal` - `boolean` - By default, users can switch between organizations and their personal workspace. This option controls whether `` will include the user's personal workspace in the organization list. Setting this to `true` will hide the personal workspace entry, and allow users to switch only between organizations. Defaults to: `false`. + A boolean that controls whether `` will include the user's personal account in the organization list. Setting this to `true` will hide the personal account option, and users will only be able to switch between organizations. Defaults to `false`. --- - - `defaultOpen` + - `hideSlug` - `boolean` - Controls the default state of the `` component. + A boolean that controls whether the optional slug field in the organization creation screen is hidden. --- - - `organizationProfileProps` - - `object` + - `organizationProfileMode` + - `'modal' | 'navigation'` - Specify options for the underlying [``][orgprofile-ref] component. For example: `{appearance: {...}}` + A boolean that controls whether clicking the **Manage organization** button will cause the [``][orgprofile-ref] component to open as a modal, or if the browser will navigate to the `organizationProfileUrl` where `` is mounted as a page. Defaults to: `'modal'`. --- - - `hideSlug` - - `boolean` + - `organizationProfileProps` + - `object` - Hides the optional slug field in the organization creation screen. + Specify options for the underlying [``][orgprofile-ref] component. For example: `{appearance: {...}}` --- - - `fallback?` - - `ReactNode` + - `organizationProfileUrl` + - `string` - An optional element to be rendered while the component is mounting. + The full URL or path where the [``][orgprofile-ref] component is mounted. ## Usage with frameworks @@ -177,99 +175,101 @@ All props below are optional. -## Usage with JavaScript + + ## Usage with JavaScript -The following methods available on an instance of the [`Clerk`](/docs/references/javascript/clerk/clerk) class are used to render and control the `` component: + The following methods available on an instance of the [`Clerk`](/docs/references/javascript/clerk/clerk) class are used to render and control the `` component: -- [`mountOrganizationSwitcher()`](#mount-organization-switcher) -- [`unmountOrganizationSwitcher()`](#unmount-organization-switcher) + - [`mountOrganizationSwitcher()`](#mount-organization-switcher) + - [`unmountOrganizationSwitcher()`](#unmount-organization-switcher) -The following examples assume that you have followed the [quickstart](/docs/quickstarts/javascript) in order to add Clerk to your JavaScript application. + The following examples assume that you have followed the [quickstart](/docs/quickstarts/javascript) in order to add Clerk to your JavaScript application. -## `mountOrganizationSwitcher()` + ## `mountOrganizationSwitcher()` -Render the `` component to an HTML `
` element. + Render the `` component to an HTML `
` element. -```typescript -function mountOrganizationSwitcher(node: HTMLDivElement, props?: OrganizationSwitcherProps): void -``` + ```typescript + function mountOrganizationSwitcher(node: HTMLDivElement, props?: OrganizationSwitcherProps): void + ``` -### mountOrganizationSwitcher() params + ### mountOrganizationSwitcher() params - - - `node` - - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) + + - `node` + - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) - The `
` element used to render in the `` component + The `
` element used to render in the `` component - --- + --- - - `props?` - - [`OrganizationSwitcherProps`](#properties) + - `props?` + - [`OrganizationSwitcherProps`](#properties) - The properties to pass to the `` component - + The properties to pass to the `` component + -### mountOrganizationSwitcher() usage + ### mountOrganizationSwitcher() usage -```js {{ filename: 'main.js', mark: [15] }} -import { Clerk } from '@clerk/clerk-js' + ```js {{ filename: 'main.js', mark: [15] }} + import { Clerk } from '@clerk/clerk-js' -// Initialize Clerk with your Clerk Publishable Key -const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + // Initialize Clerk with your Clerk Publishable Key + const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY -const clerk = new Clerk(clerkPubKey) -await clerk.load() + const clerk = new Clerk(clerkPubKey) + await clerk.load() -document.getElementById('app').innerHTML = ` -
-` + document.getElementById('app').innerHTML = ` +
+ ` -const orgSwitcherDiv = document.getElementById('organization-switcher') + const orgSwitcherDiv = document.getElementById('organization-switcher') -clerk.mountOrganizationSwitcher(orgSwitcherDiv) -``` + clerk.mountOrganizationSwitcher(orgSwitcherDiv) + ``` -## unmountOrganizationSwitcher() + ## unmountOrganizationSwitcher() -Unmount and run cleanup on an existing `` component instance. + Unmount and run cleanup on an existing `` component instance. -```typescript -function unmountOrganizationSwitcher(node: HTMLDivElement): void -``` + ```typescript + function unmountOrganizationSwitcher(node: HTMLDivElement): void + ``` -### unmountOrganizationSwitcher() params + ### unmountOrganizationSwitcher() params - - - `node` - - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) + + - `node` + - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) - The container `
` element with a rendered `` component instance - + The container `
` element with a rendered `` component instance + -### unmountOrganizationSwitcher() usage + ### unmountOrganizationSwitcher() usage -```js {{ filename: 'main.js', mark: [19] }} -import { Clerk } from '@clerk/clerk-js' + ```js {{ filename: 'main.js', mark: [19] }} + import { Clerk } from '@clerk/clerk-js' -// Initialize Clerk with your Clerk Publishable Key -const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + // Initialize Clerk with your Clerk Publishable Key + const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY -const clerk = new Clerk(clerkPubKey) -await clerk.load() + const clerk = new Clerk(clerkPubKey) + await clerk.load() -document.getElementById('app').innerHTML = ` -
-` + document.getElementById('app').innerHTML = ` +
+ ` -const orgSwitcherDiv = document.getElementById('organization-switcher') + const orgSwitcherDiv = document.getElementById('organization-switcher') -clerk.mountOrganizationSwitcher(orgSwitcherDiv) + clerk.mountOrganizationSwitcher(orgSwitcherDiv) -// ... + // ... -clerk.unmountOrganizationSwitcher(orgSwitcherDiv) -``` + clerk.unmountOrganizationSwitcher(orgSwitcherDiv) + ``` + ## Customization diff --git a/docs/organizations/accept-organization-invitations.mdx b/docs/custom-flows/accept-organization-invitations.mdx similarity index 87% rename from docs/organizations/accept-organization-invitations.mdx rename to docs/custom-flows/accept-organization-invitations.mdx index 4bbaf5f407..8322df1f1e 100644 --- a/docs/organizations/accept-organization-invitations.mdx +++ b/docs/custom-flows/accept-organization-invitations.mdx @@ -1,17 +1,17 @@ --- -title: Accept organization invitations -description: Learn how to use the Clerk API to build a custom flows for handling organization invitations. +title: Handle accepting organization invitation links +description: Learn how to use the Clerk API to build a custom flows for accepting organization invitations. --- -When a user visits an [organization invitation](/docs/organizations/invitations) link, and no custom redirect URL was specified, and they have an account for your application, then they will be redirected to the [Account Portal sign-in page](/docs/customization/account-portal/overview#sign-in-or-up). If they do not have an account for your application, they will be redirected to the [Account Portal sign-up page](/docs/customization/account-portal/overview#sign-in-or-up). +When a user visits an [organization invitation](/docs/organizations/invitations) link, and no custom redirect URL was specified, then they will be redirected to the [Account Portal sign-in page](/docs/customization/account-portal/overview#sign-in). -However, if you specified [a redirect URL when creating the invitation](/docs/users/invitations#redirect-url), you must handle the sign-up and sign-in flows in your code for that page. You can either embed the [`](/docs/components/authentication/sign-in) component on that page, or if the prebuilt component doesn't meet your specific needs or if you require more control over the logic, you can rebuild the existing Clerk flows using the Clerk API. +However, if you specified [a redirect URL when creating the invitation](/docs/organizations/invitations#redirect-url), you must handle the sign-up and sign-in flows in your code for that page. You can either embed the [``](/docs/components/authentication/sign-in) component on that page, or if the prebuilt component doesn't meet your specific needs or if you require more control over the logic, you can rebuild the existing Clerk flows using the Clerk API. -This guide will demonstrate how to build custom flows to handle organization invitations. +This guide demonstrates how to use Clerk's API to build a custom flow for accepting organization invitations from a link. -## Create the sign-up flow +## Build the custom flow Once the user visits the invitation link and is redirected to the specified URL, the query parameters `__clerk_ticket` and `__clerk_status` will be appended to the URL. @@ -33,7 +33,7 @@ The following example demonstrates how to handle both sign-up and sign-in flows - ```tsx {{ filename: 'app/accept-invitation/page.tsx' }} + ```tsx {{ filename: 'app/accept-invitation/page.tsx', collapsible: true }} 'use client' import * as React from 'react' diff --git a/docs/custom-flows/invitations.mdx b/docs/custom-flows/application-invitations.mdx similarity index 95% rename from docs/custom-flows/invitations.mdx rename to docs/custom-flows/application-invitations.mdx index fe173b5e8f..836c402491 100644 --- a/docs/custom-flows/invitations.mdx +++ b/docs/custom-flows/application-invitations.mdx @@ -5,13 +5,13 @@ description: Learn how to use the Clerk API to build a custom flow for handling -When a user visits an [invitation](/docs/users/invitations) link, and no custom redirect URL was specified, then they will be redirected to the [Account Portal sign-up page](/docs/customization/account-portal/overview#sign-in-or-up) and **their email address will be automatically verified.** +When a user visits an [invitation](/docs/users/invitations) link, and no custom redirect URL was specified, then they will be redirected to the [Account Portal sign-up page](/docs/customization/account-portal/overview#sign-up) and **their email address will be automatically verified.** However, if you specified [a redirect URL when creating the invitation](/docs/users/invitations#redirect-url), you must handle the sign-up flow in your code for that page. You can either embed the [``](/docs/components/authentication/sign-up) component on that page, or if the prebuilt component doesn't meet your specific needs or if you require more control over the logic, you can rebuild the existing Clerk flows using the Clerk API. -This guide will demonstrate how to build a custom sign-up flow to handle application invitations. +This guide demonstrates how to use Clerk's API to build a custom flow for accepting application invitations. -## Create the sign-up flow +## Build the custom flow Once the user visits the invitation link and is redirected to the specified URL, the query parameter `__clerk_ticket` will be appended to the URL. This query parameter contains the invitation token. @@ -21,7 +21,7 @@ To create a sign-up flow using the invitation token, you need to extract the tok - ```tsx {{ filename: 'app/accept-invitation/page.tsx' }} + ```tsx {{ filename: 'app/accept-invitation/page.tsx', collapsible: true }} 'use client' import * as React from 'react' @@ -132,7 +132,7 @@ To create a sign-up flow using the invitation token, you need to extract the tok - ```html {{ filename: 'index.html' }} + ```html {{ filename: 'index.html', collapsible: true }} @@ -161,7 +161,7 @@ To create a sign-up flow using the invitation token, you need to extract the tok ``` - ```js {{ filename: 'main.js' }} + ```js {{ filename: 'main.js', collapsible: true }} import { Clerk } from '@clerk/clerk-js' const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY diff --git a/docs/organizations/creating-organizations.mdx b/docs/custom-flows/create-organizations.mdx similarity index 80% rename from docs/organizations/creating-organizations.mdx rename to docs/custom-flows/create-organizations.mdx index c8c9dc4410..67f4aebc9b 100644 --- a/docs/organizations/creating-organizations.mdx +++ b/docs/custom-flows/create-organizations.mdx @@ -5,15 +5,13 @@ description: Learn how to use the Clerk API to build a custom flow for creating -[Organizations](/docs/organizations/overview) are a powerful feature in Clerk that allow you to group users together and manage their permissions. Organizations can be created and managed using the [Clerk Dashboard](https://dashboard.clerk.com), but you can also allow users within your application to create organizations. - -This guide will demonstrate how to use the Clerk API to build a custom flow for creating organizations. +This guide demonstrates how to use Clerk's API to build a custom flow for creating organizations. - The following example uses the [`useOrganizationList()`](/docs/references/react/use-organization-list) hook to get the `createOrganization()` method. This method is used to create a new organization with the provided name. + The following example uses the [`useOrganizationList()`](/docs/references/react/use-organization-list) hook to access the `createOrganization()` method. This method is used to create a new organization with the provided name. - This example is written for Next.js App Router but can be adapted for any React meta framework, such as Remix. + This example is written for Next.js App Router but can be adapted for any React-based framework. ```tsx {{ filename: 'app/components/CreateOrganization.tsx' }} 'use client' @@ -22,12 +20,22 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for import { FormEventHandler, useState } from 'react' export default function CreateOrganization() { - const { createOrganization } = useOrganizationList() + const { isLoaded, createOrganization } = useOrganizationList() const [organizationName, setOrganizationName] = useState('') - const handleSubmit: FormEventHandler = (e) => { + if (!isLoaded) return null + + const handleSubmit: FormEventHandler = async (e) => { e.preventDefault() createOrganization({ name: organizationName }) + .then((res) => { + console.log(res) + }) + .catch((err) => { + // See https://clerk.com/docs/custom-flows/error-handling + // for more info on error handling + console.error(JSON.stringify(err, null, 2)) + }) setOrganizationName('') } @@ -84,7 +92,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for throw new Error('Add your VITE_CLERK_PUBLISHABLE_KEY to .env file') } - const clerk = new Clerk('{{pub_key}}') + const clerk = new Clerk(pubKey) await clerk.load() if (clerk.user) { diff --git a/docs/organizations/manage-membership-requests.mdx b/docs/custom-flows/manage-membership-requests.mdx similarity index 85% rename from docs/organizations/manage-membership-requests.mdx rename to docs/custom-flows/manage-membership-requests.mdx index 568a3f99cf..66180d43ca 100644 --- a/docs/organizations/manage-membership-requests.mdx +++ b/docs/custom-flows/manage-membership-requests.mdx @@ -9,15 +9,16 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for - The following example uses the [`useOrganization()`](/docs/references/react/use-organization) hook to get `membershipRequests`, which is a list of the active organization's membership requests. - - `membershipRequests` is an object with `data` that contains an array of [`OrganizationMembershipRequest`](/docs/references/javascript/types/organization-membership-request) objects. + The following example: - Each `OrganizationMembershipRequest` object has an [`accept()`](/docs/references/javascript/types/organization-membership-request#accept) and [`reject()`](/docs/references/javascript/types/organization-membership-request#reject) method to accept or reject the membership request, respectively. + 1. Uses the [`useOrganization()`](/docs/references/react/use-organization) hook to get `membershipRequests`, which is a list of the active organization's membership requests. + - `membershipRequests` is an object with `data` that contains an array of [`OrganizationMembershipRequest`](/docs/references/javascript/types/organization-membership-request) objects. + - Each `OrganizationMembershipRequest` object has an [`accept()`](/docs/references/javascript/types/organization-membership-request#accept) and [`reject()`](/docs/references/javascript/types/organization-membership-request#reject) method to accept or reject the membership request, respectively. + 1. Maps over the `data` array to display the membership requests in a table, providing an "Accept" and "Reject" button for each request that calls the `accept()` and `reject()` methods, respectively. - This example is written for Next.js App Router but can be adapted for any React meta framework, such as Remix. + This example is written for Next.js App Router but can be adapted for any React-based framework. - ```jsx {{ filename: 'app/components/MembershipRequests.tsx' }} + ```jsx {{ filename: 'app/components/MembershipRequests.tsx', collapsible: true }} 'use client' import { useOrganization } from '@clerk/nextjs' @@ -105,7 +106,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for Use the tabs to view the code necessary for the `index.html` and `main.js` files. - ```html {{ filename: 'index.html' }} + ```html {{ filename: 'index.html', collapsible: true }} @@ -134,7 +135,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for ``` - ```js {{ filename: 'main.js' }} + ```js {{ filename: 'main.js', collapsible: true }} import { Clerk } from '@clerk/clerk-js' const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY diff --git a/docs/organizations/inviting-users.mdx b/docs/custom-flows/manage-organization-invitations.mdx similarity index 96% rename from docs/organizations/inviting-users.mdx rename to docs/custom-flows/manage-organization-invitations.mdx index 14f93f99b7..3583ffed1e 100644 --- a/docs/organizations/inviting-users.mdx +++ b/docs/custom-flows/manage-organization-invitations.mdx @@ -3,8 +3,6 @@ title: Build a custom flow for creating and managing organization invitations description: Learn how to use the Clerk API to build a custom flow for creating and managing organization invitations. --- -{/* TODO: POST-IA rename this file. Don't do right now because the sidebar is going to be changed for the IA anyways, and it's one less redirect we have to deal with. */} - Organization members with appropriate [permissions](/docs/organizations/roles-permissions) can invite new users to their organization and manage those invitations. The invitation recipient can be either an existing user of your application or a new user. If they are a new user, they will need to sign up in order to accept the invitation. @@ -15,6 +13,8 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for > [!NOTE] > This guide is for creating and managing organization invitations client-side. You can also create an organization invitation using the Backend API. See the [organization invitations reference](/docs/organizations/invitations) for more information. +> +> Also, see the [custom flow for accepting organization invitations](/docs/custom-flows/accept-organization-invitations). @@ -33,9 +33,9 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for - An `` component that allows administrators to invite new members to their organization. - An `` component that lists all pending invitations and allows administrators to revoke them. - This example is written for Next.js App Router but can be adapted for any React meta framework, such as Remix. + This example is written for Next.js App Router but can be adapted for any React-based framework. - ```tsx {{ filename: 'app/components/InvitationList.tsx' }} + ```tsx {{ filename: 'app/components/InvitationList.tsx', collapsible: true }} 'use client' import { useOrganization } from '@clerk/nextjs' @@ -240,7 +240,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for Use the tabs to view the code necessary for the `index.html` and `main.js` files. - ```html {{ filename: 'index.html' }} + ```html {{ filename: 'index.html', collapsible: true }} @@ -269,7 +269,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for ``` - ```js {{ filename: 'main.js' }} + ```js {{ filename: 'main.js', collapsible: true }} import { Clerk } from '@clerk/clerk-js' const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY @@ -390,4 +390,4 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for ## Next steps -Now that you've created a flow for managing organization invitations, you might want to create a flow for accepting invitations. See the [dedicated custom flow guide](/docs/organizations/accept-organization-invitations) for more information. +Now that you've created a flow for managing organization invitations, you might want to create a flow for accepting invitations. See the [dedicated custom flow guide](/docs/custom-flows/accept-organization-invitations) for more information. diff --git a/docs/organizations/managing-roles.mdx b/docs/custom-flows/manage-roles.mdx similarity index 90% rename from docs/organizations/managing-roles.mdx rename to docs/custom-flows/manage-roles.mdx index a45c96ee98..0df2e6ec74 100644 --- a/docs/organizations/managing-roles.mdx +++ b/docs/custom-flows/manage-roles.mdx @@ -11,15 +11,16 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for - The following example uses the [`useOrganization()`](/docs/references/react/use-organization) hook to get `memberships`, which is a list of the active organization's memberships. + The following example: - `memberships` is an object with `data` that contains an array of [`OrganizationMembership`](/docs/references/javascript/types/organization-membership) objects. + 1. Uses the [`useOrganization()`](/docs/references/react/use-organization) hook to get `memberships`, which is a list of the active organization's memberships. + - `memberships` is an object with `data` that contains an array of [`OrganizationMembership`](/docs/references/javascript/types/organization-membership) objects. + - Each `OrganizationMembership` object has an [`update()`](/docs/references/javascript/types/organization-membership#update) and [`destroy()`](/docs/references/javascript/types/organization-membership#destroy) method to update the member's role and remove the member from the organization, respectively. + 1. Maps over the `data` array to display the memberships in a table, providing an "Update Role" and "Remove Member" button for each membership that calls the `update()` and `destroy()` methods, respectively. - Each `OrganizationMembership` object has an [`update()`](/docs/references/javascript/types/organization-membership#update) and [`destroy()`](/docs/references/javascript/types/organization-membership#destroy) method to update the member's role and remove the member from the organization, respectively. + This example is written for Next.js App Router but can be adapted for any React-based framework. - This example is written for Next.js App Router but can be adapted for any React meta framework, such as Remix. - - ```tsx {{ filename: 'app/components/ManageRoles.tsx' }} + ```tsx {{ filename: 'app/components/ManageRoles.tsx', collapsible: true }} 'use client' import { useState, useEffect, ChangeEventHandler, useRef } from 'react' @@ -160,7 +161,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for Use the tabs to view the code necessary for the `index.html` and `main.js` files. - ```html {{ filename: 'index.html' }} + ```html {{ filename: 'index.html', collapsible: true }} @@ -191,7 +192,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for ``` - ```js {{ filename: 'main.js' }} + ```js {{ filename: 'main.js', collapsible: true }} import { Clerk } from '@clerk/clerk-js' const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY diff --git a/docs/organizations/manage-invitations.mdx b/docs/custom-flows/manage-user-org-invitations.mdx similarity index 85% rename from docs/organizations/manage-invitations.mdx rename to docs/custom-flows/manage-user-org-invitations.mdx index e99bb5953c..dddaa2d30e 100644 --- a/docs/organizations/manage-invitations.mdx +++ b/docs/custom-flows/manage-user-org-invitations.mdx @@ -9,15 +9,16 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for - The following example uses the [`useOrganizationList()`](/docs/references/react/use-organization) hook to get `userInvitations`, which is a list of the user's organization invitations. - - `userInvitations` is an object with `data` that contains an array of [`UserOrganizationInvitation`](/docs/references/javascript/types/user-organization-invitation) objects. + The following example: - Each `UserOrganizationInvitation` object has an [`accept()`](/docs/references/javascript/types/organization-membership-request#accept) method that accepts the invitation to the organization. + 1. Uses the [`useOrganizationList()`](/docs/references/react/use-organization) hook to get `userInvitations`, which is a list of the user's organization invitations. + - `userInvitations` is an object with `data` that contains an array of [`UserOrganizationInvitation`](/docs/references/javascript/types/user-organization-invitation) objects. + - Each `UserOrganizationInvitation` object has an [`accept()`](/docs/references/javascript/types/organization-membership-request#accept) method that accepts the invitation to the organization. + 1. Maps over the `data` array to display the invitations in a table, providing an "Accept" button for each invitation that calls the `accept()` method. - This example is written for Next.js App Router but can be adapted for any React meta framework, such as Remix. + This example is written for Next.js App Router but can be adapted for any React-based framework. - ```jsx {{ filename: 'app/components/UserInvitationsList.tsx' }} + ```jsx {{ filename: 'app/components/UserInvitationsList.tsx', collapsible: true }} 'use client' import { useOrganizationList } from '@clerk/clerk-react' @@ -81,7 +82,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for Use the following tabs to view the code necessary for the `index.html` and `main.js` files. - ```html {{ filename: 'index.html' }} + ```html {{ filename: 'index.html', collapsible: true }} @@ -112,7 +113,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for ``` - ```js {{ filename: 'main.js' }} + ```js {{ filename: 'main.js', collapsible: true }} import { Clerk } from '@clerk/clerk-js' const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY diff --git a/docs/custom-flows/organization-switcher.mdx b/docs/custom-flows/organization-switcher.mdx new file mode 100644 index 0000000000..4bdb72b2a8 --- /dev/null +++ b/docs/custom-flows/organization-switcher.mdx @@ -0,0 +1,310 @@ +--- +title: Build a custom flow for switching organizations +description: Learn how to use the Clerk API to build a custom flow for switching between organizations. +--- + + + +This guide will demonstrate how to use the Clerk API to build a custom flow for switching between organizations. + + + + Two examples are provided: one for a paginated list and one for an infinite list. + + The following examples: + + 1. Use the [`useOrganizationList()`](/docs/references/react/use-organization-list) hook to get `memberships`, which is a list of the current user's organization memberships. `memberships` returns `data`, which is an array of [`OrganizationMembership`](/docs/references/javascript/types/organization-membership) objects. + 1. Map over the `data` array to display the user's organization memberships in a table, providing a button that calls `setActive()` to set the selected organization as the active organization. + - If there are no organizations, the [`` component (custom-flow version, not the Clerk component)](/docs/custom-flows/create-organizations) is rendered to allow the user to create an organization. + + The difference between the two examples is the parameters passed to the `useOrganizationList()` hook in order to determine how the list is paginated. + + - The "Paginated list" example provides a button to load more organizations if there are more available. The `data` array is paginated and will only return the first 5 results, so the `fetchNext()` method is used to load more organizations if they are available. + - The "Infinite list" example sets the `infinite` option to `true` to enable infinite results. + + This example is written for Next.js App Router but can be adapted for any React-based framework. + + + ```jsx {{ filename: 'app/components/CustomOrganizationSwitcher.tsx', collapsible: true }} + 'use client' + + import { useAuth, useOrganizationList } from '@clerk/nextjs' + import CreateOrganization from '../components/create-organization' // See /docs/custom-flows/create-organizations for this component + + // List user's organization memberships + export default function JoinedOrganizations() { + const { isLoaded, setActive, userMemberships } = useOrganizationList({ + userMemberships: { + // Set pagination parameters + pageSize: 5, + keepPreviousData: true, + }, + }) + const { orgId } = useAuth() + + if (!isLoaded) { + return

Loading...

+ } + + return ( + <> +

Joined organizations

+ {userMemberships?.data?.length > 0 && ( + <> + + + + + + + + + + + + {userMemberships?.data?.map((mem) => ( + + + + + + + + ))} + +
IdentifierOrganizationJoinedRoleSet as active org
{mem.publicUserData.identifier}{mem.organization.name}{mem.createdAt.toLocaleDateString()}{mem.role} + {orgId === mem.organization.id ? ( + + ) : ( +

Currently active

+ )} +
+ +
+ + + +
+ + )} + {userMemberships?.data?.length === 0 && ( +
+

No organizations found

+ +
+ )} + + ) + } + ``` + + ```jsx {{ filename: 'app/components/CustomOrganizationSwitcher.tsx', collapsible: true }} + 'use client' + + import { useAuth, useOrganizationList } from '@clerk/nextjs' + import CreateOrganization from '../components/create-organization' // See /docs/custom-flows/create-organizations for this component + + // List user's organization memberships + export default function JoinedOrganizations() { + const { isLoaded, setActive, userMemberships } = useOrganizationList({ + userMemberships: { + // Set pagination parameters + infinite: true, + }, + }) + const { orgId } = useAuth() + + if (!isLoaded) { + return

Loading...

+ } + + return ( + <> +

Joined organizations

+ {userMemberships?.data?.length > 0 && ( + + + + + + + + + + + + {userMemberships?.data?.map((mem) => ( + + + + + + + + ))} + +
IdentifierOrganizationJoinedRoleSet as active org
{mem.publicUserData.identifier}{mem.organization.name}{mem.createdAt.toLocaleDateString()}{mem.role} + {orgId === mem.organization.id ? ( + + ) : ( +

Currently active

+ )} +
+ )} + {userMemberships?.data?.length === 0 && ( +
+

No organizations found

+ +
+ )} + + ) + } + ``` +
+
+ + + The following example: + + 1. Calls the [`getOrganizationMemberships()`](/docs/references/javascript/user/user#get-organization-memberships) method to retrieve the list of organizations the current user is a part of. This method returns `data`, which is an array of [`OrganizationMembership`](/docs/references/javascript/types/organization-membership) objects. + 1. Maps over the `data` array to display the user's organization memberships in a list, providing a button that calls [`setActive()`](/docs/references/javascript/clerk/session-methods#set-active) to set the selected organization as the active organization. + + Use the tabs to view the code necessary for the `index.html` and `main.js` files. + + + ```html {{ filename: 'index.html', collapsible: true }} + + + + + + Clerk + JavaScript App + + +
+ +

Joined organizations

+ + + + + + + + + + + +
IdentifierOrganizationJoinedRoleSet as active org
+ + + + + + + ``` + + ```js {{ filename: 'main.js', collapsible: true }} + import { Clerk } from '@clerk/clerk-js' + + const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + + if (!pubKey) { + throw new Error('Add your VITE_CLERK_PUBLISHABLE_KEY to .env file') + } + + const clerk = new Clerk('{{pub_key}}') + await clerk.load() + + if (clerk.user) { + // Check for an active organization + if (clerk.organization) { + const { data } = await clerk.user.getOrganizationMemberships() + const memberships = data + + memberships.map((membership) => { + const membershipTable = document.getElementById('memberships-table-body') + const row = membershipTable.insertRow() + row.insertCell().textContent = membership.publicUserData.identifier + row.insertCell().textContent = membership.organization.name + row.insertCell().textContent = membership.createdAt.toLocaleDateString() + row.insertCell().textContent = membership.role + + // Set as active organization + const addBtn = document.createElement('button') + addBtn.textContent = 'Set as active' + addBtn.addEventListener('click', async function (e) { + e.preventDefault() + + await clerk + .setActive({ organization: membership.organization.id }) + .then((res) => { + console.log('Set as active:', res) + }) + .catch((err) => { + // See https://clerk.com/docs/custom-flows/error-handling + // for more info on error handling + console.error(JSON.stringify(err, null, 2)) + }) + }) + row.insertCell().appendChild(addBtn) + }) + } else { + // If there is no active organization, + // render a form to create an organization + document.getElementById('create-organization-container').removeAttribute('hidden') + const form = document.getElementById('create-organization') + + form.addEventListener('submit', function (e) { + e.preventDefault() + + const inputEl = document.getElementById('name') + + if (!inputEl) { + // ... handle empty input + return + } + + clerk + .createOrganization({ name: inputEl.value }) + .then((res) => console.log(res)) + .catch((error) => console.log('An error occurred:', error)) + }) + } + } else { + // If there is no active user, mount Clerk's + // or add your sign-in custom flow + document.getElementById('app').innerHTML = ` +
+ ` + + const signInDiv = document.getElementById('sign-in') + + clerk.mountSignIn(signInDiv) + } + ``` +
+
+
diff --git a/docs/organizations/updating-organizations.mdx b/docs/custom-flows/update-organizations.mdx similarity index 82% rename from docs/organizations/updating-organizations.mdx rename to docs/custom-flows/update-organizations.mdx index 6fd3ddf347..810cdbc9f6 100644 --- a/docs/organizations/updating-organizations.mdx +++ b/docs/custom-flows/update-organizations.mdx @@ -5,20 +5,20 @@ description: Learn how to use the Clerk API to build a custom flow for updating -Organization members with appropriate [permissions](/docs/organizations/roles-permissions) can update an organization. Currently, only the organization name and slug can be updated. +Organization members with appropriate [permissions](/docs/organizations/roles-permissions) can update an organization. -This guide will demonstrate how to use the Clerk API to build a custom flow for updating an organization. +This guide will demonstrate how to use Clerk's API to build a custom flow for updating an organization. The following example: 1. Uses the [`useOrganization()`](/docs/references/react/use-organization) hook to fetch the active `organization`. - 1. Uses `organization` to call the [`update()`](/docs/references/javascript/organization/organization#update) method with the desired name to update the organization. + 1. Uses `organization` to call the `update()` method with the desired name to update the organization. To see what other attributes can be updated, see the [`update()` reference doc](/docs/references/javascript/organization/organization#update). - This example is written for Next.js App Router but can be adapted for any React meta framework, such as Remix. + This example is written for Next.js App Router but can be adapted for any React-based framework. - ```tsx {{ filename: 'app/components/UpdateOrganization.tsx' }} + ```tsx {{ filename: 'app/components/UpdateOrganization.tsx', collapsible: true }} 'use client' import { useState, useEffect } from 'react' @@ -68,12 +68,12 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for - The following example uses the [`organization.update()`](/docs/references/javascript/organization/organization#update) method to update the active organization's name. + The following example uses the `organization.update()` method to update the active organization's name. To see what other attributes can be updated, see the [`update()` reference doc](/docs/references/javascript/organization/organization#update). Use the tabs to view the code necessary for the `index.html` and `main.js` files. - ```html {{ filename: 'index.html' }} + ```html {{ filename: 'index.html', collapsible: true }} @@ -96,7 +96,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for ``` - ```js {{ filename: 'main.js' }} + ```js {{ filename: 'main.js', collapsible: true }} import { Clerk } from '@clerk/clerk-js' const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY diff --git a/docs/guides/multi-tenant-architecture.mdx b/docs/guides/multi-tenant-architecture.mdx index a942f3392d..c718e02aec 100644 --- a/docs/guides/multi-tenant-architecture.mdx +++ b/docs/guides/multi-tenant-architecture.mdx @@ -54,7 +54,7 @@ B2B SaaS applications with the following characteristics are well-supported with Clerk offers a number of building blocks to help integrate organizations into your application: -- The [`` component](/docs/components/organization/organization-switcher) provides a way for your users to select which organization is active. The [`useOrganizationList()` hook](/docs/organizations/custom-organization-switcher) can be used for more control. +- The [`` component](/docs/components/organization/organization-switcher) provides a way for your users to select which organization is active. The [`useOrganizationList()` hook](/docs/custom-flows/organization-switcher) can be used for more control. - The [`useOrganization()` hook](/docs/references/react/use-organization) can be used to fetch the current, active organization. - The [`` component](/docs/components/protect) enables you to limit who can view certain pages based on their role. Additionally, Clerk exposes a number of helper functions, such as [`auth()`](/docs/references/nextjs/auth), and hooks, such as [`useAuth()`](/docs/references/react/use-auth#how-to-use-the-use-auth-hook), to check the user's authorization throughout your app and API endpoints. diff --git a/docs/manifest.json b/docs/manifest.json index 0f222cb741..26f8e0105b 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -2572,91 +2572,49 @@ "title": "Roles and permissions", "href": "/docs/organizations/roles-permissions" }, + { + "title": "Create roles and assign permissions", + "href": "/docs/organizations/create-roles-permissions" + }, + { + "title": "Reassign the default role", + "href": "/docs/organizations/default-role" + }, + { + "title": "Reassign the creator role", + "href": "/docs/organizations/creator-role" + }, + { + "title": "Invitations", + "href": "/docs/organizations/invitations" + }, { "title": "Verified domains", "href": "/docs/organizations/verified-domains" }, + { + "title": "Verify the active user's permissions", + "href": "/docs/organizations/verify-user-permissions" + }, { "title": "Manage SSO", "href": "/docs/organizations/manage-sso" }, { - "title": "Guides", - "items": [ - [ - { - "title": "Create roles and assign permissions", - "href": "/docs/organizations/create-roles-permissions" - }, - { - "title": "Verify the active user’s permissions", - "href": "/docs/organizations/verify-user-permissions" - }, - { - "title": "Reassign the default role", - "href": "/docs/organizations/default-role" - }, - { - "title": "Reassign the creator role", - "href": "/docs/organizations/creator-role" - }, - { - "title": "Organization workspaces", - "href": "/docs/organizations/organization-workspaces" - }, - { - "title": "Create organizations on behalf of users", - "href": "/docs/organizations/create-orgs-for-users" - }, - { - "title": "Hide personal accounts and force organizations", - "href": "/docs/organizations/force-organizations" - } - ] - ] + "title": "Metadata", + "href": "/docs/organizations/metadata" }, { - "title": "Building custom flows", - "items": [ - [ - { - "title": "Using metadata", - "href": "/docs/organizations/metadata" - }, - { - "title": "Create an organization", - "href": "/docs/organizations/creating-organizations" - }, - { - "title": "Update an organization", - "href": "/docs/organizations/updating-organizations" - }, - { - "title": "Invite users to an organization", - "href": "/docs/organizations/inviting-users" - }, - { - "title": "Accept organization invitations", - "href": "/docs/organizations/accept-organization-invitations" - }, - { - "title": "Manage member roles", - "href": "/docs/organizations/managing-roles" - }, - { - "title": "View a user's organization memberships", - "href": "/docs/organizations/viewing-memberships" - }, - { - "title": "Manage membership requests", - "href": "/docs/organizations/manage-membership-requests" - }, - { - "title": "Switch between organizations", - "href": "/docs/organizations/custom-organization-switcher" - } - ] - ] + "title": "Organization workspaces in the Dashboard", + "href": "/docs/organizations/organization-workspaces" + }, + { + "title": "Create organizations on behalf of users", + "href": "/docs/organizations/create-orgs-for-users" + }, + { + "title": "Hide personal accounts and force organizations", + "href": "/docs/organizations/force-organizations" } ] ] @@ -3647,7 +3605,7 @@ }, { "title": "Sign-up with application invitations", - "href": "/docs/custom-flows/invitations" + "href": "/docs/custom-flows/application-invitations" }, { "title": "Embedded email links", @@ -3664,6 +3622,45 @@ ] ] }, + { + "title": "Organizations", + "items": [ + [ + { + "title": "Create an organization", + "href": "/docs/custom-flows/create-organizations" + }, + { + "title": "Update an organization", + "href": "/docs/custom-flows/update-organizations" + }, + { + "title": "Manage member roles", + "href": "/docs/custom-flows/manage-roles" + }, + { + "title": "List and switch between organizations", + "href": "/docs/custom-flows/organization-switcher" + }, + { + "title": "Create and manage organization invitations", + "href": "/docs/custom-flows/manage-organization-invitations" + }, + { + "title": "Manage a user's organization invitations", + "href": "/docs/custom-flows/manage-user-org-invitations" + }, + { + "title": "Accept organization invitation links", + "href": "/docs/custom-flows/accept-organization-invitations" + }, + { + "title": "Manage membership requests", + "href": "/docs/custom-flows/manage-membership-requests" + } + ] + ] + }, { "title": "Account updates", "items": [ diff --git a/docs/organizations/creator-role.mdx b/docs/organizations/creator-role.mdx index 1390b7c756..fe187b6b9a 100644 --- a/docs/organizations/creator-role.mdx +++ b/docs/organizations/creator-role.mdx @@ -4,7 +4,7 @@ description: Learn how to assign Clerk's Creator role to any other role in an or content-type: guide --- -When a user creates a new organization, that user is automatically added as the organization's first member and assigned whichever role is that org's [**Creator** role](/docs/organizations/roles-permissions#the-creator-role). By default, that role is `admin`. +When a user creates a new organization, that user is automatically added as the organization's first member and assigned whichever role is that org's [**Creator** role](/docs/organizations/roles-permissions#the-creator-role). By default, that role is `org:admin`. You cannot delete an organization role if it is used as the organization **Creator** role. But, you _can_ reassign the **Creator** role to any other role with the right permissions. For example, if you want to delete the `admin` role, you will have to assign another role as the **Creator** role. diff --git a/docs/organizations/custom-organization-switcher.mdx b/docs/organizations/custom-organization-switcher.mdx deleted file mode 100644 index 37b5ee7c1e..0000000000 --- a/docs/organizations/custom-organization-switcher.mdx +++ /dev/null @@ -1,146 +0,0 @@ ---- -title: Build a custom flow for switching organizations -description: Learn how to use the Clerk API to build a custom flow for switching between organizations. ---- - - - -This guide will demonstrate how to use the Clerk API to build a custom flow for switching between organizations. - - - - The following example: - - - Uses the [`useOrganizationList()`](/docs/references/react/use-organization-list) hook to get `memberships`, which is a list of the current user's organization memberships. `memberships` returns `data`, which is an array of [`OrganizationMembership`](/docs/references/javascript/types/organization-membership) objects. - - Maps over the `data` array to display the user's organization memberships in a list. - - Provides a button that calls `setActive()` to set the selected organization as the active organization. - - Provides a button to load more organizations if there are more available. The `data` array is paginated and will only return the first 10 results, so the `fetchNext()` method is used to load more organizations if they are available. - - This example is written for Next.js App Router but can be adapted for any React meta framework, such as Remix. - - ```jsx {{ filename: 'app/components/CustomOrganizationSwitcher.tsx' }} - 'use client' - - import { useOrganizationList } from '@clerk/nextjs' - - export const CustomOrganizationSwitcher = () => { - const { isLoaded, setActive, userMemberships } = useOrganizationList({ - userMemberships: { - infinite: true, - }, - }) - - if (!isLoaded) { - return

Loading

- } - - return ( - <> -

Custom Organization Switcher

-
    - {userMemberships.data?.map((mem) => ( -
  • - {mem.organization.name} - -
  • - ))} -
- - - - ) - } - ``` -
- - - The following example: - - 1. Calls the [`getOrganizationMemberships()`](/docs/references/javascript/user/user#get-organization-memberships) method to retrieve the list of organizations the current user is a part of. This method returns `data`, which is an array of [`OrganizationMembership`](/docs/references/javascript/types/organization-membership) objects. - 1. Maps over the `data` array to display the user's organization memberships in a list. - 1. Provides a button that calls [`setActive()`](/docs/references/javascript/clerk/session-methods#set-active) to set the selected organization as the active organization. - - Use the tabs to view the code necessary for the `index.html` and `main.js` files. - - - ```html {{ filename: 'index.html' }} - - - - - - Clerk + JavaScript App - - -
- -

Custom Organization Switcher

-
    - - - - - ``` - - ```js {{ filename: 'main.js' }} - import { Clerk } from '@clerk/clerk-js' - - const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY - - if (!pubKey) { - throw new Error('Add your VITE_CLERK_PUBLISHABLE_KEY to .env file') - } - - const clerk = new Clerk('{{pub_key}}') - await clerk.load() - - if (clerk.user) { - // Check for an active organization - if (clerk.organization) { - const orgList = document.getElementById('custom-org-switcher') - try { - const { data } = await clerk.user.getOrganizationMemberships() - const userMemberships = data - - userMemberships.map((membership) => { - const li = document.createElement('li') - li.textContent = membership.organization.name - const button = document.createElement('button') - button.textContent = 'Select' - button.addEventListener('click', async function () { - await clerk.setActive({ organization: membership.organization.id }) - }) - li.appendChild(button) - orgList.appendChild(li) - }) - } catch (err) { - console.error(err) - } - } else { - // If there is no active organization, - // mount Clerk's - // to allow the user to set an organization as active - document.getElementById('app').innerHTML = ` -

    Select an organization to set it as active

    -
    - ` - - const orgSwitcherDiv = document.getElementById('org-switcher') - - clerk.mountOrganizationSwitcher(orgSwitcherDiv) - } - } else { - document.getElementById('app').innerHTML = ` -
    - ` - - const signInDiv = document.getElementById('sign-in') - - clerk.mountSignIn(signInDiv) - } - ``` -
    -
    -
    diff --git a/docs/organizations/force-organizations.mdx b/docs/organizations/force-organizations.mdx index 1b319d88fa..8a93613a10 100644 --- a/docs/organizations/force-organizations.mdx +++ b/docs/organizations/force-organizations.mdx @@ -1,6 +1,6 @@ --- -title: Hide Personal Accounts and force organizations -description: Learn how to hide Personal Accounts and force organizations in your Clerk application. +title: Hide personal accounts and force organizations +description: Learn how to hide personal accounts and force organizations in your Clerk application. --- - - Hide Personal Accounts from UI components + - Hide personal accounts from UI components - Detect an active organization - Set an active organization - Limit access to users with active organizations only -In this guide, you will learn how to hide a user's Personal Account in order to appear as if they only have access to organizations. You will also learn how to limit access to your application to only users with active organizations, further enforcing organization-centric access. This is useful for applications that are built for organizations only, such as B2B applications. +This guide demonstrates how to hide a user's personal account in order to appear as if they only have access to organizations, and how to limit access to your application to only users with active organizations, further enforcing organization-centric access. This is useful for applications that are built for organizations only, such as B2B applications. This guide will be written for Next.js applications using App Router, but the same concepts can be applied to any application using Clerk. - ## Hide Personal Accounts from UI components + ## Hide personal accounts from UI components - To begin forcing organizations in your application, you need to remove a user's Personal Account from the UI. A user's Personal Account cannot be disabled; it can only be hidden. + To begin forcing organizations in your application, you need to remove a user's personal account from the UI. A user's personal account cannot be disabled; it can only be hidden. - If you have an application set up to use organizations, you might have the [``](/docs/components/organization/organization-list) and [``](/docs/components/organization/organization-switcher) components in your application. These components will show a user's Personal Account by default, but you can hide it by passing the `hidePersonal` prop. + If you have an application set up to use organizations, you might have the [``](/docs/components/organization/organization-list) and [``](/docs/components/organization/organization-switcher) components in your application. These components will show a user's personal account by default, but you can hide it by passing the `hidePersonal` prop. ", ""]}> - ```tsx {{ filename: 'app/organization-list/[[...organization-list]]/page.tsx', mark: [6] }} - import { OrganizationList } from '@clerk/nextjs' - - export default function OrganizationListPage() { + ```tsx {{ mark: [4] }} + export default function Page() { return ( - ```tsx {{ filename: 'app/sidebar.tsx', mark: [6] }} - import { OrganizationSwitcher } from '@clerk/nextjs' - - export default function Sidebar() { + ```tsx {{ mark: [4] }} + export default function Page() { return ( + The [`auth()`](/docs/references/nextjs/auth) helper function can be used to get the `orgId` from the session _server-side_. If the `orgId` is `null`, then the user does not have an active organization. @@ -109,56 +105,16 @@ This guide will be written for Next.js applications using App Router, but the sa ### Set an active organization - In the case of [prebuilt components](/docs/components/overview), an organization will _automatically_ be set as active each time the user creates an organization, accepts an invitation, or selects a membership from the organization switcher. In the case of custom flows, you will need to implement the logic for setting an organization as active. The [`useOrganizationList()`](/docs/references/react/use-organization-list) hook provides a `setActive` method to help you with this. - - In the following example, a custom organization switcher is created. It allows a user to select an organization from a list of their memberships. The `useOrganizationList()` hook is used to fetch a list of the user's memberships, and the `setActive` method is used to set the selected organization as active. - - > [!WARNING] - > Setting an active organization can only be performed client-side. - - ```tsx {{ filename: 'app/custom-org-switcher.tsx' }} - 'use client' - - import { useOrganizationList } from '@clerk/nextjs' - - export const CustomOrgSwitcher = () => { - const { isLoaded, setActive, userMemberships } = useOrganizationList({ - userMemberships: { - infinite: true, - }, - }) - - if (!isLoaded) { - return <>Loading - } - - return ( - <> -
      - {userMemberships.data?.map((mem) => ( -
    • - {mem.organization.name} - -
    • - ))} -
    - - - - ) - } - ``` + If you are using [prebuilt components](/docs/components/overview), an organization will _automatically_ be set as active each time the user creates an organization, accepts an invitation, or selects a membership. If prebuilt components don't meet your specific needs or if you require more control over the logic, you can rebuild the existing Clerk flows using the Clerk API. See the [organization switcher custom flow guide](/docs/custom-flows/organization-switcher). ### Set an active organization based on the URL > [!WARNING] - > This approach still depends on the `setActive` method, which only runs on the client. Due to this limitation, during SSR, it is possible that `orgId` from `auth()` returns an incorrect value that does not match the organization ID in the URL. + > This approach depends on the `setActive()` method, which only runs on the client. Due to this limitation, during SSR, it is possible that `orgId` from `auth()` returns an incorrect value that does not match the organization ID in the URL. In some cases, you might want to set an active organization based on the URL. For example, if you have a URL like `/organizations/org_123`, you might want to set `org_123` as the active organization. - In the example below, a component called `SyncActiveOrganization` is created. The Next.js [`useParams()`](https://nextjs.org/docs/app/api-reference/functions/use-params) hook is used to get the organization ID from the URL, and then the `setActive` method is used to set the organization as active. + In the following example, a component called `SyncActiveOrganization` is created. The Next.js [`useParams()`](https://nextjs.org/docs/app/api-reference/functions/use-params) hook is used to get the organization ID from the URL, and then the `setActive()` method is used to set the organization as active. ```tsx {{ filename: 'app/utils/sync-active-organization.tsx' }} 'use client' @@ -168,6 +124,7 @@ This guide will be written for Next.js applications using App Router, but the sa import { useAuth, useOrganizationList } from '@clerk/nextjs' export function SyncActiveOrganization() { + // Use `useOrganizationList()` to access the `setActive()` method const { setActive, isLoaded } = useOrganizationList() // Get the organization ID from the session @@ -185,7 +142,9 @@ This guide will be written for Next.js applications using App Router, but the sa return } - // If the org ID in the URL is not the same as the org ID in the session (the active organization), set the active organization to be the org ID from the URL + // If the org ID in the URL is not the same as + // the org ID in the session (the active organization), + // set the active organization to be the org ID from the URL if (urlOrgId && urlOrgId !== orgId) { void setActive({ organization: urlOrgId }) } @@ -195,7 +154,7 @@ This guide will be written for Next.js applications using App Router, but the sa } ``` - Now you can place the `SyncActiveOrganization` helper that you created above in a [layout](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#layouts) and use the layout for all pages that require an active organization. In the example below, the `SyncActiveOrganization` component will run on every page in the `/app/[orgId]` directory. + Now you can place the `SyncActiveOrganization` helper that you created above in a [layout](https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#layouts) and use the layout for all pages that require an active organization. In the following example, the `SyncActiveOrganization` component will run on every page in the `/app/[orgId]` directory. ```tsx {{ filename: 'app/[orgId]/layout.tsx' }} import { type PropsWithChildren } from 'react' @@ -211,86 +170,92 @@ This guide will be written for Next.js applications using App Router, but the sa } ``` - ## Limit access to users with active organizations only + ## Limit access to only users with active organizations - Now that you have hidden Personal Accounts from the UI and can detect and set an active organization, you can limit access to your application to users with active organizations only. This will ensure that users without active organizations cannot access your application. + Now that you have hidden personal accounts from the UI and can detect and set an active organization, you can limit access to your application to users with active organizations only. This will ensure that users without active organizations cannot access your application. It's possible for a user to be signed in, but not have an active organization. This can happen in two cases: - The user created an account and hasn't created an organization yet - The user joined or created multiple organizations, and left or deleted the active organization - There are two ways to limit access to users with active organizations only: + There are two ways to limit access to only users with active organizations: - - [Limit access using the `clerkMiddleware()` helper](#limit-access-using-the-clerk-middleware-helper) - - [Limit access using an App Router layout](#limit-access-using-an-app-router-layout) + - [Using the `clerkMiddleware()` helper](#limit-access-using-the-clerk-middleware-helper) + - [Using an App Router layout](#limit-access-using-an-app-router-layout) Based on your use case, you can decide to limit users either in the entire app or a specific part of it. For example, a B2B application might need to limit access to only users with an active organization, whereas a B2B2C application might limit only the `/dashboard` path to users with an active organization. ### Limit access using the `clerkMiddleware()` helper - Clerk's [`clerkMiddleware()`](/docs/references/nextjs/clerk-middleware) helper can be used in both App Router and Pages Router applications to limit access to users with active organizations only. - - In the example below, the `clerkMiddleware()` helper is used to redirect signed in users to the organization selection page if they are not active in an organization. - - ```tsx {{ filename: 'middleware.ts' }} - import { clerkMiddleware } from '@clerk/nextjs/server' - - export default clerkMiddleware(async (auth, req) => { - const { userId, orgId } = await auth() + The [`clerkMiddleware()`](/docs/references/nextjs/clerk-middleware) helper can be used to limit access to only users with active organizations. - // Redirect signed in users to organization selection page if they are not active in an organization - if (userId && !orgId && req.nextUrl.pathname !== '/org-selection') { - const searchParams = new URLSearchParams({ redirectUrl: req.url }) - - const orgSelection = new URL(`/org-selection?${searchParams.toString()}`, req.url) + + + In the following example, the `clerkMiddleware()` helper is used to redirect signed in users to the organization selection page if they are not active in an organization. - return NextResponse.redirect(orgSelection) - } - }) - - 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)(.*)', - ], - } - ``` + ```tsx {{ filename: 'middleware.ts' }} + import { clerkMiddleware } from '@clerk/nextjs/server' - To limit access to a specific part of the application, you can check if the path the user is visiting is the path you want to limit access to. In this example, the `/dashboard` path is limited to users with active organizations only. If the user is not active in an organization, they will be redirected to the `/dashboard/org-selection` page. + export default clerkMiddleware(async (auth, req) => { + const { userId, orgId } = await auth() - ```tsx {{ filename: 'middleware.ts' }} - import { clerkMiddleware } from '@clerk/nextjs/server' + // Redirect signed in users to organization selection page if they are not active in an organization + if (userId && !orgId && req.nextUrl.pathname !== '/org-selection') { + const searchParams = new URLSearchParams({ redirectUrl: req.url }) - export default clerkMiddleware(async (auth, req) => { - const { userId, orgId } = await auth() + const orgSelection = new URL(`/org-selection?${searchParams.toString()}`, req.url) - // Redirect signed in users to organization selection page if they are not active in an organization - if ( - userId && - !orgId && - req.nextUrl.pathname.startsWith('/dashboard') && - req.nextUrl.pathname !== '/dashboard/org-selection' - ) { - const searchParams = new URLSearchParams({ redirectUrl: req.url }) + return NextResponse.redirect(orgSelection) + } + }) - const orgSelection = new URL(`/dashboard/org-selection?${searchParams.toString()}`, req.url) + 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)(.*)', + ], + } + ``` + - return NextResponse.redirect(orgSelection) - } - }) - - 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)(.*)', - ], - } - ``` + + To limit access to a specific part of the application, you can check if the path the user is visiting is the path you want to limit access to. In this example, the `/dashboard` path is limited to users with active organizations only. If the user is not active in an organization, they will be redirected to the `/dashboard/org-selection` page. + + ```tsx {{ filename: 'middleware.ts' }} + import { clerkMiddleware } from '@clerk/nextjs/server' + + export default clerkMiddleware(async (auth, req) => { + const { userId, orgId } = await auth() + + // Redirect signed in users to organization selection page if they are not active in an organization + if ( + userId && + !orgId && + req.nextUrl.pathname.startsWith('/dashboard') && + req.nextUrl.pathname !== '/dashboard/org-selection' + ) { + const searchParams = new URLSearchParams({ redirectUrl: req.url }) + + const orgSelection = new URL(`/dashboard/org-selection?${searchParams.toString()}`, req.url) + + return NextResponse.redirect(orgSelection) + } + }) + + 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)(.*)', + ], + } + ``` + + Now that you have configured your middleware to redirect users to the `/org-selection` page if they are not active in an organization, you need to create the `/org-selection` page. This page will allow users to select an organization or create their own organization. @@ -324,9 +289,9 @@ This guide will be written for Next.js applications using App Router, but the sa ### Limit access using an App Router layout - In Next.js App Router applications, instead of using `clerkMiddleware()`, you also have the option to use a layout to limit access to users with active organizations only. + In Next.js App Router applications, instead of using `clerkMiddleware()`, you also have the option to use a layout to limit access to only users with active organizations. - In the example below, a component called `RequiredActiveOrgLayout` is created. This component will be used as a layout for all pages that require an active organization. If the user has an active organization, the route the user is visiting will be rendered. If the user does not have an active organization, the organization selection page will be rendered. + In the following example, a component called `RequiredActiveOrgLayout` is created. This component will be used as a layout for all pages that require an active organization. If the user has an active organization, the route the user is visiting will be rendered. If the user does not have an active organization, the organization selection page will be rendered. ```tsx {{ filename: 'app/(with-active-organization)/layout.tsx' }} import { OrganizationList } from '@clerk/nextjs' @@ -357,15 +322,3 @@ This guide will be written for Next.js applications using App Router, but the sa } ```
    - -## Finished 🎉 - -You have configured your Clerk application to hide personal accounts and enforce organization-centric access. By following this guide, you've learned how to: - -- hide Personal Accounts from UI components -- detect an active organization by using Clerk's `auth()` helper function or `useAuth()` hook -- set an active organization automatically through Clerk components or manually using the `setActive` method -- set an active organization based on the URL -- limit access to your application to only users with active organizations by utilizing Clerk's `clerkMiddleware()` helper or by implementing an App Router layout - -Now that you've completed these steps, your application is well-configured for organizational use, providing a streamlined experience for users associated with organizations. diff --git a/docs/organizations/invitations.mdx b/docs/organizations/invitations.mdx index 3f2b1a6791..61d1d5779e 100644 --- a/docs/organizations/invitations.mdx +++ b/docs/organizations/invitations.mdx @@ -3,9 +3,17 @@ title: Invite users to your organization description: Learn how to invite users to your organization. --- -Organization invitations allow you to add new members to your organization, granting them access to organization-specific features and resources. +Organization invitations allow you to add new members to your organization. When you send an invitation, Clerk sends an email to the invited user with a unique invitation link. When the user visits the organization invitation link, they will be redirected to the [Account Portal sign-in page](/docs/customization/account-portal/overview#sign-in). If the user is already signed in, they will be redirected to your application's homepage (`/`). If you want to redirect the user to a specific page in your application, you can [specify a redirect URL when creating the invitation](#redirect-url). -Once you create an invitation, Clerk sends an email to the invited user with a unique invitation link. When the user visits the organization invitation link, they will be redirected to the [Account Portal sign-in page](/docs/customization/account-portal/overview#sign-in-or-up). If the user is already signed in, they will be redirected to your application's homepage (`/`). If you want to redirect the user to a specific page in your application, you can [specify a redirect URL when creating the invitation](#redirect-url). +By default, only admins can invite users to an organization. + +This feature requires that **Email address** is enabled as an [identifier](/docs/authentication/configuration/sign-up-sign-in-options#identifiers), as Clerk uses the user's email address to send the invitation. You can still disable **Email address** as an authentication option if you do not want users to be able to sign-in with their email address. + +To configure your application's **Email address** settings: + +1. In the Clerk Dashboard, navigate to the [**Email, phone, username**](https://dashboard.clerk.com/last-active?path=user-authentication/email-phone-username) page. +1. In the **Contact information** section, ensure that **Email address** is enabled. +1. Next to **Email address**, select the settings icon to configure the email address settings. Here, at least **Require** should be enabled in order to send invitations. ## Create an invitation @@ -15,11 +23,11 @@ However, if you want to build custom flows, see the following sections. ### Client-side -To create an organization invitation on the client-side, see the [dedicated guide](/docs/organizations/inviting-users). Note that this uses the [`organizations.inviteMember()`](/docs/references/javascript/organization/invitations#invite-member) method, which does not allow you to specify a redirect URL; it will always redirect to the Account Portal sign-in page. If you want to specify a redirect URL, you must create the invitation on the server-side. +To create an organization invitation on the client-side, see the [dedicated guide](/docs/custom-flows/manage-organization-invitations). Note that this uses the [`organizations.inviteMember()`](/docs/references/javascript/organization/invitations#invite-member) method, which does not allow you to specify a redirect URL; it will always redirect to the Account Portal sign-in page. If you want to specify a redirect URL, you must create the invitation on the server-side. ### Server-side -You can also create organization invitations via the [Backend API](/docs/reference/backend-api/tag/Organization-Invitations#operation/CreateOrganizationInvitation){{ target: '_blank' }} either by using a cURL command or the [JavaScript Backend SDK](/docs/references/backend/overview). The JavaScript Backend SDK is a wrapper around the Backend API that makes it easier to interact with the API. +To create organization invitations on the server-side, use the [Backend API](/docs/reference/backend-api/tag/Organization-Invitations#operation/CreateOrganizationInvitation){{ target: '_blank' }} either by using a cURL command or the [JavaScript Backend SDK](/docs/references/backend/overview). The JavaScript Backend SDK is a wrapper around the Backend API that makes it easier to interact with the API. Use the following tabs to see examples for each method. @@ -28,19 +36,27 @@ Use the following tabs to see examples for each method. The following example demonstrates how to create an organization invitation using cURL. - Replace the `` with the ID of the organization you want to invite the user to. Replace the `user_123` with the ID of the user who is inviting the other user. Replace the email address with the email address you want to invite. Your Secret Key is already injected into the code snippet. + - Your Secret Key is already injected into the code snippet. + - Replace the `org_123` with the ID of the organization you want to invite the user to. + - Replace the `user_123` with the ID of the user who is inviting the other user. + - Replace the email address with the email address you want to invite. + - Replace the `role` with the role you want to assign to the invited user. - Replace the `` with the ID of the organization you want to invite the user to. Replace the `user_123` with the ID of the user who is inviting the other user. Replace the email address with the email address you want to invite. Replace `YOUR_SECRET_KEY` with your Clerk Secret Key. You can find your Secret Key on the [**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard. + - Replace `YOUR_SECRET_KEY` with your Clerk Secret Key. You can find your Secret Key on the [**API Keys**](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard. + - Replace the `org_123` with the ID of the organization you want to invite the user to. + - Replace the `user_123` with the ID of the user who is inviting the other user. + - Replace the email address with the email address you want to invite. + - Replace the `role` with the role you want to assign to the invited user. ```bash {{ filename: 'terminal' }} - curl 'https://api.clerk.com/v1/organizations//invitations' \ + curl 'https://api.clerk.com/v1/organizations/{org_123}/invitations' \ -X POST \ -H 'Authorization: Bearer {{secret}}' \ -H 'Content-Type: application/json' \ - -d '{ "inviter_user_id": "user_123", "email_address": "test@gmail.com", "role": "org:member" }' + -d '{ "inviter_user_id": "user_123", "email_address": "email@example.com", "role": "org:member" }' ```
    @@ -49,49 +65,43 @@ Use the following tabs to see examples for each method.
    -See the [Backend API reference](/docs/reference/backend-api/tag/Organization-Invitations#operation/CreateOrganizationInvitation){{ target: '_blank' }} for an example of the response. +For an example of the response, see the [Backend API reference](/docs/reference/backend-api/tag/Organization-Invitations#operation/CreateOrganizationInvitation){{ target: '_blank' }}. ### Redirect URL When you create an invitation, you can specify a `redirect_url` parameter. This parameter tells Clerk where to redirect the user when they visit the invitation link. -The following example demonstrates how to use cURL to create an invitation with the `redirect_url` set to `https://www.example.com/accept-invitation`: +The following example demonstrates how to use cURL to create an invitation with the `redirect_url` set to `https://www.example.com/accept-invitation`. ```bash -curl 'https://api.clerk.com/v1/organizations//invitations' \ +curl 'https://api.clerk.com/v1/organizations/{org_123}/invitations' \ -X POST \ -H 'Authorization: Bearer {{secret}}' \ -H 'Content-Type: application/json' \ - -d '{ "inviter_user_id": "user_123", "email_address": "test@gmail.com", "role": "org:member", "redirect_url": "https://www.example.com/accept-invitation" }' + -d '{ "inviter_user_id": "user_123", "email_address": "email@example.com", "role": "org:member", "redirect_url": "https://www.example.com/accept-invitation" }' ``` -Once the user visits the invitation link, they will be redirected to the page you specified. On that page, you must handle the authentication flow in your code. You can either embed the [`](/docs/components/authentication/sign-in) component or, if the prebuilt component doesn't meet your needs or you require more control over the logic, you can build a [custom flow](/docs/organizations/accept-organization-invitations). +Once the user visits the invitation link, they will be redirected to the page you specified. On that page, you must handle the authentication flow in your code. You can either embed the [``](/docs/components/authentication/sign-in) component or, if the prebuilt component doesn't meet your needs or you require more control over the logic, you can build a [custom flow](/docs/custom-flows/accept-organization-invitations). > [!TIP] -> To test redirect URLs in your development environment, you can pass your port (`http://localhost:3000`). If you'd like to use Clerk's Account Portal, pass your Clerk Frontend API URL as the base URL. For example, `https://prepared-phoenix-98.clerk.accounts.dev/sign-up` redirects the user to the Account Portal sign-up page. You can find your Frontend API URL on the [**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard. In the left sidebar, select **Show API URLs**. +> +> - To test redirect URLs in your development environment, pass your port. For example, `http://localhost:3000/accept-invitation`. +> - To use Clerk's Account Portal, pass your Clerk Frontend API URL as the base URL. For example, `https://prepared-phoenix-98.clerk.accounts.dev/accept-invitation` redirects the user to the Account Portal sign-up page. You can find your Frontend API URL on the [**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard. In the left sidebar, select **Show API URLs**. ### Invitation metadata -You can also add metadata to an invitation when creating the invitation through the Backend API. Once the invited user signs up using the invitation link, the invitation metadata (`OrganizationInvitation.public_metadata`) will be stored in the organization membership's metadata (`OrganizationMembership.publicMetadata`). You can find more information about organization membership metadata in the [Organization Membership](/docs/references/javascript/types/organization-membership) docs. +You can also add metadata to an invitation when creating the invitation through the Backend API. Once the invited user signs up using the invitation link, the **invitation** metadata (`OrganizationInvitation.publicMetadata`) will be stored in the organization **membership's** metadata (`OrganizationMembership.publicMetadata`). You can find more information about organization membership metadata in the [Organization Membership](/docs/references/javascript/types/organization-membership) docs. -To add metadata to an invitation, you can use the `public_metadata` property when the invitation is created. +To add metadata to an invitation, add the `public_metadata` parameter when creating the invitation. -The following example demonstrates how to create an invitation with metadata using cURL. - - - Replace the `` with the ID of the organization you want to invite the user to. Replace the `user_123` with the ID of the user who is inviting the other user. Replace the email address with the email address you want to invite. Your Secret Key is already injected into the code snippet. - - - - Replace the `` with the ID of the organization you want to invite the user to. Replace the `user_123` with the ID of the user who is inviting the other user. Replace the email address with the email address you want to invite. Replace `YOUR_SECRET_KEY` with your Clerk Secret Key. You can find your Secret Key on the [**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard. - +The following example demonstrates how to use cURL to create an invitation with metadata. ```bash -curl 'https://api.clerk.com/v1/organizations//invitations' \ +curl 'https://api.clerk.com/v1/organizations/{org_123}/invitations' \ -X POST \ -H 'Authorization: Bearer {{secret}}' \ -H 'Content-Type: application/json' \ - -d '{ "inviter_user_id": "user_123", "email_address": "test@gmail.com", "role": "org:member", "public_metadata": {"department": "marketing"} }' + -d '{ "inviter_user_id": "user_123", "email_address": "email@example.com", "role": "org:member", "public_metadata": {"department": "marketing"} }' ``` ## Revoke an invitation @@ -100,28 +110,32 @@ Revoking an invitation prevents the user from using the invitation link that was ### Client-side -To revoke an invitation client-side, see the [dedicated guide](/docs/organizations/inviting-users). +To revoke an invitation client-side, see the [dedicated guide](/docs/custom-flows/manage-organization-invitations). ### Server-side -To revoke an invitation server-side, you can use the [Backend API](/docs/reference/backend-api/tag/Organization-Invitations#operation/RevokeOrganizationInvitation){{ target: '_blank' }}. +To revoke an invitation server-side, use the [Backend API](/docs/reference/backend-api/tag/Organization-Invitations#operation/RevokeOrganizationInvitation){{ target: '_blank' }}. either by using a cURL command or the [JavaScript Backend SDK](/docs/references/backend/overview). The JavaScript Backend SDK is a wrapper around the Backend API that makes it easier to interact with the API. -You can either use a cURL command or Clerk's [JavaScript Backend SDK](/docs/references/backend/overview) to create an invitation. Use the following tabs to see examples for each method. +Use the following tabs to see examples for each method. The following example demonstrates how to revoke an invitation using cURL. - Replace the `` with the ID of the organization you want to revoke the invitation from. Replace the `` with the ID of the invitation you want to revoke. Replace `user_123` with the ID of the user who is revoking the invitation. Your Secret Key is already injected into the code snippet. + - Your Secret Key is already injected into the code snippet. + - Replace the `inv_123` with the ID of the invitation you want to revoke. + - Replace the `user_123` with the ID of the user who is revoking the invitation. - Replace the `` with the ID of the organization you want to revoke the invitation from. Replace the `` with the ID of the invitation you want to revoke. Replace `user_123` with the ID of the user who is revoking the invitation. Replace `YOUR_SECRET_KEY` with your Clerk Secret Key. You can find your Secret Key on the [**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard. + - Replace `YOUR_SECRET_KEY` with your Clerk Secret Key. You can find your Secret Key on the [**API Keys**](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard. + - Replace the `inv_123` with the ID of the invitation you want to revoke. + - Replace the `user_123` with the ID of the user who is revoking the invitation. ```bash {{ filename: 'terminal' }} - curl 'https://api.clerk.com/v1/organizations//invitations//revoke' \ + curl 'https://api.clerk.com/v1/organizations/{org_123}/invitations/{inv_123}/revoke' \ -X POST \ -H 'Authorization: Bearer {{secret}}' \ -H 'Content-Type: application/json' \ diff --git a/docs/organizations/manage-sso.mdx b/docs/organizations/manage-sso.mdx index 7a1a9d3708..7dedfb65c8 100644 --- a/docs/organizations/manage-sso.mdx +++ b/docs/organizations/manage-sso.mdx @@ -1,25 +1,22 @@ --- -title: Organization-level SSO -description: Learn how to set up and manage SSO for organizations. +title: Organization-level enterprise SSO +description: Learn how to set up and manage enterprise SSO for organizations. --- -Clerk supports adding SSO connections to organizations, enabling users to sign in with an Identity Provider (IdP) and easily join organizations. There are three types of [enterprise connections](/docs/authentication/enterprise-connections/authentication-flows) that are supported: EASIE, SAML, and OIDC. +Clerk supports enabling enterprise SSO connections for specific organizations. When users sign up or sign in using an organization's enterprise connection, they're automatically added as members of that organization and assigned the [default role](/docs/organizations/roles-permissions#default-roles), which can be either `member` or `admin`. -When users sign in or up using an organization's enterprise connection, they're automatically added as members of that organization and assigned the [default role](/docs/organizations/roles-permissions#default-roles), which can be either `member` or `admin`. +## Add an enterprise SSO connection for an organization -> [!WARNING] -> A domain used for enterprise SSO can't be used as a [verified domain](/docs/organizations/verified-domains) for the same organization. +Clerk supports enterprise SSO via [SAML](/docs/authentication/enterprise-connections/overview#saml) or via the [OpenID Connect (OIDC) protocol](/docs/authentication/enterprise-connections/overview#oidc), either through EASIE or by integrating with any OIDC-compatible provider. -## Add an organization-level SSO connection +To add an enterprise SSO connection for an organization, follow the appropriate guide based on the platform you want to use, such as the [Google SAML guide](/docs/authentication/enterprise-connections/saml/google). When configuring the connection in the Clerk Dashboard, there will be an option to select the **Organization** for which you want to enable this connection. If you don't select an organization, the connection will be added for your entire application. -1. In the Clerk Dashboard, navigate to the [**SSO Connections**](https://dashboard.clerk.com/last-active?path=user-authentication/sso-connections) page. -1. Select **Add connection** and select **For specific domains or organizations**. -1. Select a Identity Provider. -1. Add the **Domain** for which you want to enable this connection and select an **Organization**. +> [!WARNING] +> A domain used for enterprise SSO can't be used as a [verified domain](/docs/organizations/verified-domains) for the same organization. ## Onboarding flows -The two common onboarding flows for organizations with SSO are to either create an organization first or to have users initiate the setup themselves. +The two common onboarding flows for organizations with enterprise SSO are to either create an organization first or to have users initiate the setup themselves. #### Organization created first (top-down approach) @@ -27,8 +24,8 @@ This flow is common for enterprise sales where the relationship is established b 1. [Create an organization](/docs/organizations/overview#create-an-organization) for your customer through the Clerk Dashboard. 1. Collaborate with the customer's IT administrator to obtain the necessary configuration details. -1. Configure the SSO connection for the organization. -1. Invite users to the organization, who can then sign in using SSO. +1. Configure the enterprise SSO connection for the organization. +1. Invite users to the organization, who can then sign in using enterprise SSO. #### User-initiated setup (bottom-up approach) @@ -36,24 +33,24 @@ This flow is common when individual users try the product before company-wide ad 1. An end user signs up to evaluate your application, starting with an individual account. 1. After adopting the application, the user [creates an organization](/docs/organizations/overview#create-an-organization) for their company. -1. Configure SSO for the organization through the Clerk Dashboard. +1. Configure enterprise SSO for the organization through the Clerk Dashboard. 1. All subsequent users from that organization can now sign in using enterprise SSO. -## Enforcing SSO by domain +## Enforce enterprise SSO by domain -SSO connections are enforced on a per-domain basis in organizations, enabling flexible access management: +Enterprise SSO connections are enforced on a per-domain basis in organizations, enabling flexible access management: -- Configure SSO for your primary domain (e.g., `company.com`) to enforce SSO authentication for employees. -- Add additional domains without SSO for external collaborators (e.g., contractors, consultants) +- Configure enterprise SSO for your primary domain (e.g., `company.com`) to enforce enterprise SSO authentication for employees. +- Add additional domains without enterprise SSO for external collaborators (e.g., contractors, consultants). - Each domain in an organization can have different authentication requirements. -## Managing memberships +## Manage memberships -### Removing a member from your organization +### Remove a member from your organization -Users cannot leave the organization themselves, but they can be removed in the Clerk Dashboard, using [Clerk's Backend API](/docs/reference/backend-api/tag/Organization-Memberships#operation/DeleteOrganizationMembership) endpoint, and by another organization member with the [manage members permission](/docs/organizations/roles-permissions#system-permissions) (`org:sys_memberships:manage`). However, the user will be added back to the organization on next sign in, unless they are removed from the IdP or the enterprise connection is no longer associated with the organization. +When a user is tied to an organization through their enterprise connection, they cannot leave the organization themselves, but they can be removed either in the Clerk Dashboard, using [Clerk's Backend API](/docs/reference/backend-api/tag/Organization-Memberships#operation/DeleteOrganizationMembership) endpoint, or by another organization member with the [manage members permission](/docs/organizations/roles-permissions#system-permissions) (`org:sys_memberships:manage`). However, the user will be added back to the organization on next sign-in, unless they are removed from the IdP or the enterprise connection is no longer associated with the organization. -## Updating organization from existing enterprise connection +## Update an organization from an existing enterprise connection When transitioning an enterprise connection to a new organization, existing members will remain part of the original organization. However, they will automatically join the new organization upon their next sign-in. diff --git a/docs/organizations/metadata.mdx b/docs/organizations/metadata.mdx index d8a3131ff0..67b3425433 100644 --- a/docs/organizations/metadata.mdx +++ b/docs/organizations/metadata.mdx @@ -3,9 +3,6 @@ title: Organization metadata description: Organization objects hold a set of metadata that can be used internally to store arbitrary information. --- -> [!CAUTION] -> This guide is for users who want to build a _custom_ user interface using the Clerk API. To create organizations using a _prebuilt_ UI, you should use the [prebuilt components](/docs/components/overview). - Organization metadata allows you to store information about an organization that is not part of the standard fields, such as custom attributes that are specific to your application. There are two types of metadata: **public** and **private**. @@ -20,6 +17,19 @@ Both the `Organization` and `Organization Membership` objects have the metadata ## Set organization metadata +There are two ways to set organization metadata: + +- In the Clerk Dashboard +- Using the [Backend SDK](/docs/references/backend/overview) + +### In the Clerk Dashboard + +1. In the Clerk Dashboard, navigate to the [**Organizations**](https://dashboard.clerk.com/last-active?path=organizations) page. +1. Select the organization you want to update. +1. In the **Organization metadata** section, select **Edit** next to the metadata you want to update. + +### Using the Backend SDK + To ease the flow of setting metadata, Clerk provides the `updateOrganizationMetadata()` and `updateOrganizationMembershipMetadata()` methods from the [Backend SDK](/docs/references/backend/overview), which is a wrapper around the [Backend API](/docs/reference/backend-api){{ target: '_blank' }}. > [!WARNING] diff --git a/docs/organizations/overview.mdx b/docs/organizations/overview.mdx index 60438a38b5..78e9e812aa 100644 --- a/docs/organizations/overview.mdx +++ b/docs/organizations/overview.mdx @@ -16,55 +16,25 @@ Organizations are disabled by default. To enable organizations: 1. In the Clerk Dashboard, navigate to the [**Organizations Settings**](https://dashboard.clerk.com/last-active?path=organizations-settings) page. -1. Toggle on **Enable Organizations**. +1. Select **Enable Organizations**. Once organizations are enabled, you will be presented with the default settings, roles, and permissions that are applied to all organizations in that application instance. The following sections will explain these settings in more detail. -### Roles and permissions - -Roles determine a user's level of access and permissions within an organization. Learn more about [how roles and permissions work and how to create your own with Clerk](/docs/organizations/roles-permissions). - -### Membership limit - -There is no limit to the number of organizations a user can be a member of. - -However, there is a limit to how many members total can be in a single organization. By default, the membership limit is set to 5 members. To change this limit, scroll to the **Default membership limit** section and update the membership limit. - -If you are on the Free plan, you can update the membership limit to a maximum of 5 members. - -If you have the Pro plan, you can set the membership limit to unlimited. - -You can also change this limit on a per-organization basis: - -1. In the top in the Clerk Dashboard, select [**Organizations**](https://dashboard.clerk.com/last-active?path=organizations). -1. Select the organization you want to update. -1. In the **Membership limit** section, update the membership limit. Note that this will not apply to organizations that already exist. - -### Default ability to delete - -By default, organizations are deletable. Any member with the "Delete organization" permission can delete an organization. To prevent organizations from being deleted, you can disable the ability to delete organizations by scrolling to the **Default ability to delete** section and unchecking the option. Note that this will not apply to organizations that already exist. - -### Verified domains - -Verified domains can be used to streamline enrollment into an organization. For example, if the domain `@clerk.com` is added to an organization, any user with a `@clerk.com` email address can be automatically invited or be suggested to join this organization. This feature is useful for organizations that want to restrict membership to users with specific email domains. See the [verified domains](/docs/organizations/verified-domains) documentation for more information. - -#### Automatic invitations and suggestions - -Users with email addresses that match the organization's verified domain(s) can receive automatic invitations or automatic suggestions to join the organization. - -For example, if the domain `@clerk.com` is the verified domain for an organization, any user with an email address ending in `@clerk.com` will receive an automatic invitation or suggestion to join the organization. - -To allow automatic invitations and suggestions in your application, you must first enable them in the Clerk Dashboard. See the [Verified domains](/docs/organizations/verified-domains#enable-verified-domains) guide for more information. +## Organization management -Once enabled for your application, when an organization admin is creating a verified domain in your application, they are able to enable automatic invitations _or_ suggestions for that domain. They cannot enable both at the same time. +As the application owner, you have control over all of the organizations within your application - both those created by you and those created by your users. You can create, update, and delete organizations, as well as manage their members and settings. -Once invitations are enabled for a domain, users with email addresses that match the domain will see **Join** button next to the organization in the Clerk UI. Selecting the button will accept the invitation and the user will be added as a member of the organization. +1. In the top in the Clerk Dashboard, select [**Organizations**](https://dashboard.clerk.com/last-active?path=organizations). Here, you can view and manage all organizations in your application. +1. Select a specific organization to view its details, members, and settings. Here, you can update the organization's name, slug, and logo. You can also set the organization's [membership limit](#membership-limit) and public and private metadata. -Once suggestions are enabled for a domain, users with email addresses that match the domain will see a **Request to join** button next to the organization in the Clerk UI. Selecting the button will send a [membership request](#membership-requests) to the organization. +For managing organizations in your application, Clerk provides a set of prebuilt components: -#### Membership requests +- [``](/docs/components/organization/create-organization) - A form for a user to create a new organization. +- [``](/docs/components/organization/organization-profile) - A profile page for the user's currently active organization. +- [``](/docs/components/organization/organization-list) - A list of organizations that a user is a member of. +- [``](/docs/components/organization/organization-switcher) - A dropdown menu that handles all organization flows. It allows a user to create an organization, switch between their personal account and their organization account(s), and view their organization's profile, which allows them to manage the organization's settings, invitations, and current members. -Membership requests are requests from users who want to join an organization. A membership request is created when a user sees a suggestion to join an organization and selects the **Request to join** button. Therefore, membership requests are only available for organizations that have the [**Verified domains** feature enabled](#verified-domains) and the [**Automatic suggestions** feature enabled in both the Dashboard and for the specific domain](#suggestions). +If the prebuilt components don't meet your specific needs or if you require more control over the logic, you can rebuild and customize the existing Clerk flows using the Clerk API. See the [custom flows](/docs/custom-flows/overview) for more information. ## Active organization @@ -74,25 +44,13 @@ By default, when a user initially signs in to a Clerk-powered application, they The easiest way to allow users to set an organization as active is to use the [``](/docs/components/organization/organization-switcher) component. -You can also use the `setActive()` method, which is available on the [`Clerk`](/docs/references/javascript/clerk/session-methods#set-active) object and is returned by the [`useOrganizationList()`](/docs/references/react/use-organization-list) hook. - -If you would like to hide personal workspaces and require users to always have an organization set as active, see the [Force organizations](/docs/organizations/force-organizations) guide. - -## Create an organization - -[You can create organizations in the Clerk Dashboard](#application-owner), or [your end users can create organizations in your application](#application-user). - -### Create an organization in the Clerk Dashboard - -To create an organization in the Clerk Dashboard: +You can also use the `setActive()` method, which is returned by the [`useOrganizationList()`](/docs/references/react/use-organization-list) hook. If you aren't using hooks, you can access the `setActive()` method from the [`Clerk`](/docs/references/javascript/clerk/session-methods#set-active) object. -1. In the top in the Clerk Dashboard, select [**Organizations**](https://dashboard.clerk.com/last-active?path=organizations). -1. Select the **Create Organization** button. -1. Enter the organization's name and slug. The slug is a unique identifier for the organization and is used in URLs. +If you would like to hide personal workspaces and require users to always have an organization set as active, see the [dedicated guide](/docs/organizations/force-organizations). -#### Monthly active organization (MAO) +## Monthly Active Organization (MAO) -The number of organizations you can have in a single Clerk application depends on your [Clerk plan](/pricing){{ target: '_blank' }} and the type of instance (development or production), and is measured by "monthly active organizations" (MAOs). An MAO is an organization with at least two users that have signed in that month, at least one of which must have interacted with the organization during the current billing cycle. +The number of organizations you can have in a single Clerk application depends on your [Clerk plan](/pricing){{ target: '_blank' }} and the type of instance (development or production), and is measured by Monthly Active Organizations (MAOs). An MAO is an organization with at least two users that have signed in that month, at least one of which must have interacted with the organization during the current billing cycle. With the Free plan: @@ -108,6 +66,23 @@ For more information on pricing, see the [pricing page](/pricing){{ target: '_bl If you need more organizations or custom pricing, contact the [sales team](/contact/sales){{ target: '_blank' }} to upgrade to the Enterprise plan. +## Create an organization + +There are two ways to create an organization: + +- [In the Clerk Dashboard](#create-an-organization-in-the-clerk-dashboard) +- [In your application](#create-an-organization-in-your-application) + +How many organizations you can create depends on how many [MAOs](#monthly-active-organization-mao) you have. + +### Create an organization in the Clerk Dashboard + +To create an organization in the Clerk Dashboard: + +1. In the top in the Clerk Dashboard, select [**Organizations**](https://dashboard.clerk.com/last-active?path=organizations). +1. Select the **Create Organization** button. +1. Enter the organization's name. Optionally, upload the organization's logo, enter the organization's slug, and select the organization's owner. The slug is a unique identifier for the organization that is used in URLs, such as `example-name`. + ### Create an organization in your application By default, users have the permission to create organizations within your application. To configure this permission for all users: @@ -125,43 +100,42 @@ When a user creates an organization, they become the organization's admin. As th A single user within one of your applications can create _up to_ 100 organizations in that application. If you need users to be able to create more organizations than this, [contact support](/contact/support){{ target: '_blank' }} to have the limit raised. -The easiest way to allow users to create organizations is to use the [``](/docs/components/organization/create-organization) and/or [``](/docs/components/organization/organization-switcher) components. +The easiest way to allow users to create organizations is to use the [``](/docs/components/organization/create-organization) and/or [``](/docs/components/organization/organization-switcher) components. The `` component is more comprehensive, as it handles all organization flows. -## Organization invitations +## Roles and permissions + +Roles determine a user's level of access and permissions within an organization. Learn more about [how roles and permissions work and how to create your own with Clerk](/docs/organizations/roles-permissions). -Organization invitations are a way to invite users to join an organization. When a user is invited to an organization, they will receive an email with a link to accept the invitation. Once they accept the invitation, they will be added as a member of the organization. +## Membership limit -By default, only admins can invite users to an organization. +There is no limit to the number of organizations a user can be a member of. -This feature requires that **Email address** is enabled as an [identifier](/docs/authentication/configuration/sign-up-sign-in-options#identifiers), as Clerk uses the user's email address to send the invitation. You can still disable **Email address** as an authentication option if you do not want users to be able to sign-in with their email address. +However, there is a limit to how many members total can be in a single organization. By default, the membership limit is set to 5 members. To change this limit, scroll to the **Default membership limit** section and update the membership limit. -To configure your application's **Email address** settings: +If you are on the Free plan, you can update the membership limit to a maximum of 5 members. -1. In the Clerk Dashboard, navigate to the [**Email, phone, username**](https://dashboard.clerk.com/last-active?path=user-authentication/email-phone-username) page. -1. In the **Contact information** section, ensure that **Email address** is toggled on. -1. Next to **Email address**, select the settings icon to configure the email address settings. Here, at least **Require** should be toggled on. +If you have the Pro plan, you can set the membership limit to unlimited. -## Organization management +You can also change this limit on a per-organization basis: -As the application owner, you have control over all of the organizations within your application - both those created by you and those created by your users. You can create, update, and delete organizations, as well as manage their members and settings. +1. In the top in the Clerk Dashboard, select [**Organizations**](https://dashboard.clerk.com/last-active?path=organizations). +1. Select the organization you want to update. +1. In the **Membership limit** section, update the membership limit. Note that this will not apply to organizations that already exist. -1. In the top in the Clerk Dashboard, select [**Organizations**](https://dashboard.clerk.com/last-active?path=organizations). Here, you can view and manage all organizations in your application. -1. Select a specific organization to view its details, members, and settings. Here, you can update the organization's name, slug, and logo. You can also set the organization's [membership limit](#how-many-organizations-can-i-create) and public and private metadata. +## Default ability to delete -For managing organizations in your application, Clerk provides a set of prebuilt components: +By default, organizations are deletable. Any member with the "Delete organization" permission can delete an organization. To prevent organizations from being deleted, you can disable the ability to delete organizations by following these steps: -- [``](/docs/components/organization/create-organization) - A form for a user to create a new organization. -- [``](/docs/components/organization/organization-profile) - A profile page for the user's currently active organization. -- [``](/docs/components/organization/organization-switcher) - A dropdown menu that allows a user to switch between their personal account and their organization account(s), as well as create a new organization if they have permission to do so. -- [``](/docs/components/organization/organization-list) - A list of organizations that a user is a member of. +1. In the Clerk Dashboard, navigate to the [**Organizations Settings**](https://dashboard.clerk.com/last-active?path=organizations-settings) page. +1. Scroll to the **Default ability to delete** section and uncheck the option. Note that this will not apply to organizations that already exist. + +## Verified domains -If the prebuilt components don't meet your specific needs or if you require more control over the logic, you can rebuild and customize the existing Clerk flows using the Clerk API. Some useful guides include: +Verified domains can be used to streamline enrollment into an organization. For example, if the domain `@clerk.com` is added to an organization, any user with a `@clerk.com` email address can be automatically invited or be suggested to join this organization. This feature is useful for organizations that want to restrict membership to users with specific email domains. See the [guide on verified domains](/docs/organizations/verified-domains) for more information. + +## Organization invitations -- [Creating an organization](/docs/organizations/creating-organizations) -- [Updating an organization](/docs/organizations/updating-organizations) -- [Creating an infinite paginated list of memberships](/docs/organizations/viewing-memberships) -- [Inviting users to an organization](/docs/organizations/inviting-users), which also includes code for creating a custom list of invitations -- [Managing memberships](/docs/organizations/managing-roles), which includes code for updating and deleting a user's membership, for inviting a user, and for creating a custom list of memberships, invitations, and requests +Organization invitations are a way to invite users to join an organization. See the [guide on organization invitations](/docs/organizations/invitations) for more information. ## Manage SSO diff --git a/docs/organizations/verified-domains.mdx b/docs/organizations/verified-domains.mdx index 8b2b8ec992..12d73041ae 100644 --- a/docs/organizations/verified-domains.mdx +++ b/docs/organizations/verified-domains.mdx @@ -10,6 +10,8 @@ A verified domain cannot be a disposable domain or common email provider. For ex > [!WARNING] > A verified domain can't be added if it's already in use for the [organization's SSO](/docs/organizations/manage-sso). +The easiest way to add and verify domains, and manage all settings related to verified domains is to use Clerk's [``](/docs/components/organization/organization-switcher) component. + ## Enable verified domains Enabling verified domains applies to all organizations and cannot currently be managed on a per-organization basis. @@ -17,23 +19,36 @@ Enabling verified domains applies to all organizations and cannot currently be m In order to enable this feature: 1. In the Clerk Dashboard, navigate to the [**Organizations Settings**](https://dashboard.clerk.com/last-active?path=organizations-settings) page. -1. In the **Verified domains** section, check the **Enable verified domains** checkbox. +1. In the **Verified domains** section, enable **Enable verified domains**. 1. The following setting will appear: - -- [**Enrollment mode**](#enrollment-mode) - Choose between **Automatic invitation** and **Automatic suggestion**. + - [**Enrollment mode**](#enrollment-mode) - **Automatic invitation** and **Automatic suggestion**. ### Enrollment mode You can enable the following enrollment modes to be available for your application: -- **Automatic invitation** - During sign-up, a user will receive an invitation for the organization if their email's domain matches the verified domain. The user will join the organization if they accept the automatic invitation. -- **Automatic suggestion** - During sign-up, a user will receive a suggestion for the organization if their email's domain matches the verified domain. The user can request to join, and an administrator must accept this request before the user can join the organization. +- [**Automatic invitation**](#automatic-invitations) - Users are automatically invited to join the organization when they sign-up and can join anytime. +- [**Automatic suggestion**](#automatic-suggestions) - Users receive a suggestion to request to join, but must be approved by an admin before they are able to join the organization. + +Then, in your application, when a user with the `org:sys_domains:manage` permission has added and verified a domain, they can enable an enrollment mode. Only one enrollment mode can be enabled for a verified domain at a time. + +### Automatic invitations + +After sign-up, a user will receive an **invitation** for the organization if their email's domain matches the verified domain. If your app uses the `` component, the user will see a notification on the component. When they open the component, they will see a **Join** button next to the organization they were invited to. Selecting the button will accept the invitation and the user will instantly be added as a member of the organization. + +### Automatic suggestions + +After sign-up, a user will receive a **suggestion** for the organization if their email's domain matches the verified domain. If your app uses the `` component, the user will see a **Request to join** button next to the organization. Selecting the button will send a [membership request](#membership-requests) to the organization. + +### Membership requests + +Membership requests are requests from users who saw an organization suggestion and requested to join an organization. Membership requests are only available for organizations that have the **Verified domains** feature enabled and the **Automatic suggestions** feature enabled in both the Dashboard and for the specific domain. -Once a domain is added and verified in your organization, the user will have the option to select an enrollment mode from those you made available. +When a user sends an organization membership request, users with the `org:sys_memberships:manage` permission (by default, admins) will see a notification on their `` component. They will need to accept the request before the user can join the organization. -## Adding and verifying domains +## Add and verify domains -Domains can be added and verified under an organization by any user with the `org:sys_domains:manage` permission. By default, admins have this permission. The easiest way to add and verify domains is to use Clerk's [``](/docs/components/organization/organization-switcher) component. In the **General** tab, there will be a **Verified domains** section. +Domains can be added and verified under an organization by any user with the `org:sys_domains:manage` permission. By default, admins have this permission. To add and verify domains in the [``](/docs/components/organization/organization-switcher) component, select the **General** tab. There will be a **Verified domains** section. Domains can be verified through an email verification code sent to an email that matches the domain. If the user adding the domain already has a verified email using that domain in their account, the domain will be automatically verified. diff --git a/docs/organizations/verify-user-permissions.mdx b/docs/organizations/verify-user-permissions.mdx index 98738cd6b2..318289f149 100644 --- a/docs/organizations/verify-user-permissions.mdx +++ b/docs/organizations/verify-user-permissions.mdx @@ -6,7 +6,7 @@ description: A collection of utility functions and components in order to allow > [!IMPORTANT] > The following authorization checks are predicated on a user having an active organization. Without this, they will likely always evaluate to false by default. Learn more about [active organizations](/docs/organizations/overview#active-organization). If you would like to perform authorization checks without using Clerk's organizations feature, see [the Role Based Access Control (RBAC) guide](/docs/references/nextjs/basic-rbac). -In general, you should always verify whether or not a user is authorized to access sensitive information, important content, or exclusive features. The most secure way to implement authorization is by checking the active user's [role or permissions](/docs/organizations/roles-permissions#permissions). +In general, you should always verify whether or not a user is authorized to access sensitive information, important content, or exclusive features. The most secure way to implement authorization is by checking the active user's [role or permissions](/docs/organizations/roles-permissions#permissions). It's recommended to use permission-based authorization over role-based authorization, as it's more flexible, easier to manage, and more secure. Clerk enables two broad approaches to role and permissions-based authorization: @@ -296,8 +296,9 @@ If you are not using React or any of the meta-frameworks we support, you can use ```tsx {{ filename: 'main.js' }} import { Clerk } from '@clerk/clerk-js' - // Initialize Clerk with your Clerk Publishable Key - const clerk = new Clerk('{{pub_key}}') + const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + + const clerk = new Clerk(clerkPubKey) await clerk.load() // Check if the user is authenticated @@ -314,7 +315,7 @@ If you are not using React or any of the meta-frameworks we support, you can use ## Authorize with roles > [!WARNING] -> It's best practice to use permission-based authorization over role-based authorization, as it reduces complexity and increases security. Usually, complex role checks can be refactored with a single permission check. +> It's best practice to use permission-based authorization over role-based authorization, as it's more flexible and easier to manage. Usually, complex role checks can be refactored with a single permission check. You can pass a `role` the same way you can pass a `permission` in all the examples above. @@ -384,7 +385,7 @@ You can pass a `role` the same way you can pass a `permission` in all the exampl -## How to add types for roles and permissions +## Add custom types for roles and permissions In order to enhance typesafety in your project, you can define a global `ClerkAuthorization` interface, which defines the acceptable values for roles and permissions. diff --git a/docs/organizations/viewing-memberships.mdx b/docs/organizations/viewing-memberships.mdx deleted file mode 100644 index 310bfea185..0000000000 --- a/docs/organizations/viewing-memberships.mdx +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: Build a custom flow for viewing a user's organization memberships -description: Learn how to use the Clerk API to build a custom flow for viewing a user's organization memberships. ---- - - - -This guide will demonstrate how to use the Clerk API to build a custom flow for viewing the list of a user's organization memberships. - - - - The following example: - - 1. Uses the [`useOrganizationList()`](/docs/references/react/use-organization-list) hook to get `memberships`, which is a list of the current user's organization memberships. - `memberships` returns `data`, which is an array of [`OrganizationMembership`](/docs/references/javascript/types/organization-membership) objects. - 1. Maps over the `data` array to display the user's organization memberships in a table. - - This example is written for Next.js App Router but can be adapted for any React meta framework, such as Remix. - - ```jsx {{ filename: 'app/components/JoinedOrganizations.tsx' }} - 'use client' - - import { useOrganizationList, useUser } from '@clerk/nextjs' - - export const userMembershipsParams = { - memberships: { - pageSize: 5, - keepPreviousData: true, - }, - } - - // List of the user's organization memberships. - export const JoinedOrganizations = () => { - const { user } = useUser() - const { isLoaded, userMemberships } = useOrganizationList({ - userMemberships: userMembershipsParams, - }) - - if (!isLoaded) { - return <>Loading - } - - return ( - <> -

    Joined organizations

    - - - - - - - - - - - {userMemberships?.data?.map((mem) => ( - - - - - - - ))} - -
    IdentifierOrganizationJoinedRole
    {mem.publicUserData.identifier}{mem.organization.name}{mem.createdAt.toLocaleDateString()}{mem.role}
    - -
    - - - -
    - - ) - } - ``` -
    - - - The following example: - - 1. Calls the `getOrganizationMemberships()` method to retrieve the list of organizations the current user is a part of. This method returns `data`, which is an array of `OrganizationMembership` objects. - 1. Maps over the `data` array to display the user's organization memberships in a table. - - Use the tabs to view the code necessary for the `index.html` and `main.js` files. - - - ```html {{ filename: 'index.html' }} - - - - - - Clerk + JavaScript App - - -
    - -

    Joined organizations

    - - - - - - - - - - -
    IdentifierOrganizationJoinedRole
    - - - - - ``` - - ```js {{ filename: 'main.js' }} - import { Clerk } from '@clerk/clerk-js' - - const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY - - if (!pubKey) { - throw new Error('Add your VITE_CLERK_PUBLISHABLE_KEY to .env file') - } - - const clerk = new Clerk('{{pub_key}}') - await clerk.load() - - if (clerk.user) { - // Check for an active organization - if (clerk.organization) { - const { data } = await clerk.user.getOrganizationMemberships() - const memberships = data - - memberships.map((membership) => { - const membershipTable = document.getElementById('memberships_table') - const row = membershipTable.insertRow() - row.insertCell().textContent = membership.publicUserData.identifier - row.insertCell().textContent = membership.organization.name - row.insertCell().textContent = membership.createdAt.toLocaleDateString() - row.insertCell().textContent = membership.role - }) - } else { - // If there is no active organization, - // mount Clerk's - // to allow the user to set an organization as active - document.getElementById('app').innerHTML = ` -

    Select an organization to set it as active

    -
    - ` - - const orgSwitcherDiv = document.getElementById('org-switcher') - - clerk.mountOrganizationSwitcher(orgSwitcherDiv) - } - } else { - // If there is no active user, mount Clerk's - document.getElementById('app').innerHTML = ` -
    - ` - - const signInDiv = document.getElementById('sign-in') - - clerk.mountSignIn(signInDiv) - } - ``` -
    -
    -
    diff --git a/docs/references/backend/invitations/create-invitation.mdx b/docs/references/backend/invitations/create-invitation.mdx index 13b77793c9..fc498f5b9b 100644 --- a/docs/references/backend/invitations/create-invitation.mdx +++ b/docs/references/backend/invitations/create-invitation.mdx @@ -26,7 +26,7 @@ function createInvitation(params: CreateParams): Promise - `redirectUrl?` - `string` - The full URL or path where the user is redirected upon visiting the invitation link, where they can accept the invitation. Required if you have implemented a [custom flow for handling application invitations](/docs/custom-flows/invitations). + The full URL or path where the user is redirected upon visiting the invitation link, where they can accept the invitation. Required if you have implemented a [custom flow for handling application invitations](/docs/custom-flows/application-invitations). --- diff --git a/docs/references/javascript/clerk/session-methods.mdx b/docs/references/javascript/clerk/session-methods.mdx index 0af447e662..292dc59432 100644 --- a/docs/references/javascript/clerk/session-methods.mdx +++ b/docs/references/javascript/clerk/session-methods.mdx @@ -19,4 +19,4 @@ The `setActive()` method is most commonly used when building a [custom flow](/do For example, during authentication, when a user signs in or signs up successfully, a new session is created. `setActive()` needs to be used to set the new session as the active session. See the implementation of this in the [Custom authentication flow](/docs/custom-flows/overview) guide. -Another example is when a user switches organizations in a multi-organization application. `setActive()` needs to be used to set the new organization as the active organization. See the implementation of this in the [Custom organization switcher](/docs/organizations/custom-organization-switcher) guide. +Another example is when a user switches organizations in a multi-organization application. `setActive()` needs to be used to set the new organization as the active organization. See the implementation of this in the [Custom organization switcher](/docs/custom-flows/organization-switcher) guide. diff --git a/docs/references/javascript/organization/organization.mdx b/docs/references/javascript/organization/organization.mdx index ccb03a155b..3b66576e66 100644 --- a/docs/references/javascript/organization/organization.mdx +++ b/docs/references/javascript/organization/organization.mdx @@ -120,6 +120,27 @@ function update(params: UpdateOrganizationParams): Promise - `string | undefined` The organization slug. + + --- + + - `maxAllowedMemberships?` + - `number | undefined` + + The maximum number of memberships allowed for the organization. + + --- + + - `publicMetadata?` + - [`OrganizationPublicMetadata`](/docs/references/javascript/types/metadata#organization-public-metadata) + + Metadata that can be read from both the Frontend API and [Backend API](/docs/reference/backend-api){{ target: '_blank' }}, but can be set only from the Backend API. + + --- + + - `privateMetadata?` + - [`OrganizationPrivateMetadata`](/docs/references/javascript/types/metadata#organization-private-metadata) + + Metadata that is only visible to your [Backend API](/docs/reference/backend-api){{ target: '_blank' }}. #### Example diff --git a/docs/references/javascript/sign-up/sign-up.mdx b/docs/references/javascript/sign-up/sign-up.mdx index a7d206be10..b66836580f 100644 --- a/docs/references/javascript/sign-up/sign-up.mdx +++ b/docs/references/javascript/sign-up/sign-up.mdx @@ -252,7 +252,7 @@ function create(params: SignUpCreateParams): Promise - `ticket` - `string` - **Required** if `strategy` is set to `'ticket'`. The [ticket _or token_](/docs/custom-flows/invitations#create-the-sign-up-flow) generated from the Backend API. + **Required** if `strategy` is set to `'ticket'`. The [ticket _or token_](/docs/custom-flows/application-invitations#create-the-sign-up-flow) generated from the Backend API. --- diff --git a/docs/references/javascript/types/user-organization-invitation.mdx b/docs/references/javascript/types/user-organization-invitation.mdx index 53970d1782..ef13f5b6a7 100644 --- a/docs/references/javascript/types/user-organization-invitation.mdx +++ b/docs/references/javascript/types/user-organization-invitation.mdx @@ -85,4 +85,4 @@ function accept(): Promise ### Example -To see an example of how to use the `accept()` method, see the [custom flow guide for managing invitations](/docs/organizations/manage-invitations). +To see an example of how to use the `accept()` method, see the [custom flow guide for managing invitations](/docs/custom-flows/manage-user-org-invitations). diff --git a/docs/users/invitations.mdx b/docs/users/invitations.mdx index 7d2e2e83f7..d54f2151a8 100644 --- a/docs/users/invitations.mdx +++ b/docs/users/invitations.mdx @@ -61,7 +61,7 @@ The following example demonstrates how to use cURL to create an invitation with curl https://api.clerk.com/v1/invitations -X POST -d '{"email_address": "email@example.com", "redirect_url": "https://www.example.com/accept-invitation"}' -H "Authorization:Bearer {{secret}}" -H 'Content-Type:application/json' ``` -Once the user visits the invitation link, they will be redirected to the page you specified, which means you must handle the sign-up flow in your code for that page. You can either embed the [``](/docs/components/authentication/sign-up) component on that page, or if the prebuilt component doesn't meet your specific needs or if you require more control over the logic, you can build a [custom flow](/docs/custom-flows/invitations). +Once the user visits the invitation link, they will be redirected to the page you specified, which means you must handle the sign-up flow in your code for that page. You can either embed the [``](/docs/components/authentication/sign-up) component on that page, or if the prebuilt component doesn't meet your specific needs or if you require more control over the logic, you can build a [custom flow](/docs/custom-flows/application-invitations). > [!TIP] > To test redirect URLs in your development environment, you can pass your port (`http://localhost:3000`). If you want to use the Account Portal, you can pass your Clerk Frontend API URL as the base URL. For example, `https://prepared-phoenix-98.clerk.accounts.dev/sign-up` redirects the user to the Account Portal sign-up page. You can find your Frontend API URL on the [**API keys**](https://dashboard.clerk.com/last-active?path=api-keys) page in the Clerk Dashboard. On the left side, select **Show API URLs**. @@ -130,4 +130,4 @@ See the [Backend API reference](/docs/reference/backend-api/tag/Invitations#oper ## Custom flow -Clerk's [prebuilt components](/docs/components/overview) and [Account Portal pages](/docs/customization/account-portal/overview) handle the sign-up flow for you, including the invitation flow. If you want to build a **custom** sign-up flow, see the [custom flow](/docs/custom-flows/invitations) guide. +Clerk's [prebuilt components](/docs/components/overview) and [Account Portal pages](/docs/customization/account-portal/overview) handle the sign-up flow for you, including the invitation flow. If you want to build a **custom** sign-up flow, see the [custom flow](/docs/custom-flows/application-invitations) guide.