@logto/schemas 1.35.0 → 1.37.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/1.37.0-1770295353-add-default-id-token-config.ts +30 -0
- package/alterations/1.37.0-1770361004-add-oidc-model-instances-session-account-id-indexes.ts +37 -0
- package/alterations/1.37.0-1770362227-add-client-id-column-to-oidc-session-extensions-table.ts +20 -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/alterations-js/1.37.0-1770295353-add-default-id-token-config.js +23 -0
- package/alterations-js/1.37.0-1770361004-add-oidc-model-instances-session-account-id-indexes.js +31 -0
- package/alterations-js/1.37.0-1770362227-add-client-id-column-to-oidc-session-extensions-table.js +16 -0
- package/lib/db-entries/index.d.ts +2 -0
- package/lib/db-entries/index.js +2 -0
- package/lib/db-entries/oidc-session-extension.d.ts +3 -1
- package/lib/db-entries/oidc-session-extension.js +4 -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 +3 -0
- package/lib/foundations/jsonb-types/account-centers.js +1 -0
- package/lib/foundations/jsonb-types/custom-profile-fields.d.ts +8 -8
- package/lib/foundations/jsonb-types/hooks.d.ts +4 -3
- package/lib/foundations/jsonb-types/hooks.js +2 -0
- package/lib/foundations/jsonb-types/logs.d.ts +700 -0
- package/lib/foundations/jsonb-types/logs.js +51 -0
- package/lib/foundations/jsonb-types/oidc-module.d.ts +343 -3
- package/lib/foundations/jsonb-types/oidc-module.js +57 -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/logto-config.d.ts +6 -1
- package/lib/seeds/logto-config.js +11 -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 +36 -36
- package/lib/types/hook.d.ts +2 -2
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- 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 +1190 -91
- package/lib/types/logto-config/index.js +9 -0
- package/lib/types/logto-config/jwt-customizer.d.ts +2059 -136
- package/lib/types/logto-config/jwt-customizer.js +22 -2
- package/lib/types/logto-config/jwt-customizer.test.js +27 -1
- package/lib/types/logto-config/oidc-provider.d.ts +8 -8
- package/lib/types/saml-application.d.ts +7 -7
- package/lib/types/sign-in-experience.d.ts +6 -0
- package/lib/types/user-logto-config.d.ts +49 -0
- package/lib/types/user-logto-config.js +23 -0
- package/lib/types/user-sessions.d.ts +3208 -0
- package/lib/types/user-sessions.js +26 -0
- package/lib/types/user.d.ts +7 -7
- package/lib/types/verification-records/verification-type.d.ts +1 -0
- package/lib/types/verification-records/verification-type.js +1 -0
- package/lib/types/verification-records/web-authn-verification.d.ts +145 -8
- package/lib/types/verification-records/web-authn-verification.js +17 -3
- package/package.json +5 -5
- package/tables/oidc_model_instances.sql +7 -0
- package/tables/oidc_session_extensions.sql +1 -0
- 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
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const idTokenConfigKey = 'idToken';
|
|
6
|
+
|
|
7
|
+
const defaultIdTokenConfig = Object.freeze({
|
|
8
|
+
enabledExtendedClaims: ['roles', 'organizations', 'organization_roles'],
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const alteration: AlterationScript = {
|
|
12
|
+
up: async (pool) => {
|
|
13
|
+
const tenants = await pool.any<{ id: string }>(sql`select id from tenants`);
|
|
14
|
+
|
|
15
|
+
for (const { id: tenantId } of tenants) {
|
|
16
|
+
// eslint-disable-next-line no-await-in-loop
|
|
17
|
+
await pool.query(sql`
|
|
18
|
+
insert into logto_configs (tenant_id, key, value)
|
|
19
|
+
values (${tenantId}, ${idTokenConfigKey}, ${sql.jsonb(defaultIdTokenConfig)})
|
|
20
|
+
`);
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
down: async (pool) => {
|
|
24
|
+
await pool.query(sql`
|
|
25
|
+
delete from logto_configs where key = ${idTokenConfigKey}
|
|
26
|
+
`);
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default alteration;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
|
|
3
|
+
import type { AlterationScript } from '../lib/types/alteration.js';
|
|
4
|
+
|
|
5
|
+
const alteration: AlterationScript = {
|
|
6
|
+
beforeUp: async (pool) => {
|
|
7
|
+
// Add index to optimize the query performance for cleaning up expired OIDC model instances.
|
|
8
|
+
await pool.query(sql`
|
|
9
|
+
create index concurrently if not exists oidc_model_instances__expires_at
|
|
10
|
+
on oidc_model_instances (tenant_id, expires_at);
|
|
11
|
+
`);
|
|
12
|
+
|
|
13
|
+
// Add index to optimize the query performance for querying non-expired session instances by accountId.
|
|
14
|
+
await pool.query(sql`
|
|
15
|
+
create index concurrently if not exists oidc_model_instances__session_payload_account_id_expires_at
|
|
16
|
+
on oidc_model_instances (tenant_id, (payload->>'accountId'), expires_at)
|
|
17
|
+
WHERE model_name = 'Session';
|
|
18
|
+
`);
|
|
19
|
+
},
|
|
20
|
+
up: async () => {
|
|
21
|
+
/** 'concurrently' cannot be used inside a transaction, so this up is intentionally left empty. */
|
|
22
|
+
},
|
|
23
|
+
beforeDown: async (pool) => {
|
|
24
|
+
await pool.query(sql`
|
|
25
|
+
drop index concurrently if exists oidc_model_instances__expires_at;
|
|
26
|
+
`);
|
|
27
|
+
|
|
28
|
+
await pool.query(sql`
|
|
29
|
+
drop index concurrently if exists oidc_model_instances__session_payload_account_id_expires_at;
|
|
30
|
+
`);
|
|
31
|
+
},
|
|
32
|
+
down: async () => {
|
|
33
|
+
/** 'concurrently' cannot be used inside a transaction, so this up is intentionally left empty. */
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default alteration;
|
package/alterations/1.37.0-1770362227-add-client-id-column-to-oidc-session-extensions-table.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
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 oidc_session_extensions
|
|
9
|
+
add column client_id varchar(21) null
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
down: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
alter table oidc_session_extensions
|
|
15
|
+
drop column client_id
|
|
16
|
+
`);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
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;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const idTokenConfigKey = 'idToken';
|
|
3
|
+
const defaultIdTokenConfig = Object.freeze({
|
|
4
|
+
enabledExtendedClaims: ['roles', 'organizations', 'organization_roles'],
|
|
5
|
+
});
|
|
6
|
+
const alteration = {
|
|
7
|
+
up: async (pool) => {
|
|
8
|
+
const tenants = await pool.any(sql `select id from tenants`);
|
|
9
|
+
for (const { id: tenantId } of tenants) {
|
|
10
|
+
// eslint-disable-next-line no-await-in-loop
|
|
11
|
+
await pool.query(sql `
|
|
12
|
+
insert into logto_configs (tenant_id, key, value)
|
|
13
|
+
values (${tenantId}, ${idTokenConfigKey}, ${sql.jsonb(defaultIdTokenConfig)})
|
|
14
|
+
`);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
down: async (pool) => {
|
|
18
|
+
await pool.query(sql `
|
|
19
|
+
delete from logto_configs where key = ${idTokenConfigKey}
|
|
20
|
+
`);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
export default alteration;
|
package/alterations-js/1.37.0-1770361004-add-oidc-model-instances-session-account-id-indexes.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
beforeUp: async (pool) => {
|
|
4
|
+
// Add index to optimize the query performance for cleaning up expired OIDC model instances.
|
|
5
|
+
await pool.query(sql `
|
|
6
|
+
create index concurrently if not exists oidc_model_instances__expires_at
|
|
7
|
+
on oidc_model_instances (tenant_id, expires_at);
|
|
8
|
+
`);
|
|
9
|
+
// Add index to optimize the query performance for querying non-expired session instances by accountId.
|
|
10
|
+
await pool.query(sql `
|
|
11
|
+
create index concurrently if not exists oidc_model_instances__session_payload_account_id_expires_at
|
|
12
|
+
on oidc_model_instances (tenant_id, (payload->>'accountId'), expires_at)
|
|
13
|
+
WHERE model_name = 'Session';
|
|
14
|
+
`);
|
|
15
|
+
},
|
|
16
|
+
up: async () => {
|
|
17
|
+
/** 'concurrently' cannot be used inside a transaction, so this up is intentionally left empty. */
|
|
18
|
+
},
|
|
19
|
+
beforeDown: async (pool) => {
|
|
20
|
+
await pool.query(sql `
|
|
21
|
+
drop index concurrently if exists oidc_model_instances__expires_at;
|
|
22
|
+
`);
|
|
23
|
+
await pool.query(sql `
|
|
24
|
+
drop index concurrently if exists oidc_model_instances__session_payload_account_id_expires_at;
|
|
25
|
+
`);
|
|
26
|
+
},
|
|
27
|
+
down: async () => {
|
|
28
|
+
/** 'concurrently' cannot be used inside a transaction, so this up is intentionally left empty. */
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
export default alteration;
|
package/alterations-js/1.37.0-1770362227-add-client-id-column-to-oidc-session-extensions-table.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
up: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
alter table oidc_session_extensions
|
|
6
|
+
add column client_id varchar(21) null
|
|
7
|
+
`);
|
|
8
|
+
},
|
|
9
|
+
down: async (pool) => {
|
|
10
|
+
await pool.query(sql `
|
|
11
|
+
alter table oidc_session_extensions
|
|
12
|
+
drop column client_id
|
|
13
|
+
`);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
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';
|
|
@@ -9,6 +9,7 @@ export type CreateOidcSessionExtension = {
|
|
|
9
9
|
sessionUid: string;
|
|
10
10
|
accountId: string;
|
|
11
11
|
lastSubmission?: JsonObject;
|
|
12
|
+
clientId?: string | null;
|
|
12
13
|
createdAt?: number;
|
|
13
14
|
updatedAt?: number;
|
|
14
15
|
};
|
|
@@ -17,8 +18,9 @@ export type OidcSessionExtension = {
|
|
|
17
18
|
sessionUid: string;
|
|
18
19
|
accountId: string;
|
|
19
20
|
lastSubmission: JsonObject;
|
|
21
|
+
clientId: string | null;
|
|
20
22
|
createdAt: number;
|
|
21
23
|
updatedAt: number;
|
|
22
24
|
};
|
|
23
|
-
export type OidcSessionExtensionKeys = 'tenantId' | 'sessionUid' | 'accountId' | 'lastSubmission' | 'createdAt' | 'updatedAt';
|
|
25
|
+
export type OidcSessionExtensionKeys = 'tenantId' | 'sessionUid' | 'accountId' | 'lastSubmission' | 'clientId' | 'createdAt' | 'updatedAt';
|
|
24
26
|
export declare const OidcSessionExtensions: GeneratedSchema<OidcSessionExtensionKeys, CreateOidcSessionExtension, OidcSessionExtension, 'oidc_session_extensions', 'oidc_session_extension'>;
|