Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,31 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
return this
}

/**
* Creates a new [MfaApiClient] to handle a multi-factor authentication transaction.
*
* Example usage:
* ```
* try {
* val credentials = authClient.login("user@example.com", "password").await()
* } catch (error: AuthenticationException) {
* if (error.isMultifactorRequired) {
* val mfaToken = error.mfaToken
* if (mfaToken != null) {
* val mfaClient = authClient.mfa(mfaToken)
* // Use mfaClient to handle MFA flow
* }
* }
* }
* ```
*
* @param mfaToken The token received in the 'mfa_required' error from a login attempt.
* @return A new [MfaApiClient] instance configured for the transaction.
*/
public fun mfa(mfaToken: String): MfaApiClient {
return MfaApiClient(this.auth0, mfaToken)
}

/**
* Log in a user with email/username and password for a connection/realm.
* It will use the password-realm grant type for the `/oauth/token` endpoint
Expand Down Expand Up @@ -1081,7 +1106,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
return factory.get(url.toString(), userProfileAdapter, dPoP)
}

private companion object {
internal companion object {
private const val SMS_CONNECTION = "sms"
private const val EMAIL_CONNECTION = "email"
private const val USERNAME_KEY = "username"
Expand Down Expand Up @@ -1122,7 +1147,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
private const val WELL_KNOWN_PATH = ".well-known"
private const val JWKS_FILE_PATH = "jwks.json"
private const val TAG = "AuthenticationAPIClient"
private fun createErrorAdapter(): ErrorAdapter<AuthenticationException> {
internal fun createErrorAdapter(): ErrorAdapter<AuthenticationException> {
val mapAdapter = forMap(GsonProvider.gson)
return object : ErrorAdapter<AuthenticationException> {
override fun fromRawResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import android.util.Log
import com.auth0.android.Auth0Exception
import com.auth0.android.NetworkErrorException
import com.auth0.android.provider.TokenValidationException
import com.auth0.android.request.internal.GsonProvider
import com.auth0.android.result.MfaRequirements

public class AuthenticationException : Auth0Exception {
private var code: String? = null
Expand Down Expand Up @@ -147,6 +149,26 @@ public class AuthenticationException : Auth0Exception {
public val isMultifactorEnrollRequired: Boolean
get() = "a0.mfa_registration_required" == code || "unsupported_challenge_type" == code

/**
* The MFA token returned when multi-factor authentication is required.
* This token should be used to create an [MfaApiClient] to continue the MFA flow.
*/
public val mfaToken: String?
get() = getValue("mfa_token") as? String

/**
* The MFA requirements returned when multi-factor authentication is required.
* Contains information about the required challenge types.
*/
public val mfaRequirements: MfaRequirements?
get() = (getValue("mfa_requirements") as? Map<*, *>)?.let {
@Suppress("UNCHECKED_CAST")
GsonProvider.gson.fromJson(
GsonProvider.gson.toJson(it),
MfaRequirements::class.java
)
}

/// When Bot Protection flags the request as suspicious
public val isVerificationRequired: Boolean
get() = "requires_verification" == code
Expand Down
Loading