@logto/schemas 1.40.1 → 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.41.0-1779864280-add-password-expiration-policy.ts +23 -0
- package/alterations/1.41.0-1779864281-add-is-password-expired-to-users.ts +18 -0
- package/alterations/1.41.0-1780358400-drop-oidc-model-instances-legacy-grant-id-index.ts +25 -0
- package/alterations/1.41.0-1780381219-add-username-policy.ts +41 -0
- package/alterations/1.41.0-1780643665-set-sign-up-profile-fields-default.ts +20 -0
- package/alterations/1.41.0-1780906060-add-verification-code-policy.ts +19 -0
- package/alterations/1.41.0-1781689400-add-sentinel-activities-created-at-index.ts +25 -0
- package/alterations/1.41.0-1782354362-set-admin-account-center-profile-fields.ts +28 -0
- package/alterations/1.41.0-1782375106-cover-service-logs-tenant-type-index-with-created-at.ts +36 -0
- 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.41.0-1779864280-add-password-expiration-policy.js +19 -0
- package/alterations-js/1.41.0-1779864281-add-is-password-expired-to-users.js +14 -0
- package/alterations-js/1.41.0-1780358400-drop-oidc-model-instances-legacy-grant-id-index.js +21 -0
- package/alterations-js/1.41.0-1780381219-add-username-policy.js +37 -0
- package/alterations-js/1.41.0-1780643665-set-sign-up-profile-fields-default.js +16 -0
- package/alterations-js/1.41.0-1780906060-add-verification-code-policy.js +15 -0
- package/alterations-js/1.41.0-1781689400-add-sentinel-activities-created-at-index.js +21 -0
- package/alterations-js/1.41.0-1782354362-set-admin-account-center-profile-fields.js +23 -0
- package/alterations-js/1.41.0-1782375106-cover-service-logs-tenant-type-index-with-created-at.js +32 -0
- 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 +3 -0
- package/lib/consts/experience.js +3 -0
- package/lib/consts/index.d.ts +2 -0
- package/lib/consts/index.js +2 -0
- package/lib/consts/message-rate-limit.d.ts +65 -0
- package/lib/consts/message-rate-limit.js +29 -0
- package/lib/consts/message-rate-limit.test.d.ts +1 -0
- package/lib/consts/message-rate-limit.test.js +20 -0
- package/lib/consts/verification-code.d.ts +10 -0
- package/lib/consts/verification-code.js +10 -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/db-entries/sign-in-experience.d.ts +10 -4
- package/lib/db-entries/sign-in-experience.js +13 -1
- package/lib/db-entries/user.d.ts +5 -1
- package/lib/db-entries/user.js +8 -0
- package/lib/foundations/jsonb-types/account-centers.d.ts +3 -0
- package/lib/foundations/jsonb-types/account-centers.js +1 -0
- package/lib/foundations/jsonb-types/custom-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 +2 -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/sentinel.d.ts +16 -1
- package/lib/foundations/jsonb-types/sentinel.js +15 -0
- package/lib/foundations/jsonb-types/sign-in-experience.d.ts +90 -3
- package/lib/foundations/jsonb-types/sign-in-experience.js +20 -0
- package/lib/foundations/jsonb-types/sign-in-experience.test.js +49 -1
- package/lib/foundations/jsonb-types/users.d.ts +9 -0
- package/lib/foundations/jsonb-types/users.js +1 -0
- package/lib/seeds/account-center.js +1 -0
- package/lib/seeds/sign-in-experience.js +1 -0
- package/lib/seeds/sign-in-experience.test.js +5 -1
- package/lib/types/application.d.ts +131 -1
- package/lib/types/application.js +8 -0
- package/lib/types/consent.d.ts +8 -0
- package/lib/types/custom-profile-fields.d.ts +5 -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 +5 -12
- 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 +91 -13
- package/lib/types/logto-config/index.js +12 -0
- package/lib/types/logto-config/index.test.js +25 -1
- package/lib/types/logto-config/jwt-customizer.d.ts +162 -1
- package/lib/types/logto-config/jwt-customizer.js +14 -0
- package/lib/types/organization.d.ts +136 -1
- package/lib/types/organization.js +3 -2
- package/lib/types/saml-application.d.ts +3 -0
- package/lib/types/saml-application.js +3 -0
- package/lib/types/sign-in-experience.d.ts +11 -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/ssr.d.ts +11 -0
- package/lib/types/user-assets.d.ts +10 -0
- package/lib/types/user-assets.js +17 -0
- package/lib/types/user-sessions.d.ts +309 -34
- package/lib/types/user-sessions.js +5 -0
- package/lib/types/user.d.ts +15 -0
- package/lib/types/user.js +1 -0
- package/lib/types/verification-records/one-time-token-verification.d.ts +5 -0
- package/package.json +8 -8
- package/tables/domains.sql +1 -0
- package/tables/oidc_model_instances.sql +0 -8
- package/tables/saml_sso_connector_signing_keys.sql +21 -0
- package/tables/sentinel_activities.sql +4 -0
- package/tables/service_logs.sql +7 -2
- package/tables/sign_in_experiences.sql +15 -2
- package/tables/users.sql +7 -0
|
@@ -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';
|
|
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"];
|
|
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"]>;
|
|
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"]>, "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
|
/**
|
|
@@ -62,6 +62,8 @@ export const hookEvents = Object.freeze([
|
|
|
62
62
|
'OrganizationScope.Deleted',
|
|
63
63
|
'OrganizationScope.Data.Updated',
|
|
64
64
|
'Identifier.Lockout',
|
|
65
|
+
'Message.RateLimited',
|
|
66
|
+
'Grant.LimitExceeded',
|
|
65
67
|
]);
|
|
66
68
|
export const hookEventGuard = z.enum(hookEvents);
|
|
67
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;
|
|
@@ -39,7 +39,22 @@ export declare enum SentinelActivityAction {
|
|
|
39
39
|
/**
|
|
40
40
|
* The subject tries to pass a backup code MFA verification.
|
|
41
41
|
*/
|
|
42
|
-
MfaBackupCode = "MfaBackupCode"
|
|
42
|
+
MfaBackupCode = "MfaBackupCode",
|
|
43
|
+
/**
|
|
44
|
+
* A verification code is sent to the target (email/phone), e.g. sign-in,
|
|
45
|
+
* register, forgot-password, or identifier-binding codes.
|
|
46
|
+
*
|
|
47
|
+
* Used by the message rate limit to throttle sends, distinct from the
|
|
48
|
+
* failure-based lockout actions above.
|
|
49
|
+
*/
|
|
50
|
+
VerificationCodeSend = "VerificationCodeSend",
|
|
51
|
+
/**
|
|
52
|
+
* A non-verification-code message is sent to the target (email/phone),
|
|
53
|
+
* e.g. an organization invitation.
|
|
54
|
+
*
|
|
55
|
+
* Used by the message rate limit to throttle sends.
|
|
56
|
+
*/
|
|
57
|
+
MessageSend = "MessageSend"
|
|
43
58
|
}
|
|
44
59
|
export declare const sentinelActivityActionGuard: z.ZodNativeEnum<typeof SentinelActivityAction>;
|
|
45
60
|
export type SentinelActivityPayload = Record<string, unknown>;
|
|
@@ -42,6 +42,21 @@ export var SentinelActivityAction;
|
|
|
42
42
|
* The subject tries to pass a backup code MFA verification.
|
|
43
43
|
*/
|
|
44
44
|
SentinelActivityAction["MfaBackupCode"] = "MfaBackupCode";
|
|
45
|
+
/**
|
|
46
|
+
* A verification code is sent to the target (email/phone), e.g. sign-in,
|
|
47
|
+
* register, forgot-password, or identifier-binding codes.
|
|
48
|
+
*
|
|
49
|
+
* Used by the message rate limit to throttle sends, distinct from the
|
|
50
|
+
* failure-based lockout actions above.
|
|
51
|
+
*/
|
|
52
|
+
SentinelActivityAction["VerificationCodeSend"] = "VerificationCodeSend";
|
|
53
|
+
/**
|
|
54
|
+
* A non-verification-code message is sent to the target (email/phone),
|
|
55
|
+
* e.g. an organization invitation.
|
|
56
|
+
*
|
|
57
|
+
* Used by the message rate limit to throttle sends.
|
|
58
|
+
*/
|
|
59
|
+
SentinelActivityAction["MessageSend"] = "MessageSend";
|
|
45
60
|
})(SentinelActivityAction || (SentinelActivityAction = {}));
|
|
46
61
|
export const sentinelActivityActionGuard = z.nativeEnum(SentinelActivityAction);
|
|
47
62
|
export const sentinelActivityPayloadGuard = z.record(z.unknown());
|
|
@@ -193,6 +193,7 @@ export declare const signInGuard: z.ZodObject<{
|
|
|
193
193
|
}[];
|
|
194
194
|
}>;
|
|
195
195
|
export type SignIn = z.infer<typeof signInGuard>;
|
|
196
|
+
export { defaultUsernamePolicy, usernamePolicyGuard, type UsernamePolicy } from '@logto/core-kit';
|
|
196
197
|
export type SocialSignIn = {
|
|
197
198
|
/**
|
|
198
199
|
* If account linking should be performed when a user signs in with a social identity that is new
|
|
@@ -359,36 +360,73 @@ export declare const sentinelPolicyGuard: z.ZodObject<{
|
|
|
359
360
|
lockoutDuration?: number | undefined;
|
|
360
361
|
}>;
|
|
361
362
|
/**
|
|
362
|
-
*
|
|
363
|
+
* Verification code policy.
|
|
363
364
|
*
|
|
364
365
|
* @remarks
|
|
365
|
-
* This policy
|
|
366
|
+
* This policy controls the expiration duration and maximum retry attempts for verification codes.
|
|
366
367
|
*/
|
|
368
|
+
export type VerificationCodePolicy = {
|
|
369
|
+
/**
|
|
370
|
+
* The duration in seconds that a verification code remains valid.
|
|
371
|
+
* @default 600 (10 minutes)
|
|
372
|
+
*/
|
|
373
|
+
expirationDuration?: number;
|
|
374
|
+
/**
|
|
375
|
+
* Maximum number of failed verification attempts allowed before the code is invalidated.
|
|
376
|
+
* @default 10
|
|
377
|
+
*/
|
|
378
|
+
maxRetryAttempts?: number;
|
|
379
|
+
};
|
|
380
|
+
export declare const verificationCodePolicyGuard: z.ZodObject<{
|
|
381
|
+
expirationDuration: z.ZodOptional<z.ZodNumber>;
|
|
382
|
+
maxRetryAttempts: z.ZodOptional<z.ZodNumber>;
|
|
383
|
+
}, "strip", z.ZodTypeAny, {
|
|
384
|
+
expirationDuration?: number | undefined;
|
|
385
|
+
maxRetryAttempts?: number | undefined;
|
|
386
|
+
}, {
|
|
387
|
+
expirationDuration?: number | undefined;
|
|
388
|
+
maxRetryAttempts?: number | undefined;
|
|
389
|
+
}>;
|
|
367
390
|
export type EmailBlocklistPolicy = {
|
|
368
391
|
blockDisposableAddresses?: boolean;
|
|
369
392
|
blockSubaddressing?: boolean;
|
|
370
393
|
/**
|
|
371
|
-
* Custom
|
|
394
|
+
* Custom allowlist of email addresses, domains, or wildcard email address patterns.
|
|
395
|
+
*
|
|
396
|
+
* The matching semantics are the same as {@link customBlocklist}.
|
|
397
|
+
*/
|
|
398
|
+
customAllowlist?: string[];
|
|
399
|
+
/**
|
|
400
|
+
* Custom blocklist of email addresses, domains, or wildcard email address patterns.
|
|
372
401
|
*
|
|
373
402
|
* @example
|
|
374
403
|
* Email address: abc@xyx.com
|
|
375
404
|
*
|
|
376
405
|
* @example
|
|
377
406
|
* Domain name: @xyz.com
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
* Wildcard email address pattern: foo*@example.com
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* Wildcard domain pattern: @*.example.com
|
|
378
413
|
*/
|
|
379
414
|
customBlocklist?: string[];
|
|
380
415
|
};
|
|
381
416
|
export declare const emailBlocklistPolicyGuard: z.ZodObject<{
|
|
382
417
|
blockDisposableAddresses: z.ZodOptional<z.ZodBoolean>;
|
|
383
418
|
blockSubaddressing: z.ZodOptional<z.ZodBoolean>;
|
|
419
|
+
customAllowlist: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
384
420
|
customBlocklist: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
385
421
|
}, "strip", z.ZodTypeAny, {
|
|
386
422
|
blockDisposableAddresses?: boolean | undefined;
|
|
387
423
|
blockSubaddressing?: boolean | undefined;
|
|
424
|
+
customAllowlist?: string[] | undefined;
|
|
388
425
|
customBlocklist?: string[] | undefined;
|
|
389
426
|
}, {
|
|
390
427
|
blockDisposableAddresses?: boolean | undefined;
|
|
391
428
|
blockSubaddressing?: boolean | undefined;
|
|
429
|
+
customAllowlist?: string[] | undefined;
|
|
392
430
|
customBlocklist?: string[] | undefined;
|
|
393
431
|
}>;
|
|
394
432
|
export declare enum ForgotPasswordMethod {
|
|
@@ -441,4 +479,53 @@ export declare const signUpProfileFieldsGuard: z.ZodArray<z.ZodObject<{
|
|
|
441
479
|
name: string;
|
|
442
480
|
}>, "many">;
|
|
443
481
|
export type SignUpProfileFields = z.infer<typeof signUpProfileFieldsGuard>;
|
|
482
|
+
/**
|
|
483
|
+
* Password lifecycle policy for configuring password expiration and rotation.
|
|
484
|
+
*
|
|
485
|
+
* @remarks
|
|
486
|
+
* This policy is evaluated server-side during sign-in (after local password verification) to determine
|
|
487
|
+
* whether the user's password has expired.
|
|
488
|
+
*
|
|
489
|
+
* If the password age >= `validPeriodDays`, the sign-in is blocked and the user must reset their
|
|
490
|
+
* password before continuing.
|
|
491
|
+
*/
|
|
492
|
+
export type PasswordExpirationPolicy = {
|
|
493
|
+
/**
|
|
494
|
+
* Whether the password expiration policy is enabled.
|
|
495
|
+
* @default false
|
|
496
|
+
*/
|
|
497
|
+
enabled?: false;
|
|
498
|
+
} | {
|
|
499
|
+
enabled: true;
|
|
500
|
+
/**
|
|
501
|
+
* Number of days a password is valid before it expires and the user is
|
|
502
|
+
* forced to reset it on sign-in.
|
|
503
|
+
*/
|
|
504
|
+
validPeriodDays: number;
|
|
505
|
+
/**
|
|
506
|
+
* Epoch milliseconds when the policy was enabled. Used as the expiry anchor for users
|
|
507
|
+
* that have no `passwordUpdatedAt` (e.g. legacy accounts), so enabling the policy grants
|
|
508
|
+
* them a full valid period instead of expiring them against their account creation date.
|
|
509
|
+
*/
|
|
510
|
+
enabledAt?: number;
|
|
511
|
+
};
|
|
512
|
+
export declare const passwordExpirationPolicyGuard: z.ZodUnion<[z.ZodObject<{
|
|
513
|
+
enabled: z.ZodOptional<z.ZodLiteral<false>>;
|
|
514
|
+
}, "strip", z.ZodTypeAny, {
|
|
515
|
+
enabled?: false | undefined;
|
|
516
|
+
}, {
|
|
517
|
+
enabled?: false | undefined;
|
|
518
|
+
}>, z.ZodObject<{
|
|
519
|
+
enabled: z.ZodLiteral<true>;
|
|
520
|
+
validPeriodDays: z.ZodNumber;
|
|
521
|
+
enabledAt: z.ZodOptional<z.ZodNumber>;
|
|
522
|
+
}, "strip", z.ZodTypeAny, {
|
|
523
|
+
enabled: true;
|
|
524
|
+
validPeriodDays: number;
|
|
525
|
+
enabledAt?: number | undefined;
|
|
526
|
+
}, {
|
|
527
|
+
enabled: true;
|
|
528
|
+
validPeriodDays: number;
|
|
529
|
+
enabledAt?: number | undefined;
|
|
530
|
+
}>]>;
|
|
444
531
|
export { customUiCspGuard, type CustomUiCsp } from '@logto/core-kit';
|
|
@@ -62,6 +62,9 @@ export const signInGuard = z.object({
|
|
|
62
62
|
})
|
|
63
63
|
.array(),
|
|
64
64
|
});
|
|
65
|
+
// The username policy is owned by @logto/core-kit (like passwordPolicyGuard) so core, experience,
|
|
66
|
+
// and console share one source of truth; re-exported here for the `@use UsernamePolicy` column.
|
|
67
|
+
export { defaultUsernamePolicy, usernamePolicyGuard } from '@logto/core-kit';
|
|
65
68
|
export const socialSignInGuard = z.object({
|
|
66
69
|
automaticAccountLinking: z.boolean().optional(),
|
|
67
70
|
skipRequiredIdentifiers: z.boolean().optional(),
|
|
@@ -120,9 +123,14 @@ export const sentinelPolicyGuard = z.object({
|
|
|
120
123
|
maxAttempts: z.number().optional(),
|
|
121
124
|
lockoutDuration: z.number().optional(),
|
|
122
125
|
});
|
|
126
|
+
export const verificationCodePolicyGuard = z.object({
|
|
127
|
+
expirationDuration: z.number().int().min(60).max(3600).optional(),
|
|
128
|
+
maxRetryAttempts: z.number().int().min(1).max(100).optional(),
|
|
129
|
+
});
|
|
123
130
|
export const emailBlocklistPolicyGuard = z.object({
|
|
124
131
|
blockDisposableAddresses: z.boolean().optional(),
|
|
125
132
|
blockSubaddressing: z.boolean().optional(),
|
|
133
|
+
customAllowlist: z.string().array().optional(),
|
|
126
134
|
customBlocklist: z.string().array().optional(),
|
|
127
135
|
});
|
|
128
136
|
export var ForgotPasswordMethod;
|
|
@@ -142,4 +150,16 @@ export const signUpProfileFieldItemGuard = z.object({
|
|
|
142
150
|
name: z.string(),
|
|
143
151
|
});
|
|
144
152
|
export const signUpProfileFieldsGuard = z.array(signUpProfileFieldItemGuard);
|
|
153
|
+
// Intentionally not `.strict()` for backward compatibility: legacy rows may carry removed fields
|
|
154
|
+
// (e.g. `reminderPeriodDays`), which are stripped on parse instead of rejected.
|
|
155
|
+
export const passwordExpirationPolicyGuard = z.union([
|
|
156
|
+
z.object({
|
|
157
|
+
enabled: z.literal(false).optional(),
|
|
158
|
+
}),
|
|
159
|
+
z.object({
|
|
160
|
+
enabled: z.literal(true),
|
|
161
|
+
validPeriodDays: z.number().int().min(1),
|
|
162
|
+
enabledAt: z.number().int().nonnegative().optional(),
|
|
163
|
+
}),
|
|
164
|
+
]);
|
|
145
165
|
export { customUiCspGuard } from '@logto/core-kit';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { customUiCspGuard } from './sign-in-experience.js';
|
|
2
|
+
import { customUiCspGuard, passwordExpirationPolicyGuard } from './sign-in-experience.js';
|
|
3
3
|
describe('customUiCspGuard', () => {
|
|
4
4
|
it.each([
|
|
5
5
|
{},
|
|
@@ -16,3 +16,51 @@ describe('customUiCspGuard', () => {
|
|
|
16
16
|
expect(customUiCspGuard.safeParse({ imgSrc: ['https://example.com'] }).success).toBe(false);
|
|
17
17
|
});
|
|
18
18
|
});
|
|
19
|
+
describe('passwordExpirationPolicyGuard', () => {
|
|
20
|
+
it.each([
|
|
21
|
+
[{}, {}],
|
|
22
|
+
[{ enabled: false }, { enabled: false }],
|
|
23
|
+
])('accepts disabled policy %p', (value, expected) => {
|
|
24
|
+
expect(passwordExpirationPolicyGuard.parse(value)).toEqual(expected);
|
|
25
|
+
});
|
|
26
|
+
it('accepts enabled policy with a valid period', () => {
|
|
27
|
+
expect(passwordExpirationPolicyGuard.parse({
|
|
28
|
+
enabled: true,
|
|
29
|
+
validPeriodDays: 30,
|
|
30
|
+
})).toEqual({
|
|
31
|
+
enabled: true,
|
|
32
|
+
validPeriodDays: 30,
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
it('accepts enabled policy with an enabledAt timestamp', () => {
|
|
36
|
+
expect(passwordExpirationPolicyGuard.parse({
|
|
37
|
+
enabled: true,
|
|
38
|
+
validPeriodDays: 30,
|
|
39
|
+
enabledAt: 1_700_000_000_000,
|
|
40
|
+
})).toEqual({
|
|
41
|
+
enabled: true,
|
|
42
|
+
validPeriodDays: 30,
|
|
43
|
+
enabledAt: 1_700_000_000_000,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
it('strips unknown keys from legacy rows (e.g. the removed reminderPeriodDays)', () => {
|
|
47
|
+
expect(passwordExpirationPolicyGuard.parse({
|
|
48
|
+
enabled: true,
|
|
49
|
+
validPeriodDays: 30,
|
|
50
|
+
reminderPeriodDays: 5,
|
|
51
|
+
})).toEqual({
|
|
52
|
+
enabled: true,
|
|
53
|
+
validPeriodDays: 30,
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
it.each([
|
|
57
|
+
{ enabled: true },
|
|
58
|
+
{ enabled: true, validPeriodDays: 0 },
|
|
59
|
+
{ enabled: true, validPeriodDays: 1.5 },
|
|
60
|
+
{ enabled: true, reminderPeriodDays: 5 },
|
|
61
|
+
{ enabled: true, validPeriodDays: 30, enabledAt: -1 },
|
|
62
|
+
{ enabled: true, validPeriodDays: 30, enabledAt: 1.5 },
|
|
63
|
+
])('rejects invalid policy %p', (value) => {
|
|
64
|
+
expect(passwordExpirationPolicyGuard.safeParse(value).success).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -150,6 +150,7 @@ export declare const baseMfaVerification: {
|
|
|
150
150
|
};
|
|
151
151
|
export declare const mfaVerificationTotp: z.ZodObject<{
|
|
152
152
|
key: z.ZodString;
|
|
153
|
+
lastUsedTimeStep: z.ZodOptional<z.ZodNumber>;
|
|
153
154
|
id: z.ZodString;
|
|
154
155
|
createdAt: z.ZodString;
|
|
155
156
|
lastUsedAt: z.ZodOptional<z.ZodString>;
|
|
@@ -159,12 +160,14 @@ export declare const mfaVerificationTotp: z.ZodObject<{
|
|
|
159
160
|
id: string;
|
|
160
161
|
key: string;
|
|
161
162
|
createdAt: string;
|
|
163
|
+
lastUsedTimeStep?: number | undefined;
|
|
162
164
|
lastUsedAt?: string | undefined;
|
|
163
165
|
}, {
|
|
164
166
|
type: MfaFactor.TOTP;
|
|
165
167
|
id: string;
|
|
166
168
|
key: string;
|
|
167
169
|
createdAt: string;
|
|
170
|
+
lastUsedTimeStep?: number | undefined;
|
|
168
171
|
lastUsedAt?: string | undefined;
|
|
169
172
|
}>;
|
|
170
173
|
export type MfaVerificationTotp = z.infer<typeof mfaVerificationTotp>;
|
|
@@ -244,6 +247,7 @@ export declare const mfaVerificationBackupCode: z.ZodObject<{
|
|
|
244
247
|
export type MfaVerificationBackupCode = z.infer<typeof mfaVerificationBackupCode>;
|
|
245
248
|
export declare const mfaVerificationGuard: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
246
249
|
key: z.ZodString;
|
|
250
|
+
lastUsedTimeStep: z.ZodOptional<z.ZodNumber>;
|
|
247
251
|
id: z.ZodString;
|
|
248
252
|
createdAt: z.ZodString;
|
|
249
253
|
lastUsedAt: z.ZodOptional<z.ZodString>;
|
|
@@ -253,12 +257,14 @@ export declare const mfaVerificationGuard: z.ZodDiscriminatedUnion<"type", [z.Zo
|
|
|
253
257
|
id: string;
|
|
254
258
|
key: string;
|
|
255
259
|
createdAt: string;
|
|
260
|
+
lastUsedTimeStep?: number | undefined;
|
|
256
261
|
lastUsedAt?: string | undefined;
|
|
257
262
|
}, {
|
|
258
263
|
type: MfaFactor.TOTP;
|
|
259
264
|
id: string;
|
|
260
265
|
key: string;
|
|
261
266
|
createdAt: string;
|
|
267
|
+
lastUsedTimeStep?: number | undefined;
|
|
262
268
|
lastUsedAt?: string | undefined;
|
|
263
269
|
}>, z.ZodObject<{
|
|
264
270
|
rpId: z.ZodOptional<z.ZodString>;
|
|
@@ -333,6 +339,7 @@ export declare const mfaVerificationGuard: z.ZodDiscriminatedUnion<"type", [z.Zo
|
|
|
333
339
|
export type MfaVerification = z.infer<typeof mfaVerificationGuard>;
|
|
334
340
|
export declare const mfaVerificationsGuard: z.ZodArray<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
335
341
|
key: z.ZodString;
|
|
342
|
+
lastUsedTimeStep: z.ZodOptional<z.ZodNumber>;
|
|
336
343
|
id: z.ZodString;
|
|
337
344
|
createdAt: z.ZodString;
|
|
338
345
|
lastUsedAt: z.ZodOptional<z.ZodString>;
|
|
@@ -342,12 +349,14 @@ export declare const mfaVerificationsGuard: z.ZodArray<z.ZodDiscriminatedUnion<"
|
|
|
342
349
|
id: string;
|
|
343
350
|
key: string;
|
|
344
351
|
createdAt: string;
|
|
352
|
+
lastUsedTimeStep?: number | undefined;
|
|
345
353
|
lastUsedAt?: string | undefined;
|
|
346
354
|
}, {
|
|
347
355
|
type: MfaFactor.TOTP;
|
|
348
356
|
id: string;
|
|
349
357
|
key: string;
|
|
350
358
|
createdAt: string;
|
|
359
|
+
lastUsedTimeStep?: number | undefined;
|
|
351
360
|
lastUsedAt?: string | undefined;
|
|
352
361
|
}>, z.ZodObject<{
|
|
353
362
|
rpId: z.ZodOptional<z.ZodString>;
|
|
@@ -19,9 +19,13 @@ describe('createAdminTenantSignInExperience', () => {
|
|
|
19
19
|
expect(row.tenantId).toBe(adminTenantId);
|
|
20
20
|
});
|
|
21
21
|
});
|
|
22
|
-
describe('createDefaultSignInExperience
|
|
22
|
+
describe('createDefaultSignInExperience', () => {
|
|
23
23
|
it('still has a two-parameter signature and seeds an empty passwordPolicy', () => {
|
|
24
24
|
const row = createDefaultSignInExperience('some-tenant-id', false);
|
|
25
25
|
expect(row.passwordPolicy).toEqual({});
|
|
26
26
|
});
|
|
27
|
+
it('keeps legacy null signUpProfileFields for seeded default tenants', () => {
|
|
28
|
+
const row = createDefaultSignInExperience('some-tenant-id', false);
|
|
29
|
+
expect(row.signUpProfileFields).toBeNull();
|
|
30
|
+
});
|
|
27
31
|
});
|