Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
069abf7
Remove old localization keys and update title/subtitle
LauraBeatris Jun 9, 2026
258e0f2
Introduce domains form
LauraBeatris Jun 9, 2026
c2ccaca
Add localizations for TXT paragraphs
LauraBeatris Jun 9, 2026
75b4cc2
Add hardcoded version of TXT records table
LauraBeatris Jun 9, 2026
1972f19
Move to new module
LauraBeatris Jun 9, 2026
6aa7f93
Remove references to user primary email
LauraBeatris Jun 9, 2026
8940c3e
Add new organization domains API for TXT verification
LauraBeatris Jun 10, 2026
dd5ff60
Fetch domains for `enterprise_sso` enrollment mode
LauraBeatris Jun 10, 2026
bb12e9c
Rename domains to plural
LauraBeatris Jun 10, 2026
a70ab86
Create separate mutation object for domains
LauraBeatris Jun 10, 2026
e8bf5ce
Add enterprise SSO as enrollment mode badge
LauraBeatris Jun 10, 2026
2c7451b
Allow creating new domains
LauraBeatris Jun 10, 2026
319ee74
Create enterprise connection with organization domains
LauraBeatris Jun 10, 2026
ccd495d
Adjust UI tests
LauraBeatris Jun 10, 2026
a1979cf
Refactor table UI to use cards
LauraBeatris Jun 11, 2026
53f5819
Apply renaming
LauraBeatris Jun 11, 2026
4cacc0a
Add methods for bulk attempt and prepare
LauraBeatris Jun 12, 2026
a3d2804
Add internal logic for attempt
LauraBeatris Jun 12, 2026
bd55ab3
Add domain suggestion card
LauraBeatris Jun 12, 2026
b7b3ad3
Add changeset
LauraBeatris Jun 12, 2026
7d0dd2c
Allow going back on select provider step
LauraBeatris Jun 12, 2026
7d86f12
Use readonly input for TXT record value
LauraBeatris Jun 12, 2026
2d0ad12
Apply animation on the height on status transition
LauraBeatris Jun 12, 2026
1a5c6bb
Bump `clerk.browser.js` bundle size limit
LauraBeatris Jun 15, 2026
ff77bb0
Fix unit tests
LauraBeatris Jun 15, 2026
bbc9c81
Do not render domain on empty name
LauraBeatris Jun 15, 2026
8db073a
Move organization domains to `useOrganizationEnterpriseConnection`
LauraBeatris Jun 15, 2026
873b4bd
Move domains check to shared function
LauraBeatris Jun 15, 2026
d68eb75
Fix scroll on card transition
LauraBeatris Jun 15, 2026
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
8 changes: 8 additions & 0 deletions .changeset/curvy-hounds-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@clerk/localizations': minor
'@clerk/clerk-js': minor
'@clerk/shared': minor
'@clerk/ui': minor
---

Introduce organization domains with TXT verification on self-serve SSO flow
2 changes: 1 addition & 1 deletion packages/clerk-js/bundlewatch.config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"files": [
{ "path": "./dist/clerk.js", "maxSize": "549KB" },
{ "path": "./dist/clerk.browser.js", "maxSize": "72KB" },
{ "path": "./dist/clerk.browser.js", "maxSize": "74KB" },
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "114KB" },
{ "path": "./dist/clerk.no-rhc.js", "maxSize": "316KB" },
{ "path": "./dist/clerk.native.js", "maxSize": "72KB" },
Expand Down
53 changes: 50 additions & 3 deletions packages/clerk-js/src/core/resources/Organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {
AddMemberParams,
ClerkPaginatedResponse,
ClerkResourceReloadParams,
CreateOrganizationDomainParams,
CreateOrganizationEnterpriseConnectionParams,
CreateOrganizationParams,
DeletedObjectJSON,
Expand All @@ -24,6 +25,8 @@ import type {
InviteMembersParams,
OrganizationDomainJSON,
OrganizationDomainResource,
OrganizationDomainsBulkOwnershipVerificationJSON,
OrganizationDomainsBulkOwnershipVerificationResource,
OrganizationInvitationJSON,
OrganizationInvitationResource,
OrganizationJSON,
Expand Down Expand Up @@ -134,11 +137,17 @@ export class Organization extends BaseResource implements OrganizationResource {
getDomains = async (
getDomainParams?: GetDomainsParams,
): Promise<ClerkPaginatedResponse<OrganizationDomainResource>> => {
const { enrollmentMode, ...rest } = getDomainParams || {};
const search = convertPageToOffsetSearchParams(rest);
if (enrollmentMode) {
search.set('enrollment_mode', enrollmentMode);
}

return await BaseResource._fetch(
{
path: `/organizations/${this.id}/domains`,
method: 'GET',
search: convertPageToOffsetSearchParams(getDomainParams),
search,
},
{
forceUpdateClient: true,
Expand Down Expand Up @@ -282,8 +291,46 @@ export class Organization extends BaseResource implements OrganizationResource {
});
};

createDomain = async (name: string): Promise<OrganizationDomainResource> => {
return OrganizationDomain.create(this.id, { name });
createDomain = async (
name: string,
params?: Pick<CreateOrganizationDomainParams, 'enrollmentMode'>,
): Promise<OrganizationDomainResource> => {
return OrganizationDomain.create(this.id, { name, enrollmentMode: params?.enrollmentMode });
};

prepareOwnershipVerification = async (
domainIds: string[],
): Promise<OrganizationDomainsBulkOwnershipVerificationResource> => {
return this.bulkOwnershipVerification('prepare_ownership_verification', domainIds);
};

attemptOwnershipVerification = async (
domainIds: string[],
): Promise<OrganizationDomainsBulkOwnershipVerificationResource> => {
return this.bulkOwnershipVerification('attempt_ownership_verification', domainIds);
};

private bulkOwnershipVerification = async (
action: 'prepare_ownership_verification' | 'attempt_ownership_verification',
domainIds: string[],
): Promise<OrganizationDomainsBulkOwnershipVerificationResource> => {
const { data, errors } = (
await BaseResource._fetch(
{
path: `/organizations/${this.id}/domains/${action}/bulk`,
method: 'POST',
body: { organization_domain_ids: domainIds } as any,
},
{
forceUpdateClient: true,
},
)
)?.response as unknown as OrganizationDomainsBulkOwnershipVerificationJSON;

return {
data: (data ?? []).map(domain => new OrganizationDomain(domain)),
errors: errors ?? [],
};
};

getMemberships: GetMemberships = async getMembershipsParams => {
Expand Down
47 changes: 39 additions & 8 deletions packages/clerk-js/src/core/resources/OrganizationDomain.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type {
AttemptAffiliationVerificationParams,
CreateOrganizationDomainParams,
OrganizationDomainJSON,
OrganizationDomainOwnershipVerification,
OrganizationDomainResource,
OrganizationDomainVerification,
OrganizationEnrollmentMode,
Expand All @@ -17,6 +19,8 @@ export class OrganizationDomain extends BaseResource implements OrganizationDoma
organizationId!: string;
enrollmentMode!: OrganizationEnrollmentMode;
verification!: OrganizationDomainVerification | null;
affiliationVerification!: OrganizationDomainVerification | null;
ownershipVerification!: OrganizationDomainOwnershipVerification | null;
affiliationEmailAddress!: string | null;
createdAt!: Date;
updatedAt!: Date;
Expand All @@ -28,12 +32,15 @@ export class OrganizationDomain extends BaseResource implements OrganizationDoma
this.fromJSON(data);
}

static async create(organizationId: string, { name }: { name: string }): Promise<OrganizationDomainResource> {
static async create(
organizationId: string,
{ name, enrollmentMode }: CreateOrganizationDomainParams,
): Promise<OrganizationDomainResource> {
const json = (
await BaseResource._fetch<OrganizationDomainJSON>({
path: `/organizations/${organizationId}/domains`,
method: 'POST',
body: { name } as any,
body: { name, enrollment_mode: enrollmentMode } as any,
})
)?.response as unknown as OrganizationDomainJSON;
return new OrganizationDomain(json);
Expand Down Expand Up @@ -81,16 +88,40 @@ export class OrganizationDomain extends BaseResource implements OrganizationDoma
this.affiliationEmailAddress = data.affiliation_email_address;
this.totalPendingSuggestions = data.total_pending_suggestions;
this.totalPendingInvitations = data.total_pending_invitations;
if (data.verification) {
this.verification = {
status: data.verification.status,
strategy: data.verification.strategy,
attempts: data.verification.attempts,
expiresAt: unixEpochToDate(data.verification.expires_at),

const affiliationVerificationJSON = data.affiliation_verification ?? data.verification;
if (affiliationVerificationJSON) {
const affiliationVerification: OrganizationDomainVerification = {
status: affiliationVerificationJSON.status,
strategy: affiliationVerificationJSON.strategy,
attempts: affiliationVerificationJSON.attempts,
expiresAt: unixEpochToDate(affiliationVerificationJSON.expires_at),
};
this.affiliationVerification = affiliationVerification;
// Deprecated alias, kept in sync for backwards compatibility.
this.verification = affiliationVerification;
} else {
this.affiliationVerification = null;
this.verification = null;
}

if (data.ownership_verification) {
this.ownershipVerification = {
status: data.ownership_verification.status,
strategy: data.ownership_verification.strategy,
attempts: data.ownership_verification.attempts,
expiresAt: data.ownership_verification.expire_at
? unixEpochToDate(data.ownership_verification.expire_at)
: null,
verifiedAt: data.ownership_verification.verified_at
? unixEpochToDate(data.ownership_verification.verified_at)
: null,
txtRecordName: data.ownership_verification.txt_record_name ?? null,
txtRecordValue: data.ownership_verification.txt_record_value ?? null,
};
} else {
this.ownershipVerification = null;
}
}
return this;
}
Expand Down
40 changes: 19 additions & 21 deletions packages/localizations/src/ar-SA.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,29 +196,27 @@ export const arSA: LocalizationResource = {
},
warning: 'بمجرد اختيار المزود لا يمكنك التغيير مرة أخرى حتى انتهاء التكوين',
},
verifyEmailDomainStep: {
title: 'التحقق من البريد الإلكتروني',
subtitle: 'تحقق من عنوان البريد الإلكتروني الذي تريد تفعيل اتصال المؤسسة عليه.',
addEmailAddress: {
formTitle: 'نحتاج إلى بريدك الإلكتروني',
formSubtitle: 'للبدء، نحتاج إلى عنوان بريدك الإلكتروني',
inputPlaceholder: 'name@company.com',
inputLabel: 'عنوان البريد الإلكتروني',
},
emailCode: {
formTitle: 'تحقق من عنوان بريدك الإلكتروني',
formSubtitle: 'أدخل رمز التحقق المرسل إلى {{identifier}}',
resendButton: 'لم تتلقَّ الرمز؟ إعادة الإرسال',
verified: {
title: 'لقد تلقينا بريدك الإلكتروني',
subtitle: 'لقد تحققت من عنوان بريدك الإلكتروني التالي',
inputLabel: 'عنوان البريد الإلكتروني الذي تم التحقق منه',
organizationDomainsStep: {
title: 'إضافة نطاقات SSO',
subtitle: 'أضِف نطاقات مؤسستك المستخدمة لتسجيل الدخول وتحقّق من ملكيتها.',
formFieldLabel__domain: 'النطاقات',
formFieldInputPlaceholder__domain: 'اكتب نطاقك هنا وانقر على إضافة للبدء',
formButtonPrimary__add: 'إضافة',
domainSuggestion: {
messageLabel: 'بريدك الإلكتروني يستخدم {{domain}}. هل تريد إضافته؟',
formButtonPrimary__add: 'إضافة {{domain}}',
},
domainCard: {
badge__verified: 'تم التحقق',
badge__unverified: 'لم يتم التحقق',
verifiedAtLabel: "تم التحقق في {{ date | shortDate('en-US') }}",
txtRecord: {
instructions: 'أضِف سجل TXT هذا إلى مزوّد DNS الخاص بك. سنتحقق تلقائيًا بمجرد أن يصبح السجل نشطًا.',
typeLabel: 'النوع',
hostLabel: 'المضيف / الاسم',
valueLabel: 'القيمة',
},
},
domainTaken: {
title: 'هذا النطاق ({{domain}}) لديه بالفعل اتصال SSO',
subtitle: 'تواصل مع مسؤول التطبيق للحصول على الوصول من خلال الاتصال الحالي.',
},
},
Comment thread
LauraBeatris marked this conversation as resolved.
},
createOrganization: {
Expand Down
41 changes: 20 additions & 21 deletions packages/localizations/src/be-BY.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,29 +196,28 @@ export const beBY: LocalizationResource = {
},
warning: 'Пасля выбару правайдэра вы не зможаце змяніць яго, пакуль не скончыце канфігурацыю',
},
verifyEmailDomainStep: {
title: 'Пацвердзіць адрас электроннай пошты',
subtitle: 'Пацвердзіце адрас электроннай пошты, на якім вы хочаце ўключыць карпаратыўнае падключэнне.',
addEmailAddress: {
formTitle: 'Нам патрэбна ваша пошта',
formSubtitle: 'Каб пачаць, нам спатрэбіцца ваш адрас электроннай пошты',
inputPlaceholder: 'name@company.com',
inputLabel: 'Адрас электроннай пошты',
},
emailCode: {
formTitle: 'Пацвердзіце ваш адрас электроннай пошты',
formSubtitle: 'Увядзіце код пацверджання, дасланы на {{identifier}}',
resendButton: 'Не атрымалі код? Адправіць паўторна',
verified: {
title: 'Мы атрымалі вашу пошту',
subtitle: 'Вы пацвердзілі свой адрас электроннай пошты з наступнай поштай',
inputLabel: 'Пацверджаны адрас электроннай пошты',
organizationDomainsStep: {
title: 'Дадаць дамены SSO',
subtitle: 'Дадайце і пацвердзіце права ўласнасці на дамены, якія ваша арганізацыя выкарыстоўвае для ўваходу.',
formFieldLabel__domain: 'Дамены',
formFieldInputPlaceholder__domain: 'Увядзіце свой дамен тут і націсніце «Дадаць», каб пачаць',
formButtonPrimary__add: 'Дадаць',
domainSuggestion: {
messageLabel: 'Ваша электронная пошта выкарыстоўвае {{domain}}. Хочаце дадаць яго?',
formButtonPrimary__add: 'Дадаць {{domain}}',
},
domainCard: {
badge__verified: 'Пацверджана',
badge__unverified: 'Не пацверджана',
verifiedAtLabel: "Пацверджана {{ date | shortDate('be-BY') }}",
txtRecord: {
instructions:
'Дадайце гэты TXT-запіс да вашага DNS-правайдара. Мы аўтаматычна выканаем праверку, як толькі запіс стане актыўным.',
typeLabel: 'Тып',
hostLabel: 'Хост / Імя',
valueLabel: 'Значэнне',
},
},
domainTaken: {
title: 'Гэты дамен ({{domain}}) ужо мае SSO-падключэнне',
subtitle: 'Звяжыцеся з адміністратарам прыкладання, каб атрымаць доступ праз існуючае падключэнне.',
},
},
},
createOrganization: {
Expand Down
41 changes: 20 additions & 21 deletions packages/localizations/src/bg-BG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,29 +197,28 @@ export const bgBG: LocalizationResource = {
},
warning: 'След като изберете доставчик, не можете да го промените, докато конфигурацията не приключи',
},
verifyEmailDomainStep: {
title: 'Потвърди имейл адреса',
subtitle: 'Потвърдете имейл адреса, на който искате да активирате корпоративната връзка.',
addEmailAddress: {
formTitle: 'Нуждаем се от вашия имейл',
formSubtitle: 'За да започнем, ще ни е необходим вашият имейл адрес',
inputPlaceholder: 'name@company.com',
inputLabel: 'Имейл адрес',
},
emailCode: {
formTitle: 'Потвърдете имейл адреса си',
formSubtitle: 'Въведете кода за потвърждение, изпратен на {{identifier}}',
resendButton: 'Не получихте код? Изпрати отново',
verified: {
title: 'Получихме имейла ви',
subtitle: 'Потвърдихте имейл адреса си със следния имейл',
inputLabel: 'Потвърден имейл адрес',
organizationDomainsStep: {
title: 'Добавяне на SSO домейни',
subtitle: 'Добавете и потвърдете собствеността върху домейните, които вашата организация използва за вход.',
formFieldLabel__domain: 'Домейни',
formFieldInputPlaceholder__domain: 'Въведете домейна си тук и щракнете върху добавяне, за да започнете',
formButtonPrimary__add: 'Добави',
domainSuggestion: {
messageLabel: 'Вашият имейл използва {{domain}}. Искате ли да го добавите?',
formButtonPrimary__add: 'Добавяне на {{domain}}',
},
domainCard: {
badge__verified: 'Потвърден',
badge__unverified: 'Непотвърден',
verifiedAtLabel: "Потвърден на {{ date | shortDate('bg-BG') }}",
txtRecord: {
instructions:
'Добавете този TXT запис към вашия DNS доставчик. Ще потвърдим автоматично, след като записът стане активен.',
typeLabel: 'Тип',
hostLabel: 'Хост / Име',
valueLabel: 'Стойност',
},
},
domainTaken: {
title: 'Този домейн ({{domain}}) вече има SSO връзка',
subtitle: 'Свържете се с администратора на приложението, за да получите достъп чрез съществуващата връзка.',
},
},
},
createOrganization: {
Expand Down
Loading
Loading