@logto/schemas 1.11.0 → 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 (34) hide show
  1. package/alterations/1.12.0-1700031616-update-org-role-foreign-keys.ts +35 -0
  2. package/alterations/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.ts +21 -0
  3. package/alterations/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.ts +20 -0
  4. package/alterations-js/1.12.0-1700031616-update-org-role-foreign-keys.d.ts +3 -0
  5. package/alterations-js/1.12.0-1700031616-update-org-role-foreign-keys.js +31 -0
  6. package/alterations-js/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.d.ts +3 -0
  7. package/alterations-js/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.js +17 -0
  8. package/alterations-js/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.d.ts +3 -0
  9. package/alterations-js/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.js +16 -0
  10. package/lib/consts/index.d.ts +1 -0
  11. package/lib/consts/index.js +1 -0
  12. package/lib/consts/subscriptions.d.ts +6 -0
  13. package/lib/consts/subscriptions.js +7 -0
  14. package/lib/db-entries/sign-in-experience.d.ts +3 -1
  15. package/lib/db-entries/sign-in-experience.js +4 -0
  16. package/lib/db-entries/sso-connector.d.ts +2 -2
  17. package/lib/foundations/jsonb-types/sso-connector.d.ts +3 -0
  18. package/lib/foundations/jsonb-types/sso-connector.js +1 -0
  19. package/lib/types/hook.d.ts +1 -0
  20. package/lib/types/sso-connector.d.ts +54 -57
  21. package/lib/types/sso-connector.js +41 -7
  22. package/lib/types/tenant.js +3 -0
  23. package/lib/types/user.d.ts +3 -0
  24. package/lib/types/user.js +2 -1
  25. package/lib/utils/domain.d.ts +10 -0
  26. package/lib/utils/domain.js +28 -0
  27. package/lib/utils/domain.test.d.ts +1 -0
  28. package/lib/utils/domain.test.js +34 -0
  29. package/lib/utils/index.d.ts +1 -0
  30. package/lib/utils/index.js +1 -0
  31. package/package.json +5 -5
  32. package/tables/organization_role_user_relations.sql +8 -6
  33. package/tables/sign_in_experiences.sql +1 -0
  34. package/tables/sso_connectors.sql +4 -2
@@ -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,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;
@@ -27,7 +27,7 @@ export type SsoConnector = {
27
27
  tenantId: string;
28
28
  /** The globally unique identifier of the SSO connector. */
29
29
  id: string;
30
- /** The connector factory name of the SSO provider. */
30
+ /** The identifier of connector's SSO provider */
31
31
  providerName: string;
32
32
  /** The name of the SSO provider for display. */
33
33
  connectorName: string;
@@ -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
  });
@@ -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>;
@@ -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,71 +20,57 @@ export declare const ssoConnectorMetadataGuard: z.ZodObject<{
19
20
  darkLogo?: string | undefined;
20
21
  }>;
21
22
  export type SsoConnectorMetadata = z.infer<typeof ssoConnectorMetadataGuard>;
22
- declare const ssoConnectorFactoryDetailGuard: z.ZodObject<{
23
- providerName: z.ZodString;
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>;
24
36
  logo: z.ZodString;
37
+ logoDark: z.ZodString;
25
38
  description: z.ZodString;
39
+ name: z.ZodString;
26
40
  }, "strip", z.ZodTypeAny, {
41
+ name: string;
27
42
  logo: string;
28
43
  description: string;
29
- providerName: string;
44
+ logoDark: string;
45
+ providerName: SsoProviderName;
30
46
  }, {
47
+ name: string;
31
48
  logo: string;
32
49
  description: string;
33
- providerName: string;
50
+ logoDark: string;
51
+ providerName: SsoProviderName;
34
52
  }>;
35
- export type SsoConnectorFactoryDetail = z.infer<typeof ssoConnectorFactoryDetailGuard>;
36
- export declare const ssoConnectorFactoriesResponseGuard: z.ZodObject<{
37
- standardConnectors: z.ZodArray<z.ZodObject<{
38
- providerName: z.ZodString;
39
- logo: z.ZodString;
40
- description: z.ZodString;
41
- }, "strip", z.ZodTypeAny, {
42
- logo: string;
43
- description: string;
44
- providerName: string;
45
- }, {
46
- logo: string;
47
- description: string;
48
- providerName: string;
49
- }>, "many">;
50
- providerConnectors: z.ZodArray<z.ZodObject<{
51
- providerName: z.ZodString;
52
- logo: z.ZodString;
53
- description: z.ZodString;
54
- }, "strip", z.ZodTypeAny, {
55
- logo: string;
56
- description: string;
57
- providerName: string;
58
- }, {
59
- logo: string;
60
- description: string;
61
- providerName: string;
62
- }>, "many">;
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;
63
60
  }, "strip", z.ZodTypeAny, {
64
- standardConnectors: {
65
- logo: string;
66
- description: string;
67
- providerName: string;
68
- }[];
69
- providerConnectors: {
70
- logo: string;
71
- description: string;
72
- providerName: string;
73
- }[];
61
+ name: string;
62
+ logo: string;
63
+ description: string;
64
+ logoDark: string;
65
+ providerName: SsoProviderName;
74
66
  }, {
75
- standardConnectors: {
76
- logo: string;
77
- description: string;
78
- providerName: string;
79
- }[];
80
- providerConnectors: {
81
- logo: string;
82
- description: string;
83
- providerName: string;
84
- }[];
85
- }>;
86
- export type SsoConnectorFactoriesResponse = z.infer<typeof ssoConnectorFactoriesResponseGuard>;
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>;
87
74
  export declare const ssoConnectorWithProviderConfigGuard: z.ZodObject<{
88
75
  id: z.ZodType<string, z.ZodTypeDef, string>;
89
76
  tenantId: z.ZodType<string, z.ZodTypeDef, string>;
@@ -92,17 +79,22 @@ export declare const ssoConnectorWithProviderConfigGuard: z.ZodObject<{
92
79
  config: z.ZodType<import("@withtyped/server").JsonObject, z.ZodTypeDef, import("@withtyped/server").JsonObject>;
93
80
  domains: z.ZodType<string[], z.ZodTypeDef, string[]>;
94
81
  branding: z.ZodType<{
82
+ displayName?: string | undefined;
95
83
  logo?: string | undefined;
96
84
  darkLogo?: string | undefined;
97
85
  }, z.ZodTypeDef, {
86
+ displayName?: string | undefined;
98
87
  logo?: string | undefined;
99
88
  darkLogo?: string | undefined;
100
89
  }>;
101
- providerName: z.ZodType<string, z.ZodTypeDef, string>;
102
90
  connectorName: z.ZodType<string, z.ZodTypeDef, string>;
91
+ name: z.ZodString;
92
+ providerName: z.ZodNativeEnum<typeof SsoProviderName>;
103
93
  providerLogo: z.ZodString;
94
+ providerLogoDark: z.ZodString;
104
95
  providerConfig: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
105
96
  }, "strip", z.ZodTypeAny, {
97
+ name: string;
106
98
  id: string;
107
99
  tenantId: string;
108
100
  createdAt: number;
@@ -110,14 +102,17 @@ export declare const ssoConnectorWithProviderConfigGuard: z.ZodObject<{
110
102
  config: import("@withtyped/server").JsonObject;
111
103
  domains: string[];
112
104
  branding: {
105
+ displayName?: string | undefined;
113
106
  logo?: string | undefined;
114
107
  darkLogo?: string | undefined;
115
108
  };
116
- providerName: string;
109
+ providerName: SsoProviderName;
117
110
  connectorName: string;
118
111
  providerLogo: string;
112
+ providerLogoDark: string;
119
113
  providerConfig?: Record<string, unknown> | undefined;
120
114
  }, {
115
+ name: string;
121
116
  id: string;
122
117
  tenantId: string;
123
118
  createdAt: number;
@@ -125,12 +120,14 @@ export declare const ssoConnectorWithProviderConfigGuard: z.ZodObject<{
125
120
  config: import("@withtyped/server").JsonObject;
126
121
  domains: string[];
127
122
  branding: {
123
+ displayName?: string | undefined;
128
124
  logo?: string | undefined;
129
125
  darkLogo?: string | undefined;
130
126
  };
131
- providerName: string;
127
+ providerName: SsoProviderName;
132
128
  connectorName: string;
133
129
  providerLogo: string;
130
+ providerLogoDark: string;
134
131
  providerConfig?: Record<string, unknown> | undefined;
135
132
  }>;
136
133
  export type SsoConnectorWithProviderConfig = z.infer<typeof ssoConnectorWithProviderConfigGuard>;
@@ -9,16 +9,50 @@ export const ssoConnectorMetadataGuard = z.object({
9
9
  logo: z.string(),
10
10
  darkLogo: z.string().optional(),
11
11
  });
12
- const ssoConnectorFactoryDetailGuard = z.object({
13
- providerName: z.string(),
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),
14
43
  logo: z.string(),
44
+ logoDark: z.string(),
15
45
  description: z.string(),
46
+ name: z.string(),
16
47
  });
17
- export const ssoConnectorFactoriesResponseGuard = z.object({
18
- standardConnectors: z.array(ssoConnectorFactoryDetailGuard),
19
- providerConnectors: z.array(ssoConnectorFactoryDetailGuard),
20
- });
21
- export const ssoConnectorWithProviderConfigGuard = SsoConnectors.guard.merge(z.object({
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),
22
55
  providerLogo: z.string(),
56
+ providerLogoDark: z.string(),
23
57
  providerConfig: z.record(z.unknown()).optional(),
24
58
  }));
@@ -1,6 +1,9 @@
1
1
  export var TenantTag;
2
2
  (function (TenantTag) {
3
+ /* Development tenants are free to use but are not meant to be used as production environment */
3
4
  TenantTag["Development"] = "development";
5
+ /* @deprecated */
4
6
  TenantTag["Staging"] = "staging";
7
+ /* A production tenant must have an associated subscription plan, even if it's a free plan */
5
8
  TenantTag["Production"] = "production";
6
9
  })(TenantTag || (TenantTag = {}));
@@ -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;
@@ -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;
@@ -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<{
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.11.0",
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,10 +65,10 @@
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.7.0",
71
- "@logto/phrases-experience": "^1.4.0",
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
  },
@@ -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
  );
@@ -4,7 +4,7 @@ create table sso_connectors (
4
4
  references tenants (id) on update cascade on delete cascade,
5
5
  /** The globally unique identifier of the SSO connector. */
6
6
  id varchar(128) not null,
7
- /** The connector factory name of the SSO provider. */
7
+ /** The identifier of connector's SSO provider */
8
8
  provider_name varchar(128) not null,
9
9
  /** The name of the SSO provider for display. */
10
10
  connector_name varchar(128) not null,
@@ -18,7 +18,9 @@ create table sso_connectors (
18
18
  sync_profile boolean not null default FALSE,
19
19
  /** When the SSO connector was created. */
20
20
  created_at timestamptz not null default(now()),
21
- primary key (id)
21
+ primary key (id),
22
+ constraint sso_connectors__connector_name__unique
23
+ unique (tenant_id, connector_name)
22
24
  );
23
25
 
24
26
  create index sso_connectors__id