@logto/schemas 1.10.1 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/alterations/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.ts +18 -0
  2. package/alterations/1.11.0-1699598903-remove-sso-only-column-in-sso-connectors-table.ts +18 -0
  3. package/alterations/1.12.0-1700031616-update-org-role-foreign-keys.ts +35 -0
  4. package/alterations/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.ts +21 -0
  5. package/alterations/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.ts +20 -0
  6. package/alterations-js/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.d.ts +3 -0
  7. package/alterations-js/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.js +14 -0
  8. package/alterations-js/1.11.0-1699598903-remove-sso-only-column-in-sso-connectors-table.d.ts +3 -0
  9. package/alterations-js/1.11.0-1699598903-remove-sso-only-column-in-sso-connectors-table.js +14 -0
  10. package/alterations-js/1.12.0-1700031616-update-org-role-foreign-keys.d.ts +3 -0
  11. package/alterations-js/1.12.0-1700031616-update-org-role-foreign-keys.js +31 -0
  12. package/alterations-js/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.d.ts +3 -0
  13. package/alterations-js/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.js +17 -0
  14. package/alterations-js/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.d.ts +3 -0
  15. package/alterations-js/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.js +16 -0
  16. package/lib/consts/index.d.ts +1 -0
  17. package/lib/consts/index.js +1 -0
  18. package/lib/consts/subscriptions.d.ts +6 -0
  19. package/lib/consts/subscriptions.js +7 -0
  20. package/lib/db-entries/sign-in-experience.d.ts +3 -1
  21. package/lib/db-entries/sign-in-experience.js +4 -0
  22. package/lib/db-entries/sso-connector.d.ts +3 -7
  23. package/lib/db-entries/sso-connector.js +0 -4
  24. package/lib/db-entries/user-sso-identity.d.ts +3 -1
  25. package/lib/db-entries/user-sso-identity.js +4 -0
  26. package/lib/foundations/jsonb-types/sso-connector.d.ts +3 -0
  27. package/lib/foundations/jsonb-types/sso-connector.js +1 -0
  28. package/lib/foundations/jsonb-types/users.d.ts +6 -6
  29. package/lib/foundations/jsonb-types/users.js +1 -1
  30. package/lib/models/tenants.d.ts +1 -5
  31. package/lib/models/tenants.js +1 -6
  32. package/lib/types/hook.d.ts +1 -0
  33. package/lib/types/index.d.ts +1 -0
  34. package/lib/types/index.js +1 -0
  35. package/lib/types/logto-config.d.ts +24 -0
  36. package/lib/types/logto-config.js +8 -0
  37. package/lib/types/organization.d.ts +11 -7
  38. package/lib/types/organization.js +3 -2
  39. package/lib/types/sso-connector.d.ts +113 -0
  40. package/lib/types/sso-connector.js +48 -0
  41. package/lib/types/tenant.d.ts +5 -0
  42. package/lib/types/tenant.js +9 -0
  43. package/lib/types/user.d.ts +13 -10
  44. package/lib/types/user.js +2 -1
  45. package/lib/utils/domain.d.ts +10 -0
  46. package/lib/utils/domain.js +28 -0
  47. package/lib/utils/domain.test.d.ts +1 -0
  48. package/lib/utils/domain.test.js +34 -0
  49. package/lib/utils/index.d.ts +1 -0
  50. package/lib/utils/index.js +1 -0
  51. package/package.json +6 -6
  52. package/tables/organization_role_user_relations.sql +8 -6
  53. package/tables/sign_in_experiences.sql +1 -0
  54. package/tables/sso_connectors.sql +5 -4
  55. package/tables/user_sso_identities.sql +3 -0
@@ -0,0 +1,18 @@
1
+ import { sql } from 'slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ await pool.query(sql`
8
+ alter table user_sso_identities add column sso_connector_id varchar(128) not null references sso_connectors (id) on update cascade on delete cascade;
9
+ `);
10
+ },
11
+ down: async (pool) => {
12
+ await pool.query(sql`
13
+ alter table user_sso_identities drop column sso_connector_id;
14
+ `);
15
+ },
16
+ };
17
+
18
+ export default alteration;
@@ -0,0 +1,18 @@
1
+ import { sql } from 'slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ await pool.query(sql`
8
+ alter table sso_connectors drop column sso_only;
9
+ `);
10
+ },
11
+ down: async (pool) => {
12
+ await pool.query(sql`
13
+ alter table sso_connectors add column sso_only boolean not null default FALSE;
14
+ `);
15
+ },
16
+ };
17
+
18
+ export default alteration;
@@ -0,0 +1,35 @@
1
+ import { sql } from 'slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ await pool.query(sql`
8
+ alter table organization_role_user_relations
9
+ drop constraint organization_role_user_relations_organization_id_fkey;
10
+ alter table organization_role_user_relations
11
+ drop constraint organization_role_user_relations_user_id_fkey;
12
+ alter table organization_role_user_relations
13
+ add foreign key (tenant_id, organization_id, user_id)
14
+ references organization_user_relations (tenant_id, organization_id, user_id)
15
+ on update cascade on delete cascade;
16
+ `);
17
+ },
18
+ down: async (pool) => {
19
+ await pool.query(sql`
20
+ alter table organization_role_user_relations
21
+ -- The constraint name is strange because it's generated by Postgres and it has a 63 character limit
22
+ drop constraint organization_role_user_relati_tenant_id_organization_id_us_fkey;
23
+ alter table organization_role_user_relations
24
+ add foreign key (organization_id)
25
+ references organizations (id)
26
+ on update cascade on delete cascade;
27
+ alter table organization_role_user_relations
28
+ add foreign key (user_id)
29
+ references users (id)
30
+ on update cascade on delete cascade;
31
+ `);
32
+ },
33
+ };
34
+
35
+ export default alteration;
@@ -0,0 +1,21 @@
1
+ import { sql } from 'slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ await pool.query(sql`
8
+ alter table sso_connectors
9
+ add constraint sso_connectors__connector_name__unique
10
+ unique (tenant_id, connector_name);
11
+ `);
12
+ },
13
+ down: async (pool) => {
14
+ await pool.query(sql`
15
+ alter table sso_connectors
16
+ drop constraint sso_connectors__connector_name__unique;
17
+ `);
18
+ },
19
+ };
20
+
21
+ export default alteration;
@@ -0,0 +1,20 @@
1
+ import { sql } from 'slonik';
2
+
3
+ import type { AlterationScript } from '../lib/types/alteration.js';
4
+
5
+ const alteration: AlterationScript = {
6
+ up: async (pool) => {
7
+ await pool.query(sql`
8
+ alter table sign_in_experiences
9
+ add column single_sign_on_enabled boolean not null default false;
10
+ `);
11
+ },
12
+ down: async (pool) => {
13
+ await pool.query(sql`
14
+ alter table sign_in_experiences
15
+ drop column single_sign_on_enabled;
16
+ `);
17
+ },
18
+ };
19
+
20
+ export default alteration;
@@ -0,0 +1,3 @@
1
+ import type { AlterationScript } from '../lib/types/alteration.js';
2
+ declare const alteration: AlterationScript;
3
+ export default alteration;
@@ -0,0 +1,14 @@
1
+ import { sql } from 'slonik';
2
+ const alteration = {
3
+ up: async (pool) => {
4
+ await pool.query(sql `
5
+ alter table user_sso_identities add column sso_connector_id varchar(128) not null references sso_connectors (id) on update cascade on delete cascade;
6
+ `);
7
+ },
8
+ down: async (pool) => {
9
+ await pool.query(sql `
10
+ alter table user_sso_identities drop column sso_connector_id;
11
+ `);
12
+ },
13
+ };
14
+ export default alteration;
@@ -0,0 +1,3 @@
1
+ import type { AlterationScript } from '../lib/types/alteration.js';
2
+ declare const alteration: AlterationScript;
3
+ export default alteration;
@@ -0,0 +1,14 @@
1
+ import { sql } from 'slonik';
2
+ const alteration = {
3
+ up: async (pool) => {
4
+ await pool.query(sql `
5
+ alter table sso_connectors drop column sso_only;
6
+ `);
7
+ },
8
+ down: async (pool) => {
9
+ await pool.query(sql `
10
+ alter table sso_connectors add column sso_only boolean not null default FALSE;
11
+ `);
12
+ },
13
+ };
14
+ export default alteration;
@@ -0,0 +1,3 @@
1
+ import type { AlterationScript } from '../lib/types/alteration.js';
2
+ declare const alteration: AlterationScript;
3
+ export default alteration;
@@ -0,0 +1,31 @@
1
+ import { sql } from 'slonik';
2
+ const alteration = {
3
+ up: async (pool) => {
4
+ await pool.query(sql `
5
+ alter table organization_role_user_relations
6
+ drop constraint organization_role_user_relations_organization_id_fkey;
7
+ alter table organization_role_user_relations
8
+ drop constraint organization_role_user_relations_user_id_fkey;
9
+ alter table organization_role_user_relations
10
+ add foreign key (tenant_id, organization_id, user_id)
11
+ references organization_user_relations (tenant_id, organization_id, user_id)
12
+ on update cascade on delete cascade;
13
+ `);
14
+ },
15
+ down: async (pool) => {
16
+ await pool.query(sql `
17
+ alter table organization_role_user_relations
18
+ -- The constraint name is strange because it's generated by Postgres and it has a 63 character limit
19
+ drop constraint organization_role_user_relati_tenant_id_organization_id_us_fkey;
20
+ alter table organization_role_user_relations
21
+ add foreign key (organization_id)
22
+ references organizations (id)
23
+ on update cascade on delete cascade;
24
+ alter table organization_role_user_relations
25
+ add foreign key (user_id)
26
+ references users (id)
27
+ on update cascade on delete cascade;
28
+ `);
29
+ },
30
+ };
31
+ export default alteration;
@@ -0,0 +1,3 @@
1
+ import type { AlterationScript } from '../lib/types/alteration.js';
2
+ declare const alteration: AlterationScript;
3
+ export default alteration;
@@ -0,0 +1,17 @@
1
+ import { sql } from 'slonik';
2
+ const alteration = {
3
+ up: async (pool) => {
4
+ await pool.query(sql `
5
+ alter table sso_connectors
6
+ add constraint sso_connectors__connector_name__unique
7
+ unique (tenant_id, connector_name);
8
+ `);
9
+ },
10
+ down: async (pool) => {
11
+ await pool.query(sql `
12
+ alter table sso_connectors
13
+ drop constraint sso_connectors__connector_name__unique;
14
+ `);
15
+ },
16
+ };
17
+ export default alteration;
@@ -0,0 +1,3 @@
1
+ import type { AlterationScript } from '../lib/types/alteration.js';
2
+ declare const alteration: AlterationScript;
3
+ export default alteration;
@@ -0,0 +1,16 @@
1
+ import { sql } from 'slonik';
2
+ const alteration = {
3
+ up: async (pool) => {
4
+ await pool.query(sql `
5
+ alter table sign_in_experiences
6
+ add column single_sign_on_enabled boolean not null default false;
7
+ `);
8
+ },
9
+ down: async (pool) => {
10
+ await pool.query(sql `
11
+ alter table sign_in_experiences
12
+ drop column single_sign_on_enabled;
13
+ `);
14
+ },
15
+ };
16
+ export default alteration;
@@ -3,3 +3,4 @@ export * from './system.js';
3
3
  export * from './oidc.js';
4
4
  export * from './date.js';
5
5
  export * from './tenant.js';
6
+ export * from './subscriptions.js';
@@ -3,3 +3,4 @@ export * from './system.js';
3
3
  export * from './oidc.js';
4
4
  export * from './date.js';
5
5
  export * from './tenant.js';
6
+ export * from './subscriptions.js';
@@ -0,0 +1,6 @@
1
+ export declare enum ReservedPlanId {
2
+ Free = "free",
3
+ Hobby = "hobby",
4
+ Pro = "pro",
5
+ Development = "dev"
6
+ }
@@ -0,0 +1,7 @@
1
+ export var ReservedPlanId;
2
+ (function (ReservedPlanId) {
3
+ ReservedPlanId["Free"] = "free";
4
+ ReservedPlanId["Hobby"] = "hobby";
5
+ ReservedPlanId["Pro"] = "pro";
6
+ ReservedPlanId["Development"] = "dev";
7
+ })(ReservedPlanId || (ReservedPlanId = {}));
@@ -21,6 +21,7 @@ export type CreateSignInExperience = {
21
21
  customContent?: CustomContent;
22
22
  passwordPolicy?: PartialPasswordPolicy;
23
23
  mfa?: Mfa;
24
+ singleSignOnEnabled?: boolean;
24
25
  };
25
26
  export type SignInExperience = {
26
27
  tenantId: string;
@@ -38,6 +39,7 @@ export type SignInExperience = {
38
39
  customContent: CustomContent;
39
40
  passwordPolicy: PartialPasswordPolicy;
40
41
  mfa: Mfa;
42
+ singleSignOnEnabled: boolean;
41
43
  };
42
- export type SignInExperienceKeys = 'tenantId' | 'id' | 'color' | 'branding' | 'languageInfo' | 'termsOfUseUrl' | 'privacyPolicyUrl' | 'signIn' | 'signUp' | 'socialSignInConnectorTargets' | 'signInMode' | 'customCss' | 'customContent' | 'passwordPolicy' | 'mfa';
44
+ export type SignInExperienceKeys = 'tenantId' | 'id' | 'color' | 'branding' | 'languageInfo' | 'termsOfUseUrl' | 'privacyPolicyUrl' | 'signIn' | 'signUp' | 'socialSignInConnectorTargets' | 'signInMode' | 'customCss' | 'customContent' | 'passwordPolicy' | 'mfa' | 'singleSignOnEnabled';
43
45
  export declare const SignInExperiences: GeneratedSchema<SignInExperienceKeys, CreateSignInExperience, SignInExperience, 'sign_in_experiences', 'sign_in_experience'>;
@@ -18,6 +18,7 @@ const createGuard = z.object({
18
18
  customContent: customContentGuard.optional(),
19
19
  passwordPolicy: partialPasswordPolicyGuard.optional(),
20
20
  mfa: mfaGuard.optional(),
21
+ singleSignOnEnabled: z.boolean().optional(),
21
22
  });
22
23
  const guard = z.object({
23
24
  tenantId: z.string().max(21),
@@ -35,6 +36,7 @@ const guard = z.object({
35
36
  customContent: customContentGuard,
36
37
  passwordPolicy: partialPasswordPolicyGuard,
37
38
  mfa: mfaGuard,
39
+ singleSignOnEnabled: z.boolean(),
38
40
  });
39
41
  export const SignInExperiences = Object.freeze({
40
42
  table: 'sign_in_experiences',
@@ -55,6 +57,7 @@ export const SignInExperiences = Object.freeze({
55
57
  customContent: 'custom_content',
56
58
  passwordPolicy: 'password_policy',
57
59
  mfa: 'mfa',
60
+ singleSignOnEnabled: 'single_sign_on_enabled',
58
61
  },
59
62
  fieldKeys: [
60
63
  'tenantId',
@@ -72,6 +75,7 @@ export const SignInExperiences = Object.freeze({
72
75
  'customContent',
73
76
  'passwordPolicy',
74
77
  'mfa',
78
+ 'singleSignOnEnabled',
75
79
  ],
76
80
  createGuard,
77
81
  guard,
@@ -8,7 +8,7 @@ export type CreateSsoConnector = {
8
8
  tenantId?: string;
9
9
  /** The globally unique identifier of the SSO connector. */
10
10
  id: string;
11
- /** The connector factory name of the SSO provider. */
11
+ /** The identifier of connector's SSO provider */
12
12
  providerName: string;
13
13
  /** The name of the SSO provider for display. */
14
14
  connectorName: string;
@@ -20,8 +20,6 @@ export type CreateSsoConnector = {
20
20
  branding?: SsoBranding;
21
21
  /** Determines whether to synchronize the user's profile on each login. */
22
22
  syncProfile?: boolean;
23
- /** Determines whether SSO is the restricted sign-in method for users with the SSO registered email domains */
24
- ssoOnly?: boolean;
25
23
  /** When the SSO connector was created. */
26
24
  createdAt?: number;
27
25
  };
@@ -29,7 +27,7 @@ export type SsoConnector = {
29
27
  tenantId: string;
30
28
  /** The globally unique identifier of the SSO connector. */
31
29
  id: string;
32
- /** The connector factory name of the SSO provider. */
30
+ /** The identifier of connector's SSO provider */
33
31
  providerName: string;
34
32
  /** The name of the SSO provider for display. */
35
33
  connectorName: string;
@@ -41,10 +39,8 @@ export type SsoConnector = {
41
39
  branding: SsoBranding;
42
40
  /** Determines whether to synchronize the user's profile on each login. */
43
41
  syncProfile: boolean;
44
- /** Determines whether SSO is the restricted sign-in method for users with the SSO registered email domains */
45
- ssoOnly: boolean;
46
42
  /** When the SSO connector was created. */
47
43
  createdAt: number;
48
44
  };
49
- export type SsoConnectorKeys = 'tenantId' | 'id' | 'providerName' | 'connectorName' | 'config' | 'domains' | 'branding' | 'syncProfile' | 'ssoOnly' | 'createdAt';
45
+ export type SsoConnectorKeys = 'tenantId' | 'id' | 'providerName' | 'connectorName' | 'config' | 'domains' | 'branding' | 'syncProfile' | 'createdAt';
50
46
  export declare const SsoConnectors: GeneratedSchema<SsoConnectorKeys, CreateSsoConnector, SsoConnector, 'sso_connectors', 'sso_connector'>;
@@ -10,7 +10,6 @@ const createGuard = z.object({
10
10
  domains: ssoDomainsGuard.optional(),
11
11
  branding: ssoBrandingGuard.optional(),
12
12
  syncProfile: z.boolean().optional(),
13
- ssoOnly: z.boolean().optional(),
14
13
  createdAt: z.number().optional(),
15
14
  });
16
15
  const guard = z.object({
@@ -22,7 +21,6 @@ const guard = z.object({
22
21
  domains: ssoDomainsGuard,
23
22
  branding: ssoBrandingGuard,
24
23
  syncProfile: z.boolean(),
25
- ssoOnly: z.boolean(),
26
24
  createdAt: z.number(),
27
25
  });
28
26
  export const SsoConnectors = Object.freeze({
@@ -37,7 +35,6 @@ export const SsoConnectors = Object.freeze({
37
35
  domains: 'domains',
38
36
  branding: 'branding',
39
37
  syncProfile: 'sync_profile',
40
- ssoOnly: 'sso_only',
41
38
  createdAt: 'created_at',
42
39
  },
43
40
  fieldKeys: [
@@ -49,7 +46,6 @@ export const SsoConnectors = Object.freeze({
49
46
  'domains',
50
47
  'branding',
51
48
  'syncProfile',
52
- 'ssoOnly',
53
49
  'createdAt',
54
50
  ],
55
51
  createGuard,
@@ -14,6 +14,7 @@ export type CreateUserSsoIdentity = {
14
14
  identityId: string;
15
15
  detail?: JsonObject;
16
16
  createdAt?: number;
17
+ ssoConnectorId: string;
17
18
  };
18
19
  export type UserSsoIdentity = {
19
20
  tenantId: string;
@@ -25,6 +26,7 @@ export type UserSsoIdentity = {
25
26
  identityId: string;
26
27
  detail: JsonObject;
27
28
  createdAt: number;
29
+ ssoConnectorId: string;
28
30
  };
29
- export type UserSsoIdentityKeys = 'tenantId' | 'id' | 'userId' | 'issuer' | 'identityId' | 'detail' | 'createdAt';
31
+ export type UserSsoIdentityKeys = 'tenantId' | 'id' | 'userId' | 'issuer' | 'identityId' | 'detail' | 'createdAt' | 'ssoConnectorId';
30
32
  export declare const UserSsoIdentities: GeneratedSchema<UserSsoIdentityKeys, CreateUserSsoIdentity, UserSsoIdentity, 'user_sso_identities', 'user_sso_identity'>;
@@ -9,6 +9,7 @@ const createGuard = z.object({
9
9
  identityId: z.string().min(1).max(128),
10
10
  detail: jsonObjectGuard.optional(),
11
11
  createdAt: z.number().optional(),
12
+ ssoConnectorId: z.string().min(1).max(128),
12
13
  });
13
14
  const guard = z.object({
14
15
  tenantId: z.string().max(21),
@@ -18,6 +19,7 @@ const guard = z.object({
18
19
  identityId: z.string().min(1).max(128),
19
20
  detail: jsonObjectGuard,
20
21
  createdAt: z.number(),
22
+ ssoConnectorId: z.string().min(1).max(128),
21
23
  });
22
24
  export const UserSsoIdentities = Object.freeze({
23
25
  table: 'user_sso_identities',
@@ -30,6 +32,7 @@ export const UserSsoIdentities = Object.freeze({
30
32
  identityId: 'identity_id',
31
33
  detail: 'detail',
32
34
  createdAt: 'created_at',
35
+ ssoConnectorId: 'sso_connector_id',
33
36
  },
34
37
  fieldKeys: [
35
38
  'tenantId',
@@ -39,6 +42,7 @@ export const UserSsoIdentities = Object.freeze({
39
42
  'identityId',
40
43
  'detail',
41
44
  'createdAt',
45
+ 'ssoConnectorId',
42
46
  ],
43
47
  createGuard,
44
48
  guard,
@@ -2,12 +2,15 @@ import { z } from 'zod';
2
2
  export declare const ssoDomainsGuard: z.ZodArray<z.ZodString, "many">;
3
3
  export type SsoDomains = z.infer<typeof ssoDomainsGuard>;
4
4
  export declare const ssoBrandingGuard: z.ZodObject<{
5
+ displayName: z.ZodOptional<z.ZodString>;
5
6
  logo: z.ZodOptional<z.ZodString>;
6
7
  darkLogo: z.ZodOptional<z.ZodString>;
7
8
  }, "strip", z.ZodTypeAny, {
9
+ displayName?: string | undefined;
8
10
  logo?: string | undefined;
9
11
  darkLogo?: string | undefined;
10
12
  }, {
13
+ displayName?: string | undefined;
11
14
  logo?: string | undefined;
12
15
  darkLogo?: string | undefined;
13
16
  }>;
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  export const ssoDomainsGuard = z.array(z.string());
3
3
  export const ssoBrandingGuard = z.object({
4
+ displayName: z.string().optional(),
4
5
  logo: z.string().optional(),
5
6
  darkLogo: z.string().optional(),
6
7
  });
@@ -3,23 +3,23 @@ import { MfaFactor } from './sign-in-experience.js';
3
3
  export declare const roleNamesGuard: z.ZodArray<z.ZodString, "many">;
4
4
  declare const identityGuard: z.ZodObject<{
5
5
  userId: z.ZodString;
6
- details: z.ZodOptional<z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>>;
6
+ details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
7
7
  }, "strip", z.ZodTypeAny, {
8
8
  userId: string;
9
- details?: {} | undefined;
9
+ details?: Record<string, unknown> | undefined;
10
10
  }, {
11
11
  userId: string;
12
- details?: {} | undefined;
12
+ details?: Record<string, unknown> | undefined;
13
13
  }>;
14
14
  export declare const identitiesGuard: z.ZodRecord<z.ZodString, z.ZodObject<{
15
15
  userId: z.ZodString;
16
- details: z.ZodOptional<z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>>;
16
+ details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
17
17
  }, "strip", z.ZodTypeAny, {
18
18
  userId: string;
19
- details?: {} | undefined;
19
+ details?: Record<string, unknown> | undefined;
20
20
  }, {
21
21
  userId: string;
22
- details?: {} | undefined;
22
+ details?: Record<string, unknown> | undefined;
23
23
  }>>;
24
24
  export type Identity = z.infer<typeof identityGuard>;
25
25
  export type Identities = z.infer<typeof identitiesGuard>;
@@ -3,7 +3,7 @@ import { MfaFactor } from './sign-in-experience.js';
3
3
  export const roleNamesGuard = z.string().array();
4
4
  const identityGuard = z.object({
5
5
  userId: z.string(),
6
- details: z.object({}).optional(), // Connector's userinfo details, schemaless
6
+ details: z.record(z.unknown()).optional(), // Connector's userinfo details, schemaless
7
7
  });
8
8
  export const identitiesGuard = z.record(identityGuard);
9
9
  export const baseMfaVerification = {
@@ -1,10 +1,6 @@
1
1
  import type { InferModelType } from '@withtyped/server/model';
2
2
  import { z } from 'zod';
3
- export declare enum TenantTag {
4
- Development = "development",
5
- Staging = "staging",
6
- Production = "production"
7
- }
3
+ import { TenantTag } from '../types/tenant.js';
8
4
  export declare const Tenants: import("@withtyped/server/model").default<"tenants", {
9
5
  id: string;
10
6
  dbUser: string | null;
@@ -1,11 +1,6 @@
1
1
  import { createModel } from '@withtyped/server/model';
2
2
  import { z } from 'zod';
3
- export var TenantTag;
4
- (function (TenantTag) {
5
- TenantTag["Development"] = "development";
6
- TenantTag["Staging"] = "staging";
7
- TenantTag["Production"] = "production";
8
- })(TenantTag || (TenantTag = {}));
3
+ import { TenantTag } from '../types/tenant.js';
9
4
  export const Tenants = createModel(
10
5
  /* Sql */ `
11
6
  /* init_order = 0 */
@@ -9,6 +9,7 @@ export type HookEventPayload = {
9
9
  sessionId?: string;
10
10
  userAgent?: string;
11
11
  userId?: string;
12
+ userIp?: string;
12
13
  user?: Pick<User, (typeof userInfoSelectFields)[number]>;
13
14
  application?: Pick<Application, 'id' | 'type' | 'name' | 'description'>;
14
15
  } & Record<string, unknown>;
@@ -22,3 +22,4 @@ export * from './sentinel.js';
22
22
  export * from './mfa.js';
23
23
  export * from './organization.js';
24
24
  export * from './sso-connector.js';
25
+ export * from './tenant.js';
@@ -22,3 +22,4 @@ export * from './sentinel.js';
22
22
  export * from './mfa.js';
23
23
  export * from './organization.js';
24
24
  export * from './sso-connector.js';
25
+ export * from './tenant.js';
@@ -1,5 +1,6 @@
1
1
  import type { ZodType } from 'zod';
2
2
  import { z } from 'zod';
3
+ import { TenantTag } from './tenant.js';
3
4
  /**
4
5
  * Logto OIDC signing key types, used mainly in REST API routes.
5
6
  */
@@ -45,12 +46,35 @@ export declare const logtoOidcConfigGuard: Readonly<{
45
46
  export declare const adminConsoleDataGuard: z.ZodObject<{
46
47
  signInExperienceCustomized: z.ZodBoolean;
47
48
  organizationCreated: z.ZodBoolean;
49
+ developmentTenantMigrationNotification: z.ZodOptional<z.ZodObject<{
50
+ isPaidTenant: z.ZodBoolean;
51
+ tag: z.ZodNativeEnum<typeof TenantTag>;
52
+ readAt: z.ZodOptional<z.ZodNumber>;
53
+ }, "strip", z.ZodTypeAny, {
54
+ isPaidTenant: boolean;
55
+ tag: TenantTag;
56
+ readAt?: number | undefined;
57
+ }, {
58
+ isPaidTenant: boolean;
59
+ tag: TenantTag;
60
+ readAt?: number | undefined;
61
+ }>>;
48
62
  }, "strip", z.ZodTypeAny, {
49
63
  signInExperienceCustomized: boolean;
50
64
  organizationCreated: boolean;
65
+ developmentTenantMigrationNotification?: {
66
+ isPaidTenant: boolean;
67
+ tag: TenantTag;
68
+ readAt?: number | undefined;
69
+ } | undefined;
51
70
  }, {
52
71
  signInExperienceCustomized: boolean;
53
72
  organizationCreated: boolean;
73
+ developmentTenantMigrationNotification?: {
74
+ isPaidTenant: boolean;
75
+ tag: TenantTag;
76
+ readAt?: number | undefined;
77
+ } | undefined;
54
78
  }>;
55
79
  export type AdminConsoleData = z.infer<typeof adminConsoleDataGuard>;
56
80
  export declare const cloudConnectionDataGuard: z.ZodObject<{
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { TenantTag } from './tenant.js';
2
3
  /**
3
4
  * Logto OIDC signing key types, used mainly in REST API routes.
4
5
  */
@@ -36,6 +37,13 @@ export const logtoOidcConfigGuard = Object.freeze({
36
37
  export const adminConsoleDataGuard = z.object({
37
38
  signInExperienceCustomized: z.boolean(),
38
39
  organizationCreated: z.boolean(),
40
+ developmentTenantMigrationNotification: z
41
+ .object({
42
+ isPaidTenant: z.boolean(),
43
+ tag: z.nativeEnum(TenantTag),
44
+ readAt: z.number().optional(),
45
+ })
46
+ .optional(),
39
47
  });
40
48
  /* --- Logto tenant cloud connection config --- */
41
49
  export const cloudConnectionDataGuard = z.object({
@@ -1,11 +1,15 @@
1
1
  import { z } from 'zod';
2
- import { type OrganizationRole, type Organization, type User } from '../db-entries/index.js';
3
- import { type FeaturedUser } from './user.js';
2
+ import { type OrganizationRole, type Organization } from '../db-entries/index.js';
3
+ import { type UserInfo, type FeaturedUser } from './user.js';
4
+ /**
5
+ * The simplified organization scope entity that is returned for some endpoints.
6
+ */
7
+ export type OrganizationScopeEntity = {
8
+ id: string;
9
+ name: string;
10
+ };
4
11
  export type OrganizationRoleWithScopes = OrganizationRole & {
5
- scopes: Array<{
6
- id: string;
7
- name: string;
8
- }>;
12
+ scopes: OrganizationScopeEntity[];
9
13
  };
10
14
  export declare const organizationRoleWithScopesGuard: z.ZodType<OrganizationRoleWithScopes>;
11
15
  /**
@@ -29,7 +33,7 @@ export declare const organizationWithOrganizationRolesGuard: z.ZodType<Organizat
29
33
  * The user entity with the `organizationRoles` field that contains the roles of
30
34
  * the user in a specific organization.
31
35
  */
32
- export type UserWithOrganizationRoles = User & {
36
+ export type UserWithOrganizationRoles = UserInfo & {
33
37
  /** The roles of the user in a specific organization. */
34
38
  organizationRoles: OrganizationRoleEntity[];
35
39
  };
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { OrganizationRoles, Organizations, Users, } from '../db-entries/index.js';
2
+ import { OrganizationRoles, Organizations, } from '../db-entries/index.js';
3
+ import { userInfoGuard } from './user.js';
3
4
  export const organizationRoleWithScopesGuard = OrganizationRoles.guard.extend({
4
5
  scopes: z
5
6
  .object({
@@ -15,6 +16,6 @@ const organizationRoleEntityGuard = z.object({
15
16
  export const organizationWithOrganizationRolesGuard = Organizations.guard.extend({
16
17
  organizationRoles: organizationRoleEntityGuard.array(),
17
18
  });
18
- export const userWithOrganizationRolesGuard = Users.guard.extend({
19
+ export const userWithOrganizationRolesGuard = userInfoGuard.extend({
19
20
  organizationRoles: organizationRoleEntityGuard.array(),
20
21
  });
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { type SsoConnector } from '../db-entries/sso-connector.js';
2
3
  /**
3
4
  * SSO Connector data type that are returned to the experience client for sign-in use.
4
5
  */
@@ -19,3 +20,115 @@ export declare const ssoConnectorMetadataGuard: z.ZodObject<{
19
20
  darkLogo?: string | undefined;
20
21
  }>;
21
22
  export type SsoConnectorMetadata = z.infer<typeof ssoConnectorMetadataGuard>;
23
+ export declare enum SsoProviderName {
24
+ OIDC = "OIDC",
25
+ SAML = "SAML",
26
+ AZURE_AD = "AzureAD",
27
+ GOOGLE_WORKSPACE = "GoogleWorkspace",
28
+ OKTA = "Okta"
29
+ }
30
+ export declare const singleSignOnDomainBlackList: readonly string[];
31
+ export type SupportedSsoConnector = Omit<SsoConnector, 'providerName'> & {
32
+ providerName: SsoProviderName;
33
+ };
34
+ declare const ssoConnectorProviderDetailGuard: z.ZodObject<{
35
+ providerName: z.ZodNativeEnum<typeof SsoProviderName>;
36
+ logo: z.ZodString;
37
+ logoDark: z.ZodString;
38
+ description: z.ZodString;
39
+ name: z.ZodString;
40
+ }, "strip", z.ZodTypeAny, {
41
+ name: string;
42
+ logo: string;
43
+ description: string;
44
+ logoDark: string;
45
+ providerName: SsoProviderName;
46
+ }, {
47
+ name: string;
48
+ logo: string;
49
+ description: string;
50
+ logoDark: string;
51
+ providerName: SsoProviderName;
52
+ }>;
53
+ export type SsoConnectorProviderDetail = z.infer<typeof ssoConnectorProviderDetailGuard>;
54
+ export declare const ssoConnectorProvidersResponseGuard: z.ZodArray<z.ZodObject<{
55
+ providerName: z.ZodNativeEnum<typeof SsoProviderName>;
56
+ logo: z.ZodString;
57
+ logoDark: z.ZodString;
58
+ description: z.ZodString;
59
+ name: z.ZodString;
60
+ }, "strip", z.ZodTypeAny, {
61
+ name: string;
62
+ logo: string;
63
+ description: string;
64
+ logoDark: string;
65
+ providerName: SsoProviderName;
66
+ }, {
67
+ name: string;
68
+ logo: string;
69
+ description: string;
70
+ logoDark: string;
71
+ providerName: SsoProviderName;
72
+ }>, "many">;
73
+ export type SsoConnectorProvidersResponse = z.infer<typeof ssoConnectorProvidersResponseGuard>;
74
+ export declare const ssoConnectorWithProviderConfigGuard: z.ZodObject<{
75
+ id: z.ZodType<string, z.ZodTypeDef, string>;
76
+ tenantId: z.ZodType<string, z.ZodTypeDef, string>;
77
+ createdAt: z.ZodType<number, z.ZodTypeDef, number>;
78
+ syncProfile: z.ZodType<boolean, z.ZodTypeDef, boolean>;
79
+ config: z.ZodType<import("@withtyped/server").JsonObject, z.ZodTypeDef, import("@withtyped/server").JsonObject>;
80
+ domains: z.ZodType<string[], z.ZodTypeDef, string[]>;
81
+ branding: z.ZodType<{
82
+ displayName?: string | undefined;
83
+ logo?: string | undefined;
84
+ darkLogo?: string | undefined;
85
+ }, z.ZodTypeDef, {
86
+ displayName?: string | undefined;
87
+ logo?: string | undefined;
88
+ darkLogo?: string | undefined;
89
+ }>;
90
+ connectorName: z.ZodType<string, z.ZodTypeDef, string>;
91
+ name: z.ZodString;
92
+ providerName: z.ZodNativeEnum<typeof SsoProviderName>;
93
+ providerLogo: z.ZodString;
94
+ providerLogoDark: z.ZodString;
95
+ providerConfig: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
96
+ }, "strip", z.ZodTypeAny, {
97
+ name: string;
98
+ id: string;
99
+ tenantId: string;
100
+ createdAt: number;
101
+ syncProfile: boolean;
102
+ config: import("@withtyped/server").JsonObject;
103
+ domains: string[];
104
+ branding: {
105
+ displayName?: string | undefined;
106
+ logo?: string | undefined;
107
+ darkLogo?: string | undefined;
108
+ };
109
+ providerName: SsoProviderName;
110
+ connectorName: string;
111
+ providerLogo: string;
112
+ providerLogoDark: string;
113
+ providerConfig?: Record<string, unknown> | undefined;
114
+ }, {
115
+ name: string;
116
+ id: string;
117
+ tenantId: string;
118
+ createdAt: number;
119
+ syncProfile: boolean;
120
+ config: import("@withtyped/server").JsonObject;
121
+ domains: string[];
122
+ branding: {
123
+ displayName?: string | undefined;
124
+ logo?: string | undefined;
125
+ darkLogo?: string | undefined;
126
+ };
127
+ providerName: SsoProviderName;
128
+ connectorName: string;
129
+ providerLogo: string;
130
+ providerLogoDark: string;
131
+ providerConfig?: Record<string, unknown> | undefined;
132
+ }>;
133
+ export type SsoConnectorWithProviderConfig = z.infer<typeof ssoConnectorWithProviderConfigGuard>;
134
+ export {};
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { SsoConnectors } from '../db-entries/sso-connector.js';
2
3
  /**
3
4
  * SSO Connector data type that are returned to the experience client for sign-in use.
4
5
  */
@@ -8,3 +9,50 @@ export const ssoConnectorMetadataGuard = z.object({
8
9
  logo: z.string(),
9
10
  darkLogo: z.string().optional(),
10
11
  });
12
+ export var SsoProviderName;
13
+ (function (SsoProviderName) {
14
+ SsoProviderName["OIDC"] = "OIDC";
15
+ SsoProviderName["SAML"] = "SAML";
16
+ SsoProviderName["AZURE_AD"] = "AzureAD";
17
+ SsoProviderName["GOOGLE_WORKSPACE"] = "GoogleWorkspace";
18
+ SsoProviderName["OKTA"] = "Okta";
19
+ })(SsoProviderName || (SsoProviderName = {}));
20
+ export const singleSignOnDomainBlackList = Object.freeze([
21
+ 'gmail.com',
22
+ 'yahoo.com',
23
+ 'hotmail.com',
24
+ 'outlook.com',
25
+ 'live.com',
26
+ 'icloud.com',
27
+ 'aol.com',
28
+ 'yandex.com',
29
+ 'mail.com',
30
+ 'protonmail.com',
31
+ 'yanex.com',
32
+ 'gmx.com',
33
+ 'mail.ru',
34
+ 'zoho.com',
35
+ 'qq.com',
36
+ '163.com',
37
+ '126.com',
38
+ 'sina.com',
39
+ 'sohu.com',
40
+ ]);
41
+ const ssoConnectorProviderDetailGuard = z.object({
42
+ providerName: z.nativeEnum(SsoProviderName),
43
+ logo: z.string(),
44
+ logoDark: z.string(),
45
+ description: z.string(),
46
+ name: z.string(),
47
+ });
48
+ export const ssoConnectorProvidersResponseGuard = z.array(ssoConnectorProviderDetailGuard);
49
+ // API response guard for all the SSO connectors CRUD APIs
50
+ export const ssoConnectorWithProviderConfigGuard = SsoConnectors.guard
51
+ .omit({ providerName: true })
52
+ .merge(z.object({
53
+ name: z.string(),
54
+ providerName: z.nativeEnum(SsoProviderName),
55
+ providerLogo: z.string(),
56
+ providerLogoDark: z.string(),
57
+ providerConfig: z.record(z.unknown()).optional(),
58
+ }));
@@ -0,0 +1,5 @@
1
+ export declare enum TenantTag {
2
+ Development = "development",
3
+ Staging = "staging",
4
+ Production = "production"
5
+ }
@@ -0,0 +1,9 @@
1
+ export var TenantTag;
2
+ (function (TenantTag) {
3
+ /* Development tenants are free to use but are not meant to be used as production environment */
4
+ TenantTag["Development"] = "development";
5
+ /* @deprecated */
6
+ TenantTag["Staging"] = "staging";
7
+ /* A production tenant must have an associated subscription plan, even if it's a free plan */
8
+ TenantTag["Production"] = "production";
9
+ })(TenantTag || (TenantTag = {}));
@@ -15,10 +15,10 @@ export declare const userInfoGuard: z.ZodObject<Pick<{
15
15
  applicationId: z.ZodType<string | null, z.ZodTypeDef, string | null>;
16
16
  identities: z.ZodType<Record<string, {
17
17
  userId: string;
18
- details?: {} | undefined;
18
+ details?: Record<string, unknown> | undefined;
19
19
  }>, z.ZodTypeDef, Record<string, {
20
20
  userId: string;
21
- details?: {} | undefined;
21
+ details?: Record<string, unknown> | undefined;
22
22
  }>>;
23
23
  customData: z.ZodType<import("../foundations/index.js").JsonObject, z.ZodTypeDef, import("../foundations/index.js").JsonObject>;
24
24
  logtoConfig: z.ZodType<import("../foundations/index.js").JsonObject, z.ZodTypeDef, import("../foundations/index.js").JsonObject>;
@@ -90,7 +90,7 @@ export declare const userInfoGuard: z.ZodObject<Pick<{
90
90
  avatar: string | null;
91
91
  identities: Record<string, {
92
92
  userId: string;
93
- details?: {} | undefined;
93
+ details?: Record<string, unknown> | undefined;
94
94
  }>;
95
95
  customData: import("../foundations/index.js").JsonObject;
96
96
  logtoConfig: import("../foundations/index.js").JsonObject;
@@ -136,7 +136,7 @@ export declare const userInfoGuard: z.ZodObject<Pick<{
136
136
  avatar: string | null;
137
137
  identities: Record<string, {
138
138
  userId: string;
139
- details?: {} | undefined;
139
+ details?: Record<string, unknown> | undefined;
140
140
  }>;
141
141
  customData: import("../foundations/index.js").JsonObject;
142
142
  logtoConfig: import("../foundations/index.js").JsonObject;
@@ -184,10 +184,10 @@ export declare const userProfileResponseGuard: z.ZodObject<{
184
184
  avatar: z.ZodType<string | null, z.ZodTypeDef, string | null>;
185
185
  identities: z.ZodType<Record<string, {
186
186
  userId: string;
187
- details?: {} | undefined;
187
+ details?: Record<string, unknown> | undefined;
188
188
  }>, z.ZodTypeDef, Record<string, {
189
189
  userId: string;
190
- details?: {} | undefined;
190
+ details?: Record<string, unknown> | undefined;
191
191
  }>>;
192
192
  customData: z.ZodType<import("../foundations/index.js").JsonObject, z.ZodTypeDef, import("../foundations/index.js").JsonObject>;
193
193
  logtoConfig: z.ZodType<import("../foundations/index.js").JsonObject, z.ZodTypeDef, import("../foundations/index.js").JsonObject>;
@@ -245,6 +245,7 @@ export declare const userProfileResponseGuard: z.ZodObject<{
245
245
  isSuspended: z.ZodType<boolean, z.ZodTypeDef, boolean>;
246
246
  lastSignInAt: z.ZodType<number | null, z.ZodTypeDef, number | null>;
247
247
  hasPassword: z.ZodOptional<z.ZodBoolean>;
248
+ ssoIdentities: z.ZodOptional<z.ZodArray<import("../foundations/schemas.js").Guard<import("../db-entries/user-sso-identity.js").UserSsoIdentity>, "many">>;
248
249
  }, "strip", z.ZodTypeAny, {
249
250
  name: string | null;
250
251
  id: string;
@@ -259,7 +260,7 @@ export declare const userProfileResponseGuard: z.ZodObject<{
259
260
  avatar: string | null;
260
261
  identities: Record<string, {
261
262
  userId: string;
262
- details?: {} | undefined;
263
+ details?: Record<string, unknown> | undefined;
263
264
  }>;
264
265
  customData: import("../foundations/index.js").JsonObject;
265
266
  logtoConfig: import("../foundations/index.js").JsonObject;
@@ -292,6 +293,7 @@ export declare const userProfileResponseGuard: z.ZodObject<{
292
293
  isSuspended: boolean;
293
294
  lastSignInAt: number | null;
294
295
  hasPassword?: boolean | undefined;
296
+ ssoIdentities?: import("../db-entries/user-sso-identity.js").UserSsoIdentity[] | undefined;
295
297
  }, {
296
298
  name: string | null;
297
299
  id: string;
@@ -306,7 +308,7 @@ export declare const userProfileResponseGuard: z.ZodObject<{
306
308
  avatar: string | null;
307
309
  identities: Record<string, {
308
310
  userId: string;
309
- details?: {} | undefined;
311
+ details?: Record<string, unknown> | undefined;
310
312
  }>;
311
313
  customData: import("../foundations/index.js").JsonObject;
312
314
  logtoConfig: import("../foundations/index.js").JsonObject;
@@ -339,6 +341,7 @@ export declare const userProfileResponseGuard: z.ZodObject<{
339
341
  isSuspended: boolean;
340
342
  lastSignInAt: number | null;
341
343
  hasPassword?: boolean | undefined;
344
+ ssoIdentities?: import("../db-entries/user-sso-identity.js").UserSsoIdentity[] | undefined;
342
345
  }>;
343
346
  export type UserProfileResponse = z.infer<typeof userProfileResponseGuard>;
344
347
  export declare const userMfaVerificationResponseGuard: z.ZodArray<z.ZodObject<{
@@ -399,10 +402,10 @@ export declare const featuredUserGuard: z.ZodObject<Pick<{
399
402
  applicationId: z.ZodType<string | null, z.ZodTypeDef, string | null>;
400
403
  identities: z.ZodType<Record<string, {
401
404
  userId: string;
402
- details?: {} | undefined;
405
+ details?: Record<string, unknown> | undefined;
403
406
  }>, z.ZodTypeDef, Record<string, {
404
407
  userId: string;
405
- details?: {} | undefined;
408
+ details?: Record<string, unknown> | undefined;
406
409
  }>>;
407
410
  customData: z.ZodType<import("../foundations/index.js").JsonObject, z.ZodTypeDef, import("../foundations/index.js").JsonObject>;
408
411
  logtoConfig: z.ZodType<import("../foundations/index.js").JsonObject, z.ZodTypeDef, import("../foundations/index.js").JsonObject>;
package/lib/types/user.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { Users } from '../db-entries/index.js';
2
+ import { Users, UserSsoIdentities } from '../db-entries/index.js';
3
3
  import { MfaFactor } from '../foundations/index.js';
4
4
  export const userInfoSelectFields = Object.freeze([
5
5
  'id',
@@ -18,6 +18,7 @@ export const userInfoSelectFields = Object.freeze([
18
18
  export const userInfoGuard = Users.guard.pick(Object.fromEntries(userInfoSelectFields.map((key) => [key, true])));
19
19
  export const userProfileResponseGuard = userInfoGuard.extend({
20
20
  hasPassword: z.boolean().optional(),
21
+ ssoIdentities: z.array(UserSsoIdentities.guard).optional(),
21
22
  });
22
23
  export const userMfaVerificationResponseGuard = z
23
24
  .object({
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Find duplicated domains and blocked domains using the domain blacklist.
3
+ *
4
+ * @param domains Array of email domains.
5
+ * @returns
6
+ */
7
+ export declare const findDuplicatedOrBlockedEmailDomains: (domains?: string[]) => {
8
+ duplicatedDomains: Set<string>;
9
+ forbiddenDomains: Set<string>;
10
+ };
@@ -0,0 +1,28 @@
1
+ import { singleSignOnDomainBlackList } from '../types/sso-connector.js';
2
+ /**
3
+ * Find duplicated domains and blocked domains using the domain blacklist.
4
+ *
5
+ * @param domains Array of email domains.
6
+ * @returns
7
+ */
8
+ export const findDuplicatedOrBlockedEmailDomains = (domains) => {
9
+ const blackListSet = new Set(singleSignOnDomainBlackList);
10
+ const validDomainSet = new Set();
11
+ const duplicatedDomains = new Set();
12
+ const forbiddenDomains = new Set();
13
+ for (const domain of domains ?? []) {
14
+ if (blackListSet.has(domain)) {
15
+ forbiddenDomains.add(domain);
16
+ }
17
+ if (validDomainSet.has(domain)) {
18
+ duplicatedDomains.add(domain);
19
+ }
20
+ else {
21
+ validDomainSet.add(domain);
22
+ }
23
+ }
24
+ return {
25
+ duplicatedDomains,
26
+ forbiddenDomains,
27
+ };
28
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,34 @@
1
+ import { findDuplicatedOrBlockedEmailDomains } from './domain.js';
2
+ describe('findDuplicatedOrBlockedEmailDomains', () => {
3
+ it('should return blocked domains and duplicated domains correctly', () => {
4
+ const { duplicatedDomains, forbiddenDomains } = findDuplicatedOrBlockedEmailDomains([
5
+ 'gmail.com',
6
+ 'silverhand.io',
7
+ 'logto.io',
8
+ 'yahoo.com',
9
+ 'outlook.com',
10
+ 'logto.io',
11
+ ]);
12
+ expect(duplicatedDomains).toEqual(new Set(['logto.io']));
13
+ expect(forbiddenDomains).toEqual(new Set(['gmail.com', 'yahoo.com', 'outlook.com']));
14
+ });
15
+ it('should return empty `duplicatedDomains` and `forbiddenDomains` sets if all domains are valid', () => {
16
+ const { duplicatedDomains, forbiddenDomains } = findDuplicatedOrBlockedEmailDomains([
17
+ 'silverhand.io',
18
+ 'logto.io',
19
+ 'metalhand.io',
20
+ ]);
21
+ expect(duplicatedDomains).toEqual(new Set());
22
+ expect(forbiddenDomains).toEqual(new Set());
23
+ });
24
+ it('should return empty `duplicatedDomains` and `forbiddenDomains` sets if input is undefined', () => {
25
+ const { duplicatedDomains, forbiddenDomains } = findDuplicatedOrBlockedEmailDomains();
26
+ expect(duplicatedDomains).toEqual(new Set());
27
+ expect(forbiddenDomains).toEqual(new Set());
28
+ });
29
+ it('should return empty `duplicatedDomains` and `forbiddenDomains` sets if input is empty array', () => {
30
+ const { duplicatedDomains, forbiddenDomains } = findDuplicatedOrBlockedEmailDomains([]);
31
+ expect(duplicatedDomains).toEqual(new Set());
32
+ expect(forbiddenDomains).toEqual(new Set());
33
+ });
34
+ });
@@ -1,2 +1,3 @@
1
1
  export * from './role.js';
2
2
  export * from './management-api.js';
3
+ export * from './domain.js';
@@ -1,2 +1,3 @@
1
1
  export * from './role.js';
2
2
  export * from './management-api.js';
3
+ export * from './domain.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/schemas",
3
- "version": "1.10.1",
3
+ "version": "1.12.0",
4
4
  "author": "Silverhand Inc. <contact@silverhand.io>",
5
5
  "license": "MPL-2.0",
6
6
  "type": "module",
@@ -30,7 +30,7 @@
30
30
  "@types/inquirer": "^9.0.0",
31
31
  "@types/jest": "^29.4.0",
32
32
  "@types/node": "^18.11.18",
33
- "@types/pluralize": "^0.0.32",
33
+ "@types/pluralize": "^0.0.33",
34
34
  "camelcase": "^8.0.0",
35
35
  "chalk": "^5.0.0",
36
36
  "eslint": "^8.44.0",
@@ -65,15 +65,15 @@
65
65
  "prettier": "@silverhand/eslint-config/.prettierrc",
66
66
  "dependencies": {
67
67
  "@logto/connector-kit": "^2.0.0",
68
- "@logto/core-kit": "^2.2.0",
68
+ "@logto/core-kit": "^2.2.1",
69
69
  "@logto/language-kit": "^1.0.0",
70
- "@logto/phrases": "^1.6.0",
71
- "@logto/phrases-experience": "^1.3.1",
70
+ "@logto/phrases": "^1.8.0",
71
+ "@logto/phrases-experience": "^1.5.0",
72
72
  "@logto/shared": "^3.0.0",
73
73
  "@withtyped/server": "^0.12.9"
74
74
  },
75
75
  "peerDependencies": {
76
- "zod": "^3.22.3"
76
+ "zod": "^3.22.4"
77
77
  },
78
78
  "scripts": {
79
79
  "precommit": "lint-staged",
@@ -1,14 +1,16 @@
1
- /* init_order = 2 */
1
+ /* init_order = 3 */
2
2
 
3
3
  /** The relations between organizations, organization roles, and users. A relation means that a user has a role in an organization. */
4
4
  create table organization_role_user_relations (
5
5
  tenant_id varchar(21) not null
6
6
  references tenants (id) on update cascade on delete cascade,
7
- organization_id varchar(21) not null
8
- references organizations (id) on update cascade on delete cascade,
7
+ organization_id varchar(21) not null,
9
8
  organization_role_id varchar(21) not null
10
9
  references organization_roles (id) on update cascade on delete cascade,
11
- user_id varchar(21) not null
12
- references users (id) on update cascade on delete cascade,
13
- primary key (tenant_id, organization_id, organization_role_id, user_id)
10
+ user_id varchar(21) not null,
11
+ primary key (tenant_id, organization_id, organization_role_id, user_id),
12
+ /** User's roles in an organization should be synchronized with the user's membership in the organization. */
13
+ foreign key (tenant_id, organization_id, user_id)
14
+ references organization_user_relations (tenant_id, organization_id, user_id)
15
+ on update cascade on delete cascade
14
16
  );
@@ -17,5 +17,6 @@ create table sign_in_experiences (
17
17
  custom_content jsonb /* @use CustomContent */ not null default '{}'::jsonb,
18
18
  password_policy jsonb /* @use PartialPasswordPolicy */ not null default '{}'::jsonb,
19
19
  mfa jsonb /* @use Mfa */ not null default '{}'::jsonb,
20
+ single_sign_on_enabled boolean not null default false,
20
21
  primary key (tenant_id, id)
21
22
  );
@@ -1,9 +1,10 @@
1
+ /* init_order = 1 */
1
2
  create table sso_connectors (
2
3
  tenant_id varchar(21) not null
3
4
  references tenants (id) on update cascade on delete cascade,
4
5
  /** The globally unique identifier of the SSO connector. */
5
6
  id varchar(128) not null,
6
- /** The connector factory name of the SSO provider. */
7
+ /** The identifier of connector's SSO provider */
7
8
  provider_name varchar(128) not null,
8
9
  /** The name of the SSO provider for display. */
9
10
  connector_name varchar(128) not null,
@@ -15,11 +16,11 @@ create table sso_connectors (
15
16
  branding jsonb /* @use SsoBranding */ not null default '{}'::jsonb,
16
17
  /** Determines whether to synchronize the user's profile on each login. */
17
18
  sync_profile boolean not null default FALSE,
18
- /** Determines whether SSO is the restricted sign-in method for users with the SSO registered email domains */
19
- sso_only boolean not null default FALSE,
20
19
  /** When the SSO connector was created. */
21
20
  created_at timestamptz not null default(now()),
22
- primary key (id)
21
+ primary key (id),
22
+ constraint sso_connectors__connector_name__unique
23
+ unique (tenant_id, connector_name)
23
24
  );
24
25
 
25
26
  create index sso_connectors__id
@@ -11,6 +11,9 @@ create table user_sso_identities (
11
11
  identity_id varchar(128) not null,
12
12
  detail jsonb /* @use JsonObject */ not null default '{}'::jsonb,
13
13
  created_at timestamp not null default(now()),
14
+ sso_connector_id
15
+ varchar(128) not null
16
+ references sso_connectors (id) on update cascade on delete cascade,
14
17
  primary key (id),
15
18
  constraint user_sso_identities__issuer__identity_id
16
19
  unique (tenant_id, issuer, identity_id)