@logto/schemas 1.28.0 → 1.30.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.29.0-1748832174-add-webauthn-related-origins.ts +20 -0
- package/alterations/1.29.0-1749005587-user-sso-identities-table-add-updated-at-column.ts +31 -0
- package/alterations/1.29.0-1749026308-add-oidc-session-extension-table.ts +41 -0
- package/alterations/1.29.0-1749523818-add-custom-profile-fields.ts +58 -0
- package/alterations/1.29.0-1749724664-drop-sie-order-constraint-from-custom-profile-fields.ts +20 -0
- package/alterations/1.29.0-1750663091-change-user-password-encrypted-length.ts +18 -0
- package/alterations/1.29.0-1750744518-add-secrets-table.ts +50 -0
- package/alterations/1.29.0-1750744539-add-secret-connector-relations-table.ts +109 -0
- package/alterations/1.30.0-1750744685-add-triggers-to-delete-secrets-on-social-identities-deletion.ts +81 -0
- package/alterations/1.30.0-1750748516-add-enable-token-storage-column-to-connectors-table.ts +20 -0
- package/alterations/1.30.0-1751255436-split-secret-connector-relatioins-table.ts +359 -0
- package/alterations/1.30.0-1751337183-add-require-mfa-on-sign-in-to-users.ts +20 -0
- package/alterations/1.30.0-1751400000-move-require-mfa-on-sign-in-to-logto-config.ts +21 -0
- package/alterations/1.30.0-1751529530-add-enable-token-storage-column-to-sso-connectors-table.ts +20 -0
- package/alterations/1.30.0-1752630302-alterate-enable-column-default-value-in-account-centers-table.ts +20 -0
- package/alterations/1.30.0-1753669579-add-organization-user-relations-foreign-key.ts +46 -0
- package/alterations-js/1.29.0-1748832174-add-webauthn-related-origins.js +16 -0
- package/alterations-js/1.29.0-1749005587-user-sso-identities-table-add-updated-at-column.js +25 -0
- package/alterations-js/1.29.0-1749026308-add-oidc-session-extension-table.js +33 -0
- package/alterations-js/1.29.0-1749523818-add-custom-profile-fields.js +52 -0
- package/alterations-js/1.29.0-1749724664-drop-sie-order-constraint-from-custom-profile-fields.js +16 -0
- package/alterations-js/1.29.0-1750663091-change-user-password-encrypted-length.js +14 -0
- package/alterations-js/1.29.0-1750744518-add-secrets-table.js +42 -0
- package/alterations-js/1.29.0-1750744539-add-secret-connector-relations-table.js +99 -0
- package/alterations-js/1.30.0-1750744685-add-triggers-to-delete-secrets-on-social-identities-deletion.js +76 -0
- package/alterations-js/1.30.0-1750748516-add-enable-token-storage-column-to-connectors-table.js +16 -0
- package/alterations-js/1.30.0-1751255436-split-secret-connector-relatioins-table.js +338 -0
- package/alterations-js/1.30.0-1751337183-add-require-mfa-on-sign-in-to-users.js +16 -0
- package/alterations-js/1.30.0-1751400000-move-require-mfa-on-sign-in-to-logto-config.js +17 -0
- package/alterations-js/1.30.0-1751529530-add-enable-token-storage-column-to-sso-connectors-table.js +16 -0
- package/alterations-js/1.30.0-1752630302-alterate-enable-column-default-value-in-account-centers-table.js +16 -0
- package/alterations-js/1.30.0-1753669579-add-organization-user-relations-foreign-key.js +38 -0
- package/lib/consts/oidc.d.ts +9 -1
- package/lib/consts/oidc.js +5 -0
- package/lib/db-entries/account-center.d.ts +4 -2
- package/lib/db-entries/account-center.js +5 -1
- package/lib/db-entries/connector.d.ts +5 -1
- package/lib/db-entries/connector.js +4 -0
- package/lib/db-entries/custom-profile-field.d.ts +32 -0
- package/lib/db-entries/custom-profile-field.js +58 -0
- package/lib/db-entries/index.d.ts +5 -0
- package/lib/db-entries/index.js +5 -0
- package/lib/db-entries/oidc-session-extension.d.ts +24 -0
- package/lib/db-entries/oidc-session-extension.js +42 -0
- package/lib/db-entries/secret-enterprise-sso-connector-relation.d.ts +28 -0
- package/lib/db-entries/secret-enterprise-sso-connector-relation.js +37 -0
- package/lib/db-entries/secret-social-connector-relation.d.ts +28 -0
- package/lib/db-entries/secret-social-connector-relation.js +37 -0
- package/lib/db-entries/secret.d.ts +44 -0
- package/lib/db-entries/secret.js +62 -0
- package/lib/db-entries/sso-connector.d.ts +5 -1
- package/lib/db-entries/sso-connector.js +4 -0
- package/lib/db-entries/user-sso-identity.d.ts +5 -1
- package/lib/db-entries/user-sso-identity.js +4 -0
- package/lib/db-entries/user.js +2 -2
- package/lib/foundations/jsonb-types/account-centers.d.ts +5 -0
- package/lib/foundations/jsonb-types/account-centers.js +2 -0
- package/lib/foundations/jsonb-types/custom-profile-fields.d.ts +441 -0
- package/lib/foundations/jsonb-types/custom-profile-fields.js +44 -0
- package/lib/foundations/jsonb-types/index.d.ts +2 -1
- package/lib/foundations/jsonb-types/index.js +2 -1
- package/lib/foundations/jsonb-types/secrets.d.ts +11 -0
- package/lib/foundations/jsonb-types/secrets.js +15 -0
- package/lib/foundations/jsonb-types/sign-in-experience.d.ts +3 -1
- package/lib/foundations/jsonb-types/sign-in-experience.js +2 -0
- package/lib/foundations/jsonb-types/users.d.ts +126 -0
- package/lib/foundations/jsonb-types/users.js +22 -10
- package/lib/types/connector.d.ts +39 -0
- package/lib/types/connector.js +1 -0
- package/lib/types/consent.d.ts +44 -0
- package/lib/types/custom-profile-fields.d.ts +2587 -0
- package/lib/types/custom-profile-fields.js +159 -0
- package/lib/types/index.d.ts +4 -0
- package/lib/types/index.js +4 -0
- package/lib/types/interactions.d.ts +181 -1
- package/lib/types/interactions.js +49 -1
- package/lib/types/log/interaction.d.ts +2 -1
- package/lib/types/logto-config/index.d.ts +1139 -18
- package/lib/types/logto-config/jwt-customizer.d.ts +2529 -32
- package/lib/types/logto-config/jwt-customizer.js +55 -1
- package/lib/types/logto-config/oidc-provider.d.ts +6 -6
- package/lib/types/mfa.d.ts +10 -10
- package/lib/types/secrets.d.ts +436 -0
- package/lib/types/secrets.js +73 -0
- package/lib/types/sign-in-experience.d.ts +21 -3
- package/lib/types/sign-in-experience.js +3 -1
- package/lib/types/sso-connector.d.ts +28 -2
- package/lib/types/sso-connector.js +3 -0
- package/lib/types/tenant.d.ts +1 -0
- package/lib/types/tenant.js +1 -0
- package/lib/types/user-logto-config.d.ts +45 -0
- package/lib/types/user-logto-config.js +18 -0
- package/lib/types/user.d.ts +626 -0
- package/lib/types/user.js +17 -1
- package/lib/types/verification-records/backup-code-verification.d.ts +47 -0
- package/lib/types/verification-records/backup-code-verification.js +12 -0
- package/lib/types/verification-records/code-verification.d.ts +89 -0
- package/lib/types/verification-records/code-verification.js +22 -0
- package/lib/types/verification-records/enterprise-sso-verification.d.ts +213 -0
- package/lib/types/verification-records/enterprise-sso-verification.js +15 -0
- package/lib/types/verification-records/index.d.ts +16 -0
- package/lib/types/verification-records/index.js +16 -0
- package/lib/types/verification-records/new-password-identity-verification.d.ts +85 -0
- package/lib/types/verification-records/new-password-identity-verification.js +20 -0
- package/lib/types/verification-records/one-time-token-verification.d.ts +55 -0
- package/lib/types/verification-records/one-time-token-verification.js +13 -0
- package/lib/types/verification-records/password-verification.d.ts +40 -0
- package/lib/types/verification-records/password-verification.js +9 -0
- package/lib/types/verification-records/social-verification.d.ts +270 -0
- package/lib/types/verification-records/social-verification.js +16 -0
- package/lib/types/verification-records/totp-verification.d.ts +47 -0
- package/lib/types/verification-records/totp-verification.js +12 -0
- package/lib/types/verification-records/web-authn-verification.d.ts +124 -0
- package/lib/types/verification-records/web-authn-verification.js +17 -0
- package/package.json +6 -6
- package/tables/account_centers.sql +2 -1
- package/tables/connectors.sql +4 -0
- package/tables/custom_profile_fields.sql +31 -0
- package/tables/oidc_model_instances.sql +2 -0
- package/tables/oidc_session_extensions.sql +18 -0
- package/tables/organization_user_relations.sql +4 -1
- package/tables/secret_enterprise_sso_connector_relations.sql +60 -0
- package/tables/secret_social_connector_relations.sql +75 -0
- package/tables/secrets.sql +26 -0
- package/tables/sso_connectors.sql +2 -0
- package/tables/user_sso_identities.sql +8 -0
- package/tables/users.sql +3 -2
- /package/lib/{foundations/jsonb-types/verification-records.d.ts → types/verification-records/verification-type.d.ts} +0 -0
- /package/lib/{foundations/jsonb-types/verification-records.js → types/verification-records/verification-type.js} +0 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type VerificationIdentifier } from '../interactions.js';
|
|
3
|
+
import { VerificationType } from './verification-type.js';
|
|
4
|
+
export type PasswordVerificationRecordData = {
|
|
5
|
+
id: string;
|
|
6
|
+
type: VerificationType.Password;
|
|
7
|
+
identifier: VerificationIdentifier;
|
|
8
|
+
verified: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare const passwordVerificationRecordDataGuard: z.ZodObject<{
|
|
11
|
+
id: z.ZodString;
|
|
12
|
+
type: z.ZodLiteral<VerificationType.Password>;
|
|
13
|
+
identifier: z.ZodObject<{
|
|
14
|
+
type: z.ZodUnion<[z.ZodNativeEnum<typeof import("../../index.js").SignInIdentifier>, z.ZodNativeEnum<typeof import("../../index.js").AdditionalIdentifier>]>;
|
|
15
|
+
value: z.ZodString;
|
|
16
|
+
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
value: string;
|
|
18
|
+
type: import("../../index.js").SignInIdentifier | import("../../index.js").AdditionalIdentifier;
|
|
19
|
+
}, {
|
|
20
|
+
value: string;
|
|
21
|
+
type: import("../../index.js").SignInIdentifier | import("../../index.js").AdditionalIdentifier;
|
|
22
|
+
}>;
|
|
23
|
+
verified: z.ZodBoolean;
|
|
24
|
+
}, "strip", z.ZodTypeAny, {
|
|
25
|
+
type: VerificationType.Password;
|
|
26
|
+
id: string;
|
|
27
|
+
identifier: {
|
|
28
|
+
value: string;
|
|
29
|
+
type: import("../../index.js").SignInIdentifier | import("../../index.js").AdditionalIdentifier;
|
|
30
|
+
};
|
|
31
|
+
verified: boolean;
|
|
32
|
+
}, {
|
|
33
|
+
type: VerificationType.Password;
|
|
34
|
+
id: string;
|
|
35
|
+
identifier: {
|
|
36
|
+
value: string;
|
|
37
|
+
type: import("../../index.js").SignInIdentifier | import("../../index.js").AdditionalIdentifier;
|
|
38
|
+
};
|
|
39
|
+
verified: boolean;
|
|
40
|
+
}>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { verificationIdentifierGuard } from '../interactions.js';
|
|
3
|
+
import { VerificationType } from './verification-type.js';
|
|
4
|
+
export const passwordVerificationRecordDataGuard = z.object({
|
|
5
|
+
id: z.string(),
|
|
6
|
+
type: z.literal(VerificationType.Password),
|
|
7
|
+
identifier: verificationIdentifierGuard,
|
|
8
|
+
verified: z.boolean(),
|
|
9
|
+
});
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { type ConnectorSession, type SocialUserInfo } from '@logto/connector-kit';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { type EncryptedTokenSet } from '../secrets.js';
|
|
4
|
+
import { VerificationType } from './verification-type.js';
|
|
5
|
+
/** The JSON data type for the SocialVerification record stored in the interaction storage */
|
|
6
|
+
export type SocialVerificationRecordData = {
|
|
7
|
+
id: string;
|
|
8
|
+
connectorId: string;
|
|
9
|
+
type: VerificationType.Social;
|
|
10
|
+
/**
|
|
11
|
+
* The social identity returned by the connector.
|
|
12
|
+
*/
|
|
13
|
+
socialUserInfo?: SocialUserInfo;
|
|
14
|
+
encryptedTokenSet?: EncryptedTokenSet;
|
|
15
|
+
/**
|
|
16
|
+
* The connector session result
|
|
17
|
+
*/
|
|
18
|
+
connectorSession?: ConnectorSession;
|
|
19
|
+
};
|
|
20
|
+
export declare const socialVerificationRecordDataGuard: z.ZodObject<{
|
|
21
|
+
id: z.ZodString;
|
|
22
|
+
connectorId: z.ZodString;
|
|
23
|
+
type: z.ZodLiteral<VerificationType.Social>;
|
|
24
|
+
socialUserInfo: z.ZodOptional<z.ZodObject<{
|
|
25
|
+
id: z.ZodString;
|
|
26
|
+
email: z.ZodOptional<z.ZodString>;
|
|
27
|
+
phone: z.ZodOptional<z.ZodString>;
|
|
28
|
+
name: z.ZodOptional<z.ZodString>;
|
|
29
|
+
avatar: z.ZodOptional<z.ZodString>;
|
|
30
|
+
rawData: z.ZodOptional<z.ZodType<import("@withtyped/server").Json, z.ZodTypeDef, import("@withtyped/server").Json>>;
|
|
31
|
+
}, "strip", z.ZodTypeAny, {
|
|
32
|
+
id: string;
|
|
33
|
+
name?: string | undefined;
|
|
34
|
+
email?: string | undefined;
|
|
35
|
+
phone?: string | undefined;
|
|
36
|
+
avatar?: string | undefined;
|
|
37
|
+
rawData?: import("@withtyped/server").Json | undefined;
|
|
38
|
+
}, {
|
|
39
|
+
id: string;
|
|
40
|
+
name?: string | undefined;
|
|
41
|
+
email?: string | undefined;
|
|
42
|
+
phone?: string | undefined;
|
|
43
|
+
avatar?: string | undefined;
|
|
44
|
+
rawData?: import("@withtyped/server").Json | undefined;
|
|
45
|
+
}>>;
|
|
46
|
+
encryptedTokenSet: z.ZodOptional<z.ZodObject<{
|
|
47
|
+
encryptedTokenSetBase64: z.ZodString;
|
|
48
|
+
metadata: z.ZodObject<{
|
|
49
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
50
|
+
expiresAt: z.ZodOptional<z.ZodNumber>;
|
|
51
|
+
tokenType: z.ZodOptional<z.ZodString>;
|
|
52
|
+
hasRefreshToken: z.ZodBoolean;
|
|
53
|
+
}, "strip", z.ZodTypeAny, {
|
|
54
|
+
hasRefreshToken: boolean;
|
|
55
|
+
scope?: string | undefined;
|
|
56
|
+
expiresAt?: number | undefined;
|
|
57
|
+
tokenType?: string | undefined;
|
|
58
|
+
}, {
|
|
59
|
+
hasRefreshToken: boolean;
|
|
60
|
+
scope?: string | undefined;
|
|
61
|
+
expiresAt?: number | undefined;
|
|
62
|
+
tokenType?: string | undefined;
|
|
63
|
+
}>;
|
|
64
|
+
}, "strip", z.ZodTypeAny, {
|
|
65
|
+
metadata: {
|
|
66
|
+
hasRefreshToken: boolean;
|
|
67
|
+
scope?: string | undefined;
|
|
68
|
+
expiresAt?: number | undefined;
|
|
69
|
+
tokenType?: string | undefined;
|
|
70
|
+
};
|
|
71
|
+
encryptedTokenSetBase64: string;
|
|
72
|
+
}, {
|
|
73
|
+
metadata: {
|
|
74
|
+
hasRefreshToken: boolean;
|
|
75
|
+
scope?: string | undefined;
|
|
76
|
+
expiresAt?: number | undefined;
|
|
77
|
+
tokenType?: string | undefined;
|
|
78
|
+
};
|
|
79
|
+
encryptedTokenSetBase64: string;
|
|
80
|
+
}>>;
|
|
81
|
+
connectorSession: z.ZodOptional<z.ZodObject<{
|
|
82
|
+
nonce: z.ZodOptional<z.ZodString>;
|
|
83
|
+
redirectUri: z.ZodOptional<z.ZodString>;
|
|
84
|
+
connectorId: z.ZodOptional<z.ZodString>;
|
|
85
|
+
connectorFactoryId: z.ZodOptional<z.ZodString>;
|
|
86
|
+
jti: z.ZodOptional<z.ZodString>;
|
|
87
|
+
state: z.ZodOptional<z.ZodString>;
|
|
88
|
+
}, "strip", z.ZodUnknown, z.objectOutputType<{
|
|
89
|
+
nonce: z.ZodOptional<z.ZodString>;
|
|
90
|
+
redirectUri: z.ZodOptional<z.ZodString>;
|
|
91
|
+
connectorId: z.ZodOptional<z.ZodString>;
|
|
92
|
+
connectorFactoryId: z.ZodOptional<z.ZodString>;
|
|
93
|
+
jti: z.ZodOptional<z.ZodString>;
|
|
94
|
+
state: z.ZodOptional<z.ZodString>;
|
|
95
|
+
}, z.ZodUnknown, "strip">, z.objectInputType<{
|
|
96
|
+
nonce: z.ZodOptional<z.ZodString>;
|
|
97
|
+
redirectUri: z.ZodOptional<z.ZodString>;
|
|
98
|
+
connectorId: z.ZodOptional<z.ZodString>;
|
|
99
|
+
connectorFactoryId: z.ZodOptional<z.ZodString>;
|
|
100
|
+
jti: z.ZodOptional<z.ZodString>;
|
|
101
|
+
state: z.ZodOptional<z.ZodString>;
|
|
102
|
+
}, z.ZodUnknown, "strip">>>;
|
|
103
|
+
}, "strip", z.ZodTypeAny, {
|
|
104
|
+
type: VerificationType.Social;
|
|
105
|
+
id: string;
|
|
106
|
+
connectorId: string;
|
|
107
|
+
encryptedTokenSet?: {
|
|
108
|
+
metadata: {
|
|
109
|
+
hasRefreshToken: boolean;
|
|
110
|
+
scope?: string | undefined;
|
|
111
|
+
expiresAt?: number | undefined;
|
|
112
|
+
tokenType?: string | undefined;
|
|
113
|
+
};
|
|
114
|
+
encryptedTokenSetBase64: string;
|
|
115
|
+
} | undefined;
|
|
116
|
+
socialUserInfo?: {
|
|
117
|
+
id: string;
|
|
118
|
+
name?: string | undefined;
|
|
119
|
+
email?: string | undefined;
|
|
120
|
+
phone?: string | undefined;
|
|
121
|
+
avatar?: string | undefined;
|
|
122
|
+
rawData?: import("@withtyped/server").Json | undefined;
|
|
123
|
+
} | undefined;
|
|
124
|
+
connectorSession?: z.objectOutputType<{
|
|
125
|
+
nonce: z.ZodOptional<z.ZodString>;
|
|
126
|
+
redirectUri: z.ZodOptional<z.ZodString>;
|
|
127
|
+
connectorId: z.ZodOptional<z.ZodString>;
|
|
128
|
+
connectorFactoryId: z.ZodOptional<z.ZodString>;
|
|
129
|
+
jti: z.ZodOptional<z.ZodString>;
|
|
130
|
+
state: z.ZodOptional<z.ZodString>;
|
|
131
|
+
}, z.ZodUnknown, "strip"> | undefined;
|
|
132
|
+
}, {
|
|
133
|
+
type: VerificationType.Social;
|
|
134
|
+
id: string;
|
|
135
|
+
connectorId: string;
|
|
136
|
+
encryptedTokenSet?: {
|
|
137
|
+
metadata: {
|
|
138
|
+
hasRefreshToken: boolean;
|
|
139
|
+
scope?: string | undefined;
|
|
140
|
+
expiresAt?: number | undefined;
|
|
141
|
+
tokenType?: string | undefined;
|
|
142
|
+
};
|
|
143
|
+
encryptedTokenSetBase64: string;
|
|
144
|
+
} | undefined;
|
|
145
|
+
socialUserInfo?: {
|
|
146
|
+
id: string;
|
|
147
|
+
name?: string | undefined;
|
|
148
|
+
email?: string | undefined;
|
|
149
|
+
phone?: string | undefined;
|
|
150
|
+
avatar?: string | undefined;
|
|
151
|
+
rawData?: import("@withtyped/server").Json | undefined;
|
|
152
|
+
} | undefined;
|
|
153
|
+
connectorSession?: z.objectInputType<{
|
|
154
|
+
nonce: z.ZodOptional<z.ZodString>;
|
|
155
|
+
redirectUri: z.ZodOptional<z.ZodString>;
|
|
156
|
+
connectorId: z.ZodOptional<z.ZodString>;
|
|
157
|
+
connectorFactoryId: z.ZodOptional<z.ZodString>;
|
|
158
|
+
jti: z.ZodOptional<z.ZodString>;
|
|
159
|
+
state: z.ZodOptional<z.ZodString>;
|
|
160
|
+
}, z.ZodUnknown, "strip"> | undefined;
|
|
161
|
+
}>;
|
|
162
|
+
export type SanitizedSocialVerificationRecordData = Omit<SocialVerificationRecordData, 'encryptedTokenSet' | 'connectorSession'>;
|
|
163
|
+
export declare const sanitizedSocialVerificationRecordDataGuard: z.ZodObject<Omit<{
|
|
164
|
+
id: z.ZodString;
|
|
165
|
+
connectorId: z.ZodString;
|
|
166
|
+
type: z.ZodLiteral<VerificationType.Social>;
|
|
167
|
+
socialUserInfo: z.ZodOptional<z.ZodObject<{
|
|
168
|
+
id: z.ZodString;
|
|
169
|
+
email: z.ZodOptional<z.ZodString>;
|
|
170
|
+
phone: z.ZodOptional<z.ZodString>;
|
|
171
|
+
name: z.ZodOptional<z.ZodString>;
|
|
172
|
+
avatar: z.ZodOptional<z.ZodString>;
|
|
173
|
+
rawData: z.ZodOptional<z.ZodType<import("@withtyped/server").Json, z.ZodTypeDef, import("@withtyped/server").Json>>;
|
|
174
|
+
}, "strip", z.ZodTypeAny, {
|
|
175
|
+
id: string;
|
|
176
|
+
name?: string | undefined;
|
|
177
|
+
email?: string | undefined;
|
|
178
|
+
phone?: string | undefined;
|
|
179
|
+
avatar?: string | undefined;
|
|
180
|
+
rawData?: import("@withtyped/server").Json | undefined;
|
|
181
|
+
}, {
|
|
182
|
+
id: string;
|
|
183
|
+
name?: string | undefined;
|
|
184
|
+
email?: string | undefined;
|
|
185
|
+
phone?: string | undefined;
|
|
186
|
+
avatar?: string | undefined;
|
|
187
|
+
rawData?: import("@withtyped/server").Json | undefined;
|
|
188
|
+
}>>;
|
|
189
|
+
encryptedTokenSet: z.ZodOptional<z.ZodObject<{
|
|
190
|
+
encryptedTokenSetBase64: z.ZodString;
|
|
191
|
+
metadata: z.ZodObject<{
|
|
192
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
193
|
+
expiresAt: z.ZodOptional<z.ZodNumber>;
|
|
194
|
+
tokenType: z.ZodOptional<z.ZodString>;
|
|
195
|
+
hasRefreshToken: z.ZodBoolean;
|
|
196
|
+
}, "strip", z.ZodTypeAny, {
|
|
197
|
+
hasRefreshToken: boolean;
|
|
198
|
+
scope?: string | undefined;
|
|
199
|
+
expiresAt?: number | undefined;
|
|
200
|
+
tokenType?: string | undefined;
|
|
201
|
+
}, {
|
|
202
|
+
hasRefreshToken: boolean;
|
|
203
|
+
scope?: string | undefined;
|
|
204
|
+
expiresAt?: number | undefined;
|
|
205
|
+
tokenType?: string | undefined;
|
|
206
|
+
}>;
|
|
207
|
+
}, "strip", z.ZodTypeAny, {
|
|
208
|
+
metadata: {
|
|
209
|
+
hasRefreshToken: boolean;
|
|
210
|
+
scope?: string | undefined;
|
|
211
|
+
expiresAt?: number | undefined;
|
|
212
|
+
tokenType?: string | undefined;
|
|
213
|
+
};
|
|
214
|
+
encryptedTokenSetBase64: string;
|
|
215
|
+
}, {
|
|
216
|
+
metadata: {
|
|
217
|
+
hasRefreshToken: boolean;
|
|
218
|
+
scope?: string | undefined;
|
|
219
|
+
expiresAt?: number | undefined;
|
|
220
|
+
tokenType?: string | undefined;
|
|
221
|
+
};
|
|
222
|
+
encryptedTokenSetBase64: string;
|
|
223
|
+
}>>;
|
|
224
|
+
connectorSession: z.ZodOptional<z.ZodObject<{
|
|
225
|
+
nonce: z.ZodOptional<z.ZodString>;
|
|
226
|
+
redirectUri: z.ZodOptional<z.ZodString>;
|
|
227
|
+
connectorId: z.ZodOptional<z.ZodString>;
|
|
228
|
+
connectorFactoryId: z.ZodOptional<z.ZodString>;
|
|
229
|
+
jti: z.ZodOptional<z.ZodString>;
|
|
230
|
+
state: z.ZodOptional<z.ZodString>;
|
|
231
|
+
}, "strip", z.ZodUnknown, z.objectOutputType<{
|
|
232
|
+
nonce: z.ZodOptional<z.ZodString>;
|
|
233
|
+
redirectUri: z.ZodOptional<z.ZodString>;
|
|
234
|
+
connectorId: z.ZodOptional<z.ZodString>;
|
|
235
|
+
connectorFactoryId: z.ZodOptional<z.ZodString>;
|
|
236
|
+
jti: z.ZodOptional<z.ZodString>;
|
|
237
|
+
state: z.ZodOptional<z.ZodString>;
|
|
238
|
+
}, z.ZodUnknown, "strip">, z.objectInputType<{
|
|
239
|
+
nonce: z.ZodOptional<z.ZodString>;
|
|
240
|
+
redirectUri: z.ZodOptional<z.ZodString>;
|
|
241
|
+
connectorId: z.ZodOptional<z.ZodString>;
|
|
242
|
+
connectorFactoryId: z.ZodOptional<z.ZodString>;
|
|
243
|
+
jti: z.ZodOptional<z.ZodString>;
|
|
244
|
+
state: z.ZodOptional<z.ZodString>;
|
|
245
|
+
}, z.ZodUnknown, "strip">>>;
|
|
246
|
+
}, "encryptedTokenSet" | "connectorSession">, "strip", z.ZodTypeAny, {
|
|
247
|
+
type: VerificationType.Social;
|
|
248
|
+
id: string;
|
|
249
|
+
connectorId: string;
|
|
250
|
+
socialUserInfo?: {
|
|
251
|
+
id: string;
|
|
252
|
+
name?: string | undefined;
|
|
253
|
+
email?: string | undefined;
|
|
254
|
+
phone?: string | undefined;
|
|
255
|
+
avatar?: string | undefined;
|
|
256
|
+
rawData?: import("@withtyped/server").Json | undefined;
|
|
257
|
+
} | undefined;
|
|
258
|
+
}, {
|
|
259
|
+
type: VerificationType.Social;
|
|
260
|
+
id: string;
|
|
261
|
+
connectorId: string;
|
|
262
|
+
socialUserInfo?: {
|
|
263
|
+
id: string;
|
|
264
|
+
name?: string | undefined;
|
|
265
|
+
email?: string | undefined;
|
|
266
|
+
phone?: string | undefined;
|
|
267
|
+
avatar?: string | undefined;
|
|
268
|
+
rawData?: import("@withtyped/server").Json | undefined;
|
|
269
|
+
} | undefined;
|
|
270
|
+
}>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { connectorSessionGuard, socialUserInfoGuard, } from '@logto/connector-kit';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { encryptedTokenSetGuard } from '../secrets.js';
|
|
4
|
+
import { VerificationType } from './verification-type.js';
|
|
5
|
+
export const socialVerificationRecordDataGuard = z.object({
|
|
6
|
+
id: z.string(),
|
|
7
|
+
connectorId: z.string(),
|
|
8
|
+
type: z.literal(VerificationType.Social),
|
|
9
|
+
socialUserInfo: socialUserInfoGuard.optional(),
|
|
10
|
+
encryptedTokenSet: encryptedTokenSetGuard.optional(),
|
|
11
|
+
connectorSession: connectorSessionGuard.optional(),
|
|
12
|
+
});
|
|
13
|
+
export const sanitizedSocialVerificationRecordDataGuard = socialVerificationRecordDataGuard.omit({
|
|
14
|
+
encryptedTokenSet: true,
|
|
15
|
+
connectorSession: true,
|
|
16
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { VerificationType } from './verification-type.js';
|
|
3
|
+
export type TotpVerificationRecordData = {
|
|
4
|
+
id: string;
|
|
5
|
+
type: VerificationType.TOTP;
|
|
6
|
+
/** UserId is required for verifying or binding new TOTP */
|
|
7
|
+
userId: string;
|
|
8
|
+
secret?: string;
|
|
9
|
+
verified: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare const totpVerificationRecordDataGuard: z.ZodObject<{
|
|
12
|
+
id: z.ZodString;
|
|
13
|
+
type: z.ZodLiteral<VerificationType.TOTP>;
|
|
14
|
+
userId: z.ZodString;
|
|
15
|
+
secret: z.ZodOptional<z.ZodString>;
|
|
16
|
+
verified: z.ZodBoolean;
|
|
17
|
+
}, "strip", z.ZodTypeAny, {
|
|
18
|
+
type: VerificationType.TOTP;
|
|
19
|
+
id: string;
|
|
20
|
+
userId: string;
|
|
21
|
+
verified: boolean;
|
|
22
|
+
secret?: string | undefined;
|
|
23
|
+
}, {
|
|
24
|
+
type: VerificationType.TOTP;
|
|
25
|
+
id: string;
|
|
26
|
+
userId: string;
|
|
27
|
+
verified: boolean;
|
|
28
|
+
secret?: string | undefined;
|
|
29
|
+
}>;
|
|
30
|
+
export type SanitizedTotpVerificationRecordData = Omit<TotpVerificationRecordData, 'secret'>;
|
|
31
|
+
export declare const sanitizedTotpVerificationRecordDataGuard: z.ZodObject<Omit<{
|
|
32
|
+
id: z.ZodString;
|
|
33
|
+
type: z.ZodLiteral<VerificationType.TOTP>;
|
|
34
|
+
userId: z.ZodString;
|
|
35
|
+
secret: z.ZodOptional<z.ZodString>;
|
|
36
|
+
verified: z.ZodBoolean;
|
|
37
|
+
}, "secret">, "strip", z.ZodTypeAny, {
|
|
38
|
+
type: VerificationType.TOTP;
|
|
39
|
+
id: string;
|
|
40
|
+
userId: string;
|
|
41
|
+
verified: boolean;
|
|
42
|
+
}, {
|
|
43
|
+
type: VerificationType.TOTP;
|
|
44
|
+
id: string;
|
|
45
|
+
userId: string;
|
|
46
|
+
verified: boolean;
|
|
47
|
+
}>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { VerificationType } from './verification-type.js';
|
|
3
|
+
export const totpVerificationRecordDataGuard = z.object({
|
|
4
|
+
id: z.string(),
|
|
5
|
+
type: z.literal(VerificationType.TOTP),
|
|
6
|
+
userId: z.string(),
|
|
7
|
+
secret: z.string().optional(),
|
|
8
|
+
verified: z.boolean(),
|
|
9
|
+
});
|
|
10
|
+
export const sanitizedTotpVerificationRecordDataGuard = totpVerificationRecordDataGuard.omit({
|
|
11
|
+
secret: true,
|
|
12
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type BindWebAuthn } from '../interactions.js';
|
|
3
|
+
import { VerificationType } from './verification-type.js';
|
|
4
|
+
export type WebAuthnVerificationRecordData = {
|
|
5
|
+
id: string;
|
|
6
|
+
type: VerificationType.WebAuthn;
|
|
7
|
+
/** UserId is required for verifying or binding new TOTP */
|
|
8
|
+
userId: string;
|
|
9
|
+
verified: boolean;
|
|
10
|
+
/** The challenge generated for the WebAuthn registration */
|
|
11
|
+
registrationChallenge?: string;
|
|
12
|
+
/** The challenge generated for the WebAuthn authentication */
|
|
13
|
+
authenticationChallenge?: string;
|
|
14
|
+
registrationInfo?: BindWebAuthn;
|
|
15
|
+
};
|
|
16
|
+
export declare const webAuthnVerificationRecordDataGuard: z.ZodObject<{
|
|
17
|
+
id: z.ZodString;
|
|
18
|
+
type: z.ZodLiteral<VerificationType.WebAuthn>;
|
|
19
|
+
userId: z.ZodString;
|
|
20
|
+
verified: z.ZodBoolean;
|
|
21
|
+
registrationChallenge: z.ZodOptional<z.ZodString>;
|
|
22
|
+
authenticationChallenge: z.ZodOptional<z.ZodString>;
|
|
23
|
+
registrationInfo: z.ZodOptional<z.ZodObject<{
|
|
24
|
+
type: z.ZodLiteral<import("../../index.js").MfaFactor.WebAuthn>;
|
|
25
|
+
credentialId: z.ZodString;
|
|
26
|
+
publicKey: z.ZodString;
|
|
27
|
+
transports: z.ZodArray<z.ZodEnum<["usb", "nfc", "ble", "internal", "cable", "hybrid", "smart-card"]>, "many">;
|
|
28
|
+
counter: z.ZodNumber;
|
|
29
|
+
agent: z.ZodString;
|
|
30
|
+
name: z.ZodOptional<z.ZodString>;
|
|
31
|
+
}, "strip", z.ZodTypeAny, {
|
|
32
|
+
type: import("../../index.js").MfaFactor.WebAuthn;
|
|
33
|
+
credentialId: string;
|
|
34
|
+
publicKey: string;
|
|
35
|
+
transports: ("usb" | "nfc" | "ble" | "internal" | "cable" | "hybrid" | "smart-card")[];
|
|
36
|
+
counter: number;
|
|
37
|
+
agent: string;
|
|
38
|
+
name?: string | undefined;
|
|
39
|
+
}, {
|
|
40
|
+
type: import("../../index.js").MfaFactor.WebAuthn;
|
|
41
|
+
credentialId: string;
|
|
42
|
+
publicKey: string;
|
|
43
|
+
transports: ("usb" | "nfc" | "ble" | "internal" | "cable" | "hybrid" | "smart-card")[];
|
|
44
|
+
counter: number;
|
|
45
|
+
agent: string;
|
|
46
|
+
name?: string | undefined;
|
|
47
|
+
}>>;
|
|
48
|
+
}, "strip", z.ZodTypeAny, {
|
|
49
|
+
type: VerificationType.WebAuthn;
|
|
50
|
+
id: string;
|
|
51
|
+
userId: string;
|
|
52
|
+
verified: boolean;
|
|
53
|
+
registrationChallenge?: string | undefined;
|
|
54
|
+
authenticationChallenge?: string | undefined;
|
|
55
|
+
registrationInfo?: {
|
|
56
|
+
type: import("../../index.js").MfaFactor.WebAuthn;
|
|
57
|
+
credentialId: string;
|
|
58
|
+
publicKey: string;
|
|
59
|
+
transports: ("usb" | "nfc" | "ble" | "internal" | "cable" | "hybrid" | "smart-card")[];
|
|
60
|
+
counter: number;
|
|
61
|
+
agent: string;
|
|
62
|
+
name?: string | undefined;
|
|
63
|
+
} | undefined;
|
|
64
|
+
}, {
|
|
65
|
+
type: VerificationType.WebAuthn;
|
|
66
|
+
id: string;
|
|
67
|
+
userId: string;
|
|
68
|
+
verified: boolean;
|
|
69
|
+
registrationChallenge?: string | undefined;
|
|
70
|
+
authenticationChallenge?: string | undefined;
|
|
71
|
+
registrationInfo?: {
|
|
72
|
+
type: import("../../index.js").MfaFactor.WebAuthn;
|
|
73
|
+
credentialId: string;
|
|
74
|
+
publicKey: string;
|
|
75
|
+
transports: ("usb" | "nfc" | "ble" | "internal" | "cable" | "hybrid" | "smart-card")[];
|
|
76
|
+
counter: number;
|
|
77
|
+
agent: string;
|
|
78
|
+
name?: string | undefined;
|
|
79
|
+
} | undefined;
|
|
80
|
+
}>;
|
|
81
|
+
export type SanitizedWebAuthnVerificationRecordData = Omit<WebAuthnVerificationRecordData, 'registrationInfo' | 'registrationChallenge' | 'authenticationChallenge'>;
|
|
82
|
+
export declare const sanitizedWebAuthnVerificationRecordDataGuard: z.ZodObject<Omit<{
|
|
83
|
+
id: z.ZodString;
|
|
84
|
+
type: z.ZodLiteral<VerificationType.WebAuthn>;
|
|
85
|
+
userId: z.ZodString;
|
|
86
|
+
verified: z.ZodBoolean;
|
|
87
|
+
registrationChallenge: z.ZodOptional<z.ZodString>;
|
|
88
|
+
authenticationChallenge: z.ZodOptional<z.ZodString>;
|
|
89
|
+
registrationInfo: z.ZodOptional<z.ZodObject<{
|
|
90
|
+
type: z.ZodLiteral<import("../../index.js").MfaFactor.WebAuthn>;
|
|
91
|
+
credentialId: z.ZodString;
|
|
92
|
+
publicKey: z.ZodString;
|
|
93
|
+
transports: z.ZodArray<z.ZodEnum<["usb", "nfc", "ble", "internal", "cable", "hybrid", "smart-card"]>, "many">;
|
|
94
|
+
counter: z.ZodNumber;
|
|
95
|
+
agent: z.ZodString;
|
|
96
|
+
name: z.ZodOptional<z.ZodString>;
|
|
97
|
+
}, "strip", z.ZodTypeAny, {
|
|
98
|
+
type: import("../../index.js").MfaFactor.WebAuthn;
|
|
99
|
+
credentialId: string;
|
|
100
|
+
publicKey: string;
|
|
101
|
+
transports: ("usb" | "nfc" | "ble" | "internal" | "cable" | "hybrid" | "smart-card")[];
|
|
102
|
+
counter: number;
|
|
103
|
+
agent: string;
|
|
104
|
+
name?: string | undefined;
|
|
105
|
+
}, {
|
|
106
|
+
type: import("../../index.js").MfaFactor.WebAuthn;
|
|
107
|
+
credentialId: string;
|
|
108
|
+
publicKey: string;
|
|
109
|
+
transports: ("usb" | "nfc" | "ble" | "internal" | "cable" | "hybrid" | "smart-card")[];
|
|
110
|
+
counter: number;
|
|
111
|
+
agent: string;
|
|
112
|
+
name?: string | undefined;
|
|
113
|
+
}>>;
|
|
114
|
+
}, "registrationChallenge" | "authenticationChallenge" | "registrationInfo">, "strip", z.ZodTypeAny, {
|
|
115
|
+
type: VerificationType.WebAuthn;
|
|
116
|
+
id: string;
|
|
117
|
+
userId: string;
|
|
118
|
+
verified: boolean;
|
|
119
|
+
}, {
|
|
120
|
+
type: VerificationType.WebAuthn;
|
|
121
|
+
id: string;
|
|
122
|
+
userId: string;
|
|
123
|
+
verified: boolean;
|
|
124
|
+
}>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { bindWebAuthnGuard } from '../interactions.js';
|
|
3
|
+
import { VerificationType } from './verification-type.js';
|
|
4
|
+
export const webAuthnVerificationRecordDataGuard = z.object({
|
|
5
|
+
id: z.string(),
|
|
6
|
+
type: z.literal(VerificationType.WebAuthn),
|
|
7
|
+
userId: z.string(),
|
|
8
|
+
verified: z.boolean(),
|
|
9
|
+
registrationChallenge: z.string().optional(),
|
|
10
|
+
authenticationChallenge: z.string().optional(),
|
|
11
|
+
registrationInfo: bindWebAuthnGuard.optional(),
|
|
12
|
+
});
|
|
13
|
+
export const sanitizedWebAuthnVerificationRecordDataGuard = webAuthnVerificationRecordDataGuard.omit({
|
|
14
|
+
registrationInfo: true,
|
|
15
|
+
registrationChallenge: true,
|
|
16
|
+
authenticationChallenge: true,
|
|
17
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/schemas",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
4
4
|
"author": "Silverhand Inc. <contact@silverhand.io>",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -63,14 +63,14 @@
|
|
|
63
63
|
},
|
|
64
64
|
"prettier": "@silverhand/eslint-config/.prettierrc",
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@
|
|
66
|
+
"@withtyped/server": "^0.14.0",
|
|
67
|
+
"nanoid": "^5.0.9",
|
|
68
|
+
"@logto/connector-kit": "^4.4.0",
|
|
67
69
|
"@logto/core-kit": "^2.6.0",
|
|
68
70
|
"@logto/language-kit": "^1.2.0",
|
|
69
|
-
"@logto/phrases": "^1.19.0",
|
|
70
|
-
"@logto/phrases-experience": "^1.10.0",
|
|
71
71
|
"@logto/shared": "^3.3.0",
|
|
72
|
-
"@
|
|
73
|
-
"
|
|
72
|
+
"@logto/phrases": "^1.19.0",
|
|
73
|
+
"@logto/phrases-experience": "^1.10.0"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"zod": "3.24.3"
|
|
@@ -3,8 +3,9 @@ create table account_centers (
|
|
|
3
3
|
references tenants (id) on update cascade on delete cascade,
|
|
4
4
|
id varchar(21) not null,
|
|
5
5
|
/** The whole feature can be disabled */
|
|
6
|
-
enabled boolean not null default
|
|
6
|
+
enabled boolean not null default true,
|
|
7
7
|
/** Control each fields */
|
|
8
8
|
fields jsonb /* @use AccountCenterFieldControl */ not null default '{}'::jsonb,
|
|
9
|
+
webauthn_related_origins jsonb /* @use WebauthnRelatedOrigins */ not null default '[]'::jsonb,
|
|
9
10
|
primary key (tenant_id, id)
|
|
10
11
|
);
|
package/tables/connectors.sql
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
/* init_order = 1 */
|
|
2
|
+
|
|
1
3
|
create table connectors (
|
|
2
4
|
tenant_id varchar(21) not null
|
|
3
5
|
references tenants (id) on update cascade on delete cascade,
|
|
4
6
|
id varchar(128) not null,
|
|
5
7
|
sync_profile boolean not null default FALSE,
|
|
8
|
+
/** Whether the token storage is enabled for this connector. Only applied for OAuth2/OIDC social connectors. */
|
|
9
|
+
enable_token_storage boolean not null default FALSE,
|
|
6
10
|
connector_id varchar(128) not null,
|
|
7
11
|
config jsonb /* @use JsonObject */ not null default '{}'::jsonb,
|
|
8
12
|
metadata jsonb /* @use ConfigurableConnectorMetadata */ not null default '{}'::jsonb,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
create table custom_profile_fields (
|
|
2
|
+
tenant_id varchar(21) not null
|
|
3
|
+
references tenants (id) on update cascade on delete cascade,
|
|
4
|
+
id varchar(21) not null,
|
|
5
|
+
name varchar(128) not null,
|
|
6
|
+
type varchar(128) not null /* @use CustomProfileFieldType */,
|
|
7
|
+
label varchar(128) not null default '',
|
|
8
|
+
description varchar(256),
|
|
9
|
+
required boolean not null default false,
|
|
10
|
+
config jsonb /* @use CustomProfileFieldConfig */ not null default '{}'::jsonb,
|
|
11
|
+
created_at timestamptz not null default(now()),
|
|
12
|
+
sie_order int2 not null default 0,
|
|
13
|
+
primary key (id),
|
|
14
|
+
constraint custom_profile_fields__name
|
|
15
|
+
unique (tenant_id, name)
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
create or replace function custom_profile_fields__increment_sie_order() returns trigger as
|
|
19
|
+
$$ begin
|
|
20
|
+
new.sie_order = (
|
|
21
|
+
select coalesce(max(sie_order), 0)
|
|
22
|
+
from custom_profile_fields
|
|
23
|
+
where tenant_id = (
|
|
24
|
+
select id from tenants where db_user = current_user
|
|
25
|
+
)
|
|
26
|
+
) + 1;
|
|
27
|
+
return new;
|
|
28
|
+
end; $$ language plpgsql;
|
|
29
|
+
|
|
30
|
+
create trigger custom_profile_fields__increment_sie_order before insert on custom_profile_fields
|
|
31
|
+
for each row execute procedure custom_profile_fields__increment_sie_order();
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* init_order = 2 */
|
|
2
|
+
|
|
3
|
+
create table oidc_session_extensions (
|
|
4
|
+
tenant_id varchar(21) not null
|
|
5
|
+
references tenants (id) on update cascade on delete cascade,
|
|
6
|
+
session_uid varchar(128) not null,
|
|
7
|
+
account_id varchar(12) not null
|
|
8
|
+
references users (id) on update cascade on delete cascade,
|
|
9
|
+
last_submission jsonb /* @use JsonObject */ not null default '{}'::jsonb,
|
|
10
|
+
created_at timestamptz not null default(now()),
|
|
11
|
+
updated_at timestamptz not null default(now()),
|
|
12
|
+
primary key (tenant_id, session_uid)
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
create trigger set_updated_at
|
|
16
|
+
before update on oidc_session_extensions
|
|
17
|
+
for each row
|
|
18
|
+
execute procedure set_updated_at();
|
|
@@ -8,5 +8,8 @@ create table organization_user_relations (
|
|
|
8
8
|
references organizations (id) on update cascade on delete cascade,
|
|
9
9
|
user_id varchar(21) not null
|
|
10
10
|
references users (id) on update cascade on delete cascade,
|
|
11
|
-
primary key (tenant_id, organization_id, user_id)
|
|
11
|
+
primary key (tenant_id, organization_id, user_id),
|
|
12
|
+
constraint organization_user_relations__user_id__fk
|
|
13
|
+
foreign key (tenant_id, user_id)
|
|
14
|
+
references users (tenant_id, id) on update cascade on delete cascade
|
|
12
15
|
);
|