@logto/schemas 1.41.0 → 1.43.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.42.0-1783916980-add-domain-verification-files.ts +20 -0
- package/alterations/1.42.0-1784619793-add-service-logs-created-at-index.ts +33 -0
- package/alterations/1.42.0-1784620000-add-saml-sso-connector-signing-keys-table.ts +38 -0
- package/alterations/1.43.0-1785393731-add-oidc-model-instances-account-id-partial-index.ts +33 -0
- package/alterations/1.43.0-1785481546-add-tenant-aware-scope-indexes.ts +33 -0
- package/alterations/1.43.0-1785488115-add-cimd-permission-ceiling-tables.ts +77 -0
- package/alterations/1.43.0-1785722831-add-oidc-session-extensions-account-id-index.ts +27 -0
- package/alterations/1.43.0-1785835991-add-cimd-client-identifier-columns.ts +31 -0
- package/alterations/1.43.0-1786325989-add-cimd-grant-organizations-table.ts +54 -0
- package/alterations/1.43.0-1786333495-add-trusted-devices.ts +57 -0
- package/alterations/1.43.0-1786373004-tighten-cimd-grant-organizations-pkey.ts +28 -0
- package/alterations/1.43.0-1786414712-add-service-logs-email-indexes.ts +34 -0
- package/alterations/1.43.0-1786431364-add-cimd-grant-client-snapshots-table.ts +43 -0
- package/alterations/utils/1704934999-tables.ts +22 -1
- package/alterations-js/1.42.0-1783916980-add-domain-verification-files.js +16 -0
- package/alterations-js/1.42.0-1784619793-add-service-logs-created-at-index.js +29 -0
- package/alterations-js/1.42.0-1784620000-add-saml-sso-connector-signing-keys-table.js +33 -0
- package/alterations-js/1.43.0-1785393731-add-oidc-model-instances-account-id-partial-index.js +28 -0
- package/alterations-js/1.43.0-1785481546-add-tenant-aware-scope-indexes.js +28 -0
- package/alterations-js/1.43.0-1785488115-add-cimd-permission-ceiling-tables.js +69 -0
- package/alterations-js/1.43.0-1785722831-add-oidc-session-extensions-account-id-index.js +23 -0
- package/alterations-js/1.43.0-1785835991-add-cimd-client-identifier-columns.js +27 -0
- package/alterations-js/1.43.0-1786325989-add-cimd-grant-organizations-table.js +49 -0
- package/alterations-js/1.43.0-1786333495-add-trusted-devices.js +50 -0
- package/alterations-js/1.43.0-1786373004-tighten-cimd-grant-organizations-pkey.js +24 -0
- package/alterations-js/1.43.0-1786414712-add-service-logs-email-indexes.js +30 -0
- package/alterations-js/1.43.0-1786431364-add-cimd-grant-client-snapshots-table.js +38 -0
- package/alterations-js/utils/1704934999-tables.js +19 -1
- package/lib/consts/experience.d.ts +1 -0
- package/lib/consts/experience.js +1 -0
- package/lib/consts/index.d.ts +1 -0
- package/lib/consts/index.js +1 -0
- package/lib/consts/product-event.d.ts +5 -1
- package/lib/consts/product-event.js +4 -0
- package/lib/consts/social-state.d.ts +12 -0
- package/lib/consts/social-state.js +12 -0
- package/lib/db-entries/cimd-grant-client-snapshot.d.ts +32 -0
- package/lib/db-entries/cimd-grant-client-snapshot.js +45 -0
- package/lib/db-entries/cimd-grant-organization.d.ts +30 -0
- package/lib/db-entries/cimd-grant-organization.js +37 -0
- package/lib/db-entries/cimd-organization-resource-scope.d.ts +20 -0
- package/lib/db-entries/cimd-organization-resource-scope.js +25 -0
- package/lib/db-entries/cimd-organization-scope.d.ts +20 -0
- package/lib/db-entries/cimd-organization-scope.js +25 -0
- package/lib/db-entries/cimd-resource-scope.d.ts +20 -0
- package/lib/db-entries/cimd-resource-scope.js +25 -0
- package/lib/db-entries/cimd-user-scope.d.ts +20 -0
- package/lib/db-entries/cimd-user-scope.js +25 -0
- package/lib/db-entries/domain.d.ts +4 -2
- package/lib/db-entries/domain.js +5 -1
- package/lib/db-entries/index.d.ts +8 -0
- package/lib/db-entries/index.js +8 -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/organization.d.ts +5 -1
- package/lib/db-entries/organization.js +4 -0
- package/lib/db-entries/saml-sso-connector-signing-key.d.ts +32 -0
- package/lib/db-entries/saml-sso-connector-signing-key.js +49 -0
- package/lib/db-entries/sign-in-experience.d.ts +4 -2
- package/lib/db-entries/sign-in-experience.js +5 -1
- package/lib/db-entries/trusted-device.d.ts +36 -0
- package/lib/db-entries/trusted-device.js +62 -0
- package/lib/db-entries/user.d.ts +3 -1
- package/lib/db-entries/user.js +4 -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/account-centers.test.d.ts +1 -0
- package/lib/foundations/jsonb-types/account-centers.test.js +13 -0
- package/lib/foundations/jsonb-types/custom-domain.d.ts +57 -0
- package/lib/foundations/jsonb-types/custom-domain.js +51 -0
- package/lib/foundations/jsonb-types/custom-domain.test.d.ts +1 -0
- package/lib/foundations/jsonb-types/custom-domain.test.js +38 -0
- package/lib/foundations/jsonb-types/hooks.d.ts +9 -5
- package/lib/foundations/jsonb-types/hooks.js +8 -0
- package/lib/foundations/jsonb-types/logs.d.ts +18 -0
- package/lib/foundations/jsonb-types/logs.js +6 -0
- package/lib/foundations/jsonb-types/one-time-tokens.d.ts +5 -0
- package/lib/foundations/jsonb-types/one-time-tokens.js +2 -0
- package/lib/foundations/jsonb-types/sign-in-experience.d.ts +39 -4
- package/lib/foundations/jsonb-types/sign-in-experience.js +10 -0
- package/lib/foundations/jsonb-types/sign-in-experience.test.js +17 -1
- package/lib/types/application.d.ts +131 -1
- package/lib/types/application.js +8 -0
- package/lib/types/connector.d.ts +8 -0
- package/lib/types/consent.d.ts +16 -190
- package/lib/types/consent.js +10 -1
- package/lib/types/custom-profile-fields.d.ts +2 -1
- package/lib/types/domain.d.ts +9 -0
- package/lib/types/hook.d.ts +37 -5
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/interaction-event.d.ts +11 -0
- package/lib/types/interaction-event.js +12 -0
- package/lib/types/interactions.d.ts +2 -11
- package/lib/types/interactions.js +2 -11
- package/lib/types/log/action.d.ts +7 -0
- package/lib/types/log/action.js +6 -0
- package/lib/types/log/index.d.ts +8 -2
- package/lib/types/log/index.js +2 -0
- package/lib/types/log/trusted-device.d.ts +7 -0
- package/lib/types/log/trusted-device.js +6 -0
- package/lib/types/logto-config/action.d.ts +963 -0
- package/lib/types/logto-config/action.js +77 -0
- package/lib/types/logto-config/index.d.ts +68 -26
- package/lib/types/logto-config/index.js +20 -7
- package/lib/types/logto-config/index.test.js +32 -9
- package/lib/types/logto-config/jwt-customizer.d.ts +60 -10
- package/lib/types/logto-config/jwt-customizer.js +1 -1
- package/lib/types/logto-config/oidc-provider.d.ts +2 -2
- package/lib/types/organization.d.ts +136 -1
- package/lib/types/organization.js +3 -2
- package/lib/types/service-log.d.ts +14 -1
- package/lib/types/service-log.js +13 -0
- package/lib/types/sign-in-experience.d.ts +18 -10
- package/lib/types/sign-in-experience.js +2 -1
- package/lib/types/sso-connector.d.ts +58 -0
- package/lib/types/sso-connector.js +12 -0
- package/lib/types/system.d.ts +2 -2
- package/lib/types/trusted-device.d.ts +67 -0
- package/lib/types/trusted-device.js +16 -0
- package/lib/types/user-sessions.d.ts +117 -238
- package/lib/types/user-sessions.js +9 -4
- package/lib/types/user.d.ts +10 -3
- package/lib/types/user.js +1 -0
- package/lib/types/verification-records/one-time-token-verification.d.ts +5 -0
- package/lib/utils/cimd.d.ts +14 -0
- package/lib/utils/cimd.js +14 -0
- package/lib/utils/cimd.test.d.ts +1 -0
- package/lib/utils/cimd.test.js +9 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/package.json +7 -7
- package/tables/cimd_grant_client_snapshots.sql +27 -0
- package/tables/cimd_grant_organizations.sql +38 -0
- package/tables/cimd_organization_resource_scopes.sql +16 -0
- package/tables/cimd_organization_scopes.sql +15 -0
- package/tables/cimd_resource_scopes.sql +15 -0
- package/tables/cimd_user_scopes.sql +13 -0
- package/tables/domains.sql +1 -0
- package/tables/oidc_model_instances.sql +5 -0
- package/tables/oidc_session_extensions.sql +5 -0
- package/tables/organization_scopes.sql +4 -0
- package/tables/organizations.sql +2 -0
- package/tables/saml_sso_connector_signing_keys.sql +21 -0
- package/tables/scopes.sql +4 -0
- package/tables/service_logs.sql +19 -0
- package/tables/sign_in_experiences.sql +1 -0
- package/tables/trusted_devices.sql +25 -0
- package/tables/users.sql +1 -0
- package/lib/types/logto-config/inline-hook.d.ts +0 -76
- package/lib/types/logto-config/inline-hook.js +0 -25
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
beforeUp: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
create unique index concurrently scopes__tenant_id_id
|
|
6
|
+
on scopes (tenant_id, id);
|
|
7
|
+
`);
|
|
8
|
+
await pool.query(sql `
|
|
9
|
+
create unique index concurrently organization_scopes__tenant_id_id
|
|
10
|
+
on organization_scopes (tenant_id, id);
|
|
11
|
+
`);
|
|
12
|
+
},
|
|
13
|
+
up: async () => {
|
|
14
|
+
// The indexes must be created outside of a transaction.
|
|
15
|
+
},
|
|
16
|
+
beforeDown: async (pool) => {
|
|
17
|
+
await pool.query(sql `
|
|
18
|
+
drop index concurrently if exists organization_scopes__tenant_id_id;
|
|
19
|
+
`);
|
|
20
|
+
await pool.query(sql `
|
|
21
|
+
drop index concurrently if exists scopes__tenant_id_id;
|
|
22
|
+
`);
|
|
23
|
+
},
|
|
24
|
+
down: async () => {
|
|
25
|
+
// The indexes must be dropped outside of a transaction.
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
export default alteration;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
3
|
+
/**
|
|
4
|
+
* Add the tenant-level permission ceiling tables for client ID metadata document (CIMD) clients.
|
|
5
|
+
* The ceiling is tenant-wide because CIMD clients are unregistered and have no per-client record
|
|
6
|
+
* to attach configuration to. The tables reference the tenant and scope tables directly (not the
|
|
7
|
+
* CIMD config row), so disabling CIMD does not wipe the permission settings.
|
|
8
|
+
* Rows are an allowlist: an empty table allows nothing (deny-all), and an unrestricted state is not
|
|
9
|
+
* expressible.
|
|
10
|
+
*/
|
|
11
|
+
const alteration = {
|
|
12
|
+
up: async (pool) => {
|
|
13
|
+
await pool.query(sql `
|
|
14
|
+
create table cimd_user_scopes (
|
|
15
|
+
tenant_id varchar(21) not null
|
|
16
|
+
references tenants (id) on update cascade on delete cascade,
|
|
17
|
+
user_scope varchar(64) not null,
|
|
18
|
+
primary key (tenant_id, user_scope)
|
|
19
|
+
);
|
|
20
|
+
`);
|
|
21
|
+
await applyTableRls(pool, 'cimd_user_scopes');
|
|
22
|
+
await pool.query(sql `
|
|
23
|
+
create table cimd_resource_scopes (
|
|
24
|
+
tenant_id varchar(21) not null
|
|
25
|
+
references tenants (id) on update cascade on delete cascade,
|
|
26
|
+
scope_id varchar(21) not null,
|
|
27
|
+
primary key (tenant_id, scope_id),
|
|
28
|
+
foreign key (tenant_id, scope_id)
|
|
29
|
+
references scopes (tenant_id, id) on update cascade on delete cascade
|
|
30
|
+
);
|
|
31
|
+
`);
|
|
32
|
+
await applyTableRls(pool, 'cimd_resource_scopes');
|
|
33
|
+
await pool.query(sql `
|
|
34
|
+
create table cimd_organization_scopes (
|
|
35
|
+
tenant_id varchar(21) not null
|
|
36
|
+
references tenants (id) on update cascade on delete cascade,
|
|
37
|
+
organization_scope_id varchar(21) not null,
|
|
38
|
+
primary key (tenant_id, organization_scope_id),
|
|
39
|
+
foreign key (tenant_id, organization_scope_id)
|
|
40
|
+
references organization_scopes (tenant_id, id) on update cascade on delete cascade
|
|
41
|
+
);
|
|
42
|
+
`);
|
|
43
|
+
await applyTableRls(pool, 'cimd_organization_scopes');
|
|
44
|
+
await pool.query(sql `
|
|
45
|
+
create table cimd_organization_resource_scopes (
|
|
46
|
+
tenant_id varchar(21) not null
|
|
47
|
+
references tenants (id) on update cascade on delete cascade,
|
|
48
|
+
scope_id varchar(21) not null,
|
|
49
|
+
primary key (tenant_id, scope_id),
|
|
50
|
+
foreign key (tenant_id, scope_id)
|
|
51
|
+
references scopes (tenant_id, id) on update cascade on delete cascade
|
|
52
|
+
);
|
|
53
|
+
`);
|
|
54
|
+
await applyTableRls(pool, 'cimd_organization_resource_scopes');
|
|
55
|
+
},
|
|
56
|
+
down: async (pool) => {
|
|
57
|
+
await dropTableRls(pool, 'cimd_organization_resource_scopes');
|
|
58
|
+
await dropTableRls(pool, 'cimd_organization_scopes');
|
|
59
|
+
await dropTableRls(pool, 'cimd_resource_scopes');
|
|
60
|
+
await dropTableRls(pool, 'cimd_user_scopes');
|
|
61
|
+
await pool.query(sql `
|
|
62
|
+
drop table cimd_organization_resource_scopes;
|
|
63
|
+
drop table cimd_organization_scopes;
|
|
64
|
+
drop table cimd_resource_scopes;
|
|
65
|
+
drop table cimd_user_scopes;
|
|
66
|
+
`);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
export default alteration;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
beforeUp: async (pool) => {
|
|
4
|
+
// Add index to optimize deleting session extensions by accountId, including the
|
|
5
|
+
// cascades from the users table. See the index comment in tables/oidc_session_extensions.sql.
|
|
6
|
+
await pool.query(sql `
|
|
7
|
+
create index concurrently oidc_session_extensions__account_id
|
|
8
|
+
on oidc_session_extensions (account_id);
|
|
9
|
+
`);
|
|
10
|
+
},
|
|
11
|
+
up: async () => {
|
|
12
|
+
/** 'concurrently' cannot be used inside a transaction, so this up is intentionally left empty. */
|
|
13
|
+
},
|
|
14
|
+
beforeDown: async (pool) => {
|
|
15
|
+
await pool.query(sql `
|
|
16
|
+
drop index concurrently oidc_session_extensions__account_id;
|
|
17
|
+
`);
|
|
18
|
+
},
|
|
19
|
+
down: async () => {
|
|
20
|
+
/** 'concurrently' cannot be used inside a transaction, so this down is intentionally left empty. */
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
export default alteration;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
/**
|
|
3
|
+
* Registered application IDs (21 chars, referencing `applications`) and CIMD (client ID metadata
|
|
4
|
+
* document) client identifier URLs are different value spaces, so each gets its own column instead
|
|
5
|
+
* of sharing one.
|
|
6
|
+
*/
|
|
7
|
+
const alteration = {
|
|
8
|
+
up: async (pool) => {
|
|
9
|
+
await pool.query(sql `
|
|
10
|
+
alter table users
|
|
11
|
+
add column cimd_client_id varchar(2048);
|
|
12
|
+
|
|
13
|
+
alter table oidc_session_extensions
|
|
14
|
+
add column cimd_client_id varchar(2048);
|
|
15
|
+
`);
|
|
16
|
+
},
|
|
17
|
+
down: async (pool) => {
|
|
18
|
+
await pool.query(sql `
|
|
19
|
+
alter table users
|
|
20
|
+
drop column cimd_client_id;
|
|
21
|
+
|
|
22
|
+
alter table oidc_session_extensions
|
|
23
|
+
drop column cimd_client_id;
|
|
24
|
+
`);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
export default alteration;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
3
|
+
/**
|
|
4
|
+
* Add the grant-scoped organization authorization table for client ID metadata document (CIMD)
|
|
5
|
+
* clients. CIMD clients are unregistered URL identities with no deletion lifecycle and
|
|
6
|
+
* transferable ownership, so organization access follows the single authorization instead of a
|
|
7
|
+
* long-lived cross-grant relation: rows cascade away with the Grant row (revocation hard-deletes
|
|
8
|
+
* it) and with the user's organization membership.
|
|
9
|
+
*/
|
|
10
|
+
const alteration = {
|
|
11
|
+
up: async (pool) => {
|
|
12
|
+
await pool.query(sql `
|
|
13
|
+
create table cimd_grant_organizations (
|
|
14
|
+
tenant_id varchar(21) not null
|
|
15
|
+
references tenants (id) on update cascade on delete cascade,
|
|
16
|
+
grant_id varchar(128) not null,
|
|
17
|
+
grant_model_name varchar(64) not null default 'Grant',
|
|
18
|
+
organization_id varchar(21) not null,
|
|
19
|
+
user_id varchar(21) not null,
|
|
20
|
+
primary key (tenant_id, grant_id, organization_id),
|
|
21
|
+
constraint cimd_grant_organizations__grant_model_name
|
|
22
|
+
check (grant_model_name = 'Grant'),
|
|
23
|
+
foreign key (tenant_id, grant_model_name, grant_id)
|
|
24
|
+
references oidc_model_instances (tenant_id, model_name, id)
|
|
25
|
+
on update cascade on delete cascade,
|
|
26
|
+
foreign key (tenant_id, organization_id, user_id)
|
|
27
|
+
references organization_user_relations (tenant_id, organization_id, user_id)
|
|
28
|
+
on update cascade on delete cascade
|
|
29
|
+
);
|
|
30
|
+
`);
|
|
31
|
+
/**
|
|
32
|
+
* Backs the membership FK's cascade lookup; the PK leaves `organization_id` outside its
|
|
33
|
+
* usable prefix. The table is created empty in this transaction, so a plain (non-concurrent)
|
|
34
|
+
* index build is free.
|
|
35
|
+
*/
|
|
36
|
+
await pool.query(sql `
|
|
37
|
+
create index cimd_grant_organizations__organization_id_user_id
|
|
38
|
+
on cimd_grant_organizations (tenant_id, organization_id, user_id);
|
|
39
|
+
`);
|
|
40
|
+
await applyTableRls(pool, 'cimd_grant_organizations');
|
|
41
|
+
},
|
|
42
|
+
down: async (pool) => {
|
|
43
|
+
await dropTableRls(pool, 'cimd_grant_organizations');
|
|
44
|
+
await pool.query(sql `
|
|
45
|
+
drop table cimd_grant_organizations;
|
|
46
|
+
`);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
export default alteration;
|
|
@@ -0,0 +1,50 @@
|
|
|
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 trusted_devices (
|
|
7
|
+
tenant_id varchar(21) not null
|
|
8
|
+
references tenants (id) on update cascade on delete cascade,
|
|
9
|
+
id varchar(21) not null,
|
|
10
|
+
user_id varchar(12) not null
|
|
11
|
+
references users (id) on update cascade on delete cascade,
|
|
12
|
+
secret_hash bytea not null,
|
|
13
|
+
user_agent text,
|
|
14
|
+
ip text,
|
|
15
|
+
country varchar(16),
|
|
16
|
+
city text,
|
|
17
|
+
created_at timestamptz not null default(now()),
|
|
18
|
+
last_used_at timestamptz not null default(now()),
|
|
19
|
+
expires_at timestamptz not null,
|
|
20
|
+
primary key (id)
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
create index trusted_devices__tenant_user_expires_at
|
|
24
|
+
on trusted_devices (tenant_id, user_id, expires_at);
|
|
25
|
+
|
|
26
|
+
create index trusted_devices__tenant_expires_at
|
|
27
|
+
on trusted_devices (tenant_id, expires_at);
|
|
28
|
+
|
|
29
|
+
alter table sign_in_experiences
|
|
30
|
+
add column trusted_device jsonb not null default '{}'::jsonb;
|
|
31
|
+
|
|
32
|
+
alter table organizations
|
|
33
|
+
add column is_trusted_device_allowed boolean not null default true;
|
|
34
|
+
`);
|
|
35
|
+
await applyTableRls(pool, 'trusted_devices');
|
|
36
|
+
},
|
|
37
|
+
down: async (pool) => {
|
|
38
|
+
await dropTableRls(pool, 'trusted_devices');
|
|
39
|
+
await pool.query(sql `
|
|
40
|
+
drop table trusted_devices;
|
|
41
|
+
|
|
42
|
+
alter table sign_in_experiences
|
|
43
|
+
drop column trusted_device;
|
|
44
|
+
|
|
45
|
+
alter table organizations
|
|
46
|
+
drop column is_trusted_device_allowed;
|
|
47
|
+
`);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
export default alteration;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
/**
|
|
3
|
+
* Tighten the `cimd_grant_organizations` primary key to `(tenant_id, grant_id)` so the schema
|
|
4
|
+
* itself enforces the at-most-one-organization-per-grant invariant that token issuance relies on,
|
|
5
|
+
* instead of leaving it to the consent flow's composition. The table is dev-feature-gated and
|
|
6
|
+
* empty in every environment, so swapping the constraint needs no data migration.
|
|
7
|
+
*/
|
|
8
|
+
const alteration = {
|
|
9
|
+
up: async (pool) => {
|
|
10
|
+
await pool.query(sql `
|
|
11
|
+
alter table cimd_grant_organizations
|
|
12
|
+
drop constraint cimd_grant_organizations_pkey,
|
|
13
|
+
add primary key (tenant_id, grant_id);
|
|
14
|
+
`);
|
|
15
|
+
},
|
|
16
|
+
down: async (pool) => {
|
|
17
|
+
await pool.query(sql `
|
|
18
|
+
alter table cimd_grant_organizations
|
|
19
|
+
drop constraint cimd_grant_organizations_pkey,
|
|
20
|
+
add primary key (tenant_id, grant_id, organization_id);
|
|
21
|
+
`);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export default alteration;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
beforeUp: async (pool) => {
|
|
4
|
+
await pool.query(sql `
|
|
5
|
+
create index concurrently service_logs__email__tenant_id__created_at__id
|
|
6
|
+
on service_logs (tenant_id, created_at desc, id desc)
|
|
7
|
+
where type in ('sendEmail', 'sendEmailFailed');
|
|
8
|
+
`);
|
|
9
|
+
await pool.query(sql `
|
|
10
|
+
create index concurrently service_logs__email__tenant_id__recipient__created_at__id
|
|
11
|
+
on service_logs (tenant_id, lower(payload->'data'->>'to'), created_at desc, id desc)
|
|
12
|
+
where type in ('sendEmail', 'sendEmailFailed');
|
|
13
|
+
`);
|
|
14
|
+
},
|
|
15
|
+
up: async () => {
|
|
16
|
+
/** 'concurrently' cannot be used inside a transaction, so this up is intentionally left empty. */
|
|
17
|
+
},
|
|
18
|
+
beforeDown: async (pool) => {
|
|
19
|
+
await pool.query(sql `
|
|
20
|
+
drop index concurrently service_logs__email__tenant_id__recipient__created_at__id;
|
|
21
|
+
`);
|
|
22
|
+
await pool.query(sql `
|
|
23
|
+
drop index concurrently service_logs__email__tenant_id__created_at__id;
|
|
24
|
+
`);
|
|
25
|
+
},
|
|
26
|
+
down: async () => {
|
|
27
|
+
/** 'concurrently' cannot be used inside a transaction, so this down is intentionally left empty. */
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
export default alteration;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js';
|
|
3
|
+
/**
|
|
4
|
+
* Add the per-grant client display snapshot table for client ID metadata document (CIMD) clients.
|
|
5
|
+
* CIMD clients are unregistered URL identities without an `applications` row, and an unvetted
|
|
6
|
+
* client can rewrite its hosted document at any time — so the grant list renders the snapshot
|
|
7
|
+
* captured at consent instead of refetching the document. Rows cascade away with the Grant row.
|
|
8
|
+
*/
|
|
9
|
+
const alteration = {
|
|
10
|
+
up: async (pool) => {
|
|
11
|
+
await pool.query(sql `
|
|
12
|
+
create table cimd_grant_client_snapshots (
|
|
13
|
+
tenant_id varchar(21) not null
|
|
14
|
+
references tenants (id) on update cascade on delete cascade,
|
|
15
|
+
grant_id varchar(128) not null,
|
|
16
|
+
grant_model_name varchar(64) not null default 'Grant',
|
|
17
|
+
client_id varchar(2048) not null,
|
|
18
|
+
name varchar(256),
|
|
19
|
+
logo_uri varchar(2048),
|
|
20
|
+
created_at timestamptz not null default(now()),
|
|
21
|
+
primary key (tenant_id, grant_id),
|
|
22
|
+
constraint cimd_grant_client_snapshots__grant_model_name
|
|
23
|
+
check (grant_model_name = 'Grant'),
|
|
24
|
+
foreign key (tenant_id, grant_model_name, grant_id)
|
|
25
|
+
references oidc_model_instances (tenant_id, model_name, id)
|
|
26
|
+
on update cascade on delete cascade
|
|
27
|
+
);
|
|
28
|
+
`);
|
|
29
|
+
await applyTableRls(pool, 'cimd_grant_client_snapshots');
|
|
30
|
+
},
|
|
31
|
+
down: async (pool) => {
|
|
32
|
+
await dropTableRls(pool, 'cimd_grant_client_snapshots');
|
|
33
|
+
await pool.query(sql `
|
|
34
|
+
drop table cimd_grant_client_snapshots;
|
|
35
|
+
`);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
export default alteration;
|
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import { sql } from '@silverhand/slonik';
|
|
2
2
|
const getId = (value) => sql.identifier([value]);
|
|
3
|
+
/**
|
|
4
|
+
* Returns the suffix used in the base Logto role name for the current database
|
|
5
|
+
* (`logto_tenant_<suffix>`).
|
|
6
|
+
*
|
|
7
|
+
* Historically the suffix was the raw database name (which may contain hyphens). A later
|
|
8
|
+
* convention normalised it by replacing hyphens with underscores. To remain compatible with
|
|
9
|
+
* both conventions we probe `pg_roles` for the normalised name first and fall back to the
|
|
10
|
+
* raw database name for legacy installations.
|
|
11
|
+
*/
|
|
3
12
|
const getDatabaseName = async (pool) => {
|
|
4
13
|
const { currentDatabase } = await pool.one(sql `
|
|
5
14
|
select current_database();
|
|
6
15
|
`);
|
|
7
|
-
|
|
16
|
+
const normalized = currentDatabase.replaceAll('-', '_');
|
|
17
|
+
if (normalized === currentDatabase) {
|
|
18
|
+
return currentDatabase;
|
|
19
|
+
}
|
|
20
|
+
const { exists } = await pool.one(sql `
|
|
21
|
+
select exists(
|
|
22
|
+
select 1 from pg_roles where rolname = ${'logto_tenant_' + normalized}
|
|
23
|
+
) as exists;
|
|
24
|
+
`);
|
|
25
|
+
return exists ? normalized : currentDatabase;
|
|
8
26
|
};
|
|
9
27
|
/**
|
|
10
28
|
* A function to call after the table is created. It will apply the necessary row-level security
|
|
@@ -10,6 +10,7 @@ export declare const experience: Readonly<{
|
|
|
10
10
|
readonly identifierRegister: "identifier-register";
|
|
11
11
|
readonly switchAccount: "switch-account";
|
|
12
12
|
readonly oneTimeToken: "one-time-token";
|
|
13
|
+
readonly accountSuspended: "account-suspended";
|
|
13
14
|
}>;
|
|
14
15
|
}>;
|
|
15
16
|
/** `postMessage` sender tag from admin Console sign-in experience live preview. */
|
package/lib/consts/experience.js
CHANGED
package/lib/consts/index.d.ts
CHANGED
package/lib/consts/index.js
CHANGED
|
@@ -67,7 +67,11 @@ export declare enum ProductEvent {
|
|
|
67
67
|
MfaEnabled = "mfa enabled",
|
|
68
68
|
MfaDisabled = "mfa disabled",
|
|
69
69
|
CustomDomainCreated = "custom domain created",
|
|
70
|
-
CustomDomainDeleted = "custom domain deleted"
|
|
70
|
+
CustomDomainDeleted = "custom domain deleted",
|
|
71
|
+
/** The tenant has turned on dynamic app (CIMD) client access. */
|
|
72
|
+
DynamicAppEnabled = "dynamic app enabled",
|
|
73
|
+
/** The tenant has turned off dynamic app (CIMD) client access. */
|
|
74
|
+
DynamicAppDisabled = "dynamic app disabled"
|
|
71
75
|
}
|
|
72
76
|
/** The PostHog groups for product events. */
|
|
73
77
|
export declare enum EventGroup {
|
|
@@ -69,6 +69,10 @@ export var ProductEvent;
|
|
|
69
69
|
ProductEvent["MfaDisabled"] = "mfa disabled";
|
|
70
70
|
ProductEvent["CustomDomainCreated"] = "custom domain created";
|
|
71
71
|
ProductEvent["CustomDomainDeleted"] = "custom domain deleted";
|
|
72
|
+
/** The tenant has turned on dynamic app (CIMD) client access. */
|
|
73
|
+
ProductEvent["DynamicAppEnabled"] = "dynamic app enabled";
|
|
74
|
+
/** The tenant has turned off dynamic app (CIMD) client access. */
|
|
75
|
+
ProductEvent["DynamicAppDisabled"] = "dynamic app disabled";
|
|
72
76
|
})(ProductEvent || (ProductEvent = {}));
|
|
73
77
|
/** The PostHog groups for product events. */
|
|
74
78
|
export var EventGroup;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefixes of the OAuth `state` parameter used by the first-party clients that share the
|
|
3
|
+
* `/callback/:connectorId` social callback URI.
|
|
4
|
+
*
|
|
5
|
+
* Logto Core routes an incoming callback by looking at the `state` prefix, so the prefixes must
|
|
6
|
+
* never be a prefix of each other. Keep them defined together to preserve that invariant.
|
|
7
|
+
*
|
|
8
|
+
* A `state` carrying no known prefix falls back to the Sign-in Experience, which keeps in-flight
|
|
9
|
+
* requests and custom sign-in UIs that build their own `state` working.
|
|
10
|
+
*/
|
|
11
|
+
export declare const accountCenterSocialStatePrefix = "ac_";
|
|
12
|
+
export declare const experienceSocialStatePrefix = "se_";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefixes of the OAuth `state` parameter used by the first-party clients that share the
|
|
3
|
+
* `/callback/:connectorId` social callback URI.
|
|
4
|
+
*
|
|
5
|
+
* Logto Core routes an incoming callback by looking at the `state` prefix, so the prefixes must
|
|
6
|
+
* never be a prefix of each other. Keep them defined together to preserve that invariant.
|
|
7
|
+
*
|
|
8
|
+
* A `state` carrying no known prefix falls back to the Sign-in Experience, which keeps in-flight
|
|
9
|
+
* requests and custom sign-in UIs that build their own `state` working.
|
|
10
|
+
*/
|
|
11
|
+
export const accountCenterSocialStatePrefix = 'ac_';
|
|
12
|
+
export const experienceSocialStatePrefix = 'se_';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { GeneratedSchema } from './../foundations/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The client identity a user approved at consent time for a client ID metadata document (CIMD) client, captured per Grant. CIMD clients are unregistered URL identities without an `applications` row, and an unvetted client can rewrite its hosted document at any time — so the grant list renders this snapshot instead of refetching the document.
|
|
4
|
+
*
|
|
5
|
+
* @remarks This is a type for database creation.
|
|
6
|
+
* @see {@link CimdGrantClientSnapshot} for the original type.
|
|
7
|
+
*/
|
|
8
|
+
export type CreateCimdGrantClientSnapshot = {
|
|
9
|
+
tenantId?: string;
|
|
10
|
+
/** The ID of the Grant row in `oidc_model_instances`. */
|
|
11
|
+
grantId: string;
|
|
12
|
+
/** Fixed discriminator so the composite foreign key can target Grant rows in the polymorphic `oidc_model_instances` table. */
|
|
13
|
+
grantModelName?: string;
|
|
14
|
+
clientId: string;
|
|
15
|
+
name?: string | null;
|
|
16
|
+
logoUri?: string | null;
|
|
17
|
+
createdAt?: number;
|
|
18
|
+
};
|
|
19
|
+
/** The client identity a user approved at consent time for a client ID metadata document (CIMD) client, captured per Grant. CIMD clients are unregistered URL identities without an `applications` row, and an unvetted client can rewrite its hosted document at any time — so the grant list renders this snapshot instead of refetching the document. */
|
|
20
|
+
export type CimdGrantClientSnapshot = {
|
|
21
|
+
tenantId: string;
|
|
22
|
+
/** The ID of the Grant row in `oidc_model_instances`. */
|
|
23
|
+
grantId: string;
|
|
24
|
+
/** Fixed discriminator so the composite foreign key can target Grant rows in the polymorphic `oidc_model_instances` table. */
|
|
25
|
+
grantModelName: string;
|
|
26
|
+
clientId: string;
|
|
27
|
+
name: string | null;
|
|
28
|
+
logoUri: string | null;
|
|
29
|
+
createdAt: number;
|
|
30
|
+
};
|
|
31
|
+
export type CimdGrantClientSnapshotKeys = 'tenantId' | 'grantId' | 'grantModelName' | 'clientId' | 'name' | 'logoUri' | 'createdAt';
|
|
32
|
+
export declare const CimdGrantClientSnapshots: GeneratedSchema<CimdGrantClientSnapshotKeys, CreateCimdGrantClientSnapshot, CimdGrantClientSnapshot, 'cimd_grant_client_snapshots', 'cimd_grant_client_snapshot'>;
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
grantId: z.string().min(1).max(128),
|
|
6
|
+
grantModelName: z.string().min(1).max(64).optional(),
|
|
7
|
+
clientId: z.string().min(1).max(2048),
|
|
8
|
+
name: z.string().max(256).nullable().optional(),
|
|
9
|
+
logoUri: z.string().max(2048).nullable().optional(),
|
|
10
|
+
createdAt: z.number().optional(),
|
|
11
|
+
});
|
|
12
|
+
const guard = z.object({
|
|
13
|
+
tenantId: z.string().max(21),
|
|
14
|
+
grantId: z.string().min(1).max(128),
|
|
15
|
+
grantModelName: z.string().max(64),
|
|
16
|
+
clientId: z.string().min(1).max(2048),
|
|
17
|
+
name: z.string().max(256).nullable(),
|
|
18
|
+
logoUri: z.string().max(2048).nullable(),
|
|
19
|
+
createdAt: z.number(),
|
|
20
|
+
});
|
|
21
|
+
export const CimdGrantClientSnapshots = Object.freeze({
|
|
22
|
+
table: 'cimd_grant_client_snapshots',
|
|
23
|
+
tableSingular: 'cimd_grant_client_snapshot',
|
|
24
|
+
fields: {
|
|
25
|
+
tenantId: 'tenant_id',
|
|
26
|
+
grantId: 'grant_id',
|
|
27
|
+
grantModelName: 'grant_model_name',
|
|
28
|
+
clientId: 'client_id',
|
|
29
|
+
name: 'name',
|
|
30
|
+
logoUri: 'logo_uri',
|
|
31
|
+
createdAt: 'created_at',
|
|
32
|
+
},
|
|
33
|
+
fieldKeys: [
|
|
34
|
+
'tenantId',
|
|
35
|
+
'grantId',
|
|
36
|
+
'grantModelName',
|
|
37
|
+
'clientId',
|
|
38
|
+
'name',
|
|
39
|
+
'logoUri',
|
|
40
|
+
'createdAt',
|
|
41
|
+
],
|
|
42
|
+
createGuard,
|
|
43
|
+
guard,
|
|
44
|
+
updateGuard: guard.partial(),
|
|
45
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { GeneratedSchema } from './../foundations/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The organization authorized on a single OIDC Grant for a client ID metadata document (CIMD) client. CIMD clients are unregistered URL identities with no deletion lifecycle and transferable ownership, so organization access follows the single authorization instead of a long-lived cross-grant relation: rows cascade away with the Grant row and with the user's organization membership.
|
|
4
|
+
*
|
|
5
|
+
* @remarks This is a type for database creation.
|
|
6
|
+
* @see {@link CimdGrantOrganization} for the original type.
|
|
7
|
+
*/
|
|
8
|
+
export type CreateCimdGrantOrganization = {
|
|
9
|
+
tenantId?: string;
|
|
10
|
+
/** The ID of the Grant row in `oidc_model_instances`. */
|
|
11
|
+
grantId: string;
|
|
12
|
+
/** Fixed discriminator so the composite foreign key can target Grant rows in the polymorphic `oidc_model_instances` table. */
|
|
13
|
+
grantModelName?: string;
|
|
14
|
+
organizationId: string;
|
|
15
|
+
/** The user the grant was issued to and the membership FK's target. Must match the Grant payload's `accountId` (unreachable by FK) — the consent write enforces the pairing. */
|
|
16
|
+
userId: string;
|
|
17
|
+
};
|
|
18
|
+
/** The organization authorized on a single OIDC Grant for a client ID metadata document (CIMD) client. CIMD clients are unregistered URL identities with no deletion lifecycle and transferable ownership, so organization access follows the single authorization instead of a long-lived cross-grant relation: rows cascade away with the Grant row and with the user's organization membership. */
|
|
19
|
+
export type CimdGrantOrganization = {
|
|
20
|
+
tenantId: string;
|
|
21
|
+
/** The ID of the Grant row in `oidc_model_instances`. */
|
|
22
|
+
grantId: string;
|
|
23
|
+
/** Fixed discriminator so the composite foreign key can target Grant rows in the polymorphic `oidc_model_instances` table. */
|
|
24
|
+
grantModelName: string;
|
|
25
|
+
organizationId: string;
|
|
26
|
+
/** The user the grant was issued to and the membership FK's target. Must match the Grant payload's `accountId` (unreachable by FK) — the consent write enforces the pairing. */
|
|
27
|
+
userId: string;
|
|
28
|
+
};
|
|
29
|
+
export type CimdGrantOrganizationKeys = 'tenantId' | 'grantId' | 'grantModelName' | 'organizationId' | 'userId';
|
|
30
|
+
export declare const CimdGrantOrganizations: GeneratedSchema<CimdGrantOrganizationKeys, CreateCimdGrantOrganization, CimdGrantOrganization, 'cimd_grant_organizations', 'cimd_grant_organization'>;
|
|
@@ -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
|
+
grantId: z.string().min(1).max(128),
|
|
6
|
+
grantModelName: z.string().min(1).max(64).optional(),
|
|
7
|
+
organizationId: z.string().min(1).max(21),
|
|
8
|
+
userId: z.string().min(1).max(21),
|
|
9
|
+
});
|
|
10
|
+
const guard = z.object({
|
|
11
|
+
tenantId: z.string().max(21),
|
|
12
|
+
grantId: z.string().min(1).max(128),
|
|
13
|
+
grantModelName: z.string().max(64),
|
|
14
|
+
organizationId: z.string().min(1).max(21),
|
|
15
|
+
userId: z.string().min(1).max(21),
|
|
16
|
+
});
|
|
17
|
+
export const CimdGrantOrganizations = Object.freeze({
|
|
18
|
+
table: 'cimd_grant_organizations',
|
|
19
|
+
tableSingular: 'cimd_grant_organization',
|
|
20
|
+
fields: {
|
|
21
|
+
tenantId: 'tenant_id',
|
|
22
|
+
grantId: 'grant_id',
|
|
23
|
+
grantModelName: 'grant_model_name',
|
|
24
|
+
organizationId: 'organization_id',
|
|
25
|
+
userId: 'user_id',
|
|
26
|
+
},
|
|
27
|
+
fieldKeys: [
|
|
28
|
+
'tenantId',
|
|
29
|
+
'grantId',
|
|
30
|
+
'grantModelName',
|
|
31
|
+
'organizationId',
|
|
32
|
+
'userId',
|
|
33
|
+
],
|
|
34
|
+
createGuard,
|
|
35
|
+
guard,
|
|
36
|
+
updateGuard: guard.partial(),
|
|
37
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { GeneratedSchema } from './../foundations/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The organization API resource scopes (permissions) that client ID metadata document (CIMD) clients of the tenant can request. The scopes are granted to users through organization roles. Rows are an allowlist: an empty table allows nothing (deny-all), and an unrestricted state is not expressible.
|
|
4
|
+
*
|
|
5
|
+
* @remarks This is a type for database creation.
|
|
6
|
+
* @see {@link CimdOrganizationResourceScope} for the original type.
|
|
7
|
+
*/
|
|
8
|
+
export type CreateCimdOrganizationResourceScope = {
|
|
9
|
+
tenantId?: string;
|
|
10
|
+
/** The globally unique identifier of the API resource scope. */
|
|
11
|
+
scopeId: string;
|
|
12
|
+
};
|
|
13
|
+
/** The organization API resource scopes (permissions) that client ID metadata document (CIMD) clients of the tenant can request. The scopes are granted to users through organization roles. Rows are an allowlist: an empty table allows nothing (deny-all), and an unrestricted state is not expressible. */
|
|
14
|
+
export type CimdOrganizationResourceScope = {
|
|
15
|
+
tenantId: string;
|
|
16
|
+
/** The globally unique identifier of the API resource scope. */
|
|
17
|
+
scopeId: string;
|
|
18
|
+
};
|
|
19
|
+
export type CimdOrganizationResourceScopeKeys = 'tenantId' | 'scopeId';
|
|
20
|
+
export declare const CimdOrganizationResourceScopes: GeneratedSchema<CimdOrganizationResourceScopeKeys, CreateCimdOrganizationResourceScope, CimdOrganizationResourceScope, 'cimd_organization_resource_scopes', 'cimd_organization_resource_scope'>;
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
scopeId: z.string().min(1).max(21),
|
|
6
|
+
});
|
|
7
|
+
const guard = z.object({
|
|
8
|
+
tenantId: z.string().max(21),
|
|
9
|
+
scopeId: z.string().min(1).max(21),
|
|
10
|
+
});
|
|
11
|
+
export const CimdOrganizationResourceScopes = Object.freeze({
|
|
12
|
+
table: 'cimd_organization_resource_scopes',
|
|
13
|
+
tableSingular: 'cimd_organization_resource_scope',
|
|
14
|
+
fields: {
|
|
15
|
+
tenantId: 'tenant_id',
|
|
16
|
+
scopeId: 'scope_id',
|
|
17
|
+
},
|
|
18
|
+
fieldKeys: [
|
|
19
|
+
'tenantId',
|
|
20
|
+
'scopeId',
|
|
21
|
+
],
|
|
22
|
+
createGuard,
|
|
23
|
+
guard,
|
|
24
|
+
updateGuard: guard.partial(),
|
|
25
|
+
});
|