Skip to content

Commit 2b1b511

Browse files
committed
Using Sealed class for enroll and verify for cross platform sdk consistency and handled review comment
1 parent 52bc78d commit 2b1b511

7 files changed

Lines changed: 520 additions & 291 deletions

File tree

EXAMPLES.md

Lines changed: 233 additions & 45 deletions
Large diffs are not rendered by default.

auth0/src/main/java/com/auth0/android/authentication/AuthenticationAPIClient.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public class AuthenticationAPIClient @VisibleForTesting(otherwise = VisibleForTe
9393
* val credentials = authClient.login("user@example.com", "password").await()
9494
* } catch (error: AuthenticationException) {
9595
* if (error.isMultifactorRequired) {
96-
* val mfaToken = error.mfaToken
96+
* val mfaToken = error.mfaRequiredErrorPayload?.mfaToken
9797
* if (mfaToken != null) {
9898
* val mfaClient = authClient.mfaClient(mfaToken)
9999
* // Use mfaClient to handle MFA flow

auth0/src/main/java/com/auth0/android/authentication/AuthenticationException.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import android.util.Log
55
import com.auth0.android.Auth0Exception
66
import com.auth0.android.NetworkErrorException
77
import com.auth0.android.provider.TokenValidationException
8-
import com.auth0.android.request.internal.GsonProvider
98
import com.auth0.android.result.MfaFactor
109
import com.auth0.android.result.MfaRequiredErrorPayload
1110
import com.auth0.android.result.MfaRequirements

auth0/src/main/java/com/auth0/android/authentication/MfaApiClient.kt

Lines changed: 126 additions & 205 deletions
Large diffs are not rendered by default.

auth0/src/main/java/com/auth0/android/authentication/MfaException.kt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public sealed class MfaException(
3939
* Exception thrown when listing authenticators fails.
4040
*
4141
* SDK-thrown errors:
42-
* - `invalid_request`: challengeType is required and must contain at least one challenge type
42+
* - `invalid_request`: factorsAllowed is required and must contain at least one factor type
4343
*
4444
* Additional errors may be returned by the Auth0 API and forwarded by the SDK.
4545
*
@@ -59,8 +59,9 @@ public sealed class MfaException(
5959
private val code: String,
6060
private val description: String,
6161
private val values: Map<String, Any> = emptyMap(),
62-
override val statusCode: Int = 0
63-
) : MfaException("MFA authenticator listing failed: $code") {
62+
override val statusCode: Int = 0,
63+
cause: Throwable? = null
64+
) : MfaException("MFA authenticator listing failed: $code", cause) {
6465

6566
internal constructor(values: Map<String, Any>, statusCode: Int) : this(
6667
code = (values["error"] as? String) ?: UNKNOWN_ERROR,
@@ -76,7 +77,7 @@ public sealed class MfaException(
7677
public companion object {
7778
internal const val INVALID_REQUEST = "invalid_request"
7879

79-
/**feature discovery on the SDKevaluating/learning the usage patternsimplementationdeployment to production
80+
/**
8081
* Creates an exception for SDK validation errors.
8182
*/
8283
internal fun invalidRequest(description: String): MfaListAuthenticatorsException {
@@ -107,8 +108,9 @@ public sealed class MfaException(
107108
private val code: String,
108109
private val description: String,
109110
private val values: Map<String, Any> = emptyMap(),
110-
override val statusCode: Int = 0
111-
) : MfaException("MFA enrollment failed: $code") {
111+
override val statusCode: Int = 0,
112+
cause: Throwable? = null
113+
) : MfaException("MFA enrollment failed: $code", cause) {
112114

113115
internal constructor(values: Map<String, Any>, statusCode: Int) : this(
114116
code = (values["error"] as? String) ?: UNKNOWN_ERROR,
@@ -141,8 +143,9 @@ public sealed class MfaException(
141143
private val code: String,
142144
private val description: String,
143145
private val values: Map<String, Any> = emptyMap(),
144-
override val statusCode: Int = 0
145-
) : MfaException("MFA challenge failed: $code") {
146+
override val statusCode: Int = 0,
147+
cause: Throwable? = null
148+
) : MfaException("MFA challenge failed: $code", cause) {
146149

147150
internal constructor(values: Map<String, Any>, statusCode: Int) : this(
148151
code = (values["error"] as? String) ?: UNKNOWN_ERROR,
@@ -175,8 +178,9 @@ public sealed class MfaException(
175178
private val code: String,
176179
private val description: String,
177180
private val values: Map<String, Any> = emptyMap(),
178-
override val statusCode: Int = 0
179-
) : MfaException("MFA verification failed: $code") {
181+
override val statusCode: Int = 0,
182+
cause: Throwable? = null
183+
) : MfaException("MFA verification failed: $code", cause) {
180184

181185
internal constructor(values: Map<String, Any>, statusCode: Int) : this(
182186
code = (values["error"] as? String) ?: UNKNOWN_ERROR,
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package com.auth0.android.authentication
2+
3+
/**
4+
* Represents the type of MFA enrollment to perform.
5+
*
6+
* Use one of the subclasses to specify the enrollment method when calling [MfaApiClient.enroll].
7+
*
8+
* ## Usage
9+
*
10+
* ```kotlin
11+
* // Phone (SMS) enrollment
12+
* mfaClient.enroll(MfaEnrollmentType.Phone("+12025550135"))
13+
*
14+
* // Email enrollment
15+
* mfaClient.enroll(MfaEnrollmentType.Email("user@example.com"))
16+
*
17+
* // TOTP (Authenticator app) enrollment
18+
* mfaClient.enroll(MfaEnrollmentType.Otp)
19+
*
20+
* // Push notification enrollment
21+
* mfaClient.enroll(MfaEnrollmentType.Push)
22+
* ```
23+
*
24+
* @see MfaApiClient.enroll
25+
*/
26+
public sealed class MfaEnrollmentType {
27+
/**
28+
* Enrolls a phone number for SMS-based MFA.
29+
*
30+
* An SMS with a verification code will be sent to the specified phone number.
31+
*
32+
* @property phoneNumber The phone number to enroll, including country code (e.g., `+12025550135`).
33+
*/
34+
public data class Phone(val phoneNumber: String) : MfaEnrollmentType()
35+
36+
/**
37+
* Enrolls an email address for email-based MFA.
38+
*
39+
* Verification codes will be sent to the specified email address during authentication.
40+
*
41+
* @property email The email address to enroll for MFA.
42+
*/
43+
public data class Email(val email: String) : MfaEnrollmentType()
44+
45+
/**
46+
* Enrolls a time-based one-time password (TOTP) authenticator for MFA.
47+
*
48+
* The response will contain a QR code and secret that can be scanned by authenticator apps
49+
* like Google Authenticator or Authy.
50+
*/
51+
public object Otp : MfaEnrollmentType()
52+
53+
/**
54+
* Enrolls push notification as an MFA factor.
55+
*
56+
* Users will receive authentication requests via push notifications on their enrolled device
57+
* using Auth0 Guardian.
58+
*/
59+
public object Push : MfaEnrollmentType()
60+
}
61+
62+
/**
63+
* Represents the type of MFA verification to perform.
64+
*
65+
* Use one of the subclasses to specify the verification method when calling [MfaApiClient.verify].
66+
*
67+
* ## Usage
68+
*
69+
* ```kotlin
70+
* // Verify with OOB code (SMS/Email)
71+
* mfaClient.verify(MfaVerificationType.Oob(oobCode = "Fe26.2*...", bindingCode = "123456"))
72+
*
73+
* // Verify with OTP code (Authenticator app)
74+
* mfaClient.verify(MfaVerificationType.Otp(otp = "123456"))
75+
*
76+
* // Verify with recovery code
77+
* mfaClient.verify(MfaVerificationType.RecoveryCode(code = "RECOVERY_CODE_123"))
78+
* ```
79+
*
80+
* @see MfaApiClient.verify
81+
*/
82+
public sealed class MfaVerificationType {
83+
/**
84+
* Verifies an MFA challenge using an out-of-band (OOB) code.
85+
*
86+
* This is used after receiving an SMS or email challenge. The oobCode is obtained from the
87+
* challenge response, and the bindingCode is the verification code entered by the user.
88+
*
89+
* @property oobCode The out-of-band code from the challenge response.
90+
* @property bindingCode Optional binding code (the code sent to the user's phone/email).
91+
*/
92+
public data class Oob(
93+
val oobCode: String,
94+
val bindingCode: String? = null
95+
) : MfaVerificationType()
96+
97+
/**
98+
* Verifies an MFA challenge using a one-time password (OTP) code.
99+
*
100+
* This is used when the user has an authenticator app (like Google Authenticator or Authy)
101+
* that generates time-based codes.
102+
*
103+
* @property otp The 6-digit one-time password code from the authenticator app.
104+
*/
105+
public data class Otp(val otp: String) : MfaVerificationType()
106+
107+
/**
108+
* Verifies an MFA challenge using a recovery code.
109+
*
110+
* Recovery codes are used when users don't have access to their primary MFA factor.
111+
* Upon successful verification, a new recovery code is returned in the credentials.
112+
*
113+
* @property code The recovery code provided during MFA enrollment.
114+
*/
115+
public data class RecoveryCode(val code: String) : MfaVerificationType()
116+
}

0 commit comments

Comments
 (0)