diff --git a/.changeset/fresh-waves-sneeze.md b/.changeset/fresh-waves-sneeze.md new file mode 100644 index 00000000000..f3da116e3c5 --- /dev/null +++ b/.changeset/fresh-waves-sneeze.md @@ -0,0 +1,23 @@ +--- +"@clerk/react-router": minor +--- + +Added `organizationSyncOptions` option to `clerkMiddleware()`. It's used to activate a specific organization or personal account based on URL path parameters. + +Usage: + +```ts +// app/root.tsx +export const middleware: Route.MiddlewareFunction[] = [ + clerkMiddleware({ + organizationSyncOptions: { + organizationPatterns: [ + '/orgs/:slug', // Match the org slug + '/orgs/:slug/(.*)', // Wildcard match for optional trailing path segments + ], + } + }) +] +``` + +To learn more about best practices for using organization slugs to manage the active organization, check out this [guide](https://clerk.com/docs/organizations/org-slugs-in-urls). diff --git a/packages/react-router/src/server/clerkMiddleware.ts b/packages/react-router/src/server/clerkMiddleware.ts index 458fb7c7bf4..2e1f2d62d72 100644 --- a/packages/react-router/src/server/clerkMiddleware.ts +++ b/packages/react-router/src/server/clerkMiddleware.ts @@ -36,7 +36,9 @@ export const clerkMiddleware = (options?: ClerkMiddlewareOptions): MiddlewareFun const loadedOptions = loadOptions(args, options); const { audience, authorizedParties } = loadedOptions; const { signInUrl, signUpUrl, afterSignInUrl, afterSignUpUrl } = loadedOptions; + const { organizationSyncOptions } = loadedOptions; const requestState = await clerkClient(args).authenticateRequest(clerkRequest, { + organizationSyncOptions, audience, authorizedParties, signInUrl, diff --git a/packages/react-router/src/server/types.ts b/packages/react-router/src/server/types.ts index 9f8e0847496..9c4725a9689 100644 --- a/packages/react-router/src/server/types.ts +++ b/packages/react-router/src/server/types.ts @@ -1,5 +1,10 @@ import type { Organization, Session, User, VerifyTokenOptions } from '@clerk/backend'; -import type { RequestState, SignedInAuthObject, SignedOutAuthObject } from '@clerk/backend/internal'; +import type { + OrganizationSyncOptions, + RequestState, + SignedInAuthObject, + SignedOutAuthObject, +} from '@clerk/backend/internal'; import type { LegacyRedirectProps, MultiDomainAndOrProxy, @@ -31,6 +36,12 @@ export type ClerkMiddlewareOptions = { machineSecretKey?: string; signInUrl?: string; signUpUrl?: string; + /** + * Used to activate a specific [organization](https://clerk.com/docs/guides/organizations/overview) or [personal account](https://clerk.com/docs/guides/dashboard/overview) based on URL path parameters. If there's a mismatch between the active organization in the session (e.g., as reported by `auth()`) and the organization indicated by the URL, an attempt to activate the organization specified in the URL will be made. + * + * If the activation can't be performed, either because an organization doesn't exist or the user lacks access, the active organization in the session won't be changed. Ultimately, it's the responsibility of the page to verify that the resources are appropriate to render given the URL and handle mismatches appropriately (e.g., by returning a 404). + */ + organizationSyncOptions?: OrganizationSyncOptions; } & Pick & MultiDomainAndOrProxy & SignInForceRedirectUrl &