diff --git a/.changeset/stale-pans-clean.md b/.changeset/stale-pans-clean.md new file mode 100644 index 0000000000..a20fba237d --- /dev/null +++ b/.changeset/stale-pans-clean.md @@ -0,0 +1,5 @@ +--- +"@clerk/backend": minor +--- + +Updates `organizationPatterns` to take precedence over `personalAccountPatterns` in `organizationSyncOptions` diff --git a/packages/backend/src/tokens/__tests__/request.test.ts b/packages/backend/src/tokens/__tests__/request.test.ts index 61b5c89339..e187432ee7 100644 --- a/packages/backend/src/tokens/__tests__/request.test.ts +++ b/packages/backend/src/tokens/__tests__/request.test.ts @@ -298,14 +298,15 @@ export default (QUnit: QUnit) => { }, }, { - name: 'personal account match precedes org match', + name: 'org match match precedes personal account', whenOrgSyncOptions: { - organizationPatterns: ['/personal-account'], // bad practice - personalAccountPatterns: ['/personal-account'], + personalAccountPatterns: ['/', '/(.*)'], // Personal account captures everything + organizationPatterns: ['/orgs/:slug'], // that isn't org scoped }, - whenAppRequestPath: '/personal-account', + whenAppRequestPath: '/orgs/my-org', thenExpectActivationEntity: { - type: 'personalAccount', + type: 'organization', + organizationSlug: 'my-org', }, }, { diff --git a/packages/backend/src/tokens/request.ts b/packages/backend/src/tokens/request.ts index 2e4ed6b9fa..39b5e4769c 100644 --- a/packages/backend/src/tokens/request.ts +++ b/packages/backend/src/tokens/request.ts @@ -735,22 +735,6 @@ export function getOrganizationSyncTarget( return null; } - // Check for personal account activation - if (matchers.PersonalAccountMatcher) { - let personalResult: Match>>; - try { - personalResult = matchers.PersonalAccountMatcher(url.pathname); - } catch (e) { - // Intentionally not logging the path to avoid potentially leaking anything sensitive - console.error(`Failed to apply personal account pattern "${options.personalAccountPatterns}" to a path`, e); - return null; - } - - if (personalResult) { - return { type: 'personalAccount' }; - } - } - // Check for organization activation if (matchers.OrganizationMatcher) { let orgResult: Match>>; @@ -776,6 +760,22 @@ export function getOrganizationSyncTarget( ); } } + + // Check for personal account activation + if (matchers.PersonalAccountMatcher) { + let personalResult: Match>>; + try { + personalResult = matchers.PersonalAccountMatcher(url.pathname); + } catch (e) { + // Intentionally not logging the path to avoid potentially leaking anything sensitive + console.error(`Failed to apply personal account pattern "${options.personalAccountPatterns}" to a path`, e); + return null; + } + + if (personalResult) { + return { type: 'personalAccount' }; + } + } return null; } diff --git a/packages/backend/src/tokens/types.ts b/packages/backend/src/tokens/types.ts index 0e97c0026c..235b08eb07 100644 --- a/packages/backend/src/tokens/types.ts +++ b/packages/backend/src/tokens/types.ts @@ -36,7 +36,7 @@ export type OrganizationSyncOptions = { * organization-related fields will be set to null. The server component must detect this and respond * with an appropriate error (e.g., notFound()). * - * If the route also matches the personalAccountPatterns, the personalAccountPattern takes precedence. + * If the route also matches the personalAccountPatterns, this takes precedence. * * Must have a path token named either ":id" (matches a clerk organization ID) or ":slug" (matches a clerk * organization slug). @@ -50,7 +50,7 @@ export type OrganizationSyncOptions = { /** * URL patterns for resources in the context of a clerk personal account (user-specific, outside any organization). - * If the route also matches the organizationPattern, this takes precedence. + * If the route also matches the organizationPattern, the organizationPatterns takes precedence. * * Common examples: * - ["/user", "/user/(.*)"]