@logto/schemas 1.35.0 → 1.36.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.36.0-1767193412-allow-token-exchange.ts +34 -0
- package/alterations/1.36.0-1767859553-passkey-sign-in.ts +21 -0
- package/alterations/1.36.0-1768192304-enable-account-center-for-admin-tenant.ts +32 -0
- package/alterations/1.36.0-1768464306-enable-mfa-for-admin-tenant.ts +30 -0
- package/alterations/1.36.0-1768758295-add-user-geo-location.ts +32 -0
- package/alterations/1.36.0-1768891516-add-user-sign-in-countries-table.ts +33 -0
- package/alterations/1.36.0-1769067642-add-adaptive-mfa-configuration.ts +19 -0
- package/alterations/1.36.0-1769172677-enable-organization-mfa-policy-for-admin-tenant.ts +31 -0
- package/alterations-js/1.36.0-1767193412-allow-token-exchange.js +30 -0
- package/alterations-js/1.36.0-1767859553-passkey-sign-in.js +17 -0
- package/alterations-js/1.36.0-1768192304-enable-account-center-for-admin-tenant.js +27 -0
- package/alterations-js/1.36.0-1768464306-enable-mfa-for-admin-tenant.js +25 -0
- package/alterations-js/1.36.0-1768758295-add-user-geo-location.js +27 -0
- package/alterations-js/1.36.0-1768891516-add-user-sign-in-countries-table.js +28 -0
- package/alterations-js/1.36.0-1769067642-add-adaptive-mfa-configuration.js +15 -0
- package/alterations-js/1.36.0-1769172677-enable-organization-mfa-policy-for-admin-tenant.js +26 -0
- package/lib/db-entries/index.d.ts +2 -0
- package/lib/db-entries/index.js +2 -0
- package/lib/db-entries/sign-in-experience.d.ts +6 -2
- package/lib/db-entries/sign-in-experience.js +9 -1
- package/lib/db-entries/user-geo-location.d.ts +24 -0
- package/lib/db-entries/user-geo-location.js +37 -0
- package/lib/db-entries/user-sign-in-country.d.ts +24 -0
- package/lib/db-entries/user-sign-in-country.js +33 -0
- package/lib/foundations/jsonb-types/account-centers.d.ts +2 -2
- package/lib/foundations/jsonb-types/logs.d.ts +703 -0
- package/lib/foundations/jsonb-types/logs.js +52 -0
- package/lib/foundations/jsonb-types/oidc-module.d.ts +15 -3
- package/lib/foundations/jsonb-types/oidc-module.js +15 -3
- package/lib/foundations/jsonb-types/saml-application-configs.d.ts +1 -1
- package/lib/foundations/jsonb-types/sentinel.d.ts +13 -1
- package/lib/foundations/jsonb-types/sentinel.js +12 -0
- package/lib/foundations/jsonb-types/sign-in-experience.d.ts +59 -0
- package/lib/foundations/jsonb-types/sign-in-experience.js +11 -0
- package/lib/seeds/account-center.d.ts +6 -0
- package/lib/seeds/account-center.js +24 -0
- package/lib/seeds/cloud-api.d.ts +3 -1
- package/lib/seeds/cloud-api.js +2 -0
- package/lib/seeds/sign-in-experience.js +6 -1
- package/lib/types/application.d.ts +6 -0
- package/lib/types/consent.d.ts +4 -0
- package/lib/types/custom-profile-fields.d.ts +3 -3
- package/lib/types/hook.d.ts +2 -2
- package/lib/types/interactions.d.ts +2 -2
- package/lib/types/log/index.d.ts +12 -6
- package/lib/types/log/interaction.d.ts +5 -1
- package/lib/types/logto-config/index.d.ts +9 -9
- package/lib/types/logto-config/jwt-customizer.d.ts +17 -17
- package/lib/types/saml-application.d.ts +7 -7
- package/lib/types/sign-in-experience.d.ts +6 -0
- package/lib/types/user.d.ts +7 -7
- package/package.json +5 -5
- package/tables/sign_in_experiences.sql +2 -0
- package/tables/user_geo_locations.sql +14 -0
- package/tables/user_sign_in_countries.sql +16 -0
- package/tables/users.sql +3 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const alteration: AlterationScript = {
|
|
6
|
+
up: async (pool) => {
|
|
7
|
+
/**
|
|
8
|
+
* For backward compatibility, set allowTokenExchange = true for existing first-party
|
|
9
|
+
* Traditional, Native, and SPA applications.
|
|
10
|
+
* M2M applications were never allowed to use token exchange before this feature.
|
|
11
|
+
*
|
|
12
|
+
* The admin-console should keep token exchange disabled, so we skip it here.
|
|
13
|
+
*/
|
|
14
|
+
await pool.query(sql`
|
|
15
|
+
update applications
|
|
16
|
+
set custom_client_metadata = custom_client_metadata || '{"allowTokenExchange": true}'::jsonb
|
|
17
|
+
where is_third_party = false
|
|
18
|
+
and type in ('Traditional', 'Native', 'SPA')
|
|
19
|
+
and id != 'admin-console';
|
|
20
|
+
`);
|
|
21
|
+
},
|
|
22
|
+
down: async (pool) => {
|
|
23
|
+
// Remove allowTokenExchange only from applications that were updated in the `up` migration
|
|
24
|
+
await pool.query(sql`
|
|
25
|
+
update applications
|
|
26
|
+
set custom_client_metadata = custom_client_metadata - 'allowTokenExchange'
|
|
27
|
+
where is_third_party = false
|
|
28
|
+
and type in ('Traditional', 'Native', 'SPA')
|
|
29
|
+
and id != 'admin-console';
|
|
30
|
+
`);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default alteration;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { sql } from '@silverhand/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 passkey_sign_in jsonb /* @use PasskeySignIn */ not null default '{}'::jsonb;
|
|
10
|
+
create index users_mfa_verifications_gin on users using gin (mfa_verifications jsonb_path_ops);
|
|
11
|
+
`);
|
|
12
|
+
},
|
|
13
|
+
down: async (pool) => {
|
|
14
|
+
await pool.query(sql`
|
|
15
|
+
alter table sign_in_experiences drop column passkey_sign_in;
|
|
16
|
+
drop index users_mfa_verifications_gin;
|
|
17
|
+
`);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default alteration;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const adminTenantId = 'admin';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Enable the account center for the admin tenant and set all fields to Edit.
|
|
9
|
+
* This allows the console profile page to use the Account API.
|
|
10
|
+
*/
|
|
11
|
+
const alteration: AlterationScript = {
|
|
12
|
+
up: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
update account_centers
|
|
15
|
+
set enabled = true,
|
|
16
|
+
fields = '{"name": "Edit", "avatar": "Edit", "profile": "Edit", "email": "Edit", "phone": "Edit", "password": "Edit", "username": "Edit", "social": "Edit", "customData": "Edit", "mfa": "Edit"}'::jsonb
|
|
17
|
+
where tenant_id = ${adminTenantId}
|
|
18
|
+
and id = 'default'
|
|
19
|
+
`);
|
|
20
|
+
},
|
|
21
|
+
down: async (pool) => {
|
|
22
|
+
await pool.query(sql`
|
|
23
|
+
update account_centers
|
|
24
|
+
set enabled = false,
|
|
25
|
+
fields = '{}'::jsonb
|
|
26
|
+
where tenant_id = ${adminTenantId}
|
|
27
|
+
and id = 'default'
|
|
28
|
+
`);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default alteration;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const adminTenantId = 'admin';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Enable MFA (TOTP and WebAuthn) for the admin tenant with NoPrompt policy.
|
|
9
|
+
* This allows users to optionally set up MFA via the account center.
|
|
10
|
+
*/
|
|
11
|
+
const alteration: AlterationScript = {
|
|
12
|
+
up: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
update sign_in_experiences
|
|
15
|
+
set mfa = '{"factors":["Totp","WebAuthn","BackupCode"],"policy":"NoPrompt"}'::jsonb
|
|
16
|
+
where tenant_id = ${adminTenantId}
|
|
17
|
+
and id = 'default'
|
|
18
|
+
`);
|
|
19
|
+
},
|
|
20
|
+
down: async (pool) => {
|
|
21
|
+
await pool.query(sql`
|
|
22
|
+
update sign_in_experiences
|
|
23
|
+
set mfa = '{"factors":[],"policy":"UserControlled"}'::jsonb
|
|
24
|
+
where tenant_id = ${adminTenantId}
|
|
25
|
+
and id = 'default'
|
|
26
|
+
`);
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default alteration;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
6
|
+
|
|
7
|
+
const alteration: AlterationScript = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
await pool.query(sql`
|
|
10
|
+
create table user_geo_locations (
|
|
11
|
+
tenant_id varchar(21) not null
|
|
12
|
+
references tenants (id) on update cascade on delete cascade,
|
|
13
|
+
user_id varchar(12) not null
|
|
14
|
+
references users (id) on update cascade on delete cascade,
|
|
15
|
+
latitude numeric(9,6),
|
|
16
|
+
longitude numeric(9,6),
|
|
17
|
+
updated_at timestamptz not null default now(),
|
|
18
|
+
primary key (tenant_id, user_id),
|
|
19
|
+
check ((latitude is null) = (longitude is null))
|
|
20
|
+
);
|
|
21
|
+
`);
|
|
22
|
+
await applyTableRls(pool, 'user_geo_locations');
|
|
23
|
+
},
|
|
24
|
+
down: async (pool) => {
|
|
25
|
+
await dropTableRls(pool, 'user_geo_locations');
|
|
26
|
+
await pool.query(sql`
|
|
27
|
+
drop table user_geo_locations;
|
|
28
|
+
`);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default alteration;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
6
|
+
|
|
7
|
+
const alteration: AlterationScript = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
await pool.query(sql`
|
|
10
|
+
create table user_sign_in_countries (
|
|
11
|
+
tenant_id varchar(21) not null
|
|
12
|
+
references tenants (id) on update cascade on delete cascade,
|
|
13
|
+
user_id varchar(12) not null
|
|
14
|
+
references users (id) on update cascade on delete cascade,
|
|
15
|
+
country varchar(16) not null,
|
|
16
|
+
last_sign_in_at timestamptz not null default(now()),
|
|
17
|
+
primary key (tenant_id, user_id, country)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
create index user_sign_in_countries__tenant_user_last_sign_in_at
|
|
21
|
+
on user_sign_in_countries (tenant_id, user_id, last_sign_in_at desc);
|
|
22
|
+
`);
|
|
23
|
+
await applyTableRls(pool, 'user_sign_in_countries');
|
|
24
|
+
},
|
|
25
|
+
down: async (pool) => {
|
|
26
|
+
await dropTableRls(pool, 'user_sign_in_countries');
|
|
27
|
+
await pool.query(sql`
|
|
28
|
+
drop table user_sign_in_countries;
|
|
29
|
+
`);
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default alteration;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { sql } from '@silverhand/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 adaptive_mfa jsonb not null default '{}'::jsonb;
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
down: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
alter table sign_in_experiences drop column adaptive_mfa;
|
|
15
|
+
`);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default alteration;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const adminTenantId = 'admin';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Enable organization required MFA policy for the admin tenant.
|
|
9
|
+
* This allows tenant admins to require MFA for all tenant members
|
|
10
|
+
* by setting `isMfaRequired: true` on the organization.
|
|
11
|
+
*/
|
|
12
|
+
const alteration: AlterationScript = {
|
|
13
|
+
up: async (pool) => {
|
|
14
|
+
await pool.query(sql`
|
|
15
|
+
update sign_in_experiences
|
|
16
|
+
set mfa = jsonb_set(mfa, '{organizationRequiredMfaPolicy}', '"Mandatory"')
|
|
17
|
+
where tenant_id = ${adminTenantId}
|
|
18
|
+
and id = 'default'
|
|
19
|
+
`);
|
|
20
|
+
},
|
|
21
|
+
down: async (pool) => {
|
|
22
|
+
await pool.query(sql`
|
|
23
|
+
update sign_in_experiences
|
|
24
|
+
set mfa = mfa - 'organizationRequiredMfaPolicy'
|
|
25
|
+
where tenant_id = ${adminTenantId}
|
|
26
|
+
and id = 'default'
|
|
27
|
+
`);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default alteration;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
up: async (pool) => {
|
|
4
|
+
/**
|
|
5
|
+
* For backward compatibility, set allowTokenExchange = true for existing first-party
|
|
6
|
+
* Traditional, Native, and SPA applications.
|
|
7
|
+
* M2M applications were never allowed to use token exchange before this feature.
|
|
8
|
+
*
|
|
9
|
+
* The admin-console should keep token exchange disabled, so we skip it here.
|
|
10
|
+
*/
|
|
11
|
+
await pool.query(sql `
|
|
12
|
+
update applications
|
|
13
|
+
set custom_client_metadata = custom_client_metadata || '{"allowTokenExchange": true}'::jsonb
|
|
14
|
+
where is_third_party = false
|
|
15
|
+
and type in ('Traditional', 'Native', 'SPA')
|
|
16
|
+
and id != 'admin-console';
|
|
17
|
+
`);
|
|
18
|
+
},
|
|
19
|
+
down: async (pool) => {
|
|
20
|
+
// Remove allowTokenExchange only from applications that were updated in the `up` migration
|
|
21
|
+
await pool.query(sql `
|
|
22
|
+
update applications
|
|
23
|
+
set custom_client_metadata = custom_client_metadata - 'allowTokenExchange'
|
|
24
|
+
where is_third_party = false
|
|
25
|
+
and type in ('Traditional', 'Native', 'SPA')
|
|
26
|
+
and id != 'admin-console';
|
|
27
|
+
`);
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export default alteration;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
up: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
alter table sign_in_experiences
|
|
6
|
+
add column passkey_sign_in jsonb /* @use PasskeySignIn */ not null default '{}'::jsonb;
|
|
7
|
+
create index users_mfa_verifications_gin on users using gin (mfa_verifications jsonb_path_ops);
|
|
8
|
+
`);
|
|
9
|
+
},
|
|
10
|
+
down: async (pool) => {
|
|
11
|
+
await pool.query(sql `
|
|
12
|
+
alter table sign_in_experiences drop column passkey_sign_in;
|
|
13
|
+
drop index users_mfa_verifications_gin;
|
|
14
|
+
`);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
export default alteration;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const adminTenantId = 'admin';
|
|
3
|
+
/**
|
|
4
|
+
* Enable the account center for the admin tenant and set all fields to Edit.
|
|
5
|
+
* This allows the console profile page to use the Account API.
|
|
6
|
+
*/
|
|
7
|
+
const alteration = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
await pool.query(sql `
|
|
10
|
+
update account_centers
|
|
11
|
+
set enabled = true,
|
|
12
|
+
fields = '{"name": "Edit", "avatar": "Edit", "profile": "Edit", "email": "Edit", "phone": "Edit", "password": "Edit", "username": "Edit", "social": "Edit", "customData": "Edit", "mfa": "Edit"}'::jsonb
|
|
13
|
+
where tenant_id = ${adminTenantId}
|
|
14
|
+
and id = 'default'
|
|
15
|
+
`);
|
|
16
|
+
},
|
|
17
|
+
down: async (pool) => {
|
|
18
|
+
await pool.query(sql `
|
|
19
|
+
update account_centers
|
|
20
|
+
set enabled = false,
|
|
21
|
+
fields = '{}'::jsonb
|
|
22
|
+
where tenant_id = ${adminTenantId}
|
|
23
|
+
and id = 'default'
|
|
24
|
+
`);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export default alteration;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const adminTenantId = 'admin';
|
|
3
|
+
/**
|
|
4
|
+
* Enable MFA (TOTP and WebAuthn) for the admin tenant with NoPrompt policy.
|
|
5
|
+
* This allows users to optionally set up MFA via the account center.
|
|
6
|
+
*/
|
|
7
|
+
const alteration = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
await pool.query(sql `
|
|
10
|
+
update sign_in_experiences
|
|
11
|
+
set mfa = '{"factors":["Totp","WebAuthn","BackupCode"],"policy":"NoPrompt"}'::jsonb
|
|
12
|
+
where tenant_id = ${adminTenantId}
|
|
13
|
+
and id = 'default'
|
|
14
|
+
`);
|
|
15
|
+
},
|
|
16
|
+
down: async (pool) => {
|
|
17
|
+
await pool.query(sql `
|
|
18
|
+
update sign_in_experiences
|
|
19
|
+
set mfa = '{"factors":[],"policy":"UserControlled"}'::jsonb
|
|
20
|
+
where tenant_id = ${adminTenantId}
|
|
21
|
+
and id = 'default'
|
|
22
|
+
`);
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
export default alteration;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
3
|
+
const alteration = {
|
|
4
|
+
up: async (pool) => {
|
|
5
|
+
await pool.query(sql `
|
|
6
|
+
create table user_geo_locations (
|
|
7
|
+
tenant_id varchar(21) not null
|
|
8
|
+
references tenants (id) on update cascade on delete cascade,
|
|
9
|
+
user_id varchar(12) not null
|
|
10
|
+
references users (id) on update cascade on delete cascade,
|
|
11
|
+
latitude numeric(9,6),
|
|
12
|
+
longitude numeric(9,6),
|
|
13
|
+
updated_at timestamptz not null default now(),
|
|
14
|
+
primary key (tenant_id, user_id),
|
|
15
|
+
check ((latitude is null) = (longitude is null))
|
|
16
|
+
);
|
|
17
|
+
`);
|
|
18
|
+
await applyTableRls(pool, 'user_geo_locations');
|
|
19
|
+
},
|
|
20
|
+
down: async (pool) => {
|
|
21
|
+
await dropTableRls(pool, 'user_geo_locations');
|
|
22
|
+
await pool.query(sql `
|
|
23
|
+
drop table user_geo_locations;
|
|
24
|
+
`);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export default alteration;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
3
|
+
const alteration = {
|
|
4
|
+
up: async (pool) => {
|
|
5
|
+
await pool.query(sql `
|
|
6
|
+
create table user_sign_in_countries (
|
|
7
|
+
tenant_id varchar(21) not null
|
|
8
|
+
references tenants (id) on update cascade on delete cascade,
|
|
9
|
+
user_id varchar(12) not null
|
|
10
|
+
references users (id) on update cascade on delete cascade,
|
|
11
|
+
country varchar(16) not null,
|
|
12
|
+
last_sign_in_at timestamptz not null default(now()),
|
|
13
|
+
primary key (tenant_id, user_id, country)
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
create index user_sign_in_countries__tenant_user_last_sign_in_at
|
|
17
|
+
on user_sign_in_countries (tenant_id, user_id, last_sign_in_at desc);
|
|
18
|
+
`);
|
|
19
|
+
await applyTableRls(pool, 'user_sign_in_countries');
|
|
20
|
+
},
|
|
21
|
+
down: async (pool) => {
|
|
22
|
+
await dropTableRls(pool, 'user_sign_in_countries');
|
|
23
|
+
await pool.query(sql `
|
|
24
|
+
drop table user_sign_in_countries;
|
|
25
|
+
`);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
export default alteration;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
up: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
alter table sign_in_experiences
|
|
6
|
+
add column adaptive_mfa jsonb not null default '{}'::jsonb;
|
|
7
|
+
`);
|
|
8
|
+
},
|
|
9
|
+
down: async (pool) => {
|
|
10
|
+
await pool.query(sql `
|
|
11
|
+
alter table sign_in_experiences drop column adaptive_mfa;
|
|
12
|
+
`);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
export default alteration;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const adminTenantId = 'admin';
|
|
3
|
+
/**
|
|
4
|
+
* Enable organization required MFA policy for the admin tenant.
|
|
5
|
+
* This allows tenant admins to require MFA for all tenant members
|
|
6
|
+
* by setting `isMfaRequired: true` on the organization.
|
|
7
|
+
*/
|
|
8
|
+
const alteration = {
|
|
9
|
+
up: async (pool) => {
|
|
10
|
+
await pool.query(sql `
|
|
11
|
+
update sign_in_experiences
|
|
12
|
+
set mfa = jsonb_set(mfa, '{organizationRequiredMfaPolicy}', '"Mandatory"')
|
|
13
|
+
where tenant_id = ${adminTenantId}
|
|
14
|
+
and id = 'default'
|
|
15
|
+
`);
|
|
16
|
+
},
|
|
17
|
+
down: async (pool) => {
|
|
18
|
+
await pool.query(sql `
|
|
19
|
+
update sign_in_experiences
|
|
20
|
+
set mfa = mfa - 'organizationRequiredMfaPolicy'
|
|
21
|
+
where tenant_id = ${adminTenantId}
|
|
22
|
+
and id = 'default'
|
|
23
|
+
`);
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
export default alteration;
|
|
@@ -62,6 +62,8 @@ export * from './sso-connector-idp-initiated-auth-config.js';
|
|
|
62
62
|
export * from './sso-connector.js';
|
|
63
63
|
export * from './subject-token.js';
|
|
64
64
|
export * from './system.js';
|
|
65
|
+
export * from './user-geo-location.js';
|
|
66
|
+
export * from './user-sign-in-country.js';
|
|
65
67
|
export * from './user-sso-identity.js';
|
|
66
68
|
export * from './user.js';
|
|
67
69
|
export * from './users-role.js';
|
package/lib/db-entries/index.js
CHANGED
|
@@ -63,6 +63,8 @@ export * from './sso-connector-idp-initiated-auth-config.js';
|
|
|
63
63
|
export * from './sso-connector.js';
|
|
64
64
|
export * from './subject-token.js';
|
|
65
65
|
export * from './system.js';
|
|
66
|
+
export * from './user-geo-location.js';
|
|
67
|
+
export * from './user-sign-in-country.js';
|
|
66
68
|
export * from './user-sso-identity.js';
|
|
67
69
|
export * from './user.js';
|
|
68
70
|
export * from './users-role.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Color, Branding, LanguageInfo, SignIn, SignUp, SocialSignIn, ConnectorTargets, CustomContent, CustomUiAssets, PartialPasswordPolicy, Mfa, CaptchaPolicy, SentinelPolicy, EmailBlocklistPolicy, ForgotPasswordMethods, GeneratedSchema } from './../foundations/index.js';
|
|
1
|
+
import { Color, Branding, LanguageInfo, SignIn, SignUp, SocialSignIn, ConnectorTargets, CustomContent, CustomUiAssets, PartialPasswordPolicy, Mfa, AdaptiveMfa, CaptchaPolicy, SentinelPolicy, EmailBlocklistPolicy, ForgotPasswordMethods, PasskeySignIn, GeneratedSchema } from './../foundations/index.js';
|
|
2
2
|
import { AgreeToTermsPolicy, SignInMode } from './custom-types.js';
|
|
3
3
|
/**
|
|
4
4
|
*
|
|
@@ -26,6 +26,7 @@ export type CreateSignInExperience = {
|
|
|
26
26
|
customUiAssets?: CustomUiAssets | null;
|
|
27
27
|
passwordPolicy?: PartialPasswordPolicy;
|
|
28
28
|
mfa?: Mfa;
|
|
29
|
+
adaptiveMfa?: AdaptiveMfa;
|
|
29
30
|
singleSignOnEnabled?: boolean;
|
|
30
31
|
supportEmail?: string | null;
|
|
31
32
|
supportWebsiteUrl?: string | null;
|
|
@@ -34,6 +35,7 @@ export type CreateSignInExperience = {
|
|
|
34
35
|
sentinelPolicy?: SentinelPolicy;
|
|
35
36
|
emailBlocklistPolicy?: EmailBlocklistPolicy;
|
|
36
37
|
forgotPasswordMethods?: ForgotPasswordMethods | null;
|
|
38
|
+
passkeySignIn?: PasskeySignIn;
|
|
37
39
|
};
|
|
38
40
|
export type SignInExperience = {
|
|
39
41
|
tenantId: string;
|
|
@@ -56,6 +58,7 @@ export type SignInExperience = {
|
|
|
56
58
|
customUiAssets: CustomUiAssets | null;
|
|
57
59
|
passwordPolicy: PartialPasswordPolicy;
|
|
58
60
|
mfa: Mfa;
|
|
61
|
+
adaptiveMfa: AdaptiveMfa;
|
|
59
62
|
singleSignOnEnabled: boolean;
|
|
60
63
|
supportEmail: string | null;
|
|
61
64
|
supportWebsiteUrl: string | null;
|
|
@@ -64,6 +67,7 @@ export type SignInExperience = {
|
|
|
64
67
|
sentinelPolicy: SentinelPolicy;
|
|
65
68
|
emailBlocklistPolicy: EmailBlocklistPolicy;
|
|
66
69
|
forgotPasswordMethods: ForgotPasswordMethods | null;
|
|
70
|
+
passkeySignIn: PasskeySignIn;
|
|
67
71
|
};
|
|
68
|
-
export type SignInExperienceKeys = 'tenantId' | 'id' | 'color' | 'branding' | 'hideLogtoBranding' | 'languageInfo' | 'termsOfUseUrl' | 'privacyPolicyUrl' | 'agreeToTermsPolicy' | 'signIn' | 'signUp' | 'socialSignIn' | 'socialSignInConnectorTargets' | 'signInMode' | 'customCss' | 'customContent' | 'customUiAssets' | 'passwordPolicy' | 'mfa' | 'singleSignOnEnabled' | 'supportEmail' | 'supportWebsiteUrl' | 'unknownSessionRedirectUrl' | 'captchaPolicy' | 'sentinelPolicy' | 'emailBlocklistPolicy' | 'forgotPasswordMethods';
|
|
72
|
+
export type SignInExperienceKeys = 'tenantId' | 'id' | 'color' | 'branding' | 'hideLogtoBranding' | 'languageInfo' | 'termsOfUseUrl' | 'privacyPolicyUrl' | 'agreeToTermsPolicy' | 'signIn' | 'signUp' | 'socialSignIn' | 'socialSignInConnectorTargets' | 'signInMode' | 'customCss' | 'customContent' | 'customUiAssets' | 'passwordPolicy' | 'mfa' | 'adaptiveMfa' | 'singleSignOnEnabled' | 'supportEmail' | 'supportWebsiteUrl' | 'unknownSessionRedirectUrl' | 'captchaPolicy' | 'sentinelPolicy' | 'emailBlocklistPolicy' | 'forgotPasswordMethods' | 'passkeySignIn';
|
|
69
73
|
export declare const SignInExperiences: GeneratedSchema<SignInExperienceKeys, CreateSignInExperience, SignInExperience, 'sign_in_experiences', 'sign_in_experience'>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { colorGuard, brandingGuard, languageInfoGuard, signInGuard, signUpGuard, socialSignInGuard, connectorTargetsGuard, customContentGuard, customUiAssetsGuard, partialPasswordPolicyGuard, mfaGuard, captchaPolicyGuard, sentinelPolicyGuard, emailBlocklistPolicyGuard, forgotPasswordMethodsGuard } from './../foundations/index.js';
|
|
3
|
+
import { colorGuard, brandingGuard, languageInfoGuard, signInGuard, signUpGuard, socialSignInGuard, connectorTargetsGuard, customContentGuard, customUiAssetsGuard, partialPasswordPolicyGuard, mfaGuard, adaptiveMfaGuard, captchaPolicyGuard, sentinelPolicyGuard, emailBlocklistPolicyGuard, forgotPasswordMethodsGuard, passkeySignInGuard } from './../foundations/index.js';
|
|
4
4
|
import { AgreeToTermsPolicy, SignInMode } from './custom-types.js';
|
|
5
5
|
const createGuard = z.object({
|
|
6
6
|
tenantId: z.string().max(21).optional(),
|
|
@@ -22,6 +22,7 @@ const createGuard = z.object({
|
|
|
22
22
|
customUiAssets: customUiAssetsGuard.nullable().optional(),
|
|
23
23
|
passwordPolicy: partialPasswordPolicyGuard.optional(),
|
|
24
24
|
mfa: mfaGuard.optional(),
|
|
25
|
+
adaptiveMfa: adaptiveMfaGuard.optional(),
|
|
25
26
|
singleSignOnEnabled: z.boolean().optional(),
|
|
26
27
|
supportEmail: z.string().nullable().optional(),
|
|
27
28
|
supportWebsiteUrl: z.string().nullable().optional(),
|
|
@@ -30,6 +31,7 @@ const createGuard = z.object({
|
|
|
30
31
|
sentinelPolicy: sentinelPolicyGuard.optional(),
|
|
31
32
|
emailBlocklistPolicy: emailBlocklistPolicyGuard.optional(),
|
|
32
33
|
forgotPasswordMethods: forgotPasswordMethodsGuard.nullable().optional(),
|
|
34
|
+
passkeySignIn: passkeySignInGuard.optional(),
|
|
33
35
|
});
|
|
34
36
|
const guard = z.object({
|
|
35
37
|
tenantId: z.string().max(21),
|
|
@@ -51,6 +53,7 @@ const guard = z.object({
|
|
|
51
53
|
customUiAssets: customUiAssetsGuard.nullable(),
|
|
52
54
|
passwordPolicy: partialPasswordPolicyGuard,
|
|
53
55
|
mfa: mfaGuard,
|
|
56
|
+
adaptiveMfa: adaptiveMfaGuard,
|
|
54
57
|
singleSignOnEnabled: z.boolean(),
|
|
55
58
|
supportEmail: z.string().nullable(),
|
|
56
59
|
supportWebsiteUrl: z.string().nullable(),
|
|
@@ -59,6 +62,7 @@ const guard = z.object({
|
|
|
59
62
|
sentinelPolicy: sentinelPolicyGuard,
|
|
60
63
|
emailBlocklistPolicy: emailBlocklistPolicyGuard,
|
|
61
64
|
forgotPasswordMethods: forgotPasswordMethodsGuard.nullable(),
|
|
65
|
+
passkeySignIn: passkeySignInGuard,
|
|
62
66
|
});
|
|
63
67
|
export const SignInExperiences = Object.freeze({
|
|
64
68
|
table: 'sign_in_experiences',
|
|
@@ -83,6 +87,7 @@ export const SignInExperiences = Object.freeze({
|
|
|
83
87
|
customUiAssets: 'custom_ui_assets',
|
|
84
88
|
passwordPolicy: 'password_policy',
|
|
85
89
|
mfa: 'mfa',
|
|
90
|
+
adaptiveMfa: 'adaptive_mfa',
|
|
86
91
|
singleSignOnEnabled: 'single_sign_on_enabled',
|
|
87
92
|
supportEmail: 'support_email',
|
|
88
93
|
supportWebsiteUrl: 'support_website_url',
|
|
@@ -91,6 +96,7 @@ export const SignInExperiences = Object.freeze({
|
|
|
91
96
|
sentinelPolicy: 'sentinel_policy',
|
|
92
97
|
emailBlocklistPolicy: 'email_blocklist_policy',
|
|
93
98
|
forgotPasswordMethods: 'forgot_password_methods',
|
|
99
|
+
passkeySignIn: 'passkey_sign_in',
|
|
94
100
|
},
|
|
95
101
|
fieldKeys: [
|
|
96
102
|
'tenantId',
|
|
@@ -112,6 +118,7 @@ export const SignInExperiences = Object.freeze({
|
|
|
112
118
|
'customUiAssets',
|
|
113
119
|
'passwordPolicy',
|
|
114
120
|
'mfa',
|
|
121
|
+
'adaptiveMfa',
|
|
115
122
|
'singleSignOnEnabled',
|
|
116
123
|
'supportEmail',
|
|
117
124
|
'supportWebsiteUrl',
|
|
@@ -120,6 +127,7 @@ export const SignInExperiences = Object.freeze({
|
|
|
120
127
|
'sentinelPolicy',
|
|
121
128
|
'emailBlocklistPolicy',
|
|
122
129
|
'forgotPasswordMethods',
|
|
130
|
+
'passkeySignIn',
|
|
123
131
|
],
|
|
124
132
|
createGuard,
|
|
125
133
|
guard,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { GeneratedSchema } from './../foundations/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The last known geo coordinates per user for geo-velocity checks.
|
|
4
|
+
*
|
|
5
|
+
* @remarks This is a type for database creation.
|
|
6
|
+
* @see {@link UserGeoLocation} for the original type.
|
|
7
|
+
*/
|
|
8
|
+
export type CreateUserGeoLocation = {
|
|
9
|
+
tenantId?: string;
|
|
10
|
+
userId: string;
|
|
11
|
+
latitude?: number | null;
|
|
12
|
+
longitude?: number | null;
|
|
13
|
+
updatedAt?: number;
|
|
14
|
+
};
|
|
15
|
+
/** The last known geo coordinates per user for geo-velocity checks. */
|
|
16
|
+
export type UserGeoLocation = {
|
|
17
|
+
tenantId: string;
|
|
18
|
+
userId: string;
|
|
19
|
+
latitude: number | null;
|
|
20
|
+
longitude: number | null;
|
|
21
|
+
updatedAt: number;
|
|
22
|
+
};
|
|
23
|
+
export type UserGeoLocationKeys = 'tenantId' | 'userId' | 'latitude' | 'longitude' | 'updatedAt';
|
|
24
|
+
export declare const UserGeoLocations: GeneratedSchema<UserGeoLocationKeys, CreateUserGeoLocation, UserGeoLocation, 'user_geo_locations', 'user_geo_location'>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
const createGuard = z.object({
|
|
4
|
+
tenantId: z.string().max(21).optional(),
|
|
5
|
+
userId: z.string().min(1).max(12),
|
|
6
|
+
latitude: z.number().nullable().optional(),
|
|
7
|
+
longitude: z.number().nullable().optional(),
|
|
8
|
+
updatedAt: z.number().optional(),
|
|
9
|
+
});
|
|
10
|
+
const guard = z.object({
|
|
11
|
+
tenantId: z.string().max(21),
|
|
12
|
+
userId: z.string().min(1).max(12),
|
|
13
|
+
latitude: z.number().nullable(),
|
|
14
|
+
longitude: z.number().nullable(),
|
|
15
|
+
updatedAt: z.number(),
|
|
16
|
+
});
|
|
17
|
+
export const UserGeoLocations = Object.freeze({
|
|
18
|
+
table: 'user_geo_locations',
|
|
19
|
+
tableSingular: 'user_geo_location',
|
|
20
|
+
fields: {
|
|
21
|
+
tenantId: 'tenant_id',
|
|
22
|
+
userId: 'user_id',
|
|
23
|
+
latitude: 'latitude',
|
|
24
|
+
longitude: 'longitude',
|
|
25
|
+
updatedAt: 'updated_at',
|
|
26
|
+
},
|
|
27
|
+
fieldKeys: [
|
|
28
|
+
'tenantId',
|
|
29
|
+
'userId',
|
|
30
|
+
'latitude',
|
|
31
|
+
'longitude',
|
|
32
|
+
'updatedAt',
|
|
33
|
+
],
|
|
34
|
+
createGuard,
|
|
35
|
+
guard,
|
|
36
|
+
updateGuard: guard.partial(),
|
|
37
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { GeneratedSchema } from './../foundations/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Tracks per-user sign-in countries for adaptive MFA rules.
|
|
4
|
+
*
|
|
5
|
+
* @remarks This is a type for database creation.
|
|
6
|
+
* @see {@link UserSignInCountry} for the original type.
|
|
7
|
+
*/
|
|
8
|
+
export type CreateUserSignInCountry = {
|
|
9
|
+
tenantId?: string;
|
|
10
|
+
userId: string;
|
|
11
|
+
/** ISO 3166-1 alpha-2 country code (2 chars), stored up to 16 chars for robustness. */
|
|
12
|
+
country: string;
|
|
13
|
+
lastSignInAt?: number;
|
|
14
|
+
};
|
|
15
|
+
/** Tracks per-user sign-in countries for adaptive MFA rules. */
|
|
16
|
+
export type UserSignInCountry = {
|
|
17
|
+
tenantId: string;
|
|
18
|
+
userId: string;
|
|
19
|
+
/** ISO 3166-1 alpha-2 country code (2 chars), stored up to 16 chars for robustness. */
|
|
20
|
+
country: string;
|
|
21
|
+
lastSignInAt: number;
|
|
22
|
+
};
|
|
23
|
+
export type UserSignInCountryKeys = 'tenantId' | 'userId' | 'country' | 'lastSignInAt';
|
|
24
|
+
export declare const UserSignInCountries: GeneratedSchema<UserSignInCountryKeys, CreateUserSignInCountry, UserSignInCountry, 'user_sign_in_countries', 'user_sign_in_country'>;
|