Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DX Guide: Using Organization Slugs in URLs #1664

Merged
merged 49 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
91a05e1
WIP
izaaklauer Oct 25, 2024
3d68044
wip
izaaklauer Oct 25, 2024
edf0892
Fixing up formatting
izaaklauer Oct 28, 2024
6ffcb5d
Ran `npm run format`
izaaklauer Oct 28, 2024
a1bf396
Apply suggestions from code review
izaaklauer Oct 29, 2024
a51a8e2
Adding missing code annotation
izaaklauer Oct 29, 2024
e56fb54
Better paragraph deliniation
izaaklauer Oct 29, 2024
a481920
Update docs/guides/using-org-slugs.mdx
izaaklauer Oct 29, 2024
90a4442
Combining client and server component sections
izaaklauer Oct 29, 2024
6154d63
Updating personal account links
izaaklauer Oct 29, 2024
131e37f
Merge branch 'main' into izaak/orgs-132-dx-guide
victoriaxyz Oct 30, 2024
30139c3
Apply suggestions from code review
izaaklauer Oct 31, 2024
4c4769e
Using the proper example repository in the title
izaaklauer Oct 31, 2024
06c736d
Apply suggestions from code review
izaaklauer Oct 31, 2024
f7bb4b5
Clarifying **organization** id
izaaklauer Oct 31, 2024
50e2dbc
Apply suggestions from code review
izaaklauer Oct 31, 2024
9b2d01e
More direct slug -> id tip
izaaklauer Oct 31, 2024
4400a26
Apply suggestions from code review
izaaklauer Oct 31, 2024
44b5482
Removing handshake explanation
izaaklauer Oct 31, 2024
d3b05e6
Apply suggestions from code review
izaaklauer Oct 31, 2024
88d8811
Apply suggestions from code review
izaaklauer Oct 31, 2024
d5c6100
Moving from /guides/ to /organizations/
izaaklauer Oct 31, 2024
ef0c553
Merge branch 'main' into izaak/orgs-132-dx-guide
victoriaxyz Oct 31, 2024
787772b
Running `npm run format`
izaaklauer Oct 31, 2024
c924ca3
Removing Further Reading
izaaklauer Oct 31, 2024
7da5194
Deleting big caution block
izaaklauer Oct 31, 2024
6e3000b
Revert "Deleting big caution block"
izaaklauer Oct 31, 2024
fe23e75
Deleting the *correct* big caution block.
izaaklauer Oct 31, 2024
2826a92
Merge branch 'main' into izaak/orgs-132-dx-guide
victoriaxyz Nov 1, 2024
1b5b76f
update file name
alexisintech Nov 4, 2024
5d7de1a
edit organization of doc; edit copy
alexisintech Nov 4, 2024
2906661
fix links
alexisintech Nov 4, 2024
d8376f2
Apply suggestions from code review
alexisintech Nov 5, 2024
11be60c
Merge branch 'main' into izaak/orgs-132-dx-guide
victoriaxyz Nov 5, 2024
c69d69d
Update docs/organizations/org-slugs-in-urls.mdx
alexisintech Nov 7, 2024
5d07c2e
Merge branch 'main' into izaak/orgs-132-dx-guide
victoriaxyz Nov 8, 2024
e16822a
Merge branch 'main' into izaak/orgs-132-dx-guide
izaaklauer Dec 18, 2024
e438200
Removing title quotes
izaaklauer Dec 18, 2024
0425c13
Addressing https://github.com/clerk/clerk-docs/pull/1664#discussion_r…
izaaklauer Dec 18, 2024
4832100
Merge branch 'main' into izaak/orgs-132-dx-guide
victoriaxyz Dec 19, 2024
d9bf42f
Updating title to make it clear it's handling failed activation.
izaaklauer Dec 19, 2024
abf0670
Clarifying and fixing formatting of a comment
izaaklauer Dec 19, 2024
7790bdb
Clarifying what's being displayed
izaaklauer Dec 19, 2024
1fafbe2
Updating to new async server function pattern
izaaklauer Dec 19, 2024
f65327b
Better copy
izaaklauer Dec 19, 2024
3957f81
update
victoriaxyz Dec 19, 2024
819716f
Merge branch 'main' into izaak/orgs-132-dx-guide
victoriaxyz Dec 19, 2024
ba9a40b
fix steps for tutorialhero
victoriaxyz Dec 19, 2024
42f5802
Apply suggestions from code review
alexisintech Dec 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
397 changes: 397 additions & 0 deletions docs/guides/using-org-slugs.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,397 @@
---
title: 'Using Organization Slugs in URLs'
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
description: Learn how to use organization slugs in URLs to manage the active Clerk organization.
---

<TutorialHero
beforeYouStart={[
{
title: "Set up a Next.js + Clerk application",
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
link: "/docs/quickstarts/nextjs",
icon: "nextjs",
},
{
title: "Enable organizations for your instance",
link: "/docs/organizations/overview",
icon: "globe",
},
{
title: "Examine the complete sample repository on Github",
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
link: "https://github.com/clerk/orgs/tree/main/examples/sync-org-with-url",
icon: "git",
}
]}
>
- Allow users to choose an organization slug
- Include an organization slug in your URLs
- Render organization-specific content
</TutorialHero>

## Introduction
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

In this guide, you will learn how to include an organization slug in your application's URLs, and
how to render information about the user's active organization at runtime.

## Background

izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
With [Organizations](/docs/organizations/overview), users can create entities that represent their companies within your
application. Users can be members of multiple organizations, but can only have one
[active organization](/docs/organizations/overview#active-organization) at any given time. Clerk automatically sets organizations as
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
active when a user picks an organization with a pre-built component (e.g. the
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
[Organization Switcher](/docs/components/organization/organization-switcher)), and Clerk provides
APIs (like [<code>auth()</code>](/docs/references/nextjs/auth) and [<code>useAuth()</code>](/docs/references/react/use-auth)), that let you determine the active organization and render the appropriate
content.
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

A relatively common practice is to represent organization-scoped spaces within an app by prefixing
URLs with an organization slug. For example, imagine a B2B application named "Petstore" that has
two customers, "Acmecorp" and "Widgetco". The application may choose to have urls like:
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

`https://petstore.example.com/orgs/`<b>`acmecorp`</b>`/dashboard` ⬅ indicates **Acmecorp**'s dashboard
`https://petstore.example.com/orgs/`<b>`widgetco`</b>`/dashboard` ⬅ indicates **Widgetco**'s dashboard
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

It is also possible to use an [organization ID](/docs/references/javascript/organization/organization#properties) to
indicate the active organization in URLs:
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

`https://petstore.example.com/orgs/`<b>`org_1a2b3c4d5e6f7g8e`</b>`/dashboard` ⬅ **Acmecorp's ID** indicates Acmecorp's dashboard
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

This guide will focus on using a human-comprehensible slug, but IDs are just as well-supported in Clerk's component
configuration.
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

> [!CAUTION]
> Think carefully about whether you need to include organization slugs in URLs! Consider:
>
> - Will organization-specific links be more useful to your users than
> organization-agnostic links? If most users will only ever be in one organization (that represents
> their company), they won't need the slug to disambiguate the organization when sharing
> links with their coworkers. Public documentation, marketing, and third-party blogs are also
> made easier to write if links aren't tied to any specific organization.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this feel clear enough to folks? If not, I can add an example. It would be something like:

For example, if someone is writing a public post that includes a link to settings, if that link is /orgs/acmecorp/settings, it will only be useful to employees of Acmecorp! If the URL is just /settings, then it's universally applicable and automatically contextualized into the click-er's organization by virtue of the organization encoded on the Clerk session.

> - Adding an additional piece of state (i.e. the URL) that indicates the organization will require additional
> complexity in your application, as opposed to managing it soley and transparently in the Clerk
> session.
>
> We at Clerk generally recommend against including organization slugs in URLs. If your application
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
> does have this requirement though, this guide will cover the best practices for doing so with a NextJS application.
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

<Steps>
### Defining URL patterns
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

To begin, you can define which sections of your application are scoped to organizations and which belong to
the [personal account](https://clerk.com/glossary#personal-account). For this example, we'll assume that the prefix
`/orgs/` indicates that an organization is active, followed by the organization slug, and the prefix `/me/` indicates that the
personal account is active.

| URL | What should be active? | What should be displayed? |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of "what should be displayed", how about more along the lines of "what should be returned" by the auth helper

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate a bit here? What are examples of what would be returned by the auth helper?

With this section, i'm attempting to define the application's requirements in a fairly clerk-agnostic way, so that I can then show how it can be modeled using clerk.

Copy link
Member

@LauraBeatris LauraBeatris Oct 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant what's returned as the orgSlug from auth. This doesn't need to be used within a server component to be displayed, it could also be used within a server action.

But I understand your goal here and it also looks good to me since this guide is mostly components-based, feel free to go as you prefer.

izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
| - | - | - |
| `/orgs/acmecorp` | Organization Acmecorp | Acmecorp's home |
| `/orgs/acmecorp/settings` | Organization Acmecorp | Acmecorp's settings |
| `/me` | Personal account | Personal home |
| `/me/settings` | Personal account | Personal settings |

### Configuring the Organization Switcher
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

The [Organization Switcher](/docs/components/organization/organization-switcher) and
[Organization List](/docs/components/organization/organization-list) components each have a powerful set of options for
working with slugs and IDs.
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

This sample application uses the [personal account](https://clerk.com/glossary#personal-account), so you can set `hidePersonal` to `false` to ensure the personal
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "sample application" - are we referencing https://github.com/clerk/orgs/tree/main/examples/sync-org-with-url?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of! What I mean is something like:

The hypothetical application that we'll be fleshing out in this guide (which can also be examined in full at https://github.com/clerk/orgs/tree/main/examples/sync-org-with-url) uses the personal account.

Do you think that a bare This is clear enough?

account is selectable. You can also allow users to customize their organization's URL slug when they
create their organization initially by setting `hideSlug` to `false`.

Finally, you can give the components navigation instructions. If the user selects or creates an organization
with the slug `acmecorp`, by setting `afterCreateOrganizationUrl` and `afterSelectOrganizationUrl` to `'/orgs/:slug'`,
the component will navigate the user to `/orgs/acmecorp`. If the user selects their [personal account](https://clerk.com/glossary#personal-account),
they will be navigated to `/me` via `afterSelectPersonalUrl`.

<Tabs items={["<OrganizationSwitcher />", "<OrganizationList />"]}>
<Tab>
```tsx {{ filename: 'app/sidebar.tsx', mark: [6] }}
import { OrganizationSwitcher } from '@clerk/nextjs'

export default function Sidebar() {
return (
<OrganizationSwitcher
// prettier-ignore
hidePersonal={false}
hideSlug={false}
afterCreateOrganizationUrl="/orgs/:slug"
afterSelectOrganizationUrl="/orgs/:slug"
afterSelectPersonalUrl="/me"
/>
)
}
```
</Tab>

<Tab>
```tsx {{ filename: 'app/organization-list/[[...organization-list]]/page.tsx', mark: [6] }}
import { OrganizationList } from '@clerk/nextjs'

export default function OrganizationListPage() {
return (
<OrganizationList
// prettier-ignore
hidePersonal={false}
hideSlug={false}
afterCreateOrganizationUrl="/orgs/:slug"
afterSelectOrganizationUrl="/orgs/:slug"
afterSelectPersonalUrl="/me"
/>
)
}
```
</Tab>
</Tabs>

> [!TIP]
> If you prefer to have an [organization ID](/docs/references/javascript/organization/organization#properties) appear in
> your URL rather than a slug, you can use `:id` instead of `:slug` with both `afterCreateOrganizationUrl` and
> `afterSelectOrganizationUrl`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
> [!TIP]
> If you prefer to have an [organization ID](/docs/references/javascript/organization/organization#properties) appear in
> your URL rather than a slug, you can use `:id` instead of `:slug` with both `afterCreateOrganizationUrl` and
> `afterSelectOrganizationUrl`.
> [!TIP]
> To display an [organization ID](/docs/references/javascript/organization/organization#properties) in your URL instead of a slug, replace `:slug` with `:id` in both `afterCreateOrganizationUrl` and `afterSelectOrganizationUrl`.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more like "if your app uses organization IDs instead of slugs in URLs, you'll need to use :id instead if :slug to for navigation to work". I'll workshop another less-personal version.

Copy link
Contributor Author

@izaaklauer izaaklauer Oct 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about 9b2d01e ?


### Using clerkMiddleware to set the active organization
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

With [clerkMiddleware](https://clerk.com/docs/references/nextjs/clerk-middleware), you can
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
declare URL patterns that indicate that a specific organization or the user's [personal account](https://clerk.com/glossary#personal-account)
should be activated. If the middleware detects one
of these patterns in the URL, and it notices that a different organization is
currently active according to the session, it will activate the indicated organization if
possible.

> [!NOTE]
> Implementation Detail: To resolve a mismatch, Clerk middleware initiaties a [Handshake](docs/references/sdk/terminology#:~:text=each:Handshake).
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
> The middleware will respond to the browser with a redirect to Clerk's API with a request to activate the URL-indicated
> organization. If it's possible to do so, Clerk's API will respond with a redirect back to the original URL, including
> a session token with the desired organization as active and redirect back to your application.

For this application, the path `/orgs/:slug` with any optional trailing path segments should activate the
organization indicated by the slug. To achieve this, you can define two `organizationPatterns`: one for the
root (e.g. `/orgs/acmecorp`), and one using the wildcard matcher `(.*)` that will match
`/orgs/acmecorp/any/other/resource`.

You can use the same approach for the [personal account](https://clerk.com/glossary#personal-account) with `personalAccountPatterns`.

> [!WARNING]
> It may not always be possible to activate the organization requested in the URL. This can happen if no organization
> with the given slug exists, or the user isn't a member of the given organization. In these cases, clerkMiddleware
> **will not modify** the active organization, leaving as active whichever organization was previously active on the
> clerk session.
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

See the documentation for [organizationSyncOptions](/docs/references/nextjs/clerk-middleware#organization-sync-options)
for more specific usage of the middleware options.

```tsx {{ filename: 'middleware.ts' }}
import { clerkMiddleware } from '@clerk/nextjs/server'

export default clerkMiddleware(
(auth, req) => {
if (isProtectedRoute(req)) auth().protect()
},
{
organizationSyncOptions: {
organizationPatterns: ['/orgs/:slug', '/orgs/:slug/(.*)'],
personalAccountPatterns: ['/me', '/me/(.*)'],
},
},
)

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)(.*)',
],
}
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
```

> [!TIP]
> If you prefer to have an [organization ID](/docs/references/javascript/organization/organization#properties)
> appear in your URL rather than slugs, you can use `:id` placeholder instead of `:slug` in both `organizationPatterns`
> and `personalAccountPatterns`.
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

### Server Rendered Organization Page
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

Now that clerkMiddleware is configured to activate organizations, you can build an organization-specific home page.
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

First, you must handle the case where the middleware cannot activate the organization based on the URL. This can
happen if no organization with the given slug exists, or if the given user isn't a member of the organization.

When this happens, the middleware will not change the active organization, so whatever organization was previously
active will remain active. To help with troubleshooting, a message will also be emitted by the middleware to the server
log:

> Clerk: Organization activation handshake loop detected. This is likely due to an invalid organization ID or slug.
> Skipping organization activation.
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

> [!CAUTION]
> It is ultimately the responsibility of the page to ensure that it renders the appropriate content for a given URL, and
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO those "caution" blocks can be a bit alarming to developers. I'd suggest removing this block and putting more specific guidance with comments on the code block below.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with Laura. We should avoid having multiple callouts especially ones that are lengthy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll be honest, inspiring mild alarm in the reader is what I was going for. If a developer were to just look at the organization slug in the URL, assume this meant that the middleware authorized access to the org, then render some sensitive org-specific content from their backend, that would be a very bad bug.

Hey @BRKalow , do you happen to have an opinion on the best way to communicate this one?

> to handle the case where the expected organization is not active. In this case, we detect the actual organization slug
> as a NextJS [Dynamic Route](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes) param, and
> compare it to the active organization slug. If they don't match, we render an error message and the [Organization List](/docs/components/organization/organization-list)
> component to allow the user to select a valid organization.

```tsx {{ filename: 'src/app/orgs/[slug]/page.tsx' }}
import { auth } from '@clerk/nextjs/server'
import { OrganizationList } from '@clerk/nextjs'

export default function Home({ params }: { params: { slug: string } }) {
const authObject = auth()
const orgSlug = authObject.orgSlug

if (params.slug != orgSlug) {
return (
<>
<p>Sorry, organization {params.slug} is not valid.</p>
<OrganizationList
hidePersonal={false}
hideSlug={false}
afterCreateOrganizationUrl="/orgs/:slug"
afterSelectOrganizationUrl="/orgs/:slug"
afterSelectPersonalUrl="/me"
/>
</>
)
}
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

return <>TODO</>
}
```

### Rendering Organization-specific content
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

Next, you can render organization-specific content, including the organization's name. To do this, you can
[customize the Clerk session token](https://clerk.com/docs/backend-requests/making/custom-session-token) to include
the organization name:

1. Navigate to the [Clerk Dashboard](https://dashboard.clerk.com/last-active?path=sessions).
1. In the navigation sidebar, select **Sessions**.
1. In the **Customize session token** section, select the **Edit** button.
1. In the modal that opens, you can add any claim to your session token that you need. For this guide, add the following:

```json
{
"org_name": "{{org.name}}"
}
```

1. Select **Save**.

Now you can render the organization name in your page. This example also displays the user's role within the
organization (`authObject.orgRole`), which is included in the [default session claims](https://clerk.com/docs/backend-requests/resources/session-tokens#default-session-claims).

```tsx {{ filename: 'src/app/orgs/[slug]/page.tsx' }}
import { auth } from '@clerk/nextjs/server'
import { OrganizationList } from '@clerk/nextjs'

export default function Home({ params }: { params: { slug: string } }) {
const authObject = auth()
const orgSlug = authObject.orgSlug

if (params.slug != orgSlug) {
return (
<>
<p className="pb-8">Sorry, organization {params.slug} is not valid.</p>
<OrganizationList
hidePersonal={false}
hideSlug={false}
afterCreateOrganizationUrl="/orgs/:slug"
afterSelectOrganizationUrl="/orgs/:slug"
afterSelectPersonalUrl="/me"
/>
</>
)
}
let orgName = authObject.sessionClaims['org_name'] as string

return (
<div>
{orgName && <h2>Welcome to organization {orgName}</h2>}
<p>
Your role in this organization: <span>{authObject.orgRole}</span>
</p>
</div>
)
}
```

### Client-side Routing
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved

You can also build client-side pages that are organization-scoped according to your URLs. `clerkMiddleware` executes
before your client components render, so you don't need to explicitly activate any organization. This example shows a
client-side settings page for organizations using [OrganizationProfile](/docs/components/organization/organization-profile).

Rather than the server-specific [<code>auth()</code>](/docs/references/nextjs/auth) hook, you can use the client-side
[<code>useOrganization()</code>](/docs/references/react/use-organization) hook to get the active organization. You must
verify that the desired organization has been activated. If so, this example renders the
[OrganizationProfile](/docs/components/organization/organization-profile).

```tsx {{ filename: 'src/app/orgs/[slug]/settings/[[...rest]]/page.tsx' }}
'use client'

import { OrganizationList, OrganizationProfile, useOrganization } from '@clerk/nextjs'

export default function Home({ params }: { params: { slug: string } }) {
const { organization } = useOrganization()

if (!organization || organization.slug != params.slug) {
return (
<>
<p className="pb-8">Sorry, organization {params.slug} is not valid.</p>
<OrganizationList
hidePersonal={false}
hideSlug={false}
afterCreateOrganizationUrl="/orgs/:slug"
afterSelectOrganizationUrl="/orgs/:slug"
afterSelectPersonalUrl="/me"
/>
</>
)
}

return (
<>
<OrganizationProfile />
</>
)
}
```

### Personal account

The [personal account](https://clerk.com/glossary#personal-account) is represented by the absense of any other active
organization. Barring unexpected errors, Clerk middleware should always be able to activate the user's personal account,
so checking for an organization mismatch is optional.

To display personal-account specific settings, this example builds a server rendered page that verifies that the personal
account is active (e.g. that no other organization is active), and the render something personal-account specific.
Remember, you can gain access to other user-specific information, like the username, by [customizing the session token](/docs/backend-requests/making/custom-session-token).

```tsx {{ filename: 'src/app/me/settings/page.tsx' }}
import { auth } from '@clerk/nextjs/server'
import { notFound } from 'next/navigation'

export default function Home(): {} {
const authObject = auth()

if (authObject.orgId != null) {
notFound()
}

return (
<>
<p className="pb-8">
Welcome, user {authObject.userId} to your own personal account settings!
</p>
</>
)
}
```
</Steps>

### Further Reading

To see all of these pages working in the context of an application, visit the
[sync-orgs-with-url](https://github.com/clerk/orgs/tree/main/examples/sync-org-with-url) sample repository on Github.

The active organization can also be controlled via the client-side [setActive](https://clerk.com/docs/references/javascript/clerk/session-methods)
session method. See our [Hide Personal Accounts and force organizations](https://clerk.com/docs/guides/force-organizations#set-an-active-organization-based-on-the-url)
guide to learn how to manually activate a specific organization based on the URL.
izaaklauer marked this conversation as resolved.
Show resolved Hide resolved
Loading
Loading