-
Notifications
You must be signed in to change notification settings - Fork 61
feat: Add Multi-Factor Authentication (MFA) support for mobile #872
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
20ef247
57acc9d
07f25d8
532e5e3
6e80866
cd14822
99461eb
2a8e159
95882b6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package com.auth0.auth0_flutter | ||
|
|
||
| import androidx.annotation.NonNull | ||
| import com.auth0.android.authentication.AuthenticationAPIClient | ||
| import com.auth0.auth0_flutter.request_handlers.MethodCallRequest | ||
| import com.auth0.auth0_flutter.request_handlers.mfa.MfaRequestHandler | ||
| import com.auth0.auth0_flutter.utils.assertHasProperties | ||
| import io.flutter.plugin.common.MethodCall | ||
| import io.flutter.plugin.common.MethodChannel.MethodCallHandler | ||
| import io.flutter.plugin.common.MethodChannel.Result | ||
|
|
||
| class Auth0FlutterMfaMethodCallHandler( | ||
| private val mfaRequestHandlers: List<MfaRequestHandler> | ||
| ) : MethodCallHandler { | ||
|
|
||
| override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { | ||
| val request = MethodCallRequest.fromCall(call) | ||
|
|
||
| val handler = mfaRequestHandlers.find { it.method == call.method } | ||
| if (handler != null) { | ||
| assertHasProperties(listOf("mfaToken"), request.data) | ||
| val mfaToken = request.data["mfaToken"] as String | ||
| val client = AuthenticationAPIClient(request.account).mfaClient(mfaToken) | ||
|
Comment on lines
+21
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π§© Analysis chainπ Script executed: fd Auth0FlutterMfaMethodCallHandler.kt auth0_flutter/androidRepository: auth0/auth0-flutter Length of output: 161 π Script executed: cat -n auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/Auth0FlutterMfaMethodCallHandler.ktRepository: auth0/auth0-flutter Length of output: 1468 π Script executed: fd assertHasProperties auth0_flutter/android && cat -n auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/utils/assertHasProperties.ktRepository: auth0/auth0-flutter Length of output: 1033 π Script executed: rg "mfaToken" auth0_flutter/android --type kotlin -B 3 -A 3Repository: auth0/auth0-flutter Length of output: 40573 Use safe cast for Line 22 uses an unsafe force-cast that will crash if Per coding guidelines: ClassCastException from unsafe casts in MethodChannel handlers has caused crashes in the past β treat any unchecked cast as a bug. π€ Prompt for AI AgentsSource: Coding guidelines |
||
|
|
||
| handler.handle(client, request, result) | ||
| } else { | ||
| result.notImplemented() | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| package com.auth0.auth0_flutter | ||
|
|
||
| import com.auth0.android.authentication.mfa.MfaException | ||
| import com.auth0.android.result.Authenticator | ||
| import com.auth0.android.result.Challenge | ||
| import com.auth0.android.result.Credentials | ||
| import com.auth0.android.result.EnrollmentChallenge | ||
| import com.auth0.android.result.MfaEnrollmentChallenge | ||
| import com.auth0.android.result.OobEnrollmentChallenge | ||
| import com.auth0.android.result.RecoveryCodeEnrollmentChallenge | ||
| import com.auth0.android.result.TotpEnrollmentChallenge | ||
|
|
||
| fun Authenticator.toMfaMap(): Map<String, Any?> = buildMap { | ||
| put("id", id) | ||
| put("type", type) | ||
| put("authenticator_type", authenticatorType) | ||
| put("active", active) | ||
| put("oob_channel", oobChannel) | ||
| put("name", name) | ||
| } | ||
|
|
||
| fun Challenge.toMfaChallengeMap(): Map<String, Any?> = buildMap { | ||
| put("challenge_type", challengeType) | ||
| put("oob_code", oobCode) | ||
| put("binding_method", bindingMethod) | ||
| } | ||
|
|
||
| fun EnrollmentChallenge.toMfaEnrollmentMap(): Map<String, Any?> = buildMap { | ||
| put("id", id) | ||
| put("auth_session", authSession) | ||
| put("oob_code", oobCode) | ||
| when (val challenge = this@toMfaEnrollmentMap) { | ||
| is TotpEnrollmentChallenge -> { | ||
| put("authenticator_type", "otp") | ||
| put("totp_secret", challenge.manualInputCode) | ||
| put("barcode_uri", challenge.barcodeUri) | ||
| } | ||
| is OobEnrollmentChallenge -> { | ||
| put("authenticator_type", "oob") | ||
| put("binding_method", challenge.bindingMethod) | ||
| } | ||
| is RecoveryCodeEnrollmentChallenge -> { | ||
| put("authenticator_type", "recovery-code") | ||
| put("recovery_codes", listOf(challenge.recoveryCode)) | ||
| } | ||
| is MfaEnrollmentChallenge -> {} | ||
| else -> {} | ||
| } | ||
| } | ||
|
|
||
| fun Credentials.toMfaCredentialsMap(): Map<String, Any?> { | ||
| val scopes = scope?.split(" ") ?: listOf() | ||
| val formattedDate = expiresAt.toInstant().toString() | ||
| return mapOf( | ||
| "accessToken" to accessToken, | ||
| "idToken" to idToken, | ||
| "refreshToken" to refreshToken, | ||
| "userProfile" to user.toMap(), | ||
| "expiresAt" to formattedDate, | ||
| "scopes" to scopes, | ||
| "tokenType" to type | ||
| ) | ||
| } | ||
|
|
||
| fun MfaException.toMfaMap(): Map<String, Any> = buildMap { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All type of MFAExceptions has code and description too which is what actually carries the error . Add those two
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done. MfaException.toMfaMap() now surfaces both code and description (the actual error carried by the native SDK) in the details map, in addition to the top-level result.error code/message: put("code", getCode()) |
||
| // `code` and `description` carry the actual error from the native MFA | ||
| // SDK; surface them in the details map (in addition to the top-level | ||
| // result.error code/message) so the Dart layer always has them. | ||
| put("code", getCode()) | ||
| put("description", getDescription()) | ||
| put("_statusCode", statusCode) | ||
| put( | ||
| "_errorFlags", mapOf( | ||
| "isNetworkError" to (getCode() == "network_error") | ||
| ) | ||
| ) | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.