@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.
- package/alterations/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.ts +18 -0
- package/alterations/1.11.0-1699598903-remove-sso-only-column-in-sso-connectors-table.ts +18 -0
- package/alterations/1.12.0-1700031616-update-org-role-foreign-keys.ts +35 -0
- package/alterations/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.ts +21 -0
- package/alterations/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.ts +20 -0
- package/alterations-js/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.d.ts +3 -0
- package/alterations-js/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.js +14 -0
- package/alterations-js/1.11.0-1699598903-remove-sso-only-column-in-sso-connectors-table.d.ts +3 -0
- package/alterations-js/1.11.0-1699598903-remove-sso-only-column-in-sso-connectors-table.js +14 -0
- package/alterations-js/1.12.0-1700031616-update-org-role-foreign-keys.d.ts +3 -0
- package/alterations-js/1.12.0-1700031616-update-org-role-foreign-keys.js +31 -0
- package/alterations-js/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.d.ts +3 -0
- package/alterations-js/1.12.0-1701054133-add-unique-constraint-to-the-sso-connector-name.js +17 -0
- package/alterations-js/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.d.ts +3 -0
- package/alterations-js/1.12.0-1701245520-add-single-sign-on-enabled-flag-to-sie.js +16 -0
- package/lib/consts/index.d.ts +1 -0
- package/lib/consts/index.js +1 -0
- package/lib/consts/subscriptions.d.ts +6 -0
- package/lib/consts/subscriptions.js +7 -0
- package/lib/db-entries/sign-in-experience.d.ts +3 -1
- package/lib/db-entries/sign-in-experience.js +4 -0
- package/lib/db-entries/sso-connector.d.ts +3 -7
- package/lib/db-entries/sso-connector.js +0 -4
- package/lib/db-entries/user-sso-identity.d.ts +3 -1
- package/lib/db-entries/user-sso-identity.js +4 -0
- package/lib/foundations/jsonb-types/sso-connector.d.ts +3 -0
- package/lib/foundations/jsonb-types/sso-connector.js +1 -0
- package/lib/foundations/jsonb-types/users.d.ts +6 -6
- package/lib/foundations/jsonb-types/users.js +1 -1
- package/lib/models/tenants.d.ts +1 -5
- package/lib/models/tenants.js +1 -6
- package/lib/types/hook.d.ts +1 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/logto-config.d.ts +24 -0
- package/lib/types/logto-config.js +8 -0
- package/lib/types/organization.d.ts +11 -7
- package/lib/types/organization.js +3 -2
- package/lib/types/sso-connector.d.ts +113 -0
- package/lib/types/sso-connector.js +48 -0
- package/lib/types/tenant.d.ts +5 -0
- package/lib/types/tenant.js +9 -0
- package/lib/types/user.d.ts +13 -10
- package/lib/types/user.js +2 -1
- package/lib/utils/domain.d.ts +10 -0
- package/lib/utils/domain.js +28 -0
- package/lib/utils/domain.test.d.ts +1 -0
- package/lib/utils/domain.test.js +34 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/package.json +6 -6
- package/tables/organization_role_user_relations.sql +8 -6
- package/tables/sign_in_experiences.sql +1 -0
- package/tables/sso_connectors.sql +5 -4
- package/tables/user_sso_identities.sql +3 -0
package/alterations/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.ts
ADDED
|
@@ -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;
|
package/alterations-js/1.11.0-1699422979-add-sso-connector-id-col-to-user-sso-identities-table.js
ADDED
|
@@ -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,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,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,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,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;
|
package/lib/consts/index.d.ts
CHANGED
package/lib/consts/index.js
CHANGED
|
@@ -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
|
|
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
|
|
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' | '
|
|
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
|
}>;
|
|
@@ -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.
|
|
6
|
+
details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
7
7
|
}, "strip", z.ZodTypeAny, {
|
|
8
8
|
userId: string;
|
|
9
|
-
details?:
|
|
9
|
+
details?: Record<string, unknown> | undefined;
|
|
10
10
|
}, {
|
|
11
11
|
userId: string;
|
|
12
|
-
details?:
|
|
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.
|
|
16
|
+
details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
17
17
|
}, "strip", z.ZodTypeAny, {
|
|
18
18
|
userId: string;
|
|
19
|
-
details?:
|
|
19
|
+
details?: Record<string, unknown> | undefined;
|
|
20
20
|
}, {
|
|
21
21
|
userId: string;
|
|
22
|
-
details?:
|
|
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.
|
|
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 = {
|
package/lib/models/tenants.d.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import type { InferModelType } from '@withtyped/server/model';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
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;
|
package/lib/models/tenants.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { createModel } from '@withtyped/server/model';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
|
|
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 */
|
package/lib/types/hook.d.ts
CHANGED
|
@@ -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>;
|
package/lib/types/index.d.ts
CHANGED
package/lib/types/index.js
CHANGED
|
@@ -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
|
|
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:
|
|
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 =
|
|
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,
|
|
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 =
|
|
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,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 = {}));
|
package/lib/types/user.d.ts
CHANGED
|
@@ -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?:
|
|
18
|
+
details?: Record<string, unknown> | undefined;
|
|
19
19
|
}>, z.ZodTypeDef, Record<string, {
|
|
20
20
|
userId: string;
|
|
21
|
-
details?:
|
|
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?:
|
|
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?:
|
|
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?:
|
|
187
|
+
details?: Record<string, unknown> | undefined;
|
|
188
188
|
}>, z.ZodTypeDef, Record<string, {
|
|
189
189
|
userId: string;
|
|
190
|
-
details?:
|
|
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?:
|
|
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?:
|
|
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?:
|
|
405
|
+
details?: Record<string, unknown> | undefined;
|
|
403
406
|
}>, z.ZodTypeDef, Record<string, {
|
|
404
407
|
userId: string;
|
|
405
|
-
details?:
|
|
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
|
+
});
|
package/lib/utils/index.d.ts
CHANGED
package/lib/utils/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/schemas",
|
|
3
|
-
"version": "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.
|
|
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.
|
|
68
|
+
"@logto/core-kit": "^2.2.1",
|
|
69
69
|
"@logto/language-kit": "^1.0.0",
|
|
70
|
-
"@logto/phrases": "^1.
|
|
71
|
-
"@logto/phrases-experience": "^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.
|
|
76
|
+
"zod": "^3.22.4"
|
|
77
77
|
},
|
|
78
78
|
"scripts": {
|
|
79
79
|
"precommit": "lint-staged",
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
/* init_order =
|
|
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
|
-
|
|
13
|
-
|
|
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
|
|
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)
|