@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.
Files changed (106) hide show
  1. package/alterations/1.44.0-1788946735-add-saml-authn-request-config.ts +18 -0
  2. package/alterations/1.44.0-1789000000-extend-user-id-length.ts +61 -0
  3. package/alterations/1.45.0-1791531333-add-tenant-viewer-role.ts +38 -0
  4. package/alterations-js/1.44.0-1788946735-add-saml-authn-request-config.js +14 -0
  5. package/alterations-js/1.44.0-1789000000-extend-user-id-length.js +47 -0
  6. package/alterations-js/1.45.0-1791531333-add-tenant-viewer-role.js +32 -0
  7. package/lib/consts/experience.d.ts +3 -0
  8. package/lib/consts/experience.js +3 -0
  9. package/lib/consts/index.d.ts +1 -0
  10. package/lib/consts/index.js +1 -0
  11. package/lib/consts/license.d.ts +222 -0
  12. package/lib/consts/license.js +81 -0
  13. package/lib/consts/license.test.d.ts +1 -0
  14. package/lib/consts/license.test.js +96 -0
  15. package/lib/consts/oidc.d.ts +15 -1
  16. package/lib/consts/oidc.js +11 -0
  17. package/lib/consts/oidc.test.d.ts +1 -0
  18. package/lib/consts/oidc.test.js +15 -0
  19. package/lib/consts/subscriptions.d.ts +13 -1
  20. package/lib/consts/subscriptions.js +12 -0
  21. package/lib/db-entries/aggregated-daily-active-user.js +2 -2
  22. package/lib/db-entries/application-access-control-user-relation.js +2 -2
  23. package/lib/db-entries/application-user-consent-organization.js +2 -2
  24. package/lib/db-entries/cimd-grant-organization.js +2 -2
  25. package/lib/db-entries/daily-active-user.js +2 -2
  26. package/lib/db-entries/oidc-session-extension.js +2 -2
  27. package/lib/db-entries/organization-invitation.js +4 -4
  28. package/lib/db-entries/organization-role-user-relation.js +2 -2
  29. package/lib/db-entries/organization-user-relation.js +2 -2
  30. package/lib/db-entries/personal-access-token.js +2 -2
  31. package/lib/db-entries/saml-application-config.d.ts +4 -2
  32. package/lib/db-entries/saml-application-config.js +5 -1
  33. package/lib/db-entries/secret.js +2 -2
  34. package/lib/db-entries/subject-token.js +2 -2
  35. package/lib/db-entries/trusted-device.js +2 -2
  36. package/lib/db-entries/user-geo-location.js +2 -2
  37. package/lib/db-entries/user-sign-in-country.js +2 -2
  38. package/lib/db-entries/user-sso-identity.js +2 -2
  39. package/lib/db-entries/user.js +2 -2
  40. package/lib/db-entries/users-role.js +2 -2
  41. package/lib/db-entries/verification-record.js +2 -2
  42. package/lib/db-entries/verification-status.js +2 -2
  43. package/lib/foundations/jsonb-types/captcha.d.ts +47 -1
  44. package/lib/foundations/jsonb-types/captcha.js +20 -0
  45. package/lib/foundations/jsonb-types/hooks.d.ts +0 -3
  46. package/lib/foundations/jsonb-types/hooks.js +2 -6
  47. package/lib/foundations/jsonb-types/saml-application-configs.d.ts +23 -0
  48. package/lib/foundations/jsonb-types/saml-application-configs.js +16 -0
  49. package/lib/foundations/jsonb-types/saml-application-configs.test.js +24 -1
  50. package/lib/types/authentication-context.d.ts +224 -0
  51. package/lib/types/authentication-context.js +238 -0
  52. package/lib/types/authentication-context.test.d.ts +1 -0
  53. package/lib/types/authentication-context.test.js +186 -0
  54. package/lib/types/cookie.d.ts +4 -0
  55. package/lib/types/cookie.js +6 -1
  56. package/lib/types/index.d.ts +1 -0
  57. package/lib/types/index.js +1 -0
  58. package/lib/types/interactions.d.ts +161 -0
  59. package/lib/types/interactions.js +23 -0
  60. package/lib/types/log/interaction.d.ts +15 -2
  61. package/lib/types/log/interaction.js +5 -0
  62. package/lib/types/logto-config/index.d.ts +47 -0
  63. package/lib/types/logto-config/index.js +1 -0
  64. package/lib/types/logto-config/jwt-customizer.d.ts +92 -0
  65. package/lib/types/saml-application.d.ts +55 -1
  66. package/lib/types/saml-application.js +7 -1
  67. package/lib/types/sign-in-experience.d.ts +11 -7
  68. package/lib/types/sign-in-experience.js +2 -2
  69. package/lib/types/ssr.d.ts +2 -0
  70. package/lib/types/system.d.ts +57 -5
  71. package/lib/types/system.js +36 -0
  72. package/lib/types/system.test.d.ts +1 -0
  73. package/lib/types/system.test.js +26 -0
  74. package/lib/types/tenant-organization.d.ts +4 -2
  75. package/lib/types/tenant-organization.js +6 -2
  76. package/lib/types/tenant-organization.test.d.ts +1 -0
  77. package/lib/types/tenant-organization.test.js +16 -0
  78. package/lib/types/user-sessions.d.ts +156 -0
  79. package/lib/types/verification-records/code-verification.d.ts +17 -0
  80. package/lib/types/verification-records/code-verification.js +1 -0
  81. package/lib/utils/management-api.d.ts +5 -0
  82. package/lib/utils/management-api.js +7 -1
  83. package/lib/utils/oidc-private-key.test.js +44 -0
  84. package/package.json +7 -7
  85. package/tables/aggregated_daily_active_users.sql +1 -1
  86. package/tables/application_access_control_user_relations.sql +1 -1
  87. package/tables/application_user_consent_organizations.sql +1 -1
  88. package/tables/cimd_grant_organizations.sql +1 -1
  89. package/tables/daily_active_users.sql +1 -1
  90. package/tables/oidc_session_extensions.sql +1 -1
  91. package/tables/organization_invitations.sql +2 -2
  92. package/tables/organization_role_user_relations.sql +1 -1
  93. package/tables/organization_user_relations.sql +1 -1
  94. package/tables/personal_access_tokens.sql +1 -1
  95. package/tables/saml_application_configs.sql +1 -0
  96. package/tables/secrets.sql +1 -1
  97. package/tables/subject_tokens.sql +1 -1
  98. package/tables/trusted_devices.sql +1 -1
  99. package/tables/user_geo_locations.sql +1 -1
  100. package/tables/user_sign_in_countries.sql +1 -1
  101. package/tables/user_sso_identities.sql +1 -1
  102. package/tables/users.sql +1 -1
  103. package/tables/users_roles.sql +1 -1
  104. package/tables/verification_records.sql +1 -1
  105. package/tables/verification_statuses.sql +1 -1
  106. 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
+ });
@@ -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
  }>;
@@ -1,4 +1,9 @@
1
1
  import { z } from 'zod';
2
2
  export const logtoUiCookieGuard = z
3
- .object({ appId: z.string(), organizationId: z.string(), uiLocales: z.string() })
3
+ .object({
4
+ appId: z.string(),
5
+ organizationId: z.string(),
6
+ uiLocales: z.string(),
7
+ theme: z.string(),
8
+ })
4
9
  .partial();
@@ -37,3 +37,4 @@ export * from './secrets.js';
37
37
  export * from './user-logto-config.js';
38
38
  export * from './user-sessions.js';
39
39
  export * from './trusted-device.js';
40
+ export * from './authentication-context.js';
@@ -37,3 +37,4 @@ export * from './secrets.js';
37
37
  export * from './user-logto-config.js';
38
38
  export * from './user-sessions.js';
39
39
  export * from './trusted-device.js';
40
+ export * from './authentication-context.js';
@@ -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;