diff --git a/docs/docs/recipes/organizations/README.mdx b/docs/docs/recipes/organizations/README.mdx
index fb6b2ff556e..fb006488b06 100644
--- a/docs/docs/recipes/organizations/README.mdx
+++ b/docs/docs/recipes/organizations/README.mdx
@@ -6,11 +6,11 @@ import Availability from '@components/Availability';
# 🏢 Organizations (Multi-tenancy)
-
+
-Organization is particularly effective in business-to-business (B2B) apps. In addtion to individual consumers, business clients can also consist of teams, organizations, or entire companies. Logto introduces the entity of an organization as a foundational element for authentication and authorization in B2B products, for example, SaaS.
+Organization is particularly effective in business-to-business (B2B) apps. In addition to individual users, your clients can also consist of teams, organizations, or entire companies. Logto introduces Organizations as a foundational element for B2B authentication and authorization.
-Even if your product is not B2B, organization can still be useful for collaboration features, such as sharing resources with other users.
+Even if your product is consumer-facing, organization can still be useful for collaboration features, such as sharing resources with other users.
With this fundamental element, you can build the must-have features for multi-tenancy apps, such as:
diff --git a/docs/docs/recipes/organizations/configure-organizations.mdx b/docs/docs/recipes/organizations/configuration.mdx
similarity index 100%
rename from docs/docs/recipes/organizations/configure-organizations.mdx
rename to docs/docs/recipes/organizations/configuration.mdx
diff --git a/docs/docs/recipes/organizations/impact-on-end-users.mdx b/docs/docs/recipes/organizations/impact-on-end-users.mdx
index d7bcdb5371c..e2997ae07ab 100644
--- a/docs/docs/recipes/organizations/impact-on-end-users.mdx
+++ b/docs/docs/recipes/organizations/impact-on-end-users.mdx
@@ -8,4 +8,4 @@ Currently, there's no impact to the end-user sign-in experience since Logto deco
As the user signs in, they can get the access to all organizations with membership. For detailed information, please refer to Organization RBAC.
-We may add advanced features for organizations that change the sign-in experience in the future. Please
+We may add advanced features for organizations that change the sign-in experience in the future. Stay tuned! Join our [Discord community](https://discord.gg/UEPaF3j5e6) to share your thoughts and feedback.
diff --git a/docs/docs/recipes/organizations/integration.mdx b/docs/docs/recipes/organizations/integration.mdx
new file mode 100644
index 00000000000..77b5dcc949d
--- /dev/null
+++ b/docs/docs/recipes/organizations/integration.mdx
@@ -0,0 +1,276 @@
+---
+sidebar_position: 4
+---
+
+import TabItem from '@theme/TabItem';
+import Tabs from '@theme/Tabs';
+
+# Integrate Organizations with your app
+
+:::note
+This document assumes you have already [integrated Logto with your app](/docs/docs/recipes/integrate-logto/README.md).
+:::
+
+In your app, you may want to show a list of organizations that the user is a member of, and perform actions in the context of an organization. Let's see how to do that.
+
+## Get organization IDs of the current user
+
+Logto extends the standard [OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html) protocol to allow your app to get the organization info from the user. There are two ways to do that:
+
+- If you are using a Logto SDK with Organizations support, you can add the `urn:logto:scope:organizations` scope to `scopes` parameter of the configuration object. Usually the SDK will have an enum for this scope, e.g. `UserScope.Organizations` in [Logto JS SDKs](https://github.com/logto-io/js).
+- For other cases, you need to add the `urn:logto:scope:organizations` scope to the `scope` parameter of the SDK config (or auth request).
+
+
+
+
+
+```ts
+const logto = new LogtoClient({
+ // ...
+ scopes: [UserScope.Organizations],
+});
+```
+
+
+
+
+```tsx
+
+```
+
+
+
+
+
+```ts
+const config = {
+ // ...
+ scope: 'openid offline_access urn:logto:scope:organizations',
+};
+```
+
+
+
+
+
+Once the user finishes the authentication flow, you can get the organization info from the `idToken`:
+
+```ts
+// Use JavaScript as an example
+const idToken = await logto.getIdTokenClaims();
+
+console.log(idToken.organizations); // A string array of organization IDs
+```
+
+The `organizations` field (claim) will also be included in response from the [UserInfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo).
+
+### Get organization roles
+
+To get all organization roles of the current user:
+
+- If you are using a Logto SDK with Organizations support, you can add the `urn:logto:scope:organization_roles` scope to `scopes` parameter of the configuration object. Usually the SDK will have an enum for this scope, e.g. `UserScope.OrganizationRoles` in [Logto JS SDKs](https://github.com/logto-io/js).
+- For other cases, you need to add the `urn:logto:scope:organization_roles` scope to the `scope` parameter of the SDK config (or auth request).
+
+Then you can get the organization roles from the `idToken`:
+
+```ts
+// Use JavaScript as an example
+const idToken = await logto.getIdTokenClaims();
+
+console.log(idToken.organization_roles); // A string array of organization roles
+```
+
+Each string in the array is in the format of `organization_id:role_id`, e.g. `org_123:admin` means the user has the `admin` role in the organization with ID `org_123`.
+
+The `organization_roles` field (claim) will also be included in response from the [UserInfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo).
+
+## Fetch access token for an organization
+
+To perform actions in the context of an organization, the user needs to be granted an access token for that organization (organization token). The organization token is a JWT token that contains the organization ID and the user's permissions (scopes) in the organization.
+
+### Step 1: Add parameters to the authentication request
+
+- If you are using a Logto SDK with Organizations support, you can add the `urn:logto:scope:organization_token` scope to `scopes` parameter of the configuration object, the same way as [Get organization IDs of the current user](#get-organization-ids-of-the-current-user).
+ - Logto SDK with Organizations support will automatically handle the rest of the configuration.
+- For other cases, you need to add the `offline_access` and `urn:logto:scope:organizations` scopes to the `scope` parameter and the `urn:logto:resource:organizations` resource to the `resource` parameter of the SDK config (or auth request).
+ - Note: `offline_access` is required to get the `refresh_token` that can be used to fetch organization tokens.
+
+
+
+
+
+```ts
+const logto = new LogtoClient({
+ // ...
+ scopes: [UserScope.Organizations],
+});
+```
+
+
+
+
+```tsx
+
+```
+
+
+
+
+
+```ts
+const config = {
+ // ...
+ scope: 'openid offline_access urn:logto:scope:organizations',
+ resource: 'urn:logto:resource:organizations',
+};
+```
+
+
+
+
+
+:::info
+The `urn:logto:resource:organizations` resource is a special resource that represents the organization template.
+:::
+
+### Step 2: Fetch the organization token
+
+Logto extends the standard `refresh_token` grant type to allow your app to fetch organization tokens.
+
+- If you are using a Logto SDK with Organizations support, you can call the `getOrganizationToken()` method (or `getOrganizationTokenClaims()` method) of the SDK.
+- For other cases, you need to call the token endpoint with the following parameters:
+ - `grant_type`: `refresh_token`.
+ - `client_id`: The app ID the user used to authenticate.
+ - `refresh_token`: The `refresh_token` you got from the authentication flow.
+ - `organization_id`: The ID of the organization you want to get the token for.
+ - `scope` (optional): The scopes you want to grant to the user in the organization. If not specified, the authorization server will try to grant the same scopes as the authentication flow.
+
+
+
+
+
+```ts
+const token = await logto.getOrganizationToken('org_123');
+```
+
+
+
+
+```tsx
+const App = () => {
+ const { getOrganizationToken } = useLogto();
+
+ const getToken = async () => {
+ const token = await getOrganizationToken('org_123');
+ };
+
+ return ;
+};
+```
+
+
+
+
+
+```ts
+// Use JavaScript as an example
+
+const params = new URLSearchParams();
+
+params.append('grant_type', 'refresh_token');
+params.append('client_id', 'YOUR_CLIENT_ID');
+params.append('refresh_token', 'REFRESH_TOKEN');
+params.append('organization_id', 'org_123');
+
+const response = await fetch('https://YOUR_LOGTO_ENDPOINT/oidc/token', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ body: params,
+});
+```
+
+
+
+
+
+The response will be in the same format as the [standard token endpoint](https://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint), and the `access_token` is the organization token in JWT format.
+
+Besides regular claims of an access token, the organization token also contains the following claims:
+
+- `aud`: The audience of the organization token is `urn:logto:organization:{organization_id}`.
+- `scope`: The scopes granted to the user in the organization with space as delimiter.
+
+### Example
+
+A good example can beat a thousand words. Assume our organization template has the following setup:
+
+- Permissions: `read:logs`, `write:logs`, `read:users`, `write:users`.
+- Roles: `admin`, `member`.
+ - The `admin` role has all permissions.
+ - The `member` role has `read:logs` and `read:users` permissions.
+
+And the user has the following setup:
+
+- Organization IDs: `org_1`, `org_2`.
+- Organization roles: `org_1:admin`, `org_2:member`.
+
+In the Logto SDK config (or auth request), we set up other things properly, and added the following scopes:
+
+- `urn:logto:scope:organizations`
+- `openid`
+- `offline_access`
+- `read:logs`
+- `write:logs`
+
+Now, when the user finishes the authentication flow, we can get the organization IDs from the `idToken`:
+
+```ts
+// Use JavaScript as an example
+const idToken = await logto.getIdTokenClaims();
+
+console.log(idToken.organizations); // ['org_1', 'org_2']
+```
+
+If we want to get the organization tokens:
+
+```ts
+// Use JavaScript as an example
+const org1Token = await logto.getOrganizationTokenClaims('org_1');
+const org2Token = await logto.getOrganizationTokenClaims('org_2');
+
+console.log(org1Token.aud); // 'urn:logto:organization:org_1'
+console.log(org1Token.scope); // 'read:logs write:logs'
+console.log(org2Token.aud); // 'urn:logto:organization:org_2'
+console.log(org2Token.scope); // 'read:logs'
+
+const org3Token = await logto.getOrganizationTokenClaims('org_3'); // Error: User is not a member of the organization
+```
+
+Explanation:
+
+- For `org_1`, the user has the `admin` role, so the organization token should have all available permissions (scopes).
+- For `org_2`, the user has the `member` role, so the organization token should have `read:logs` and `read:users` permissions (scopes).
+
+Since we only requested `read:logs` and `write:logs` scopes in the authentication flow, the organization tokens have been "downscoped" accordingly, resulting in the intersection of the requested scopes and the available scopes.
+
+## Verify organization tokens
+
+Once the app gets an organization token, it can use the token in the same way as a regular access token, e.g. call the APIs with the token in the `Authorization` header in the format of `Bearer {token}`.
+
+In your API, you can verify the organization token which is similar to [Proctect your API](/docs/recipes/protect-your-api/#validate-the-authorization-token). Main differences:
+
+- Unlike access tokens for API resources, a user CANNOT get an organization token if the user is not a member of the organization.
+- The audience of the organization token is `urn:logto:organization:{organization_id}`.
+- For certain permissions (scopes), you need to check the `scope` claim of the organization token by splitting the string with space as delimiter.
diff --git a/docs/docs/recipes/organizations/understand-how-it-works.mdx b/docs/docs/recipes/organizations/understand-how-it-works.mdx
index 9756f00af30..5e3b69ffe52 100644
--- a/docs/docs/recipes/organizations/understand-how-it-works.mdx
+++ b/docs/docs/recipes/organizations/understand-how-it-works.mdx
@@ -19,7 +19,9 @@ The introduction of an organization as an entity is important, as it not only gr
In Logto, a user who has the membership of an organization is referred to as an organization member (i.e. member) within that organization's context.
-
+
+
+
## Organization permission
@@ -27,7 +29,9 @@ Organization permission refers to the authorization to perform an action in the
For example, `edit:resource`.
-
+
+
+
Organization permissions are not meaningful without the context of an organization. For example, `edit:resource` in the context of organization `org1` is different from `edit:resource` in the context of organization `org2`.
@@ -35,7 +39,9 @@ Organization permissions are not meaningful without the context of an organizati
Organization role is a grouping of organization permissions that can be assigned to users. The permissions must come from the predefined organization permissions.
-
+
+
+
Organization roles are not meaningful without the context of an organization. For example, `admin` in the context of organization `org1` is different from `admin` in the context of organization `org2`.