@logto/schemas 1.41.0 → 1.42.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/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/utils/1704934999-tables.js +19 -1
- package/lib/consts/experience.d.ts +1 -0
- package/lib/consts/experience.js +1 -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 +1 -0
- package/lib/db-entries/index.js +1 -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/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 +4 -4
- package/lib/foundations/jsonb-types/hooks.js +1 -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 +16 -1
- package/lib/foundations/jsonb-types/sign-in-experience.js +1 -0
- package/lib/types/application.d.ts +131 -1
- package/lib/types/application.js +8 -0
- package/lib/types/custom-profile-fields.d.ts +1 -1
- package/lib/types/domain.d.ts +9 -0
- package/lib/types/hook.d.ts +9 -2
- 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 +5 -2
- package/lib/types/log/index.js +1 -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 +32 -19
- package/lib/types/logto-config/index.js +7 -7
- package/lib/types/logto-config/index.test.js +9 -9
- package/lib/types/logto-config/jwt-customizer.d.ts +29 -0
- package/lib/types/organization.d.ts +136 -1
- package/lib/types/organization.js +3 -2
- package/lib/types/sign-in-experience.d.ts +2 -4
- package/lib/types/sign-in-experience.js +1 -1
- package/lib/types/sso-connector.d.ts +58 -0
- package/lib/types/sso-connector.js +12 -0
- package/lib/types/user-sessions.d.ts +78 -29
- package/lib/types/verification-records/one-time-token-verification.d.ts +5 -0
- package/package.json +5 -5
- package/tables/domains.sql +1 -0
- package/tables/saml_sso_connector_signing_keys.sql +21 -0
- package/tables/service_logs.sql +5 -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,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 domains
|
|
9
|
+
add column verification_files jsonb not null default '[]'::jsonb;
|
|
10
|
+
`);
|
|
11
|
+
},
|
|
12
|
+
down: async (pool) => {
|
|
13
|
+
await pool.query(sql`
|
|
14
|
+
alter table domains
|
|
15
|
+
drop column verification_files;
|
|
16
|
+
`);
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
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
|
+
const alteration: AlterationScript = {
|
|
6
|
+
beforeUp: async (pool) => {
|
|
7
|
+
// Support the age-based `service_logs` retention prune's global oldest-first scan
|
|
8
|
+
// (`where created_at < ? order by created_at asc limit ?`). The existing
|
|
9
|
+
// `(tenant_id, type, created_at)` index leads with `tenant_id`, so it can't drive a
|
|
10
|
+
// tenant-agnostic time-range scan. `concurrently` keeps the write path unlocked — this
|
|
11
|
+
// unbounded table is written on every send.
|
|
12
|
+
//
|
|
13
|
+
// No `if not exists`: fail loud if the index already exists (e.g. a leftover invalid build from a
|
|
14
|
+
// prior failed concurrent creation) instead of silently skipping.
|
|
15
|
+
await pool.query(sql`
|
|
16
|
+
create index concurrently service_logs__created_at
|
|
17
|
+
on service_logs (created_at);
|
|
18
|
+
`);
|
|
19
|
+
},
|
|
20
|
+
up: async () => {
|
|
21
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
22
|
+
},
|
|
23
|
+
beforeDown: async (pool) => {
|
|
24
|
+
await pool.query(sql`
|
|
25
|
+
drop index concurrently if exists service_logs__created_at;
|
|
26
|
+
`);
|
|
27
|
+
},
|
|
28
|
+
down: async () => {
|
|
29
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default alteration;
|
|
@@ -0,0 +1,38 @@
|
|
|
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 saml_sso_connector_signing_keys (
|
|
11
|
+
id varchar(21) not null,
|
|
12
|
+
tenant_id varchar(21) not null
|
|
13
|
+
references tenants (id) on update cascade on delete cascade,
|
|
14
|
+
sso_connector_id varchar(128) not null
|
|
15
|
+
references sso_connectors (id) on update cascade on delete cascade,
|
|
16
|
+
private_key text not null,
|
|
17
|
+
certificate text not null,
|
|
18
|
+
created_at timestamptz not null default now(),
|
|
19
|
+
expires_at timestamptz not null,
|
|
20
|
+
active boolean not null,
|
|
21
|
+
primary key (tenant_id, sso_connector_id, id)
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
create unique index saml_sso_connector_signing_keys__unique_active
|
|
25
|
+
on saml_sso_connector_signing_keys (tenant_id, sso_connector_id, active)
|
|
26
|
+
where active;
|
|
27
|
+
`);
|
|
28
|
+
await applyTableRls(pool, 'saml_sso_connector_signing_keys');
|
|
29
|
+
},
|
|
30
|
+
down: async (pool) => {
|
|
31
|
+
await dropTableRls(pool, 'saml_sso_connector_signing_keys');
|
|
32
|
+
await pool.query(sql`
|
|
33
|
+
drop table saml_sso_connector_signing_keys;
|
|
34
|
+
`);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export default alteration;
|
|
@@ -2,12 +2,33 @@ import { type CommonQueryMethods, sql } from '@silverhand/slonik';
|
|
|
2
2
|
|
|
3
3
|
const getId = (value: string) => sql.identifier([value]);
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Returns the suffix used in the base Logto role name for the current database
|
|
7
|
+
* (`logto_tenant_<suffix>`).
|
|
8
|
+
*
|
|
9
|
+
* Historically the suffix was the raw database name (which may contain hyphens). A later
|
|
10
|
+
* convention normalised it by replacing hyphens with underscores. To remain compatible with
|
|
11
|
+
* both conventions we probe `pg_roles` for the normalised name first and fall back to the
|
|
12
|
+
* raw database name for legacy installations.
|
|
13
|
+
*/
|
|
5
14
|
const getDatabaseName = async (pool: CommonQueryMethods) => {
|
|
6
15
|
const { currentDatabase } = await pool.one<{ currentDatabase: string }>(sql`
|
|
7
16
|
select current_database();
|
|
8
17
|
`);
|
|
9
18
|
|
|
10
|
-
|
|
19
|
+
const normalized = currentDatabase.replaceAll('-', '_');
|
|
20
|
+
|
|
21
|
+
if (normalized === currentDatabase) {
|
|
22
|
+
return currentDatabase;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { exists } = await pool.one<{ exists: boolean }>(sql`
|
|
26
|
+
select exists(
|
|
27
|
+
select 1 from pg_roles where rolname = ${'logto_tenant_' + normalized}
|
|
28
|
+
) as exists;
|
|
29
|
+
`);
|
|
30
|
+
|
|
31
|
+
return exists ? normalized : currentDatabase;
|
|
11
32
|
};
|
|
12
33
|
|
|
13
34
|
/**
|
|
@@ -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 domains
|
|
6
|
+
add column verification_files jsonb not null default '[]'::jsonb;
|
|
7
|
+
`);
|
|
8
|
+
},
|
|
9
|
+
down: async (pool) => {
|
|
10
|
+
await pool.query(sql `
|
|
11
|
+
alter table domains
|
|
12
|
+
drop column verification_files;
|
|
13
|
+
`);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
export default alteration;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { sql } from '@silverhand/slonik';
|
|
2
|
+
const alteration = {
|
|
3
|
+
beforeUp: async (pool) => {
|
|
4
|
+
// Support the age-based `service_logs` retention prune's global oldest-first scan
|
|
5
|
+
// (`where created_at < ? order by created_at asc limit ?`). The existing
|
|
6
|
+
// `(tenant_id, type, created_at)` index leads with `tenant_id`, so it can't drive a
|
|
7
|
+
// tenant-agnostic time-range scan. `concurrently` keeps the write path unlocked — this
|
|
8
|
+
// unbounded table is written on every send.
|
|
9
|
+
//
|
|
10
|
+
// No `if not exists`: fail loud if the index already exists (e.g. a leftover invalid build from a
|
|
11
|
+
// prior failed concurrent creation) instead of silently skipping.
|
|
12
|
+
await pool.query(sql `
|
|
13
|
+
create index concurrently service_logs__created_at
|
|
14
|
+
on service_logs (created_at);
|
|
15
|
+
`);
|
|
16
|
+
},
|
|
17
|
+
up: async () => {
|
|
18
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
19
|
+
},
|
|
20
|
+
beforeDown: async (pool) => {
|
|
21
|
+
await pool.query(sql `
|
|
22
|
+
drop index concurrently if exists service_logs__created_at;
|
|
23
|
+
`);
|
|
24
|
+
},
|
|
25
|
+
down: async () => {
|
|
26
|
+
/** `concurrently` cannot be used inside a transaction. */
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
export default alteration;
|
|
@@ -0,0 +1,33 @@
|
|
|
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 saml_sso_connector_signing_keys (
|
|
7
|
+
id varchar(21) not null,
|
|
8
|
+
tenant_id varchar(21) not null
|
|
9
|
+
references tenants (id) on update cascade on delete cascade,
|
|
10
|
+
sso_connector_id varchar(128) not null
|
|
11
|
+
references sso_connectors (id) on update cascade on delete cascade,
|
|
12
|
+
private_key text not null,
|
|
13
|
+
certificate text not null,
|
|
14
|
+
created_at timestamptz not null default now(),
|
|
15
|
+
expires_at timestamptz not null,
|
|
16
|
+
active boolean not null,
|
|
17
|
+
primary key (tenant_id, sso_connector_id, id)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
create unique index saml_sso_connector_signing_keys__unique_active
|
|
21
|
+
on saml_sso_connector_signing_keys (tenant_id, sso_connector_id, active)
|
|
22
|
+
where active;
|
|
23
|
+
`);
|
|
24
|
+
await applyTableRls(pool, 'saml_sso_connector_signing_keys');
|
|
25
|
+
},
|
|
26
|
+
down: async (pool) => {
|
|
27
|
+
await dropTableRls(pool, 'saml_sso_connector_signing_keys');
|
|
28
|
+
await pool.query(sql `
|
|
29
|
+
drop table saml_sso_connector_signing_keys;
|
|
30
|
+
`);
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DomainStatus, DomainDnsRecords, CloudflareData, GeneratedSchema } from './../foundations/index.js';
|
|
1
|
+
import { DomainStatus, DomainDnsRecords, DomainVerificationFiles, CloudflareData, GeneratedSchema } from './../foundations/index.js';
|
|
2
2
|
/**
|
|
3
3
|
*
|
|
4
4
|
* @remarks This is a type for database creation.
|
|
@@ -11,6 +11,7 @@ export type CreateDomain = {
|
|
|
11
11
|
status?: DomainStatus;
|
|
12
12
|
errorMessage?: string | null;
|
|
13
13
|
dnsRecords?: DomainDnsRecords;
|
|
14
|
+
verificationFiles?: DomainVerificationFiles;
|
|
14
15
|
cloudflareData?: CloudflareData | null;
|
|
15
16
|
updatedAt?: number;
|
|
16
17
|
createdAt?: number;
|
|
@@ -22,9 +23,10 @@ export type Domain = {
|
|
|
22
23
|
status: DomainStatus;
|
|
23
24
|
errorMessage: string | null;
|
|
24
25
|
dnsRecords: DomainDnsRecords;
|
|
26
|
+
verificationFiles: DomainVerificationFiles;
|
|
25
27
|
cloudflareData: CloudflareData | null;
|
|
26
28
|
updatedAt: number;
|
|
27
29
|
createdAt: number;
|
|
28
30
|
};
|
|
29
|
-
export type DomainKeys = 'tenantId' | 'id' | 'domain' | 'status' | 'errorMessage' | 'dnsRecords' | 'cloudflareData' | 'updatedAt' | 'createdAt';
|
|
31
|
+
export type DomainKeys = 'tenantId' | 'id' | 'domain' | 'status' | 'errorMessage' | 'dnsRecords' | 'verificationFiles' | 'cloudflareData' | 'updatedAt' | 'createdAt';
|
|
30
32
|
export declare const Domains: GeneratedSchema<DomainKeys, CreateDomain, Domain, 'domains', 'domain'>;
|
package/lib/db-entries/domain.js
CHANGED
|
@@ -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 { domainStatusGuard, domainDnsRecordsGuard, cloudflareDataGuard } from './../foundations/index.js';
|
|
3
|
+
import { domainStatusGuard, domainDnsRecordsGuard, domainVerificationFilesGuard, cloudflareDataGuard } from './../foundations/index.js';
|
|
4
4
|
const createGuard = z.object({
|
|
5
5
|
tenantId: z.string().max(21).optional(),
|
|
6
6
|
id: z.string().min(1).max(21),
|
|
@@ -8,6 +8,7 @@ const createGuard = z.object({
|
|
|
8
8
|
status: domainStatusGuard.optional(),
|
|
9
9
|
errorMessage: z.string().max(1024).nullable().optional(),
|
|
10
10
|
dnsRecords: domainDnsRecordsGuard.optional(),
|
|
11
|
+
verificationFiles: domainVerificationFilesGuard.optional(),
|
|
11
12
|
cloudflareData: cloudflareDataGuard.nullable().optional(),
|
|
12
13
|
updatedAt: z.number().optional(),
|
|
13
14
|
createdAt: z.number().optional(),
|
|
@@ -19,6 +20,7 @@ const guard = z.object({
|
|
|
19
20
|
status: domainStatusGuard,
|
|
20
21
|
errorMessage: z.string().max(1024).nullable(),
|
|
21
22
|
dnsRecords: domainDnsRecordsGuard,
|
|
23
|
+
verificationFiles: domainVerificationFilesGuard,
|
|
22
24
|
cloudflareData: cloudflareDataGuard.nullable(),
|
|
23
25
|
updatedAt: z.number(),
|
|
24
26
|
createdAt: z.number(),
|
|
@@ -33,6 +35,7 @@ export const Domains = Object.freeze({
|
|
|
33
35
|
status: 'status',
|
|
34
36
|
errorMessage: 'error_message',
|
|
35
37
|
dnsRecords: 'dns_records',
|
|
38
|
+
verificationFiles: 'verification_files',
|
|
36
39
|
cloudflareData: 'cloudflare_data',
|
|
37
40
|
updatedAt: 'updated_at',
|
|
38
41
|
createdAt: 'created_at',
|
|
@@ -44,6 +47,7 @@ export const Domains = Object.freeze({
|
|
|
44
47
|
'status',
|
|
45
48
|
'errorMessage',
|
|
46
49
|
'dnsRecords',
|
|
50
|
+
'verificationFiles',
|
|
47
51
|
'cloudflareData',
|
|
48
52
|
'updatedAt',
|
|
49
53
|
'createdAt',
|
|
@@ -55,6 +55,7 @@ export * from './roles-scope.js';
|
|
|
55
55
|
export * from './saml-application-config.js';
|
|
56
56
|
export * from './saml-application-secret.js';
|
|
57
57
|
export * from './saml-application-session.js';
|
|
58
|
+
export * from './saml-sso-connector-signing-key.js';
|
|
58
59
|
export * from './scope.js';
|
|
59
60
|
export * from './secret-enterprise-sso-connector-relation.js';
|
|
60
61
|
export * from './secret-social-connector-relation.js';
|
package/lib/db-entries/index.js
CHANGED
|
@@ -56,6 +56,7 @@ export * from './roles-scope.js';
|
|
|
56
56
|
export * from './saml-application-config.js';
|
|
57
57
|
export * from './saml-application-secret.js';
|
|
58
58
|
export * from './saml-application-session.js';
|
|
59
|
+
export * from './saml-sso-connector-signing-key.js';
|
|
59
60
|
export * from './scope.js';
|
|
60
61
|
export * from './secret-enterprise-sso-connector-relation.js';
|
|
61
62
|
export * from './secret-social-connector-relation.js';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { GeneratedSchema } from './../foundations/index.js';
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @remarks This is a type for database creation.
|
|
5
|
+
* @see {@link SamlSsoConnectorSigningKey} for the original type.
|
|
6
|
+
*/
|
|
7
|
+
export type CreateSamlSsoConnectorSigningKey = {
|
|
8
|
+
id: string;
|
|
9
|
+
tenantId?: string;
|
|
10
|
+
ssoConnectorId: string;
|
|
11
|
+
/** The SP private key (plaintext PEM) used to sign the SAML AuthnRequest. Tenant-isolated by RLS and never returned by the API. */
|
|
12
|
+
privateKey: string;
|
|
13
|
+
/** The PEM-encoded self-signed SP certificate matching the private key. Public and safe to expose. */
|
|
14
|
+
certificate: string;
|
|
15
|
+
createdAt?: number;
|
|
16
|
+
expiresAt: number;
|
|
17
|
+
active: boolean;
|
|
18
|
+
};
|
|
19
|
+
export type SamlSsoConnectorSigningKey = {
|
|
20
|
+
id: string;
|
|
21
|
+
tenantId: string;
|
|
22
|
+
ssoConnectorId: string;
|
|
23
|
+
/** The SP private key (plaintext PEM) used to sign the SAML AuthnRequest. Tenant-isolated by RLS and never returned by the API. */
|
|
24
|
+
privateKey: string;
|
|
25
|
+
/** The PEM-encoded self-signed SP certificate matching the private key. Public and safe to expose. */
|
|
26
|
+
certificate: string;
|
|
27
|
+
createdAt: number;
|
|
28
|
+
expiresAt: number;
|
|
29
|
+
active: boolean;
|
|
30
|
+
};
|
|
31
|
+
export type SamlSsoConnectorSigningKeyKeys = 'id' | 'tenantId' | 'ssoConnectorId' | 'privateKey' | 'certificate' | 'createdAt' | 'expiresAt' | 'active';
|
|
32
|
+
export declare const SamlSsoConnectorSigningKeys: GeneratedSchema<SamlSsoConnectorSigningKeyKeys, CreateSamlSsoConnectorSigningKey, SamlSsoConnectorSigningKey, 'saml_sso_connector_signing_keys', 'saml_sso_connector_signing_key'>;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
const createGuard = z.object({
|
|
4
|
+
id: z.string().min(1).max(21),
|
|
5
|
+
tenantId: z.string().max(21).optional(),
|
|
6
|
+
ssoConnectorId: z.string().min(1).max(128),
|
|
7
|
+
privateKey: z.string().min(1),
|
|
8
|
+
certificate: z.string().min(1),
|
|
9
|
+
createdAt: z.number().optional(),
|
|
10
|
+
expiresAt: z.number(),
|
|
11
|
+
active: z.boolean(),
|
|
12
|
+
});
|
|
13
|
+
const guard = z.object({
|
|
14
|
+
id: z.string().min(1).max(21),
|
|
15
|
+
tenantId: z.string().max(21),
|
|
16
|
+
ssoConnectorId: z.string().min(1).max(128),
|
|
17
|
+
privateKey: z.string().min(1),
|
|
18
|
+
certificate: z.string().min(1),
|
|
19
|
+
createdAt: z.number(),
|
|
20
|
+
expiresAt: z.number(),
|
|
21
|
+
active: z.boolean(),
|
|
22
|
+
});
|
|
23
|
+
export const SamlSsoConnectorSigningKeys = Object.freeze({
|
|
24
|
+
table: 'saml_sso_connector_signing_keys',
|
|
25
|
+
tableSingular: 'saml_sso_connector_signing_key',
|
|
26
|
+
fields: {
|
|
27
|
+
id: 'id',
|
|
28
|
+
tenantId: 'tenant_id',
|
|
29
|
+
ssoConnectorId: 'sso_connector_id',
|
|
30
|
+
privateKey: 'private_key',
|
|
31
|
+
certificate: 'certificate',
|
|
32
|
+
createdAt: 'created_at',
|
|
33
|
+
expiresAt: 'expires_at',
|
|
34
|
+
active: 'active',
|
|
35
|
+
},
|
|
36
|
+
fieldKeys: [
|
|
37
|
+
'id',
|
|
38
|
+
'tenantId',
|
|
39
|
+
'ssoConnectorId',
|
|
40
|
+
'privateKey',
|
|
41
|
+
'certificate',
|
|
42
|
+
'createdAt',
|
|
43
|
+
'expiresAt',
|
|
44
|
+
'active',
|
|
45
|
+
],
|
|
46
|
+
createGuard,
|
|
47
|
+
guard,
|
|
48
|
+
updateGuard: guard.partial(),
|
|
49
|
+
});
|
|
@@ -1,4 +1,61 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
export declare enum DomainVerificationFileContentType {
|
|
3
|
+
Text = "text/plain",
|
|
4
|
+
Json = "application/json"
|
|
5
|
+
}
|
|
6
|
+
export declare const domainVerificationFilePathGuard: z.ZodEffects<z.ZodString, string, string>;
|
|
7
|
+
export declare const domainVerificationFileGuard: z.ZodEffects<z.ZodObject<{
|
|
8
|
+
path: z.ZodEffects<z.ZodString, string, string>;
|
|
9
|
+
content: z.ZodString;
|
|
10
|
+
contentType: z.ZodNativeEnum<typeof DomainVerificationFileContentType>;
|
|
11
|
+
}, "strip", z.ZodTypeAny, {
|
|
12
|
+
path: string;
|
|
13
|
+
content: string;
|
|
14
|
+
contentType: DomainVerificationFileContentType;
|
|
15
|
+
}, {
|
|
16
|
+
path: string;
|
|
17
|
+
content: string;
|
|
18
|
+
contentType: DomainVerificationFileContentType;
|
|
19
|
+
}>, {
|
|
20
|
+
path: string;
|
|
21
|
+
content: string;
|
|
22
|
+
contentType: DomainVerificationFileContentType;
|
|
23
|
+
}, {
|
|
24
|
+
path: string;
|
|
25
|
+
content: string;
|
|
26
|
+
contentType: DomainVerificationFileContentType;
|
|
27
|
+
}>;
|
|
28
|
+
export type DomainVerificationFile = z.infer<typeof domainVerificationFileGuard>;
|
|
29
|
+
export declare const domainVerificationFilesGuard: z.ZodEffects<z.ZodArray<z.ZodEffects<z.ZodObject<{
|
|
30
|
+
path: z.ZodEffects<z.ZodString, string, string>;
|
|
31
|
+
content: z.ZodString;
|
|
32
|
+
contentType: z.ZodNativeEnum<typeof DomainVerificationFileContentType>;
|
|
33
|
+
}, "strip", z.ZodTypeAny, {
|
|
34
|
+
path: string;
|
|
35
|
+
content: string;
|
|
36
|
+
contentType: DomainVerificationFileContentType;
|
|
37
|
+
}, {
|
|
38
|
+
path: string;
|
|
39
|
+
content: string;
|
|
40
|
+
contentType: DomainVerificationFileContentType;
|
|
41
|
+
}>, {
|
|
42
|
+
path: string;
|
|
43
|
+
content: string;
|
|
44
|
+
contentType: DomainVerificationFileContentType;
|
|
45
|
+
}, {
|
|
46
|
+
path: string;
|
|
47
|
+
content: string;
|
|
48
|
+
contentType: DomainVerificationFileContentType;
|
|
49
|
+
}>, "many">, {
|
|
50
|
+
path: string;
|
|
51
|
+
content: string;
|
|
52
|
+
contentType: DomainVerificationFileContentType;
|
|
53
|
+
}[], {
|
|
54
|
+
path: string;
|
|
55
|
+
content: string;
|
|
56
|
+
contentType: DomainVerificationFileContentType;
|
|
57
|
+
}[]>;
|
|
58
|
+
export type DomainVerificationFiles = z.infer<typeof domainVerificationFilesGuard>;
|
|
2
59
|
export declare const domainDnsRecordGuard: z.ZodObject<{
|
|
3
60
|
name: z.ZodString;
|
|
4
61
|
type: z.ZodString;
|
|
@@ -1,4 +1,55 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
export var DomainVerificationFileContentType;
|
|
3
|
+
(function (DomainVerificationFileContentType) {
|
|
4
|
+
DomainVerificationFileContentType["Text"] = "text/plain";
|
|
5
|
+
DomainVerificationFileContentType["Json"] = "application/json";
|
|
6
|
+
})(DomainVerificationFileContentType || (DomainVerificationFileContentType = {}));
|
|
7
|
+
export const domainVerificationFilePathGuard = z
|
|
8
|
+
.string()
|
|
9
|
+
.min(1)
|
|
10
|
+
.max(256)
|
|
11
|
+
.refine((value) => /^\/[\w-][\w.-]*\.[\w-][\w.-]*$/.test(value) ||
|
|
12
|
+
/^\/\.well-known(?:\/[\w-][\w.-]*)+$/.test(value), 'The path must point to a file with an extension at the domain root or to a path under /.well-known/.');
|
|
13
|
+
export const domainVerificationFileGuard = z
|
|
14
|
+
.object({
|
|
15
|
+
path: domainVerificationFilePathGuard,
|
|
16
|
+
content: z
|
|
17
|
+
.string()
|
|
18
|
+
.min(1)
|
|
19
|
+
.max(16 * 1024),
|
|
20
|
+
contentType: z.nativeEnum(DomainVerificationFileContentType),
|
|
21
|
+
})
|
|
22
|
+
.superRefine(({ content, contentType }, context) => {
|
|
23
|
+
if (contentType !== DomainVerificationFileContentType.Json) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
JSON.parse(content);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
context.addIssue({
|
|
31
|
+
code: z.ZodIssueCode.custom,
|
|
32
|
+
message: 'The content must be valid JSON for the application/json content type.',
|
|
33
|
+
path: ['content'],
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
export const domainVerificationFilesGuard = domainVerificationFileGuard
|
|
38
|
+
.array()
|
|
39
|
+
.max(10)
|
|
40
|
+
.superRefine((files, context) => {
|
|
41
|
+
const paths = new Set();
|
|
42
|
+
for (const [index, file] of files.entries()) {
|
|
43
|
+
if (paths.has(file.path)) {
|
|
44
|
+
context.addIssue({
|
|
45
|
+
code: z.ZodIssueCode.custom,
|
|
46
|
+
message: 'Verification file paths must be unique.',
|
|
47
|
+
path: [index, 'path'],
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
paths.add(file.path);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
2
53
|
export const domainDnsRecordGuard = z.object({
|
|
3
54
|
name: z.string(),
|
|
4
55
|
type: z.string(),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { DomainVerificationFileContentType, domainVerificationFilesGuard, } from './custom-domain.js';
|
|
3
|
+
describe('domainVerificationFilesGuard', () => {
|
|
4
|
+
it.each([
|
|
5
|
+
'/MP_verify_abc123.txt',
|
|
6
|
+
'/_domainconnect.txt',
|
|
7
|
+
'/-verification.txt',
|
|
8
|
+
'/google-site-verification.html',
|
|
9
|
+
'/.well-known/apple-developer-merchantid-domain-association',
|
|
10
|
+
'/.well-known/acme-challenge/token_123',
|
|
11
|
+
'/.well-known/acme-challenge/-token_123',
|
|
12
|
+
'/.well-known/acme-challenge/_token_123',
|
|
13
|
+
])('accepts verification file path %s', (path) => {
|
|
14
|
+
expect(domainVerificationFilesGuard.safeParse([
|
|
15
|
+
{ path, content: 'verification-content', contentType: 'text/plain' },
|
|
16
|
+
]).success).toBe(true);
|
|
17
|
+
});
|
|
18
|
+
it.each(['/api/file.txt', '/../secret.txt', '/nested/file.txt', '/file', '/file%2Etxt'])('rejects verification file path %s', (path) => {
|
|
19
|
+
expect(domainVerificationFilesGuard.safeParse([
|
|
20
|
+
{ path, content: 'verification-content', contentType: 'text/plain' },
|
|
21
|
+
]).success).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
it('requires valid JSON for JSON responses', () => {
|
|
24
|
+
expect(domainVerificationFilesGuard.safeParse([
|
|
25
|
+
{
|
|
26
|
+
path: '/.well-known/verification.json',
|
|
27
|
+
content: '{invalid-json}',
|
|
28
|
+
contentType: DomainVerificationFileContentType.Json,
|
|
29
|
+
},
|
|
30
|
+
]).success).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
it('rejects duplicate paths', () => {
|
|
33
|
+
expect(domainVerificationFilesGuard.safeParse([
|
|
34
|
+
{ path: '/verify.txt', content: 'one', contentType: 'text/plain' },
|
|
35
|
+
{ path: '/verify.txt', content: 'two', contentType: 'text/plain' },
|
|
36
|
+
]).success).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -30,14 +30,14 @@ declare enum DataHookDetailMutationType {
|
|
|
30
30
|
type BasicDataHookEvent = `${DataHookSchema}.${DataHookBasicMutationType}`;
|
|
31
31
|
type CustomDataHookMutableSchema = `${DataHookSchema}.Data` | `${DataHookSchema.User}.SuspensionStatus` | `${DataHookSchema.Role}.Scopes` | `${DataHookSchema.Organization}.Membership` | `${DataHookSchema.OrganizationRole}.Scopes`;
|
|
32
32
|
type DataHookPropertyUpdateEvent = `${CustomDataHookMutableSchema}.${DataHookDetailMutationType.Updated}`;
|
|
33
|
-
export type ExceptionHookEvent = 'Identifier.Lockout' | 'Message.RateLimited';
|
|
33
|
+
export type ExceptionHookEvent = 'Identifier.Lockout' | 'Message.RateLimited' | 'Grant.LimitExceeded';
|
|
34
34
|
export type DataHookEvent = BasicDataHookEvent | DataHookPropertyUpdateEvent;
|
|
35
35
|
/** The hook event values that can be registered. */
|
|
36
|
-
export declare const hookEvents: readonly [InteractionHookEvent.PostRegister, InteractionHookEvent.PostSignIn, InteractionHookEvent.PostSignInAdaptiveMfaTriggered, InteractionHookEvent.PostResetPassword, "User.Created", "User.Deleted", "User.Data.Updated", "User.SuspensionStatus.Updated", "Role.Created", "Role.Deleted", "Role.Data.Updated", "Role.Scopes.Updated", "Scope.Created", "Scope.Deleted", "Scope.Data.Updated", "Organization.Created", "Organization.Deleted", "Organization.Data.Updated", "Organization.Membership.Updated", "OrganizationRole.Created", "OrganizationRole.Deleted", "OrganizationRole.Data.Updated", "OrganizationRole.Scopes.Updated", "OrganizationScope.Created", "OrganizationScope.Deleted", "OrganizationScope.Data.Updated", "Identifier.Lockout", "Message.RateLimited"];
|
|
36
|
+
export declare const hookEvents: readonly [InteractionHookEvent.PostRegister, InteractionHookEvent.PostSignIn, InteractionHookEvent.PostSignInAdaptiveMfaTriggered, InteractionHookEvent.PostResetPassword, "User.Created", "User.Deleted", "User.Data.Updated", "User.SuspensionStatus.Updated", "Role.Created", "Role.Deleted", "Role.Data.Updated", "Role.Scopes.Updated", "Scope.Created", "Scope.Deleted", "Scope.Data.Updated", "Organization.Created", "Organization.Deleted", "Organization.Data.Updated", "Organization.Membership.Updated", "OrganizationRole.Created", "OrganizationRole.Deleted", "OrganizationRole.Data.Updated", "OrganizationRole.Scopes.Updated", "OrganizationScope.Created", "OrganizationScope.Deleted", "OrganizationScope.Data.Updated", "Identifier.Lockout", "Message.RateLimited", "Grant.LimitExceeded"];
|
|
37
37
|
/** The type of hook event values that can be registered. */
|
|
38
38
|
export type HookEvent = (typeof hookEvents)[number];
|
|
39
|
-
export declare const hookEventGuard: z.ZodEnum<[InteractionHookEvent.PostRegister, InteractionHookEvent.PostSignIn, InteractionHookEvent.PostSignInAdaptiveMfaTriggered, InteractionHookEvent.PostResetPassword, "User.Created", "User.Deleted", "User.Data.Updated", "User.SuspensionStatus.Updated", "Role.Created", "Role.Deleted", "Role.Data.Updated", "Role.Scopes.Updated", "Scope.Created", "Scope.Deleted", "Scope.Data.Updated", "Organization.Created", "Organization.Deleted", "Organization.Data.Updated", "Organization.Membership.Updated", "OrganizationRole.Created", "OrganizationRole.Deleted", "OrganizationRole.Data.Updated", "OrganizationRole.Scopes.Updated", "OrganizationScope.Created", "OrganizationScope.Deleted", "OrganizationScope.Data.Updated", "Identifier.Lockout", "Message.RateLimited"]>;
|
|
40
|
-
export declare const hookEventsGuard: z.ZodArray<z.ZodEnum<[InteractionHookEvent.PostRegister, InteractionHookEvent.PostSignIn, InteractionHookEvent.PostSignInAdaptiveMfaTriggered, InteractionHookEvent.PostResetPassword, "User.Created", "User.Deleted", "User.Data.Updated", "User.SuspensionStatus.Updated", "Role.Created", "Role.Deleted", "Role.Data.Updated", "Role.Scopes.Updated", "Scope.Created", "Scope.Deleted", "Scope.Data.Updated", "Organization.Created", "Organization.Deleted", "Organization.Data.Updated", "Organization.Membership.Updated", "OrganizationRole.Created", "OrganizationRole.Deleted", "OrganizationRole.Data.Updated", "OrganizationRole.Scopes.Updated", "OrganizationScope.Created", "OrganizationScope.Deleted", "OrganizationScope.Data.Updated", "Identifier.Lockout", "Message.RateLimited"]>, "many">;
|
|
39
|
+
export declare const hookEventGuard: z.ZodEnum<[InteractionHookEvent.PostRegister, InteractionHookEvent.PostSignIn, InteractionHookEvent.PostSignInAdaptiveMfaTriggered, InteractionHookEvent.PostResetPassword, "User.Created", "User.Deleted", "User.Data.Updated", "User.SuspensionStatus.Updated", "Role.Created", "Role.Deleted", "Role.Data.Updated", "Role.Scopes.Updated", "Scope.Created", "Scope.Deleted", "Scope.Data.Updated", "Organization.Created", "Organization.Deleted", "Organization.Data.Updated", "Organization.Membership.Updated", "OrganizationRole.Created", "OrganizationRole.Deleted", "OrganizationRole.Data.Updated", "OrganizationRole.Scopes.Updated", "OrganizationScope.Created", "OrganizationScope.Deleted", "OrganizationScope.Data.Updated", "Identifier.Lockout", "Message.RateLimited", "Grant.LimitExceeded"]>;
|
|
40
|
+
export declare const hookEventsGuard: z.ZodArray<z.ZodEnum<[InteractionHookEvent.PostRegister, InteractionHookEvent.PostSignIn, InteractionHookEvent.PostSignInAdaptiveMfaTriggered, InteractionHookEvent.PostResetPassword, "User.Created", "User.Deleted", "User.Data.Updated", "User.SuspensionStatus.Updated", "Role.Created", "Role.Deleted", "Role.Data.Updated", "Role.Scopes.Updated", "Scope.Created", "Scope.Deleted", "Scope.Data.Updated", "Organization.Created", "Organization.Deleted", "Organization.Data.Updated", "Organization.Membership.Updated", "OrganizationRole.Created", "OrganizationRole.Deleted", "OrganizationRole.Data.Updated", "OrganizationRole.Scopes.Updated", "OrganizationScope.Created", "OrganizationScope.Deleted", "OrganizationScope.Data.Updated", "Identifier.Lockout", "Message.RateLimited", "Grant.LimitExceeded"]>, "many">;
|
|
41
41
|
export type HookEvents = z.infer<typeof hookEventsGuard>;
|
|
42
42
|
export declare const interactionHookEventGuard: z.ZodNativeEnum<typeof InteractionHookEvent>;
|
|
43
43
|
/**
|
|
@@ -63,6 +63,7 @@ export const hookEvents = Object.freeze([
|
|
|
63
63
|
'OrganizationScope.Data.Updated',
|
|
64
64
|
'Identifier.Lockout',
|
|
65
65
|
'Message.RateLimited',
|
|
66
|
+
'Grant.LimitExceeded',
|
|
66
67
|
]);
|
|
67
68
|
export const hookEventGuard = z.enum(hookEvents);
|
|
68
69
|
export const hookEventsGuard = hookEventGuard.array();
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { type InteractionEvent } from '../../types/interaction-event.js';
|
|
2
3
|
export type OneTimeTokenContext = {
|
|
3
4
|
jitOrganizationIds?: string[];
|
|
5
|
+
interactionEvent?: InteractionEvent;
|
|
4
6
|
};
|
|
5
7
|
export declare const oneTimeTokenContextGuard: z.ZodObject<{
|
|
6
8
|
jitOrganizationIds: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
9
|
+
interactionEvent: z.ZodOptional<z.ZodNativeEnum<typeof InteractionEvent>>;
|
|
7
10
|
}, "strip", z.ZodTypeAny, {
|
|
8
11
|
jitOrganizationIds?: string[] | undefined;
|
|
12
|
+
interactionEvent?: InteractionEvent | undefined;
|
|
9
13
|
}, {
|
|
10
14
|
jitOrganizationIds?: string[] | undefined;
|
|
15
|
+
interactionEvent?: InteractionEvent | undefined;
|
|
11
16
|
}>;
|
|
12
17
|
export declare enum OneTimeTokenStatus {
|
|
13
18
|
Active = "active",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { eventGuard } from '../../types/interaction-event.js';
|
|
2
3
|
export const oneTimeTokenContextGuard = z
|
|
3
4
|
.object({
|
|
4
5
|
jitOrganizationIds: z.string().array(),
|
|
6
|
+
interactionEvent: eventGuard,
|
|
5
7
|
})
|
|
6
8
|
.partial();
|
|
7
9
|
export var OneTimeTokenStatus;
|