@logto/schemas 1.43.0 → 1.45.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.44.0-1788946735-add-saml-authn-request-config.ts +18 -0
- package/alterations/1.44.0-1789000000-extend-user-id-length.ts +61 -0
- package/alterations/1.45.0-1791531333-add-tenant-viewer-role.ts +38 -0
- package/alterations-js/1.44.0-1788946735-add-saml-authn-request-config.js +14 -0
- package/alterations-js/1.44.0-1789000000-extend-user-id-length.js +47 -0
- package/alterations-js/1.45.0-1791531333-add-tenant-viewer-role.js +32 -0
- package/lib/consts/experience.d.ts +3 -0
- package/lib/consts/experience.js +3 -0
- package/lib/consts/index.d.ts +1 -0
- package/lib/consts/index.js +1 -0
- package/lib/consts/license.d.ts +222 -0
- package/lib/consts/license.js +81 -0
- package/lib/consts/license.test.d.ts +1 -0
- package/lib/consts/license.test.js +96 -0
- package/lib/consts/oidc.d.ts +15 -1
- package/lib/consts/oidc.js +11 -0
- package/lib/consts/oidc.test.d.ts +1 -0
- package/lib/consts/oidc.test.js +15 -0
- package/lib/consts/subscriptions.d.ts +13 -1
- package/lib/consts/subscriptions.js +12 -0
- package/lib/db-entries/aggregated-daily-active-user.js +2 -2
- package/lib/db-entries/application-access-control-user-relation.js +2 -2
- package/lib/db-entries/application-user-consent-organization.js +2 -2
- package/lib/db-entries/cimd-grant-organization.js +2 -2
- package/lib/db-entries/daily-active-user.js +2 -2
- package/lib/db-entries/oidc-session-extension.js +2 -2
- package/lib/db-entries/organization-invitation.js +4 -4
- package/lib/db-entries/organization-role-user-relation.js +2 -2
- package/lib/db-entries/organization-user-relation.js +2 -2
- package/lib/db-entries/personal-access-token.js +2 -2
- package/lib/db-entries/saml-application-config.d.ts +4 -2
- package/lib/db-entries/saml-application-config.js +5 -1
- package/lib/db-entries/secret.js +2 -2
- package/lib/db-entries/subject-token.js +2 -2
- package/lib/db-entries/trusted-device.js +2 -2
- package/lib/db-entries/user-geo-location.js +2 -2
- package/lib/db-entries/user-sign-in-country.js +2 -2
- package/lib/db-entries/user-sso-identity.js +2 -2
- package/lib/db-entries/user.js +2 -2
- package/lib/db-entries/users-role.js +2 -2
- package/lib/db-entries/verification-record.js +2 -2
- package/lib/db-entries/verification-status.js +2 -2
- package/lib/foundations/jsonb-types/captcha.d.ts +47 -1
- package/lib/foundations/jsonb-types/captcha.js +20 -0
- package/lib/foundations/jsonb-types/hooks.d.ts +0 -3
- package/lib/foundations/jsonb-types/hooks.js +2 -6
- package/lib/foundations/jsonb-types/saml-application-configs.d.ts +23 -0
- package/lib/foundations/jsonb-types/saml-application-configs.js +16 -0
- package/lib/foundations/jsonb-types/saml-application-configs.test.js +24 -1
- package/lib/types/authentication-context.d.ts +224 -0
- package/lib/types/authentication-context.js +238 -0
- package/lib/types/authentication-context.test.d.ts +1 -0
- package/lib/types/authentication-context.test.js +186 -0
- package/lib/types/cookie.d.ts +4 -0
- package/lib/types/cookie.js +6 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/interactions.d.ts +161 -0
- package/lib/types/interactions.js +23 -0
- package/lib/types/log/interaction.d.ts +15 -2
- package/lib/types/log/interaction.js +5 -0
- package/lib/types/logto-config/index.d.ts +47 -0
- package/lib/types/logto-config/index.js +1 -0
- package/lib/types/logto-config/jwt-customizer.d.ts +92 -0
- package/lib/types/saml-application.d.ts +55 -1
- package/lib/types/saml-application.js +7 -1
- package/lib/types/sign-in-experience.d.ts +11 -7
- package/lib/types/sign-in-experience.js +2 -2
- package/lib/types/ssr.d.ts +2 -0
- package/lib/types/system.d.ts +57 -5
- package/lib/types/system.js +36 -0
- package/lib/types/system.test.d.ts +1 -0
- package/lib/types/system.test.js +26 -0
- package/lib/types/tenant-organization.d.ts +4 -2
- package/lib/types/tenant-organization.js +6 -2
- package/lib/types/tenant-organization.test.d.ts +1 -0
- package/lib/types/tenant-organization.test.js +16 -0
- package/lib/types/user-sessions.d.ts +156 -0
- package/lib/types/verification-records/code-verification.d.ts +17 -0
- package/lib/types/verification-records/code-verification.js +1 -0
- package/lib/utils/management-api.d.ts +5 -0
- package/lib/utils/management-api.js +7 -1
- package/lib/utils/oidc-private-key.test.js +44 -0
- package/package.json +7 -7
- package/tables/aggregated_daily_active_users.sql +1 -1
- package/tables/application_access_control_user_relations.sql +1 -1
- package/tables/application_user_consent_organizations.sql +1 -1
- package/tables/cimd_grant_organizations.sql +1 -1
- package/tables/daily_active_users.sql +1 -1
- package/tables/oidc_session_extensions.sql +1 -1
- package/tables/organization_invitations.sql +2 -2
- package/tables/organization_role_user_relations.sql +1 -1
- package/tables/organization_user_relations.sql +1 -1
- package/tables/personal_access_tokens.sql +1 -1
- package/tables/saml_application_configs.sql +1 -0
- package/tables/secrets.sql +1 -1
- package/tables/subject_tokens.sql +1 -1
- package/tables/trusted_devices.sql +1 -1
- package/tables/user_geo_locations.sql +1 -1
- package/tables/user_sign_in_countries.sql +1 -1
- package/tables/user_sso_identities.sql +1 -1
- package/tables/users.sql +1 -1
- package/tables/users_roles.sql +1 -1
- package/tables/verification_records.sql +1 -1
- package/tables/verification_statuses.sql +1 -1
- package/LICENSE +0 -373
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { AuthenticationFactor, AuthenticationFactorClass, AuthenticationMethodReference, AuthenticationProofRole, AuthenticationContextMode, LogtoAcr, acrSatisfies, authenticationProofGuard, buildAuthenticationMethodReferences, getAuthenticationFactor, getAuthenticationFactorClass, getAuthenticationMethodReferences, isLogtoAcr, logtoAcrValues, requestedAuthenticationContextGuard, } from './authentication-context.js';
|
|
3
|
+
import { VerificationType } from './verification-records/verification-type.js';
|
|
4
|
+
describe('logtoAcrValues', () => {
|
|
5
|
+
it('advertises exactly the two Logto classes in order', () => {
|
|
6
|
+
expect(logtoAcrValues).toEqual(['urn:logto:acr:1fa', 'urn:logto:acr:mfa']);
|
|
7
|
+
});
|
|
8
|
+
});
|
|
9
|
+
describe('isLogtoAcr', () => {
|
|
10
|
+
it.each(logtoAcrValues)('accepts %s', (acr) => {
|
|
11
|
+
expect(isLogtoAcr(acr)).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
it.each(['urn:logto:acr:2fa', 'phr', '', undefined, null, 1, ['urn:logto:acr:mfa']])('rejects %j', (value) => {
|
|
14
|
+
expect(isLogtoAcr(value)).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe('acrSatisfies', () => {
|
|
18
|
+
it.each([
|
|
19
|
+
[LogtoAcr.FirstFactor, LogtoAcr.FirstFactor, true],
|
|
20
|
+
[LogtoAcr.Mfa, LogtoAcr.Mfa, true],
|
|
21
|
+
// `mfa` is a `1fa` context plus more, so it satisfies the weaker class as well.
|
|
22
|
+
[LogtoAcr.Mfa, LogtoAcr.FirstFactor, true],
|
|
23
|
+
[LogtoAcr.FirstFactor, LogtoAcr.Mfa, false],
|
|
24
|
+
])('%s satisfies %s: %s', (achieved, required, expected) => {
|
|
25
|
+
expect(acrSatisfies(achieved, required)).toBe(expected);
|
|
26
|
+
});
|
|
27
|
+
it.each(['urn:logto:acr:2fa', 'phr', '', undefined])('never lets the unsupported achieved value %j satisfy a class', (achieved) => {
|
|
28
|
+
for (const required of logtoAcrValues) {
|
|
29
|
+
expect(acrSatisfies(achieved, required)).toBe(false);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('requestedAuthenticationContextGuard', () => {
|
|
34
|
+
it('accepts a sign-in context that carries only the requested values', () => {
|
|
35
|
+
expect(requestedAuthenticationContextGuard.parse({ requestedAcrValues: [LogtoAcr.Mfa] })).toEqual({ requestedAcrValues: [LogtoAcr.Mfa] });
|
|
36
|
+
});
|
|
37
|
+
it('accepts a step-up context', () => {
|
|
38
|
+
const context = {
|
|
39
|
+
requestedAcrValues: [LogtoAcr.Mfa, LogtoAcr.FirstFactor],
|
|
40
|
+
selectedAcr: LogtoAcr.Mfa,
|
|
41
|
+
mode: AuthenticationContextMode.StepUp,
|
|
42
|
+
};
|
|
43
|
+
expect(requestedAuthenticationContextGuard.parse(context)).toEqual(context);
|
|
44
|
+
});
|
|
45
|
+
it('rejects unsupported classes and modes', () => {
|
|
46
|
+
expect(requestedAuthenticationContextGuard.safeParse({ requestedAcrValues: ['urn:logto:acr:2fa'] })
|
|
47
|
+
.success).toBe(false);
|
|
48
|
+
expect(requestedAuthenticationContextGuard.safeParse({
|
|
49
|
+
requestedAcrValues: [LogtoAcr.Mfa],
|
|
50
|
+
mode: 'signIn',
|
|
51
|
+
}).success).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('getAuthenticationFactorClass', () => {
|
|
55
|
+
it.each([
|
|
56
|
+
VerificationType.Password,
|
|
57
|
+
VerificationType.EmailVerificationCode,
|
|
58
|
+
VerificationType.PhoneVerificationCode,
|
|
59
|
+
VerificationType.OneTimeToken,
|
|
60
|
+
VerificationType.NewPasswordIdentity,
|
|
61
|
+
])('classifies %s as 1fa', (type) => {
|
|
62
|
+
expect(getAuthenticationFactorClass(type)).toBe(AuthenticationFactorClass.FirstFactor);
|
|
63
|
+
});
|
|
64
|
+
it.each([
|
|
65
|
+
VerificationType.TOTP,
|
|
66
|
+
VerificationType.MfaEmailVerificationCode,
|
|
67
|
+
VerificationType.MfaPhoneVerificationCode,
|
|
68
|
+
VerificationType.BackupCode,
|
|
69
|
+
])('classifies %s as mfa', (type) => {
|
|
70
|
+
expect(getAuthenticationFactorClass(type)).toBe(AuthenticationFactorClass.Mfa);
|
|
71
|
+
});
|
|
72
|
+
// A user-verified WebAuthn authenticator is possession plus user verification in one act, and
|
|
73
|
+
// Logto requires user verification for the registration and the authentication ceremony alike.
|
|
74
|
+
it.each([VerificationType.WebAuthn, VerificationType.SignInPasskey])('classifies %s as both', (type) => {
|
|
75
|
+
expect(getAuthenticationFactorClass(type)).toBe(AuthenticationFactorClass.Both);
|
|
76
|
+
});
|
|
77
|
+
it.each([VerificationType.Social, VerificationType.EnterpriseSso])('gives %s no class', (type) => {
|
|
78
|
+
expect(getAuthenticationFactorClass(type)).toBeUndefined();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe('getAuthenticationFactor', () => {
|
|
82
|
+
it.each([
|
|
83
|
+
[VerificationType.Password, AuthenticationFactor.Password],
|
|
84
|
+
[VerificationType.NewPasswordIdentity, AuthenticationFactor.Password],
|
|
85
|
+
[VerificationType.EmailVerificationCode, AuthenticationFactor.Email],
|
|
86
|
+
[VerificationType.MfaEmailVerificationCode, AuthenticationFactor.Email],
|
|
87
|
+
[VerificationType.OneTimeToken, AuthenticationFactor.Email],
|
|
88
|
+
[VerificationType.PhoneVerificationCode, AuthenticationFactor.Phone],
|
|
89
|
+
[VerificationType.MfaPhoneVerificationCode, AuthenticationFactor.Phone],
|
|
90
|
+
[VerificationType.TOTP, AuthenticationFactor.Totp],
|
|
91
|
+
[VerificationType.BackupCode, AuthenticationFactor.BackupCode],
|
|
92
|
+
[VerificationType.WebAuthn, AuthenticationFactor.WebAuthn],
|
|
93
|
+
[VerificationType.SignInPasskey, AuthenticationFactor.WebAuthn],
|
|
94
|
+
[VerificationType.Social, AuthenticationFactor.Federated],
|
|
95
|
+
[VerificationType.EnterpriseSso, AuthenticationFactor.Federated],
|
|
96
|
+
])('maps %s to the %s factor', (type, expected) => {
|
|
97
|
+
expect(getAuthenticationFactor(type)).toBe(expected);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('getAuthenticationMethodReferences', () => {
|
|
101
|
+
it.each([
|
|
102
|
+
[VerificationType.Password, ['pwd']],
|
|
103
|
+
[VerificationType.NewPasswordIdentity, ['pwd']],
|
|
104
|
+
[VerificationType.EmailVerificationCode, ['otp']],
|
|
105
|
+
[VerificationType.OneTimeToken, ['otp']],
|
|
106
|
+
[VerificationType.TOTP, ['otp']],
|
|
107
|
+
[VerificationType.MfaEmailVerificationCode, ['otp']],
|
|
108
|
+
[VerificationType.BackupCode, ['otp']],
|
|
109
|
+
[VerificationType.PhoneVerificationCode, ['sms']],
|
|
110
|
+
[VerificationType.MfaPhoneVerificationCode, ['sms']],
|
|
111
|
+
[VerificationType.WebAuthn, ['pop', 'user', 'mfa']],
|
|
112
|
+
[VerificationType.SignInPasskey, ['pop', 'user', 'mfa']],
|
|
113
|
+
[VerificationType.Social, ['fed']],
|
|
114
|
+
[VerificationType.EnterpriseSso, ['fed']],
|
|
115
|
+
])('maps %s to %j', (type, expected) => {
|
|
116
|
+
expect(getAuthenticationMethodReferences(type)).toEqual(expected);
|
|
117
|
+
});
|
|
118
|
+
it('maps every verification type', () => {
|
|
119
|
+
for (const type of Object.values(VerificationType)) {
|
|
120
|
+
expect(getAuthenticationMethodReferences(type).length).toBeGreaterThan(0);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
it('emits `fed` as the only unregistered AMR value', () => {
|
|
124
|
+
// RFC 8176 / IANA registered values used by Logto.
|
|
125
|
+
const registered = new Set(['pwd', 'otp', 'sms', 'pop', 'user', 'mfa']);
|
|
126
|
+
const emitted = new Set(Object.values(VerificationType).flatMap((type) => getAuthenticationMethodReferences(type)));
|
|
127
|
+
expect([...emitted].filter((value) => !registered.has(value))).toEqual([
|
|
128
|
+
AuthenticationMethodReference.Federated,
|
|
129
|
+
]);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
const references = (...types) => types.map((type) => getAuthenticationMethodReferences(type));
|
|
133
|
+
describe('buildAuthenticationMethodReferences', () => {
|
|
134
|
+
it('unions references in first-seen order without duplicates', () => {
|
|
135
|
+
expect(buildAuthenticationMethodReferences(references(VerificationType.Password, VerificationType.TOTP, VerificationType.BackupCode), LogtoAcr.FirstFactor)).toEqual(['pwd', 'otp']);
|
|
136
|
+
});
|
|
137
|
+
it('appends `mfa` when the achieved ACR is mfa', () => {
|
|
138
|
+
expect(buildAuthenticationMethodReferences(references(VerificationType.Password, VerificationType.TOTP), LogtoAcr.Mfa)).toEqual(['pwd', 'otp', 'mfa']);
|
|
139
|
+
});
|
|
140
|
+
it('does not duplicate `mfa` for WebAuthn', () => {
|
|
141
|
+
expect(buildAuthenticationMethodReferences(references(VerificationType.WebAuthn), LogtoAcr.Mfa)).toEqual(['pop', 'user', 'mfa']);
|
|
142
|
+
});
|
|
143
|
+
it('keeps `mfa` last when a WebAuthn record precedes another factor', () => {
|
|
144
|
+
expect(buildAuthenticationMethodReferences(references(VerificationType.WebAuthn, VerificationType.Password), LogtoAcr.Mfa)).toEqual(['pop', 'user', 'pwd', 'mfa']);
|
|
145
|
+
// WebAuthn carries `mfa` on its own even when no ACR is passed.
|
|
146
|
+
expect(buildAuthenticationMethodReferences(references(VerificationType.WebAuthn, VerificationType.Password))).toEqual(['pop', 'user', 'pwd', 'mfa']);
|
|
147
|
+
});
|
|
148
|
+
it('keeps `fed` next to an established factor without an ACR', () => {
|
|
149
|
+
expect(buildAuthenticationMethodReferences(references(VerificationType.Social, VerificationType.Password))).toEqual(['fed', 'pwd']);
|
|
150
|
+
expect(buildAuthenticationMethodReferences(references(VerificationType.Social))).toEqual([
|
|
151
|
+
'fed',
|
|
152
|
+
]);
|
|
153
|
+
});
|
|
154
|
+
it('returns nothing for no references', () => {
|
|
155
|
+
expect(buildAuthenticationMethodReferences([])).toEqual([]);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
describe('authenticationProofGuard', () => {
|
|
159
|
+
it.each([
|
|
160
|
+
{ id: '', amr: ['otp'] },
|
|
161
|
+
{ id: 'totp', amr: [] },
|
|
162
|
+
])('rejects a proof with an empty id or AMR list: %j', ({ id, amr }) => {
|
|
163
|
+
expect(authenticationProofGuard.safeParse({
|
|
164
|
+
id,
|
|
165
|
+
factor: AuthenticationFactor.Totp,
|
|
166
|
+
class: AuthenticationFactorClass.Mfa,
|
|
167
|
+
amr,
|
|
168
|
+
role: AuthenticationProofRole.Mfa,
|
|
169
|
+
}).success).toBe(false);
|
|
170
|
+
});
|
|
171
|
+
it('accepts a proof with and without a class', () => {
|
|
172
|
+
expect(authenticationProofGuard.safeParse({
|
|
173
|
+
id: 'totp',
|
|
174
|
+
factor: AuthenticationFactor.Totp,
|
|
175
|
+
class: AuthenticationFactorClass.Mfa,
|
|
176
|
+
amr: ['otp'],
|
|
177
|
+
role: AuthenticationProofRole.Mfa,
|
|
178
|
+
}).success).toBe(true);
|
|
179
|
+
expect(authenticationProofGuard.safeParse({
|
|
180
|
+
id: 'social',
|
|
181
|
+
factor: AuthenticationFactor.Federated,
|
|
182
|
+
amr: ['fed'],
|
|
183
|
+
role: AuthenticationProofRole.Identify,
|
|
184
|
+
}).success).toBe(true);
|
|
185
|
+
});
|
|
186
|
+
});
|
package/lib/types/cookie.d.ts
CHANGED
|
@@ -3,17 +3,21 @@ export type LogtoUiCookie = Partial<{
|
|
|
3
3
|
appId: string;
|
|
4
4
|
organizationId: string;
|
|
5
5
|
uiLocales: string;
|
|
6
|
+
theme: string;
|
|
6
7
|
}>;
|
|
7
8
|
export declare const logtoUiCookieGuard: z.ZodObject<{
|
|
8
9
|
appId: z.ZodOptional<z.ZodString>;
|
|
9
10
|
organizationId: z.ZodOptional<z.ZodString>;
|
|
10
11
|
uiLocales: z.ZodOptional<z.ZodString>;
|
|
12
|
+
theme: z.ZodOptional<z.ZodString>;
|
|
11
13
|
}, "strip", z.ZodTypeAny, {
|
|
12
14
|
organizationId?: string | undefined;
|
|
13
15
|
appId?: string | undefined;
|
|
14
16
|
uiLocales?: string | undefined;
|
|
17
|
+
theme?: string | undefined;
|
|
15
18
|
}, {
|
|
16
19
|
organizationId?: string | undefined;
|
|
17
20
|
appId?: string | undefined;
|
|
18
21
|
uiLocales?: string | undefined;
|
|
22
|
+
theme?: string | undefined;
|
|
19
23
|
}>;
|
package/lib/types/cookie.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export const logtoUiCookieGuard = z
|
|
3
|
-
.object({
|
|
3
|
+
.object({
|
|
4
|
+
appId: z.string(),
|
|
5
|
+
organizationId: z.string(),
|
|
6
|
+
uiLocales: z.string(),
|
|
7
|
+
theme: z.string(),
|
|
8
|
+
})
|
|
4
9
|
.partial();
|
package/lib/types/index.d.ts
CHANGED
package/lib/types/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { AdditionalIdentifier, MfaFactor, SignInIdentifier } from '../foundations/index.js';
|
|
3
|
+
import { type RequestedAuthenticationContext } from './authentication-context.js';
|
|
3
4
|
import { InteractionEvent } from './interaction-event.js';
|
|
4
5
|
import type { EmailVerificationCodePayload, PhoneVerificationCodePayload } from './verification-code.js';
|
|
6
|
+
import { VerificationType } from './verification-records/verification-type.js';
|
|
5
7
|
export { eventGuard, InteractionEvent } from './interaction-event.js';
|
|
6
8
|
export type VerificationIdentifier = {
|
|
7
9
|
type: SignInIdentifier | AdditionalIdentifier;
|
|
@@ -57,6 +59,55 @@ export declare const verificationCodeIdentifierGuard: z.ZodDiscriminatedUnion<"t
|
|
|
57
59
|
value: string;
|
|
58
60
|
type: SignInIdentifier.Phone;
|
|
59
61
|
}>]>;
|
|
62
|
+
/**
|
|
63
|
+
* The subject-bound verification code identifier: the type alone. Accepted once the interaction
|
|
64
|
+
* carries a subject; core fills the value from that user's primary email / phone. A present
|
|
65
|
+
* `value` is rejected so a malformed full identifier is never mistaken for this shape.
|
|
66
|
+
*/
|
|
67
|
+
export type SubjectVerificationCodeIdentifier = {
|
|
68
|
+
type: VerificationCodeSignInIdentifier;
|
|
69
|
+
value?: undefined;
|
|
70
|
+
};
|
|
71
|
+
export declare const subjectVerificationCodeIdentifierGuard: z.ZodObject<{
|
|
72
|
+
type: z.ZodEnum<[SignInIdentifier.Email, SignInIdentifier.Phone]>;
|
|
73
|
+
value: z.ZodUndefined;
|
|
74
|
+
}, "strip", z.ZodTypeAny, {
|
|
75
|
+
type: SignInIdentifier.Email | SignInIdentifier.Phone;
|
|
76
|
+
value?: undefined;
|
|
77
|
+
}, {
|
|
78
|
+
type: SignInIdentifier.Email | SignInIdentifier.Phone;
|
|
79
|
+
value?: undefined;
|
|
80
|
+
}>;
|
|
81
|
+
/** The full identifier, or the subject-bound shape. */
|
|
82
|
+
export type VerificationCodeIdentifierPayload = VerificationCodeIdentifier | SubjectVerificationCodeIdentifier;
|
|
83
|
+
export declare const verificationCodeIdentifierPayloadGuard: z.ZodUnion<[z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
84
|
+
type: z.ZodLiteral<SignInIdentifier.Email>;
|
|
85
|
+
value: z.ZodString;
|
|
86
|
+
}, "strip", z.ZodTypeAny, {
|
|
87
|
+
value: string;
|
|
88
|
+
type: SignInIdentifier.Email;
|
|
89
|
+
}, {
|
|
90
|
+
value: string;
|
|
91
|
+
type: SignInIdentifier.Email;
|
|
92
|
+
}>, z.ZodObject<{
|
|
93
|
+
type: z.ZodLiteral<SignInIdentifier.Phone>;
|
|
94
|
+
value: z.ZodString;
|
|
95
|
+
}, "strip", z.ZodTypeAny, {
|
|
96
|
+
value: string;
|
|
97
|
+
type: SignInIdentifier.Phone;
|
|
98
|
+
}, {
|
|
99
|
+
value: string;
|
|
100
|
+
type: SignInIdentifier.Phone;
|
|
101
|
+
}>]>, z.ZodObject<{
|
|
102
|
+
type: z.ZodEnum<[SignInIdentifier.Email, SignInIdentifier.Phone]>;
|
|
103
|
+
value: z.ZodUndefined;
|
|
104
|
+
}, "strip", z.ZodTypeAny, {
|
|
105
|
+
type: SignInIdentifier.Email | SignInIdentifier.Phone;
|
|
106
|
+
value?: undefined;
|
|
107
|
+
}, {
|
|
108
|
+
type: SignInIdentifier.Email | SignInIdentifier.Phone;
|
|
109
|
+
value?: undefined;
|
|
110
|
+
}>]>;
|
|
60
111
|
/** Payload type for `POST /api/experience/verification/{social|sso}/:connectorId/authorization-uri`. */
|
|
61
112
|
export type SocialAuthorizationUrlPayload = {
|
|
62
113
|
state: string;
|
|
@@ -128,6 +179,26 @@ export declare const passwordVerificationPayloadGuard: z.ZodObject<{
|
|
|
128
179
|
type: SignInIdentifier;
|
|
129
180
|
};
|
|
130
181
|
}>;
|
|
182
|
+
/**
|
|
183
|
+
* The subject-bound password payload: the password alone. Accepted only when the interaction
|
|
184
|
+
* carries a subject; the password is then verified against that user's credential.
|
|
185
|
+
*/
|
|
186
|
+
export type SubjectPasswordVerificationPayload = {
|
|
187
|
+
identifier?: undefined;
|
|
188
|
+
password: string;
|
|
189
|
+
};
|
|
190
|
+
export declare const subjectPasswordVerificationPayloadGuard: z.ZodObject<{
|
|
191
|
+
identifier: z.ZodUndefined;
|
|
192
|
+
password: z.ZodString;
|
|
193
|
+
}, "strip", z.ZodTypeAny, {
|
|
194
|
+
password: string;
|
|
195
|
+
identifier?: undefined;
|
|
196
|
+
}, {
|
|
197
|
+
password: string;
|
|
198
|
+
identifier?: undefined;
|
|
199
|
+
}>;
|
|
200
|
+
/** The full password payload, or the subject-bound shape. */
|
|
201
|
+
export type PasswordVerificationRequestBody = PasswordVerificationPayload | SubjectPasswordVerificationPayload;
|
|
131
202
|
/** Payload type for `POST /api/experience/verification/totp/verify`. */
|
|
132
203
|
export type TotpVerificationVerifyPayload = {
|
|
133
204
|
code: string;
|
|
@@ -472,6 +543,96 @@ export declare enum MissingProfile {
|
|
|
472
543
|
emailOrPhone = "emailOrPhone",
|
|
473
544
|
extraProfile = "extraProfile"
|
|
474
545
|
}
|
|
546
|
+
/** A social or enterprise SSO connector linked to the pinned user that can serve as subject proof. */
|
|
547
|
+
export type SubjectProofConnector = {
|
|
548
|
+
type: 'social' | 'sso';
|
|
549
|
+
connectorId: string;
|
|
550
|
+
};
|
|
551
|
+
/**
|
|
552
|
+
* The masked primary identifiers of the pinned user. Only the masked values ever leave the
|
|
553
|
+
* server: the step-up UI shows them as hints and never supplies a raw identifier.
|
|
554
|
+
*/
|
|
555
|
+
export type MaskedIdentifiers = {
|
|
556
|
+
email?: string;
|
|
557
|
+
phone?: string;
|
|
558
|
+
};
|
|
559
|
+
/**
|
|
560
|
+
* The authentication context `GET /api/experience/interaction` exposes when the interaction was
|
|
561
|
+
* created from a login prompt that carries requested `acr_values`. The requested part is copied
|
|
562
|
+
* verbatim from the prompt details at creation and never mutated; the lists are evaluated on
|
|
563
|
+
* every read from the pinned user's enrolled methods and the context the interaction achieved so
|
|
564
|
+
* far, and are never persisted, so a factor change during the interaction's lifetime is reflected.
|
|
565
|
+
*/
|
|
566
|
+
export type InteractionAuthenticationContext = RequestedAuthenticationContext & {
|
|
567
|
+
/** The methods that can still contribute to the selected class from where the interaction stands. */
|
|
568
|
+
availableMethods: VerificationType[];
|
|
569
|
+
/** The first factors the user may establish under a fresh subject proof; `[]` until enrollment lands. */
|
|
570
|
+
establishableMethods: MissingProfile[];
|
|
571
|
+
/** The MFA factors the user may enroll after a fresh first factor; `[]` until enrollment lands. */
|
|
572
|
+
enrollableFactors: MfaFactor[];
|
|
573
|
+
/** The linked connectors that can serve as subject proof in pure step-up; `[]` until enrollment lands. */
|
|
574
|
+
subjectProofConnectors: SubjectProofConnector[];
|
|
575
|
+
maskedIdentifiers: MaskedIdentifiers;
|
|
576
|
+
};
|
|
577
|
+
export declare const interactionAuthenticationContextGuard: z.ZodObject<{
|
|
578
|
+
requestedAcrValues: z.ZodArray<z.ZodNativeEnum<typeof import("./authentication-context.js").LogtoAcr>, "many">;
|
|
579
|
+
selectedAcr: z.ZodOptional<z.ZodNativeEnum<typeof import("./authentication-context.js").LogtoAcr>>;
|
|
580
|
+
mode: z.ZodOptional<z.ZodNativeEnum<typeof import("./authentication-context.js").AuthenticationContextMode>>;
|
|
581
|
+
} & {
|
|
582
|
+
availableMethods: z.ZodArray<z.ZodNativeEnum<typeof VerificationType>, "many">;
|
|
583
|
+
establishableMethods: z.ZodArray<z.ZodNativeEnum<typeof MissingProfile>, "many">;
|
|
584
|
+
enrollableFactors: z.ZodArray<z.ZodNativeEnum<typeof MfaFactor>, "many">;
|
|
585
|
+
subjectProofConnectors: z.ZodArray<z.ZodObject<{
|
|
586
|
+
type: z.ZodEnum<["social", "sso"]>;
|
|
587
|
+
connectorId: z.ZodString;
|
|
588
|
+
}, "strip", z.ZodTypeAny, {
|
|
589
|
+
type: "social" | "sso";
|
|
590
|
+
connectorId: string;
|
|
591
|
+
}, {
|
|
592
|
+
type: "social" | "sso";
|
|
593
|
+
connectorId: string;
|
|
594
|
+
}>, "many">;
|
|
595
|
+
maskedIdentifiers: z.ZodObject<{
|
|
596
|
+
email: z.ZodOptional<z.ZodString>;
|
|
597
|
+
phone: z.ZodOptional<z.ZodString>;
|
|
598
|
+
}, "strip", z.ZodTypeAny, {
|
|
599
|
+
email?: string | undefined;
|
|
600
|
+
phone?: string | undefined;
|
|
601
|
+
}, {
|
|
602
|
+
email?: string | undefined;
|
|
603
|
+
phone?: string | undefined;
|
|
604
|
+
}>;
|
|
605
|
+
}, "strip", z.ZodTypeAny, {
|
|
606
|
+
requestedAcrValues: import("./authentication-context.js").LogtoAcr[];
|
|
607
|
+
availableMethods: VerificationType[];
|
|
608
|
+
establishableMethods: MissingProfile[];
|
|
609
|
+
enrollableFactors: MfaFactor[];
|
|
610
|
+
subjectProofConnectors: {
|
|
611
|
+
type: "social" | "sso";
|
|
612
|
+
connectorId: string;
|
|
613
|
+
}[];
|
|
614
|
+
maskedIdentifiers: {
|
|
615
|
+
email?: string | undefined;
|
|
616
|
+
phone?: string | undefined;
|
|
617
|
+
};
|
|
618
|
+
mode?: import("./authentication-context.js").AuthenticationContextMode | undefined;
|
|
619
|
+
selectedAcr?: import("./authentication-context.js").LogtoAcr | undefined;
|
|
620
|
+
}, {
|
|
621
|
+
requestedAcrValues: import("./authentication-context.js").LogtoAcr[];
|
|
622
|
+
availableMethods: VerificationType[];
|
|
623
|
+
establishableMethods: MissingProfile[];
|
|
624
|
+
enrollableFactors: MfaFactor[];
|
|
625
|
+
subjectProofConnectors: {
|
|
626
|
+
type: "social" | "sso";
|
|
627
|
+
connectorId: string;
|
|
628
|
+
}[];
|
|
629
|
+
maskedIdentifiers: {
|
|
630
|
+
email?: string | undefined;
|
|
631
|
+
phone?: string | undefined;
|
|
632
|
+
};
|
|
633
|
+
mode?: import("./authentication-context.js").AuthenticationContextMode | undefined;
|
|
634
|
+
selectedAcr?: import("./authentication-context.js").LogtoAcr | undefined;
|
|
635
|
+
}>;
|
|
475
636
|
export declare const bindTotpPayloadGuard: z.ZodObject<{
|
|
476
637
|
type: z.ZodLiteral<MfaFactor.TOTP>;
|
|
477
638
|
code: z.ZodString;
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
import { emailRegEx, numberAndAlphabetRegEx, phoneRegEx, usernameRegEx } from '@logto/core-kit';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { AdditionalIdentifier, MfaFactor, SignInIdentifier, jsonObjectGuard, webAuthnTransportGuard, } from '../foundations/index.js';
|
|
5
|
+
import { requestedAuthenticationContextGuard, } from './authentication-context.js';
|
|
5
6
|
import { InteractionEvent } from './interaction-event.js';
|
|
6
7
|
import { emailVerificationCodePayloadGuard, phoneVerificationCodePayloadGuard, } from './verification-code.js';
|
|
8
|
+
import { VerificationType } from './verification-records/verification-type.js';
|
|
7
9
|
export { eventGuard, InteractionEvent } from './interaction-event.js';
|
|
8
10
|
export const verificationIdentifierGuard = z.object({
|
|
9
11
|
type: z.union([z.nativeEnum(SignInIdentifier), z.nativeEnum(AdditionalIdentifier)]),
|
|
@@ -25,6 +27,14 @@ export const verificationCodeIdentifierGuard = z.discriminatedUnion('type', [
|
|
|
25
27
|
value: z.string().regex(phoneRegEx),
|
|
26
28
|
}),
|
|
27
29
|
]);
|
|
30
|
+
export const subjectVerificationCodeIdentifierGuard = z.object({
|
|
31
|
+
type: z.enum([SignInIdentifier.Email, SignInIdentifier.Phone]),
|
|
32
|
+
value: z.undefined(),
|
|
33
|
+
});
|
|
34
|
+
export const verificationCodeIdentifierPayloadGuard = z.union([
|
|
35
|
+
verificationCodeIdentifierGuard,
|
|
36
|
+
subjectVerificationCodeIdentifierGuard,
|
|
37
|
+
]);
|
|
28
38
|
export const socialAuthorizationUrlPayloadGuard = z.object({
|
|
29
39
|
state: z.string(),
|
|
30
40
|
redirectUri: z.string(),
|
|
@@ -38,6 +48,10 @@ export const passwordVerificationPayloadGuard = z.object({
|
|
|
38
48
|
identifier: interactionIdentifierGuard,
|
|
39
49
|
password: z.string().min(1),
|
|
40
50
|
});
|
|
51
|
+
export const subjectPasswordVerificationPayloadGuard = z.object({
|
|
52
|
+
identifier: z.undefined(),
|
|
53
|
+
password: z.string().min(1),
|
|
54
|
+
});
|
|
41
55
|
export const totpVerificationVerifyPayloadGuard = z.object({
|
|
42
56
|
code: z.string().min(1),
|
|
43
57
|
verificationId: z.string().optional(),
|
|
@@ -149,6 +163,15 @@ export var MissingProfile;
|
|
|
149
163
|
MissingProfile["emailOrPhone"] = "emailOrPhone";
|
|
150
164
|
MissingProfile["extraProfile"] = "extraProfile";
|
|
151
165
|
})(MissingProfile || (MissingProfile = {}));
|
|
166
|
+
export const interactionAuthenticationContextGuard = requestedAuthenticationContextGuard.extend({
|
|
167
|
+
availableMethods: z.nativeEnum(VerificationType).array(),
|
|
168
|
+
establishableMethods: z.nativeEnum(MissingProfile).array(),
|
|
169
|
+
enrollableFactors: z.nativeEnum(MfaFactor).array(),
|
|
170
|
+
subjectProofConnectors: z
|
|
171
|
+
.object({ type: z.enum(['social', 'sso']), connectorId: z.string() })
|
|
172
|
+
.array(),
|
|
173
|
+
maskedIdentifiers: z.object({ email: z.string().optional(), phone: z.string().optional() }),
|
|
174
|
+
});
|
|
152
175
|
export const bindTotpPayloadGuard = z.object({
|
|
153
176
|
// Unlike identifier payload which has indicator like "email",
|
|
154
177
|
// mfa payload must have an additional type field to indicate type
|
|
@@ -13,7 +13,12 @@ export declare enum Field {
|
|
|
13
13
|
Mfa = "Mfa",
|
|
14
14
|
Verification = "Verification",
|
|
15
15
|
Captcha = "Captcha",
|
|
16
|
-
SignInPasskey = "SignInPasskey"
|
|
16
|
+
SignInPasskey = "SignInPasskey",
|
|
17
|
+
/**
|
|
18
|
+
* A pure step-up submission: an authenticated session re-proved its subject to reach a requested
|
|
19
|
+
* authentication context class. Not a sign-in, so it never shares `SignIn.Submit`.
|
|
20
|
+
*/
|
|
21
|
+
StepUp = "StepUp"
|
|
17
22
|
}
|
|
18
23
|
/** Method to verify the identifier */
|
|
19
24
|
export declare enum Method {
|
|
@@ -74,5 +79,13 @@ export type DeprecatedInteractionLogKey = `${Prefix}.${InteractionEvent}.${Field
|
|
|
74
79
|
* - Indicates an identifier method is being created or submitted to an interaction.
|
|
75
80
|
* - When {@link Method} is `VerificationCode`, {@link Action} can be `Create` (generate and send a code) or `Submit` (verify and submit to the identifiers);
|
|
76
81
|
* - Otherwise, {@link Action} is fixed to `Submit` (other methods can be verified on submitting).
|
|
82
|
+
*
|
|
83
|
+
* ```ts
|
|
84
|
+
* `Interaction.${InteractionEvent}.${Field.StepUp}.${Action.Submit}`
|
|
85
|
+
* ```
|
|
86
|
+
*
|
|
87
|
+
* - Indicates a pure step-up interaction is submitted: an authenticated session re-proved its
|
|
88
|
+
* subject to reach the requested authentication context class. Its payload records the requested
|
|
89
|
+
* and achieved classes and the factor families that were proved, never a credential.
|
|
77
90
|
*/
|
|
78
|
-
export type LogKey = `${Prefix}.${Action.Create | Action.End}` | `${Prefix}.${Action.Create}.${Field.Captcha}` | `${Prefix}.${InteractionEvent}.${Action.Create | Action.Update | Action.Submit}` | `${Prefix}.${InteractionEvent}.${Field.Profile}.${Action.Update}` | `${Prefix}.${InteractionEvent}.${Field.BindMfa}.${MfaFactor}.${Action.Submit}` | `${Prefix}.${InteractionEvent}.${Field.SignInPasskey}.${Action.Submit}` | `${Prefix}.${InteractionEvent}.${Field.Verification}.${VerificationType}.${Action}` | `${Prefix}.${InteractionEvent}.${Field.Identifier}.${Action.Submit}` | `${Prefix}.${InteractionEvent.SignIn}.${Field.Verification}.IdpInitiatedSso.${Action.Create}` | DeprecatedInteractionLogKey;
|
|
91
|
+
export type LogKey = `${Prefix}.${Action.Create | Action.End}` | `${Prefix}.${Action.Create}.${Field.Captcha}` | `${Prefix}.${InteractionEvent}.${Action.Create | Action.Update | Action.Submit}` | `${Prefix}.${InteractionEvent}.${Field.Profile}.${Action.Update}` | `${Prefix}.${InteractionEvent}.${Field.BindMfa}.${MfaFactor}.${Action.Submit}` | `${Prefix}.${InteractionEvent}.${Field.SignInPasskey}.${Action.Submit}` | `${Prefix}.${InteractionEvent}.${Field.Verification}.${VerificationType}.${Action}` | `${Prefix}.${InteractionEvent}.${Field.Identifier}.${Action.Submit}` | `${Prefix}.${InteractionEvent}.${Field.StepUp}.${Action.Submit}` | `${Prefix}.${InteractionEvent.SignIn}.${Field.Verification}.IdpInitiatedSso.${Action.Create}` | DeprecatedInteractionLogKey;
|
|
@@ -11,6 +11,11 @@ export var Field;
|
|
|
11
11
|
Field["Verification"] = "Verification";
|
|
12
12
|
Field["Captcha"] = "Captcha";
|
|
13
13
|
Field["SignInPasskey"] = "SignInPasskey";
|
|
14
|
+
/**
|
|
15
|
+
* A pure step-up submission: an authenticated session re-proved its subject to reach a requested
|
|
16
|
+
* authentication context class. Not a sign-in, so it never shares `SignIn.Submit`.
|
|
17
|
+
*/
|
|
18
|
+
Field["StepUp"] = "StepUp";
|
|
14
19
|
})(Field || (Field = {}));
|
|
15
20
|
/** Method to verify the identifier */
|
|
16
21
|
export var Method;
|