Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The KDoc example references error.mfaToken, but AuthenticationException doesn’t expose an mfaToken property (only mfaRequiredErrorPayload). Update the example to use error.mfaRequiredErrorPayload?.mfaToken (or add a real mfaToken convenience property if desired).

Suggested change
* val mfaToken = error.mfaToken
* val mfaToken = error.mfaRequiredErrorPayload?.mfaToken

Copilot uses AI. Check for mistakes.
* 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 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename it to mfaClient

return MfaApiClient(this.auth0, mfaToken)
}
Comment on lines +109 to +111
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description lists the new API as AuthenticationAPIClient.mfa(mfaToken), but the implementation adds mfaClient(mfaToken). Please align the public API naming with the PR description (either rename the method or update the PR description/docs to match).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation uses mfaClient(mfaToken) consistently throughout the SDK, docs (EXAMPLES.md), and sample app. This naming is more explicit and follows the pattern of returning a client object.


/**
* 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> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why make them internal ?

Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The companion object visibility is changed from private to internal, which expands the API surface. Unless there's a specific requirement for internal access to createErrorAdapter, this change increases coupling between internal components and may make future refactoring more difficult.

Suggested change
internal fun createErrorAdapter(): ErrorAdapter<AuthenticationException> {
private fun createErrorAdapter(): ErrorAdapter<AuthenticationException> {

Copilot uses AI. Check for mistakes.
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
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GsonProvider is imported but not used in this file. Please remove the unused import to keep the file clean.

Suggested change
import com.auth0.android.request.internal.GsonProvider

Copilot uses AI. Check for mistakes.
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?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep this as part of the MfaRequirements error body and not separate.

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?
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hope the name is consistent with Auth0.Swift implementation

get() = (getValue("mfa_requirements") as? Map<*, *>)?.let {
@Suppress("UNCHECKED_CAST")
GsonProvider.gson.fromJson(
GsonProvider.gson.toJson(it),
MfaRequirements::class.java
)
}
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AuthenticationException.mfaRequirements property performs JSON serialization/deserialization on every access, which is inefficient if the property is accessed multiple times. Consider caching the deserialized MfaRequirements object in a lazy-initialized backing field to avoid repeated conversions.

Copilot uses AI. Check for mistakes.

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