@dloizides/auth-web 1.11.0 → 1.12.1

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/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { ReactNode, ReactElement } from 'react';
2
- import { BffAuthClient, BffUser, HttpClient, BffLoginRequest, BffForgotPasswordRequest, BffResetPasswordRequest, BffOtpRequestResult } from '@dloizides/auth-client';
3
- export { BffAuthClient, BffAuthClientOptions, BffForgotPasswordRequest, BffLoginRequest, BffOtpRequestRequest, BffOtpRequestResult, BffOtpVerifyRequest, BffPinLoginRequest, BffRegisterRequest, BffResetPasswordRequest, BffUser, HttpClient, HttpRequest, HttpResponse, createFetchHttpClient } from '@dloizides/auth-client';
2
+ import { BffAuthClient, BffUser, DemoCredentials, HttpClient, BffLoginRequest, BffForgotPasswordRequest, BffResetPasswordRequest, BffOtpRequestResult } from '@dloizides/auth-client';
3
+ export { BffAuthClient, BffAuthClientOptions, BffForgotPasswordRequest, BffLoginRequest, BffOtpRequestRequest, BffOtpRequestResult, BffOtpVerifyRequest, BffPinLoginRequest, BffRegisterRequest, BffResetPasswordRequest, BffUser, DemoCredentials, HttpClient, HttpRequest, HttpResponse, createFetchHttpClient } from '@dloizides/auth-client';
4
4
  import { UseMutationOptions, UseMutationResult } from '@tanstack/react-query';
5
5
 
6
6
  /**
@@ -129,18 +129,6 @@ interface AuthScreenProps {
129
129
  /** The screen-level auth composition: one centered card the controls share. */
130
130
  declare function AuthScreen({ children, theme: themeProp, testID, cardTestID, maxWidth, title, subtitle, footer, }: Readonly<AuthScreenProps>): ReactElement;
131
131
 
132
- /**
133
- * Label bags for the auth components.
134
- *
135
- * `@dloizides/auth-web` ships **no** translation framework — it is i18n-agnostic
136
- * by design (each app already owns its own i18n). Every user-facing string a
137
- * component renders is supplied by the consuming app through a typed `labels`
138
- * prop. The app passes strings it has already localised with `FM()` / `t()`.
139
- *
140
- * Each bag has a `DEFAULT_*` constant (English) so a component renders sensibly
141
- * with no `labels` prop and a consuming app can spread-override only the keys
142
- * it wants to change.
143
- */
144
132
  /** Strings rendered by `<LoginForm>`. */
145
133
  interface LoginFormLabels {
146
134
  title: string;
@@ -162,6 +150,8 @@ interface LoginFormLabels {
162
150
  /** Shown when one or both fields are empty on submit. */
163
151
  missingFields: string;
164
152
  }
153
+ declare const DEFAULT_LOGIN_LABELS: LoginFormLabels;
154
+
165
155
  /** Strings rendered by `<ForgotPasswordForm>`. */
166
156
  interface ForgotPasswordFormLabels {
167
157
  title: string;
@@ -189,6 +179,9 @@ interface ForgotPasswordFieldsLabels extends ForgotPasswordFormLabels {
189
179
  /** The "close" button shown on the success state. */
190
180
  close: string;
191
181
  }
182
+ declare const DEFAULT_FORGOT_PASSWORD_LABELS: ForgotPasswordFormLabels;
183
+ declare const DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS: ForgotPasswordFieldsLabels;
184
+
192
185
  /** Strings rendered by `<OtpForm>` — the two-step email-OTP login form. */
193
186
  interface OtpFormLabels {
194
187
  /** Title shown on the request-a-code step. */
@@ -224,6 +217,8 @@ interface OtpFormLabels {
224
217
  /** The "use a different email" link text — returns to step 1. */
225
218
  changeEmail: string;
226
219
  }
220
+ declare const DEFAULT_OTP_LABELS: OtpFormLabels;
221
+
227
222
  /** Strings rendered by `<PinForm>` — the single-step event-PIN login form. */
228
223
  interface PinFormLabels {
229
224
  /** Title shown above the PIN field. */
@@ -241,6 +236,8 @@ interface PinFormLabels {
241
236
  /** Generic "that PIN was wrong / expired / locked out" message. */
242
237
  invalidPin: string;
243
238
  }
239
+ declare const DEFAULT_PIN_LABELS: PinFormLabels;
240
+
244
241
  /**
245
242
  * Strings rendered by `<DevicePinUnlockScreen>` — the returning-device unlock
246
243
  * gate. `{name}` / `{count}` placeholders are substituted by the component.
@@ -336,6 +333,10 @@ interface DevicePinSettingsLabels {
336
333
  /** Error shown when disabling fails. */
337
334
  disableFailed: string;
338
335
  }
336
+ declare const DEFAULT_DEVICE_PIN_UNLOCK_LABELS: DevicePinUnlockLabels;
337
+ declare const DEFAULT_DEVICE_PIN_ENROLL_LABELS: DevicePinEnrollLabels;
338
+ declare const DEFAULT_DEVICE_PIN_SETTINGS_LABELS: DevicePinSettingsLabels;
339
+
339
340
  /** Strings rendered by `<PasskeyLoginButton>` — the login-surface passkey CTA. */
340
341
  interface PasskeyLoginLabels {
341
342
  /** The "sign in with a passkey" button. */
@@ -365,6 +366,9 @@ interface PasskeySettingsLabels {
365
366
  /** Success line shown after returning from a registration. */
366
367
  registeredSuccess: string;
367
368
  }
369
+ declare const DEFAULT_PASSKEY_LOGIN_LABELS: PasskeyLoginLabels;
370
+ declare const DEFAULT_PASSKEY_SETTINGS_LABELS: PasskeySettingsLabels;
371
+
368
372
  /** Strings rendered by `<PreferredMethodSettingsCard>` — the cross-device default-method picker. */
369
373
  interface PreferredMethodSettingsLabels {
370
374
  title: string;
@@ -391,6 +395,9 @@ interface PreferredMethodHintLabels {
391
395
  /** Full sentence shown when the cached preference is passkey. */
392
396
  passkey: string;
393
397
  }
398
+ declare const DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS: PreferredMethodSettingsLabels;
399
+ declare const DEFAULT_PREFERRED_METHOD_HINT_LABELS: PreferredMethodHintLabels;
400
+
394
401
  /** Strings rendered by `<DemoCredentialsHint>` — the public-demo credentials panel. */
395
402
  interface DemoCredentialsHintLabels {
396
403
  /** Panel heading (e.g. "Demo access"). */
@@ -406,6 +413,8 @@ interface DemoCredentialsHintLabels {
406
413
  /** Accessibility hint for that button. */
407
414
  useCredentialsHint: string;
408
415
  }
416
+ declare const DEFAULT_DEMO_CREDENTIALS_HINT_LABELS: DemoCredentialsHintLabels;
417
+
409
418
  /** Strings rendered by `<ResetPasswordForm>`. */
410
419
  interface ResetPasswordFormLabels {
411
420
  title: string;
@@ -427,33 +436,32 @@ interface ResetPasswordFormLabels {
427
436
  /** Error: a network / 5xx failure. */
428
437
  errorNetwork: string;
429
438
  }
430
- declare const DEFAULT_LOGIN_LABELS: LoginFormLabels;
431
- declare const DEFAULT_FORGOT_PASSWORD_LABELS: ForgotPasswordFormLabels;
432
- declare const DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS: ForgotPasswordFieldsLabels;
433
- declare const DEFAULT_OTP_LABELS: OtpFormLabels;
434
- declare const DEFAULT_PIN_LABELS: PinFormLabels;
435
- declare const DEFAULT_DEVICE_PIN_UNLOCK_LABELS: DevicePinUnlockLabels;
436
- declare const DEFAULT_DEVICE_PIN_ENROLL_LABELS: DevicePinEnrollLabels;
437
- declare const DEFAULT_DEVICE_PIN_SETTINGS_LABELS: DevicePinSettingsLabels;
438
- declare const DEFAULT_PASSKEY_LOGIN_LABELS: PasskeyLoginLabels;
439
- declare const DEFAULT_PASSKEY_SETTINGS_LABELS: PasskeySettingsLabels;
440
- declare const DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS: PreferredMethodSettingsLabels;
441
- declare const DEFAULT_PREFERRED_METHOD_HINT_LABELS: PreferredMethodHintLabels;
442
- declare const DEFAULT_DEMO_CREDENTIALS_HINT_LABELS: DemoCredentialsHintLabels;
443
439
  declare const DEFAULT_RESET_PASSWORD_LABELS: ResetPasswordFormLabels;
444
440
 
445
441
  /**
446
- * `<LoginForm>` — the ready-made, themeable password-login form.
442
+ * Who asked for a prefill — the difference between "the user requested this" and
443
+ * "the app offered it".
447
444
  *
448
- * The "themeable component" half of the package design: drop it in, pass a
449
- * `client`, a `theme` (or wrap in `<AuthThemeProvider>`) and a localised
450
- * `labels` bag, and you have a branded login surface. It is built on the
451
- * headless `useBffAuth` hook, so the ready-made and custom-layout paths share
452
- * one code path.
445
+ * A seed that the USER asked for (tapping "use these credentials") must always
446
+ * apply: they can see the fields, they know what is in them, and they asked
447
+ * anyway. A seed the APP offers (a demo pair arriving from `GET /bff/config`
448
+ * mid-typing) must not: it is an unrequested, racing write against a field the
449
+ * user may already be filling in.
453
450
  *
454
- * The package ships no router and no i18n. `onSuccess` hands the signed-in
455
- * `BffUser` back to the app, which decides where to navigate (typically via
456
- * `resolvePostLoginRoute`). All copy comes from `labels`.
451
+ * Collapsing the two into a bare nonce bump is what makes a late-arriving config
452
+ * fetch overwrite the username a real account holder is halfway through typing.
453
+ * `<LoginForm>` switches on this, never on a string literal.
454
+ */
455
+ declare const enum PrefillIntent {
456
+ /** The user asked for it. Always applies, over whatever is in the fields. */
457
+ Explicit = "explicit",
458
+ /** The app offered it. Applies only to untouched, empty fields. */
459
+ Opportunistic = "opportunistic"
460
+ }
461
+
462
+ /**
463
+ * Props for `<LoginForm>`, split out of the component file to keep the render
464
+ * body under the component size limit. Purely a type module — no runtime code.
457
465
  */
458
466
 
459
467
  interface LoginFormProps {
@@ -498,12 +506,35 @@ interface LoginFormProps {
498
506
  * props alone does nothing once mounted.
499
507
  *
500
508
  * Most apps should not manage this by hand: `useDemoCredentials` returns a
501
- * `loginFormProps` bag carrying all three, ready to spread.
509
+ * `loginFormProps` bag carrying all four, ready to spread.
502
510
  */
503
511
  prefillNonce?: number;
512
+ /**
513
+ * Whether the pending re-seed was requested by the user or offered by the app.
514
+ * An {@link PrefillIntent.Opportunistic} re-seed (e.g. a demo pair arriving
515
+ * from `GET /bff/config` after mount) is DROPPED unless both fields are still
516
+ * empty, so a late fetch never overwrites what someone is typing. Defaults to
517
+ * unconditional — omit it and every nonce bump applies.
518
+ */
519
+ prefillIntent?: PrefillIntent;
504
520
  }
521
+
522
+ /**
523
+ * `<LoginForm>` — the ready-made, themeable password-login form.
524
+ *
525
+ * The "themeable component" half of the package design: drop it in, pass a
526
+ * `client`, a `theme` (or wrap in `<AuthThemeProvider>`) and a localised
527
+ * `labels` bag, and you have a branded login surface. It is built on the
528
+ * headless `useBffAuth` hook, so the ready-made and custom-layout paths share
529
+ * one code path.
530
+ *
531
+ * The package ships no router and no i18n. `onSuccess` hands the signed-in
532
+ * `BffUser` back to the app, which decides where to navigate (typically via
533
+ * `resolvePostLoginRoute`). All copy comes from `labels`.
534
+ */
535
+
505
536
  /** Themeable password-login form built on `useBffAuth`. */
506
- declare function LoginForm({ client, theme: themeProp, labels: labelsProp, onSuccess, onForgotPassword, onSignUp, testIdPrefix, chromeless, initialUsername, initialPassword, prefillNonce, }: Readonly<LoginFormProps>): ReactElement;
537
+ declare function LoginForm({ client, theme: themeProp, labels: labelsProp, onSuccess, onForgotPassword, onSignUp, testIdPrefix, chromeless, initialUsername, initialPassword, prefillNonce, prefillIntent, }: Readonly<LoginFormProps>): ReactElement;
507
538
 
508
539
  /**
509
540
  * `<ForgotPasswordForm>` — the ready-made, themeable "request a reset link"
@@ -802,6 +833,7 @@ declare const enum DevicePinErrorKey {
802
833
  * error) so the unlock / enrol UIs can route on each one rather than collapsing
803
834
  * them into a single opaque failure.
804
835
  */
836
+
805
837
  /**
806
838
  * The per-device half of `GET /bff/config` — what this device remembers about
807
839
  * the last user, all fields safe-defaulted by the parser when absent.
@@ -824,6 +856,14 @@ interface BffLoginConfig {
824
856
  registrationEnabled: boolean;
825
857
  /** The per-device remembered state. */
826
858
  deviceState: BffDeviceState;
859
+ /**
860
+ * The published demo credentials this deployment advertises, or `null` when
861
+ * none is configured. Feeds `useDemoCredentials` / `<DemoCredentialsHint>`:
862
+ * `useDemoCredentials({ credentials: config.demo ?? undefined })`. `auth-client`
863
+ * 4.2.0 populates it; older clients (and hand-rolled objects) simply omit it,
864
+ * which reads as `undefined` — treated the same as "no demo" by the hint.
865
+ */
866
+ demo?: DemoCredentials | null;
827
867
  }
828
868
  /**
829
869
  * Discriminated result of a device-PIN UNLOCK attempt. The client never rejects;
@@ -1245,23 +1285,6 @@ interface PreferredMethodHintProps {
1245
1285
  /** Themeable login-screen preferred-method hint. Renders `null` when there is nothing to say. */
1246
1286
  declare function PreferredMethodHint({ method, theme: themeProp, labels: labelsProp, testIdPrefix, }: Readonly<PreferredMethodHintProps>): ReactElement | null;
1247
1287
 
1248
- /**
1249
- * Public demo-credential contracts.
1250
- *
1251
- * A "demo credential" is a REAL account that a product publishes openly so a
1252
- * prospective evaluator can sign in without a sign-up step. It is not a bypass:
1253
- * the credentials travel the ordinary login path (same BFF, same Keycloak, same
1254
- * session cookie, same roles). The package only makes them *discoverable* and
1255
- * *one-click fillable* — it never authenticates anything itself.
1256
- */
1257
- /** A publishable demo username + password pair. */
1258
- interface DemoCredentials {
1259
- /** The demo account's username. */
1260
- username: string;
1261
- /** The demo account's password. */
1262
- password: string;
1263
- }
1264
-
1265
1288
  /**
1266
1289
  * `<DemoCredentialsHint>` — the public-demo credentials panel.
1267
1290
  *
@@ -1942,6 +1965,7 @@ interface DemoCredentialsLoginFormProps {
1942
1965
  initialUsername?: string;
1943
1966
  initialPassword?: string;
1944
1967
  prefillNonce: number;
1968
+ prefillIntent: PrefillIntent;
1945
1969
  }
1946
1970
  /** The `<DemoCredentialsHint>` props that carry the credentials + fill action. */
1947
1971
  interface DemoCredentialsHintWiring {
@@ -2231,4 +2255,4 @@ declare function validatePasswordPolicy(password: string): PasswordPolicyError[]
2231
2255
  /** `true` when the password satisfies every policy rule. */
2232
2256
  declare function isPasswordValid(password: string): boolean;
2233
2257
 
2234
- export { AuthScreen, type AuthScreenProps, AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type BffDeviceState, type BffLoginConfig, type BffLogoutClient, type CreateBffAuthClientOptions, DEFAULT_AUTH_CARD_MAX_WIDTH, DEFAULT_DEMO_CREDENTIALS_HINT_LABELS, DEFAULT_DEVICE_PIN_ENROLL_LABELS, DEFAULT_DEVICE_PIN_SETTINGS_LABELS, DEFAULT_DEVICE_PIN_UNLOCK_LABELS, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PASSKEY_LOGIN_LABELS, DEFAULT_PASSKEY_SETTINGS_LABELS, DEFAULT_PIN_LABELS, DEFAULT_PREFERRED_METHOD_HINT_LABELS, DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS, DEFAULT_RESET_PASSWORD_LABELS, DEVICE_PIN_ALLOWED_DIGITS, DEVICE_PIN_DEFAULT_DIGITS, type DemoCredentials, DemoCredentialsHint, type DemoCredentialsHintLabels, type DemoCredentialsHintProps, type DemoCredentialsHintWiring, type DemoCredentialsLoginFormProps, type DevicePinCapableClient, type DevicePinDisableCapableClient, type DevicePinEnrollCapableClient, DevicePinEnrollErrorKey, DevicePinEnrollForm, type DevicePinEnrollFormProps, type DevicePinEnrollLabels, type DevicePinEnrollResult, DevicePinErrorKey, DevicePinInput, type DevicePinInputProps, DevicePinLengthPicker, type DevicePinLengthPickerProps, DevicePinOffer, type DevicePinOfferProps, DevicePinSettingsCard, type DevicePinSettingsCardProps, type DevicePinSettingsLabels, type DevicePinUnlockCapableClient, type DevicePinUnlockLabels, type DevicePinUnlockResult, DevicePinUnlockScreen, type DevicePinUnlockScreenProps, type DevicePinUnlockedUser, ForgotPasswordFields, type ForgotPasswordFieldsLabels, type ForgotPasswordFieldsProps, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, type LoginConfigCapableClient, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasskeyCeremonyMode, PasskeyCeremonyStatus, type PasskeyErrorCode, PasskeyLoginButton, type PasskeyLoginButtonProps, type PasskeyLoginLabels, PasskeySettingsCard, type PasskeySettingsCardProps, type PasskeySettingsLabels, PasswordPolicyError, type PerformBffLogoutOptions, PinForm, type PinFormLabels, type PinFormProps, type PreferencesClientOptions, type PreferredMethodClient, PreferredMethodHint, type PreferredMethodHintLabels, type PreferredMethodHintProps, PreferredMethodSettingsCard, type PreferredMethodSettingsCardProps, type PreferredMethodSettingsLabels, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffLoginConfigResult, type UseBffResetPasswordOptions, type UseDemoCredentialsOptions, type UseDemoCredentialsResult, type UseDevicePinDisableArgs, type UseDevicePinDisableResult, type UseDevicePinEnrollArgs, type UseDevicePinEnrollResult, type UseDevicePinUnlockArgs, type UseDevicePinUnlockResult, type UseForgotPasswordSubmitArgs, type UseForgotPasswordSubmitResult, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePasskeyCeremonyOptions, type UsePasskeyCeremonyResult, type UsePinLoginOptions, type UsePinLoginResult, type UsePreferredMethodSettingArgs, type UsePreferredMethodSettingResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, cachePreferredMethod, clearCachedPreferredMethod, collectUserRoles, createBffAuthClient, createPreferencesClient, defaultAuthTheme, isAllowedPinDigits, isPasswordValid, isValidForgotPasswordEmail, performBffLogout, readCachedPreferredMethod, readPasskeyError, readPasskeyRegistered, resolveAccessibleRoutes, resolveDemoCredentials, resolvePostLoginRoute, startPasskeyLogin, startPasskeyRegistration, syncPreferredMethodFromServer, useAuthTheme, useBffAuth, useBffForgotPassword, useBffLoginConfig, useBffResetPassword, useDemoCredentials, useDevicePinDisable, useDevicePinEnroll, useDevicePinUnlock, useForgotPasswordSubmit, useOtpLogin, usePasskeyCeremony, usePinLogin, usePreferredMethodSetting, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
2258
+ export { AuthScreen, type AuthScreenProps, AuthTestIds, type AuthTheme, type AuthThemeColors, AuthThemeProvider, type AuthThemeProviderProps, type AuthThemeRadii, type AuthThemeSpacing, type AuthThemeTypography, BffAuthStatus, type BffDeviceState, type BffLoginConfig, type BffLogoutClient, type CreateBffAuthClientOptions, DEFAULT_AUTH_CARD_MAX_WIDTH, DEFAULT_DEMO_CREDENTIALS_HINT_LABELS, DEFAULT_DEVICE_PIN_ENROLL_LABELS, DEFAULT_DEVICE_PIN_SETTINGS_LABELS, DEFAULT_DEVICE_PIN_UNLOCK_LABELS, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PASSKEY_LOGIN_LABELS, DEFAULT_PASSKEY_SETTINGS_LABELS, DEFAULT_PIN_LABELS, DEFAULT_PREFERRED_METHOD_HINT_LABELS, DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS, DEFAULT_RESET_PASSWORD_LABELS, DEVICE_PIN_ALLOWED_DIGITS, DEVICE_PIN_DEFAULT_DIGITS, DemoCredentialsHint, type DemoCredentialsHintLabels, type DemoCredentialsHintProps, type DemoCredentialsHintWiring, type DemoCredentialsLoginFormProps, type DevicePinCapableClient, type DevicePinDisableCapableClient, type DevicePinEnrollCapableClient, DevicePinEnrollErrorKey, DevicePinEnrollForm, type DevicePinEnrollFormProps, type DevicePinEnrollLabels, type DevicePinEnrollResult, DevicePinErrorKey, DevicePinInput, type DevicePinInputProps, DevicePinLengthPicker, type DevicePinLengthPickerProps, DevicePinOffer, type DevicePinOfferProps, DevicePinSettingsCard, type DevicePinSettingsCardProps, type DevicePinSettingsLabels, type DevicePinUnlockCapableClient, type DevicePinUnlockLabels, type DevicePinUnlockResult, DevicePinUnlockScreen, type DevicePinUnlockScreenProps, type DevicePinUnlockedUser, ForgotPasswordFields, type ForgotPasswordFieldsLabels, type ForgotPasswordFieldsProps, ForgotPasswordForm, type ForgotPasswordFormLabels, type ForgotPasswordFormProps, type LoginConfigCapableClient, LoginForm, type LoginFormLabels, type LoginFormProps, OtpForm, type OtpFormLabels, type OtpFormProps, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasskeyCeremonyMode, PasskeyCeremonyStatus, type PasskeyErrorCode, PasskeyLoginButton, type PasskeyLoginButtonProps, type PasskeyLoginLabels, PasskeySettingsCard, type PasskeySettingsCardProps, type PasskeySettingsLabels, PasswordPolicyError, type PerformBffLogoutOptions, PinForm, type PinFormLabels, type PinFormProps, type PreferencesClientOptions, type PreferredMethodClient, PreferredMethodHint, type PreferredMethodHintLabels, type PreferredMethodHintProps, PreferredMethodSettingsCard, type PreferredMethodSettingsCardProps, type PreferredMethodSettingsLabels, PrefillIntent, ResetPasswordError, ResetPasswordForm, type ResetPasswordFormLabels, type ResetPasswordFormProps, type RoleRoute, type RoleRouteTable, type UseBffAuthOptions, type UseBffAuthResult, type UseBffForgotPasswordOptions, type UseBffLoginConfigResult, type UseBffResetPasswordOptions, type UseDemoCredentialsOptions, type UseDemoCredentialsResult, type UseDevicePinDisableArgs, type UseDevicePinDisableResult, type UseDevicePinEnrollArgs, type UseDevicePinEnrollResult, type UseDevicePinUnlockArgs, type UseDevicePinUnlockResult, type UseForgotPasswordSubmitArgs, type UseForgotPasswordSubmitResult, type UseOtpLoginOptions, type UseOtpLoginResult, type UsePasskeyCeremonyOptions, type UsePasskeyCeremonyResult, type UsePinLoginOptions, type UsePinLoginResult, type UsePreferredMethodSettingArgs, type UsePreferredMethodSettingResult, type UseResetPasswordFormArgs, type UseResetPasswordFormResult, cachePreferredMethod, clearCachedPreferredMethod, collectUserRoles, createBffAuthClient, createPreferencesClient, defaultAuthTheme, isAllowedPinDigits, isPasswordValid, isValidForgotPasswordEmail, performBffLogout, readCachedPreferredMethod, readPasskeyError, readPasskeyRegistered, resolveAccessibleRoutes, resolveDemoCredentials, resolvePostLoginRoute, startPasskeyLogin, startPasskeyRegistration, syncPreferredMethodFromServer, useAuthTheme, useBffAuth, useBffForgotPassword, useBffLoginConfig, useBffResetPassword, useDemoCredentials, useDevicePinDisable, useDevicePinEnroll, useDevicePinUnlock, useForgotPasswordSubmit, useOtpLogin, usePasskeyCeremony, usePinLogin, usePreferredMethodSetting, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
package/dist/index.js CHANGED
@@ -114,7 +114,7 @@ function AuthScreen({
114
114
  ] });
115
115
  }
116
116
 
117
- // src/components/labels.ts
117
+ // src/components/labels/loginLabels.ts
118
118
  var DEFAULT_LOGIN_LABELS = {
119
119
  title: "Sign in",
120
120
  subtitle: "Welcome back",
@@ -129,6 +129,8 @@ var DEFAULT_LOGIN_LABELS = {
129
129
  invalidCredentials: "Incorrect username or password.",
130
130
  missingFields: "Enter both your username and password."
131
131
  };
132
+
133
+ // src/components/labels/forgotPasswordLabels.ts
132
134
  var DEFAULT_FORGOT_PASSWORD_LABELS = {
133
135
  title: "Forgot password",
134
136
  description: "Enter your email and we will send you a reset link.",
@@ -145,6 +147,8 @@ var DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS = {
145
147
  cancel: "Cancel",
146
148
  close: "Close"
147
149
  };
150
+
151
+ // src/components/labels/otpLabels.ts
148
152
  var DEFAULT_OTP_LABELS = {
149
153
  requestTitle: "Sign in with a code",
150
154
  requestDescription: "Enter your email and we will send you a one-time code.",
@@ -165,6 +169,8 @@ var DEFAULT_OTP_LABELS = {
165
169
  resending: "Resending...",
166
170
  changeEmail: "Use a different email"
167
171
  };
172
+
173
+ // src/components/labels/pinLabels.ts
168
174
  var DEFAULT_PIN_LABELS = {
169
175
  title: "Enter your event PIN",
170
176
  description: "Enter the PIN for this event to sign in.",
@@ -175,6 +181,8 @@ var DEFAULT_PIN_LABELS = {
175
181
  missingPin: "Enter your event PIN.",
176
182
  invalidPin: "That PIN is incorrect, has expired, or is locked out."
177
183
  };
184
+
185
+ // src/components/labels/devicePinLabels.ts
178
186
  var DEFAULT_DEVICE_PIN_UNLOCK_LABELS = {
179
187
  title: "Welcome back, {name}",
180
188
  titleNoName: "Welcome back",
@@ -232,6 +240,8 @@ var DEFAULT_DEVICE_PIN_SETTINGS_LABELS = {
232
240
  disabling: "Disabling...",
233
241
  disableFailed: "Could not disable PIN sign-in. Try again later."
234
242
  };
243
+
244
+ // src/components/labels/passkeyLabels.ts
235
245
  var DEFAULT_PASSKEY_LOGIN_LABELS = {
236
246
  signInButton: "Sign in with a passkey",
237
247
  signInHint: "Use your fingerprint, face, or security key to sign in",
@@ -247,6 +257,8 @@ var DEFAULT_PASSKEY_SETTINGS_LABELS = {
247
257
  addHint: "Start setting up a passkey for this account",
248
258
  registeredSuccess: "Your passkey was added. You can now use it to sign in."
249
259
  };
260
+
261
+ // src/components/labels/preferredMethodLabels.ts
250
262
  var DEFAULT_PREFERRED_METHOD_SETTINGS_LABELS = {
251
263
  title: "Preferred sign-in method",
252
264
  description: "Pick how you usually sign in. We will show this method first on your devices.",
@@ -262,6 +274,8 @@ var DEFAULT_PREFERRED_METHOD_HINT_LABELS = {
262
274
  pin: "You usually sign in with your quick PIN on this account.",
263
275
  passkey: "You usually sign in with a passkey."
264
276
  };
277
+
278
+ // src/components/labels/demoLabels.ts
265
279
  var DEFAULT_DEMO_CREDENTIALS_HINT_LABELS = {
266
280
  title: "Demo access",
267
281
  description: "Explore the full product with our shared demo account.",
@@ -270,6 +284,8 @@ var DEFAULT_DEMO_CREDENTIALS_HINT_LABELS = {
270
284
  useCredentials: "Use these credentials",
271
285
  useCredentialsHint: "Fill the sign-in form with the demo username and password"
272
286
  };
287
+
288
+ // src/components/labels/resetPasswordLabels.ts
273
289
  var DEFAULT_RESET_PASSWORD_LABELS = {
274
290
  title: "Reset password",
275
291
  description: "Choose a new password for your account.",
@@ -494,21 +510,64 @@ function useAuthStyles(theme) {
494
510
  });
495
511
  }, [theme]);
496
512
  }
513
+
514
+ // src/components/PrefillIntent.ts
515
+ var PrefillIntent = /* @__PURE__ */ ((PrefillIntent2) => {
516
+ PrefillIntent2["Explicit"] = "explicit";
517
+ PrefillIntent2["Opportunistic"] = "opportunistic";
518
+ return PrefillIntent2;
519
+ })(PrefillIntent || {});
520
+
521
+ // src/components/useLoginPrefill.ts
497
522
  function useLoginPrefill({
498
523
  initialUsername,
499
524
  initialPassword,
500
- prefillNonce
525
+ prefillNonce,
526
+ prefillIntent
501
527
  }) {
502
528
  const [username, setUsername] = react.useState(initialUsername ?? "");
503
529
  const [password, setPassword] = react.useState(initialPassword ?? "");
504
530
  const [appliedNonce, setAppliedNonce] = react.useState(prefillNonce);
505
531
  if (prefillNonce !== appliedNonce) {
506
532
  setAppliedNonce(prefillNonce);
507
- setUsername(initialUsername ?? "");
508
- setPassword(initialPassword ?? "");
533
+ const isPristine = username.length === 0 && password.length === 0;
534
+ const mayApply = prefillIntent !== "opportunistic" /* Opportunistic */ || isPristine;
535
+ if (mayApply) {
536
+ setUsername(initialUsername ?? "");
537
+ setPassword(initialPassword ?? "");
538
+ }
509
539
  }
510
540
  return { username, password, setUsername, setPassword };
511
541
  }
542
+ function useLoginSubmit({
543
+ username,
544
+ password,
545
+ login,
546
+ onSuccess,
547
+ missingFieldsLabel,
548
+ invalidCredentialsLabel
549
+ }) {
550
+ const [errorText, setErrorText] = react.useState(null);
551
+ const runLogin = react.useCallback(async () => {
552
+ setErrorText(null);
553
+ const trimmedUsername = username.trim();
554
+ const hasMissingField = trimmedUsername.length === 0 || password.length === 0;
555
+ if (hasMissingField) {
556
+ setErrorText(missingFieldsLabel);
557
+ return;
558
+ }
559
+ try {
560
+ const user = await login({ username: trimmedUsername, password });
561
+ onSuccess(user);
562
+ } catch {
563
+ setErrorText(invalidCredentialsLabel);
564
+ }
565
+ }, [username, password, login, onSuccess, missingFieldsLabel, invalidCredentialsLabel]);
566
+ const handleSubmit = react.useCallback(() => {
567
+ void runLogin();
568
+ }, [runLogin]);
569
+ return { errorText, handleSubmit };
570
+ }
512
571
  var BffAuthStatus = /* @__PURE__ */ ((BffAuthStatus2) => {
513
572
  BffAuthStatus2["Loading"] = "loading";
514
573
  BffAuthStatus2["Authenticated"] = "authenticated";
@@ -623,7 +682,8 @@ function LoginForm({
623
682
  chromeless = false,
624
683
  initialUsername,
625
684
  initialPassword,
626
- prefillNonce
685
+ prefillNonce,
686
+ prefillIntent
627
687
  }) {
628
688
  const theme = useAuthTheme(themeProp);
629
689
  const styles3 = useAuthStyles(theme);
@@ -635,27 +695,17 @@ function LoginForm({
635
695
  const { username, password, setUsername, setPassword } = useLoginPrefill({
636
696
  initialUsername,
637
697
  initialPassword,
638
- prefillNonce
698
+ prefillNonce,
699
+ prefillIntent
700
+ });
701
+ const { errorText, handleSubmit } = useLoginSubmit({
702
+ username,
703
+ password,
704
+ login,
705
+ onSuccess,
706
+ missingFieldsLabel: labels.missingFields,
707
+ invalidCredentialsLabel: labels.invalidCredentials
639
708
  });
640
- const [errorText, setErrorText] = react.useState(null);
641
- const runLogin = react.useCallback(async () => {
642
- setErrorText(null);
643
- const trimmedUsername = username.trim();
644
- const hasMissingField = trimmedUsername.length === 0 || password.length === 0;
645
- if (hasMissingField) {
646
- setErrorText(labels.missingFields);
647
- return;
648
- }
649
- try {
650
- const user = await login({ username: trimmedUsername, password });
651
- onSuccess(user);
652
- } catch {
653
- setErrorText(labels.invalidCredentials);
654
- }
655
- }, [username, password, login, onSuccess, labels.missingFields, labels.invalidCredentials]);
656
- const handleSubmit = react.useCallback(() => {
657
- void runLogin();
658
- }, [runLogin]);
659
709
  const submitButtonStyle = isSubmitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
660
710
  const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
661
711
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.title, children: labels.title }),
@@ -3324,7 +3374,8 @@ var EMPTY_CONFIG = {
3324
3374
  hasPin: false,
3325
3375
  pinDigits: null,
3326
3376
  preferredMethod: null
3327
- }
3377
+ },
3378
+ demo: null
3328
3379
  };
3329
3380
  function useBffLoginConfig(client) {
3330
3381
  const [config, setConfig] = react.useState(EMPTY_CONFIG);
@@ -3421,6 +3472,12 @@ async function syncPreferredMethodFromServer(client) {
3421
3472
  return null;
3422
3473
  }
3423
3474
  }
3475
+ function isSamePair(a, b) {
3476
+ if (a === null || b === null) {
3477
+ return a === b;
3478
+ }
3479
+ return a.username === b.username && a.password === b.password;
3480
+ }
3424
3481
  function useDemoCredentials({
3425
3482
  credentials,
3426
3483
  prefillOnMount = false
@@ -3430,23 +3487,38 @@ function useDemoCredentials({
3430
3487
  prefillOnMount ? resolved : null
3431
3488
  );
3432
3489
  const [prefillNonce, setPrefillNonce] = react.useState(0);
3490
+ const [prefillIntent, setPrefillIntent] = react.useState(
3491
+ "opportunistic" /* Opportunistic */
3492
+ );
3493
+ const [autoSeeded, setAutoSeeded] = react.useState(
3494
+ prefillOnMount ? resolved : null
3495
+ );
3496
+ const shouldAutoSeed = prefillOnMount && resolved !== null && !isSamePair(resolved, autoSeeded);
3497
+ if (shouldAutoSeed) {
3498
+ setAutoSeeded(resolved);
3499
+ setFilled(resolved);
3500
+ setPrefillNonce((current) => current + 1);
3501
+ setPrefillIntent("opportunistic" /* Opportunistic */);
3502
+ }
3433
3503
  const handleUse = react.useCallback((next) => {
3434
3504
  setFilled(next);
3435
3505
  setPrefillNonce((current) => current + 1);
3506
+ setPrefillIntent("explicit" /* Explicit */);
3436
3507
  }, []);
3437
3508
  return react.useMemo(
3438
3509
  () => ({
3439
3510
  loginFormProps: {
3440
3511
  initialUsername: filled?.username,
3441
3512
  initialPassword: filled?.password,
3442
- prefillNonce
3513
+ prefillNonce,
3514
+ prefillIntent
3443
3515
  },
3444
3516
  // `credentials` is passed through unresolved on purpose: the hint applies
3445
3517
  // `resolveDemoCredentials` itself, so it fails closed even when it is used
3446
3518
  // standalone, without this hook.
3447
3519
  hintProps: { credentials, onUse: handleUse }
3448
3520
  }),
3449
- [filled, prefillNonce, credentials, handleUse]
3521
+ [filled, prefillNonce, prefillIntent, credentials, handleUse]
3450
3522
  );
3451
3523
  }
3452
3524
  async function lazyFetchHttpClient(request) {
@@ -3588,6 +3660,7 @@ exports.PasswordPolicyError = PasswordPolicyError;
3588
3660
  exports.PinForm = PinForm;
3589
3661
  exports.PreferredMethodHint = PreferredMethodHint;
3590
3662
  exports.PreferredMethodSettingsCard = PreferredMethodSettingsCard;
3663
+ exports.PrefillIntent = PrefillIntent;
3591
3664
  exports.ResetPasswordError = ResetPasswordError;
3592
3665
  exports.ResetPasswordForm = ResetPasswordForm;
3593
3666
  exports.cachePreferredMethod = cachePreferredMethod;