@dloizides/auth-web 1.17.0 → 1.18.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/dist/index.mjs CHANGED
@@ -330,6 +330,27 @@ var DEFAULT_ACCOUNT_SETTINGS_LABELS = {
330
330
  errorFailed: "Could not change your password. Try again later."
331
331
  };
332
332
 
333
+ // src/components/labels/registerLabels.ts
334
+ function field(label, placeholder) {
335
+ return { label, placeholder, hint: placeholder, inputLabel: label };
336
+ }
337
+ var DEFAULT_REGISTER_LABELS = {
338
+ title: "Create account",
339
+ subtitle: "Sign up to get started",
340
+ submit: "Create account",
341
+ submitHint: "Creates your account",
342
+ submitting: "Creating account...",
343
+ fields: {
344
+ firstName: field("First name", "Enter your first name"),
345
+ lastName: field("Last name", "Enter your last name"),
346
+ username: field("Username", "Choose a username"),
347
+ email: field("Email", "Enter your email"),
348
+ password: field("Password", "Choose a password"),
349
+ confirmPassword: field("Confirm password", "Repeat your password"),
350
+ tenantName: field("Organisation name", "Enter your organisation name")
351
+ }
352
+ };
353
+
333
354
  // src/components/testIds.ts
334
355
  var AuthTestIds = {
335
356
  loginForm: "auth-login-form",
@@ -4030,6 +4051,306 @@ function useDemoCredentials({
4030
4051
  [filled, prefillNonce, prefillIntent, credentials, accounts, handleUse]
4031
4052
  );
4032
4053
  }
4054
+
4055
+ // src/components/registerTestIds.ts
4056
+ var RegisterTestIds = {
4057
+ form: "register-form",
4058
+ firstNameInput: "register-first-name-input",
4059
+ lastNameInput: "register-last-name-input",
4060
+ usernameInput: "register-username-input",
4061
+ emailInput: "register-email-input",
4062
+ passwordInput: "register-password-input",
4063
+ confirmPasswordInput: "register-confirm-password-input",
4064
+ tenantNameInput: "register-tenant-name-input",
4065
+ submitButton: "register-submit-button",
4066
+ websiteInput: "register-website"
4067
+ };
4068
+ var FIELDS = [
4069
+ { key: "firstName", testId: RegisterTestIds.firstNameInput, autoComplete: "name" },
4070
+ { key: "lastName", testId: RegisterTestIds.lastNameInput, autoComplete: "name" },
4071
+ { key: "username", testId: RegisterTestIds.usernameInput, autoComplete: "username" },
4072
+ { key: "email", testId: RegisterTestIds.emailInput, autoComplete: "email", keyboardType: "email-address" },
4073
+ { key: "password", testId: RegisterTestIds.passwordInput, secure: true },
4074
+ { key: "confirmPassword", testId: RegisterTestIds.confirmPasswordInput, secure: true },
4075
+ { key: "tenantName", testId: RegisterTestIds.tenantNameInput }
4076
+ ];
4077
+ var OFF_SCREEN = -9999;
4078
+ var HONEYPOT_SIZE = 1;
4079
+ var HONEYPOT_PLACEHOLDER = "Website";
4080
+ var honeypotStyles = StyleSheet.create({
4081
+ honeypot: {
4082
+ position: "absolute",
4083
+ left: OFF_SCREEN,
4084
+ top: OFF_SCREEN,
4085
+ width: HONEYPOT_SIZE,
4086
+ height: HONEYPOT_SIZE,
4087
+ opacity: 0
4088
+ }
4089
+ });
4090
+ function RegisterFormFields({
4091
+ values,
4092
+ labels,
4093
+ disabled,
4094
+ theme,
4095
+ styles: styles3,
4096
+ onChange,
4097
+ testIdPrefix
4098
+ }) {
4099
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
4100
+ FIELDS.map((config) => {
4101
+ const copy = labels.fields[config.key];
4102
+ return /* @__PURE__ */ jsxs(View, { style: styles3.fieldGroup, children: [
4103
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: copy.label }),
4104
+ /* @__PURE__ */ jsx(
4105
+ TextInput,
4106
+ {
4107
+ accessibilityHint: copy.hint,
4108
+ accessibilityLabel: copy.inputLabel,
4109
+ autoCapitalize: "none",
4110
+ autoComplete: config.autoComplete,
4111
+ autoCorrect: false,
4112
+ editable: !disabled,
4113
+ keyboardType: config.keyboardType ?? "default",
4114
+ placeholder: copy.placeholder,
4115
+ placeholderTextColor: theme.colors.textSecondary,
4116
+ secureTextEntry: config.secure === true,
4117
+ style: styles3.input,
4118
+ testID: withTestIdPrefix(config.testId, testIdPrefix),
4119
+ value: values[config.key],
4120
+ onChangeText: (text) => onChange(config.key, text)
4121
+ }
4122
+ )
4123
+ ] }, config.key);
4124
+ }),
4125
+ /* @__PURE__ */ jsx(
4126
+ TextInput,
4127
+ {
4128
+ accessibilityElementsHidden: true,
4129
+ autoComplete: "off",
4130
+ focusable: false,
4131
+ importantForAccessibility: "no-hide-descendants",
4132
+ placeholder: HONEYPOT_PLACEHOLDER,
4133
+ style: honeypotStyles.honeypot,
4134
+ testID: withTestIdPrefix(RegisterTestIds.websiteInput, testIdPrefix),
4135
+ value: values.website,
4136
+ onChangeText: (text) => onChange("website", text)
4137
+ }
4138
+ )
4139
+ ] });
4140
+ }
4141
+
4142
+ // src/hooks/RegisterErrorCode.ts
4143
+ var RegisterErrorCode = /* @__PURE__ */ ((RegisterErrorCode2) => {
4144
+ RegisterErrorCode2["MissingFields"] = "missingFields";
4145
+ RegisterErrorCode2["InvalidEmail"] = "invalidEmail";
4146
+ RegisterErrorCode2["WeakPassword"] = "weakPassword";
4147
+ RegisterErrorCode2["PasswordMismatch"] = "passwordMismatch";
4148
+ RegisterErrorCode2["UsernameTaken"] = "usernameTaken";
4149
+ RegisterErrorCode2["EmailTaken"] = "emailTaken";
4150
+ RegisterErrorCode2["RealmInvalid"] = "realmInvalid";
4151
+ RegisterErrorCode2["ValidationFailed"] = "validationFailed";
4152
+ RegisterErrorCode2["Failed"] = "failed";
4153
+ RegisterErrorCode2["InFlight"] = "inFlight";
4154
+ return RegisterErrorCode2;
4155
+ })(RegisterErrorCode || {});
4156
+
4157
+ // src/hooks/registerErrors.ts
4158
+ var MIN_PASSWORD_LENGTH = 8;
4159
+ var EMAIL_REGEX4 = /^[^@\s]+@[^.\s]+\.\S+$/;
4160
+ var PASSWORD_LETTER_REGEX = /[a-zA-Z]/;
4161
+ var PASSWORD_DIGIT_REGEX = /\d/;
4162
+ var PASSWORD_SPECIAL_REGEX = /[^a-zA-Z0-9]/;
4163
+ var SERVER_ERROR_CODES = [
4164
+ ["USERNAME_TAKEN", "usernameTaken" /* UsernameTaken */],
4165
+ ["EMAIL_TAKEN", "emailTaken" /* EmailTaken */],
4166
+ ["REALM_REQUIRED", "realmInvalid" /* RealmInvalid */],
4167
+ ["REALM_INVALID", "realmInvalid" /* RealmInvalid */]
4168
+ ];
4169
+ function isRegisterPasswordStrong(password) {
4170
+ if (password.length < MIN_PASSWORD_LENGTH) {
4171
+ return false;
4172
+ }
4173
+ if (!PASSWORD_LETTER_REGEX.test(password)) {
4174
+ return false;
4175
+ }
4176
+ if (!PASSWORD_DIGIT_REGEX.test(password)) {
4177
+ return false;
4178
+ }
4179
+ return PASSWORD_SPECIAL_REGEX.test(password);
4180
+ }
4181
+ function trimRegisterValues(values) {
4182
+ return {
4183
+ firstName: values.firstName.trim(),
4184
+ lastName: values.lastName.trim(),
4185
+ username: values.username.trim(),
4186
+ email: values.email.trim(),
4187
+ password: values.password,
4188
+ confirmPassword: values.confirmPassword,
4189
+ tenantName: values.tenantName.trim(),
4190
+ website: values.website.trim()
4191
+ };
4192
+ }
4193
+ function hasMissingRequiredField(values) {
4194
+ const required = [
4195
+ values.firstName,
4196
+ values.lastName,
4197
+ values.username,
4198
+ values.email,
4199
+ values.password,
4200
+ values.tenantName
4201
+ ];
4202
+ return required.some((value) => value === "");
4203
+ }
4204
+ function validateRegisterValues(values, passwordPolicy) {
4205
+ if (hasMissingRequiredField(values)) {
4206
+ return "missingFields" /* MissingFields */;
4207
+ }
4208
+ if (!EMAIL_REGEX4.test(values.email)) {
4209
+ return "invalidEmail" /* InvalidEmail */;
4210
+ }
4211
+ if (!passwordPolicy(values.password)) {
4212
+ return "weakPassword" /* WeakPassword */;
4213
+ }
4214
+ if (values.password !== values.confirmPassword) {
4215
+ return "passwordMismatch" /* PasswordMismatch */;
4216
+ }
4217
+ return null;
4218
+ }
4219
+ function messageOf(error) {
4220
+ if (error instanceof Error) {
4221
+ return error.message;
4222
+ }
4223
+ return typeof error === "string" ? error : "";
4224
+ }
4225
+ function mapRegisterErrorCode(error) {
4226
+ const message = messageOf(error);
4227
+ if (message === "") {
4228
+ return "failed" /* Failed */;
4229
+ }
4230
+ const match = SERVER_ERROR_CODES.find(([code]) => message.includes(code));
4231
+ return match === void 0 ? "validationFailed" /* ValidationFailed */ : match[1];
4232
+ }
4233
+
4234
+ // src/hooks/useRegisterForm.ts
4235
+ var EMPTY_VALUES = {
4236
+ firstName: "",
4237
+ lastName: "",
4238
+ username: "",
4239
+ email: "",
4240
+ password: "",
4241
+ confirmPassword: "",
4242
+ tenantName: "",
4243
+ website: ""
4244
+ };
4245
+ function toSubmitValues(values) {
4246
+ return {
4247
+ firstName: values.firstName,
4248
+ lastName: values.lastName,
4249
+ username: values.username,
4250
+ email: values.email,
4251
+ password: values.password,
4252
+ tenantName: values.tenantName,
4253
+ website: values.website
4254
+ };
4255
+ }
4256
+ function useRegisterForm({
4257
+ onSubmit,
4258
+ passwordPolicy = isRegisterPasswordStrong
4259
+ }) {
4260
+ const [state, setState] = useState(EMPTY_VALUES);
4261
+ const [isSubmitting, setIsSubmitting] = useState(false);
4262
+ const [error, setError] = useState(null);
4263
+ const inFlight = useRef(false);
4264
+ const setField = useCallback((field2, value) => {
4265
+ setState((prev) => ({ ...prev, [field2]: value }));
4266
+ }, []);
4267
+ const submit = useCallback(async () => {
4268
+ if (inFlight.current) {
4269
+ return { ok: false, error: "inFlight" /* InFlight */ };
4270
+ }
4271
+ const trimmed = trimRegisterValues(state);
4272
+ const refusal = trimmed.website === "" ? validateRegisterValues(trimmed, passwordPolicy) : "failed" /* Failed */;
4273
+ if (refusal !== null) {
4274
+ setError(refusal);
4275
+ return { ok: false, error: refusal };
4276
+ }
4277
+ inFlight.current = true;
4278
+ setError(null);
4279
+ setIsSubmitting(true);
4280
+ try {
4281
+ const result = await onSubmit(toSubmitValues(trimmed));
4282
+ return { ok: true, result };
4283
+ } catch (caught) {
4284
+ const code = mapRegisterErrorCode(caught);
4285
+ setError(code);
4286
+ return { ok: false, error: code };
4287
+ } finally {
4288
+ inFlight.current = false;
4289
+ setIsSubmitting(false);
4290
+ }
4291
+ }, [onSubmit, passwordPolicy, state]);
4292
+ return { state, setField, submit, isSubmitting, error };
4293
+ }
4294
+ function RegisterForm({
4295
+ onSubmit,
4296
+ onSuccess,
4297
+ onError,
4298
+ onSubmitAttempt,
4299
+ passwordPolicy,
4300
+ labels: labelsProp,
4301
+ theme: themeProp,
4302
+ footer,
4303
+ testIdPrefix
4304
+ }) {
4305
+ const theme = useAuthTheme(themeProp);
4306
+ const styles3 = useAuthStyles(theme);
4307
+ const labels = useMemo(
4308
+ () => ({ ...DEFAULT_REGISTER_LABELS, ...labelsProp }),
4309
+ [labelsProp]
4310
+ );
4311
+ const { state, setField, submit, isSubmitting } = useRegisterForm({ onSubmit, passwordPolicy });
4312
+ const handlePress = useCallback(() => {
4313
+ onSubmitAttempt?.();
4314
+ void submit().then((outcome) => {
4315
+ if (outcome.ok) {
4316
+ onSuccess(outcome.result);
4317
+ } else if (outcome.error !== "inFlight" /* InFlight */) {
4318
+ onError?.(outcome.error);
4319
+ }
4320
+ });
4321
+ }, [onError, onSubmitAttempt, onSuccess, submit]);
4322
+ const buttonStyle = isSubmitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
4323
+ return /* @__PURE__ */ jsx(View, { style: styles3.screen, children: /* @__PURE__ */ jsxs(View, { style: styles3.card, testID: withTestIdPrefix(RegisterTestIds.form, testIdPrefix), children: [
4324
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: labels.title }),
4325
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: labels.subtitle }),
4326
+ /* @__PURE__ */ jsx(
4327
+ RegisterFormFields,
4328
+ {
4329
+ disabled: isSubmitting,
4330
+ labels,
4331
+ styles: styles3,
4332
+ testIdPrefix,
4333
+ theme,
4334
+ values: state,
4335
+ onChange: setField
4336
+ }
4337
+ ),
4338
+ /* @__PURE__ */ jsx(
4339
+ TouchableOpacity,
4340
+ {
4341
+ accessibilityHint: labels.submitHint,
4342
+ accessibilityLabel: isSubmitting ? labels.submitting : labels.submit,
4343
+ accessibilityRole: "button",
4344
+ disabled: isSubmitting,
4345
+ style: buttonStyle,
4346
+ testID: withTestIdPrefix(RegisterTestIds.submitButton, testIdPrefix),
4347
+ onPress: handlePress,
4348
+ children: isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.submit })
4349
+ }
4350
+ ),
4351
+ footer
4352
+ ] }) });
4353
+ }
4033
4354
  async function lazyFetchHttpClient(request) {
4034
4355
  const fetchImpl = typeof fetch === "function" ? fetch.bind(globalThis) : void 0;
4035
4356
  if (fetchImpl === void 0) {
@@ -4166,6 +4487,6 @@ function createBffChangePasswordClient(options = {}) {
4166
4487
  };
4167
4488
  }
4168
4489
 
4169
- export { AccountSettingsScreen, AuthScreen, AuthTestIds, AuthThemeProvider, BffAuthStatus, CHANGE_PASSWORD_ENDPOINT, ChangePasswordCard, ChangePasswordErrorKey, DEFAULT_ACCOUNT_SETTINGS_LABELS, 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, DemoCredentialsHintVariant, DevicePinEnrollErrorKey, DevicePinEnrollForm, DevicePinErrorKey, DevicePinInput, DevicePinLengthPicker, DevicePinOffer, DevicePinSettingsCard, DevicePinUnlockScreen, ForgotPasswordFields, ForgotPasswordForm, LoginForm, OtpForm, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasskeyCeremonyMode, PasskeyCeremonyStatus, PasskeyLoginButton, PasskeySettingsCard, PasswordPolicyError, PinForm, PreferredMethodHint, PreferredMethodSettingsCard, PrefillIntent, ResetPasswordError, ResetPasswordForm, cachePreferredMethod, clearCachedPreferredMethod, collectUserRoles, createBffAuthClient, createBffChangePasswordClient, createPreferencesClient, defaultAuthTheme, isAllowedPinDigits, isPasswordValid, isValidForgotPasswordEmail, performBffLogout, readCachedPreferredMethod, readPasskeyError, readPasskeyRegistered, resolveAccessibleRoutes, resolveAccountList, resolveDemoAccounts, resolveDemoCredentials, resolvePostLoginRoute, startPasskeyLogin, startPasskeyRegistration, syncPreferredMethodFromServer, useAuthTheme, useBffAuth, useBffForgotPassword, useBffLoginConfig, useBffResetPassword, useChangePassword, useDemoCredentials, useDevicePinDisable, useDevicePinEnroll, useDevicePinUnlock, useForgotPasswordSubmit, useOtpLogin, usePasskeyCeremony, usePinLogin, usePreferredMethodSetting, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
4490
+ export { AccountSettingsScreen, AuthScreen, AuthTestIds, AuthThemeProvider, BffAuthStatus, CHANGE_PASSWORD_ENDPOINT, ChangePasswordCard, ChangePasswordErrorKey, DEFAULT_ACCOUNT_SETTINGS_LABELS, 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_REGISTER_LABELS, DEFAULT_RESET_PASSWORD_LABELS, DEVICE_PIN_ALLOWED_DIGITS, DEVICE_PIN_DEFAULT_DIGITS, DemoCredentialsHint, DemoCredentialsHintVariant, DevicePinEnrollErrorKey, DevicePinEnrollForm, DevicePinErrorKey, DevicePinInput, DevicePinLengthPicker, DevicePinOffer, DevicePinSettingsCard, DevicePinUnlockScreen, ForgotPasswordFields, ForgotPasswordForm, LoginForm, OtpForm, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasskeyCeremonyMode, PasskeyCeremonyStatus, PasskeyLoginButton, PasskeySettingsCard, PasswordPolicyError, PinForm, PreferredMethodHint, PreferredMethodSettingsCard, PrefillIntent, RegisterErrorCode, RegisterForm, RegisterTestIds, ResetPasswordError, ResetPasswordForm, cachePreferredMethod, clearCachedPreferredMethod, collectUserRoles, createBffAuthClient, createBffChangePasswordClient, createPreferencesClient, defaultAuthTheme, isAllowedPinDigits, isPasswordValid, isRegisterPasswordStrong, isValidForgotPasswordEmail, mapRegisterErrorCode, performBffLogout, readCachedPreferredMethod, readPasskeyError, readPasskeyRegistered, resolveAccessibleRoutes, resolveAccountList, resolveDemoAccounts, resolveDemoCredentials, resolvePostLoginRoute, startPasskeyLogin, startPasskeyRegistration, syncPreferredMethodFromServer, useAuthTheme, useBffAuth, useBffForgotPassword, useBffLoginConfig, useBffResetPassword, useChangePassword, useDemoCredentials, useDevicePinDisable, useDevicePinEnroll, useDevicePinUnlock, useForgotPasswordSubmit, useOtpLogin, usePasskeyCeremony, usePinLogin, usePreferredMethodSetting, useRegisterForm, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
4170
4491
  //# sourceMappingURL=index.mjs.map
4171
4492
  //# sourceMappingURL=index.mjs.map