From efa884c4098d4b89659b4ae64a1fc4c1945037db Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Sat, 8 Jun 2024 10:22:38 +0800 Subject: [PATCH] chore(console,core): launch organization jit --- .changeset/smart-laws-compare.md | 20 +++ .../OrganizationDetails/Settings/index.tsx | 127 +++++++++--------- packages/core/src/libraries/user.ts | 4 +- .../core/src/routes/organization/index.ts | 7 +- 4 files changed, 85 insertions(+), 73 deletions(-) create mode 100644 .changeset/smart-laws-compare.md diff --git a/.changeset/smart-laws-compare.md b/.changeset/smart-laws-compare.md new file mode 100644 index 000000000..910fe5db0 --- /dev/null +++ b/.changeset/smart-laws-compare.md @@ -0,0 +1,20 @@ +--- +"@logto/console": minor +"@logto/phrases": minor +"@logto/schemas": minor +"@logto/core": minor +"@logto/integration-tests": patch +--- + +feature: just-in-time user provisioning for organizations + +This feature allows organizations to provision users when signing up with their email address or being added by Management API. If the user's email domain matches one of the organization's configured domains, the user will be automatically provisioned to the organization. + +To enable this feature, you can add email domain via the Management API or the Logto Console: + +- We added the following new endpoints to the Management API: + - `GET /organizations/{organizationId}/email-domains` + - `POST /organizations/{organizationId}/email-domains` + - `PUT /organizations/{organizationId}/email-domains` + - `DELETE /organizations/{organizationId}/email-domains/{emailDomain}` +- In the Logto Console, you can manage email domains in the organization details page -> "Just-in-time provisioning" section. diff --git a/packages/console/src/pages/OrganizationDetails/Settings/index.tsx b/packages/console/src/pages/OrganizationDetails/Settings/index.tsx index 77e253fff..f915b69b4 100644 --- a/packages/console/src/pages/OrganizationDetails/Settings/index.tsx +++ b/packages/console/src/pages/OrganizationDetails/Settings/index.tsx @@ -9,7 +9,6 @@ import DetailsForm from '@/components/DetailsForm'; import FormCard from '@/components/FormCard'; import MultiOptionInput from '@/components/MultiOptionInput'; import UnsavedChangesAlertModal from '@/components/UnsavedChangesAlertModal'; -import { isDevFeaturesEnabled } from '@/consts/env'; import CodeEditor from '@/ds-components/CodeEditor'; import FormField from '@/ds-components/FormField'; import RadioGroup, { Radio } from '@/ds-components/RadioGroup'; @@ -134,72 +133,70 @@ function Settings() { /> - {isDevFeaturesEnabled && ( - - - ( -
- { - field.onChange(value === 'true'); - }} - > - - - - {field.value && ( - ( - value} - validateInput={(input) => { - if (!domainRegExp.test(input)) { - return t('organization_details.jit.invalid_domain'); - } + + + ( +
+ { + field.onChange(value === 'true'); + }} + > + + + + {field.value && ( + ( + value} + validateInput={(input) => { + if (!domainRegExp.test(input)) { + return t('organization_details.jit.invalid_domain'); + } - if (value.includes(input)) { - return t('organization_details.jit.domain_already_added'); - } + if (value.includes(input)) { + return t('organization_details.jit.domain_already_added'); + } - return { value: input }; - }} - placeholder={t('organization_details.jit.email_domains_placeholder')} - error={errors.jitEmailDomains?.message} - onChange={onChange} - onError={(error) => { - setError('jitEmailDomains', { type: 'custom', message: error }); - }} - onClearError={() => { - clearErrors('jitEmailDomains'); - }} - /> - )} - /> - )} -
- )} - /> -
-
- )} + return { value: input }; + }} + placeholder={t('organization_details.jit.email_domains_placeholder')} + error={errors.jitEmailDomains?.message} + onChange={onChange} + onError={(error) => { + setError('jitEmailDomains', { type: 'custom', message: error }); + }} + onClearError={() => { + clearErrors('jitEmailDomains'); + }} + /> + )} + /> + )} +
+ )} + /> +
+
); diff --git a/packages/core/src/libraries/user.ts b/packages/core/src/libraries/user.ts index 043ec64ea..5a766e686 100644 --- a/packages/core/src/libraries/user.ts +++ b/packages/core/src/libraries/user.ts @@ -142,11 +142,11 @@ export const createUserLibrary = (queries: Queries) => { ); } + // TODO: If the user's email is not verified, we should not provision the user into any organization. const provisionOrganizations = async (): Promise => { // Just-in-time organization provisioning const userEmailDomain = data.primaryEmail?.split('@')[1]; - // TODO: Remove this check when launching - if (EnvSet.values.isDevFeaturesEnabled && userEmailDomain) { + if (userEmailDomain) { const organizationQueries = new OrganizationQueries(connection); const organizationIds = await organizationQueries.emailDomains.getOrganizationIdsByDomain( userEmailDomain diff --git a/packages/core/src/routes/organization/index.ts b/packages/core/src/routes/organization/index.ts index bf3ca873e..e15d2fcdc 100644 --- a/packages/core/src/routes/organization/index.ts +++ b/packages/core/src/routes/organization/index.ts @@ -7,7 +7,6 @@ import { import { yes } from '@silverhand/essentials'; import { z } from 'zod'; -import { EnvSet } from '#src/env-set/index.js'; import koaGuard from '#src/middleware/koa-guard.js'; import koaPagination from '#src/middleware/koa-pagination.js'; import koaQuotaGuard from '#src/middleware/koa-quota-guard.js'; @@ -139,11 +138,7 @@ export default function organizationRoutes( ); userRoleRelationRoutes(router, organizations); - - // TODO: Remove this check when launching - if (EnvSet.values.isDevFeaturesEnabled) { - emailDomainRoutes(router, organizations); - } + emailDomainRoutes(router, organizations); // MARK: Mount sub-routes organizationRoleRoutes(...args);