|
| 1 | +/* |
| 2 | + * Copyright 2025 Google Inc. All Rights Reserved. |
| 3 | + * |
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
| 5 | + * in compliance with the License. You may obtain a copy of the License at |
| 6 | + * |
| 7 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | + * |
| 9 | + * Unless required by applicable law or agreed to in writing, software distributed under the |
| 10 | + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either |
| 11 | + * express or implied. See the License for the specific language governing permissions and |
| 12 | + * limitations under the License. |
| 13 | + */ |
| 14 | + |
| 15 | +package com.firebase.ui.auth.compose.mfa |
| 16 | + |
| 17 | +import com.firebase.ui.auth.compose.configuration.MfaFactor |
| 18 | +import com.firebase.ui.auth.compose.data.CountryData |
| 19 | + |
| 20 | +/** |
| 21 | + * State class containing all the necessary information to render a custom UI for the |
| 22 | + * Multi-Factor Authentication (MFA) enrollment flow. |
| 23 | + * |
| 24 | + * This class is passed to the content slot of the MfaEnrollmentScreen composable, providing |
| 25 | + * access to the current step, user input values, callbacks for actions, and loading/error states. |
| 26 | + * |
| 27 | + * Use a `when` expression on [step] to determine which UI to render: |
| 28 | + * |
| 29 | + * ```kotlin |
| 30 | + * MfaEnrollmentScreen(user, config, onComplete, onSkip) { state -> |
| 31 | + * when (state.step) { |
| 32 | + * MfaEnrollmentStep.SelectFactor -> { |
| 33 | + * // Render factor selection UI using state.availableFactors |
| 34 | + * } |
| 35 | + * MfaEnrollmentStep.ConfigureTotp -> { |
| 36 | + * // Render TOTP setup UI using state.totpSecret and state.totpQrCodeUrl |
| 37 | + * } |
| 38 | + * MfaEnrollmentStep.VerifyFactor -> { |
| 39 | + * // Render verification UI using state.verificationCode |
| 40 | + * } |
| 41 | + * // ... other steps |
| 42 | + * } |
| 43 | + * } |
| 44 | + * ``` |
| 45 | + * |
| 46 | + * @property step The current step in the enrollment flow. Use this to determine which UI to display. |
| 47 | + * @property isLoading `true` when an asynchronous operation (like generating a secret or verifying a code) is in progress. Use this to show loading indicators. |
| 48 | + * @property error An optional error message to display to the user. Will be `null` if there's no error. |
| 49 | + * @property onBackClick Callback to navigate to the previous step in the flow. Invoked when the user clicks a back button. |
| 50 | + * |
| 51 | + * @property availableFactors (Step: [MfaEnrollmentStep.SelectFactor]) A list of MFA factors the user can choose from (e.g., SMS, TOTP). Determined by [com.firebase.ui.auth.compose.configuration.MfaConfiguration.allowedFactors]. |
| 52 | + * @property onFactorSelected (Step: [MfaEnrollmentStep.SelectFactor]) Callback invoked when the user selects an MFA factor. Receives the selected [MfaFactor]. |
| 53 | + * @property onSkipClick (Step: [MfaEnrollmentStep.SelectFactor]) Callback for the "Skip" action. Will be `null` if MFA enrollment is required via [com.firebase.ui.auth.compose.configuration.MfaConfiguration.requireEnrollment]. |
| 54 | + * |
| 55 | + * @property phoneNumber (Step: [MfaEnrollmentStep.ConfigureSms]) The current value of the phone number input field. Does not include country code prefix. |
| 56 | + * @property onPhoneNumberChange (Step: [MfaEnrollmentStep.ConfigureSms]) Callback invoked when the phone number input changes. Receives the new phone number string. |
| 57 | + * @property selectedCountry (Step: [MfaEnrollmentStep.ConfigureSms]) The currently selected country for phone number formatting. Contains dial code, country code, and flag. |
| 58 | + * @property onCountrySelected (Step: [MfaEnrollmentStep.ConfigureSms]) Callback invoked when the user selects a different country. Receives the new [CountryData]. |
| 59 | + * @property onSendSmsCodeClick (Step: [MfaEnrollmentStep.ConfigureSms]) Callback to send the SMS verification code to the entered phone number. |
| 60 | + * |
| 61 | + * @property totpSecret (Step: [MfaEnrollmentStep.ConfigureTotp]) The TOTP secret containing the shared key and configuration. Use this to display the secret key or access the underlying Firebase TOTP secret. |
| 62 | + * @property totpQrCodeUrl (Step: [MfaEnrollmentStep.ConfigureTotp]) A URI that can be rendered as a QR code or used as a deep link to open authenticator apps. Generated via [TotpSecret.generateQrCodeUrl]. |
| 63 | + * @property onContinueToVerifyClick (Step: [MfaEnrollmentStep.ConfigureTotp]) Callback to proceed to the verification step after the user has scanned the QR code or entered the secret. |
| 64 | + * |
| 65 | + * @property verificationCode (Step: [MfaEnrollmentStep.VerifyFactor]) The current value of the verification code input field. Should be a 6-digit string. |
| 66 | + * @property onVerificationCodeChange (Step: [MfaEnrollmentStep.VerifyFactor]) Callback invoked when the verification code input changes. Receives the new code string. |
| 67 | + * @property onVerifyClick (Step: [MfaEnrollmentStep.VerifyFactor]) Callback to verify the entered code and finalize MFA enrollment. |
| 68 | + * @property selectedFactor (Step: [MfaEnrollmentStep.VerifyFactor]) The MFA factor being verified (SMS or TOTP). Use this to customize UI messages. |
| 69 | + * @property onResendCodeClick (Step: [MfaEnrollmentStep.VerifyFactor], SMS only) Callback to resend the SMS verification code. Will be `null` for TOTP verification. |
| 70 | + * |
| 71 | + * @property recoveryCodes (Step: [MfaEnrollmentStep.ShowRecoveryCodes]) A list of one-time backup codes the user should save. Only present if [com.firebase.ui.auth.compose.configuration.MfaConfiguration.enableRecoveryCodes] is `true`. |
| 72 | + * @property onCodesSavedClick (Step: [MfaEnrollmentStep.ShowRecoveryCodes]) Callback invoked when the user confirms they have saved their recovery codes. Completes the enrollment flow. |
| 73 | + * |
| 74 | + * @since 10.0.0 |
| 75 | + */ |
| 76 | +data class MfaEnrollmentContentState( |
| 77 | + /** The current step in the enrollment flow. Use this to determine which UI to display. */ |
| 78 | + val step: MfaEnrollmentStep, |
| 79 | + |
| 80 | + /** `true` when an async operation is in progress. Use to show loading indicators. */ |
| 81 | + val isLoading: Boolean = false, |
| 82 | + |
| 83 | + /** Optional error message to display. `null` if no error. */ |
| 84 | + val error: String? = null, |
| 85 | + |
| 86 | + /** Callback to navigate to the previous step. */ |
| 87 | + val onBackClick: () -> Unit = {}, |
| 88 | + |
| 89 | + // SelectFactor step |
| 90 | + val availableFactors: List<MfaFactor> = emptyList(), |
| 91 | + |
| 92 | + val onFactorSelected: (MfaFactor) -> Unit = {}, |
| 93 | + |
| 94 | + val onSkipClick: (() -> Unit)? = null, |
| 95 | + |
| 96 | + // ConfigureSms step |
| 97 | + val phoneNumber: String = "", |
| 98 | + |
| 99 | + val onPhoneNumberChange: (String) -> Unit = {}, |
| 100 | + |
| 101 | + val selectedCountry: CountryData? = null, |
| 102 | + |
| 103 | + val onCountrySelected: (CountryData) -> Unit = {}, |
| 104 | + |
| 105 | + val onSendSmsCodeClick: () -> Unit = {}, |
| 106 | + |
| 107 | + // ConfigureTotp step |
| 108 | + val totpSecret: TotpSecret? = null, |
| 109 | + |
| 110 | + val totpQrCodeUrl: String? = null, |
| 111 | + |
| 112 | + val onContinueToVerifyClick: () -> Unit = {}, |
| 113 | + |
| 114 | + // VerifyFactor step |
| 115 | + val verificationCode: String = "", |
| 116 | + |
| 117 | + val onVerificationCodeChange: (String) -> Unit = {}, |
| 118 | + |
| 119 | + val onVerifyClick: () -> Unit = {}, |
| 120 | + |
| 121 | + val selectedFactor: MfaFactor? = null, |
| 122 | + |
| 123 | + val onResendCodeClick: (() -> Unit)? = null, |
| 124 | + |
| 125 | + // ShowRecoveryCodes step |
| 126 | + val recoveryCodes: List<String>? = null, |
| 127 | + |
| 128 | + val onCodesSavedClick: () -> Unit = {} |
| 129 | +) { |
| 130 | + /** |
| 131 | + * Returns true if the current state is valid for the current step. |
| 132 | + * |
| 133 | + * This can be used to enable/disable action buttons in the UI. |
| 134 | + */ |
| 135 | + val isValid: Boolean |
| 136 | + get() = when (step) { |
| 137 | + MfaEnrollmentStep.SelectFactor -> availableFactors.isNotEmpty() |
| 138 | + MfaEnrollmentStep.ConfigureSms -> phoneNumber.isNotBlank() |
| 139 | + MfaEnrollmentStep.ConfigureTotp -> totpSecret != null && totpQrCodeUrl != null |
| 140 | + MfaEnrollmentStep.VerifyFactor -> verificationCode.length == 6 |
| 141 | + MfaEnrollmentStep.ShowRecoveryCodes -> !recoveryCodes.isNullOrEmpty() |
| 142 | + } |
| 143 | + |
| 144 | + /** |
| 145 | + * Returns true if there is an error in the current state. |
| 146 | + */ |
| 147 | + val hasError: Boolean |
| 148 | + get() = !error.isNullOrBlank() |
| 149 | + |
| 150 | + /** |
| 151 | + * Returns true if the skip action is available (only for SelectFactor step when not required). |
| 152 | + */ |
| 153 | + val canSkip: Boolean |
| 154 | + get() = step == MfaEnrollmentStep.SelectFactor && onSkipClick != null |
| 155 | + |
| 156 | + /** |
| 157 | + * Returns true if the back action is available (for all steps except SelectFactor). |
| 158 | + */ |
| 159 | + val canGoBack: Boolean |
| 160 | + get() = step != MfaEnrollmentStep.SelectFactor |
| 161 | +} |
0 commit comments