forked from auth0/auth0-flutter
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
afpi: Added support for
loginWithOtp
(auth0#172)
* implement login with otp for android * implement login with otp for ios * put mfaToken in details map instead of in _errorFlags of APIException * naming improvements, remove validateClaims * add dependency overrides * fix android tests * import MethodChannel.Result * fix android tests * fix parameter order * add extra tests for ApiExceptionExtensions and AuthAPIHAndler.swift * Update auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/LoginWithOtpApiRequestHandlerTest.kt Co-authored-by: Poovamraj T T <[email protected]> * Update auth0_flutter/android/src/test/kotlin/com/auth0/auth0_flutter/request_handlers/api/LoginWithOtpApiRequestHandlerTest.kt Co-authored-by: Poovamraj T T <[email protected]> * Update auth0_flutter/android/src/main/kotlin/com/auth0/auth0_flutter/AuthenticationExceptionExtensions.kt Co-authored-by: Poovamraj T T <[email protected]> * Update auth0_flutter/lib/src/authentication_api.dart * Update auth0_flutter_platform_interface/lib/src/auth0_flutter_auth_platform.dart Co-authored-by: Rita Zerrizuela <[email protected]> Co-authored-by: Poovamraj T T <[email protected]>
- Loading branch information
1 parent
1b90c89
commit db8423d
Showing
26 changed files
with
695 additions
and
30 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
68 changes: 68 additions & 0 deletions
68
...main/kotlin/com/auth0/auth0_flutter/request_handlers/api/LoginWithOtpApiRequestHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package com.auth0.auth0_flutter.request_handlers.api | ||
|
||
import com.auth0.android.authentication.AuthenticationAPIClient | ||
import com.auth0.android.authentication.AuthenticationException | ||
import com.auth0.android.callback.Callback | ||
import com.auth0.android.jwt.JWT | ||
import com.auth0.android.result.Credentials | ||
import com.auth0.auth0_flutter.createUserProfileFromClaims | ||
import com.auth0.auth0_flutter.request_handlers.MethodCallRequest | ||
import com.auth0.auth0_flutter.toMap | ||
import com.auth0.auth0_flutter.utils.assertHasProperties | ||
import io.flutter.plugin.common.MethodChannel | ||
import java.text.SimpleDateFormat | ||
import java.util.* | ||
|
||
private const val AUTH_LOGIN_OTP_METHOD = "auth#loginOtp" | ||
|
||
class LoginWithOtpApiRequestHandler: ApiRequestHandler { | ||
override val method: String = AUTH_LOGIN_OTP_METHOD | ||
|
||
override fun handle( | ||
api: AuthenticationAPIClient, | ||
request: MethodCallRequest, | ||
result: MethodChannel.Result | ||
) { | ||
val args = request.data | ||
|
||
assertHasProperties(listOf("mfaToken", "otp"), args) | ||
|
||
val loginBuilder = api | ||
.loginWithOTP( | ||
args["mfaToken"] as String, | ||
args["otp"] as String, | ||
) | ||
|
||
loginBuilder.start(object : Callback<Credentials, AuthenticationException> { | ||
override fun onFailure(exception: AuthenticationException) { | ||
result.error( | ||
exception.getCode(), | ||
exception.getDescription(), | ||
exception.toMap() | ||
) | ||
} | ||
|
||
override fun onSuccess(credentials: Credentials) { | ||
val scope = credentials.scope?.split(" ") ?: listOf() | ||
val jwt = JWT(credentials.idToken) | ||
val userProfile = createUserProfileFromClaims(jwt.claims) | ||
val sdf = | ||
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()) | ||
|
||
val formattedDate = sdf.format(credentials.expiresAt) | ||
result.success( | ||
mapOf( | ||
"accessToken" to credentials.accessToken, | ||
"idToken" to credentials.idToken, | ||
"refreshToken" to credentials.refreshToken, | ||
"userProfile" to userProfile.toMap(), | ||
"expiresAt" to formattedDate, | ||
"scopes" to scope, | ||
"tokenType" to credentials.type | ||
) | ||
) | ||
} | ||
}) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
168 changes: 168 additions & 0 deletions
168
.../kotlin/com/auth0/auth0_flutter/request_handlers/api/LoginWithOtpApiRequestHandlerTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
package com.auth0.auth0_flutter.request_handlers.api | ||
|
||
import com.auth0.android.Auth0 | ||
import com.auth0.android.authentication.AuthenticationAPIClient | ||
import com.auth0.android.authentication.AuthenticationException | ||
import com.auth0.android.callback.Callback | ||
import com.auth0.android.request.AuthenticationRequest | ||
import com.auth0.android.result.Credentials | ||
import com.auth0.auth0_flutter.JwtTestUtils | ||
|
||
import com.auth0.auth0_flutter.request_handlers.MethodCallRequest | ||
import io.flutter.plugin.common.MethodChannel.Result | ||
import org.hamcrest.CoreMatchers.equalTo | ||
import org.hamcrest.MatcherAssert.assertThat | ||
import org.junit.Assert | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import org.mockito.kotlin.* | ||
import org.robolectric.RobolectricTestRunner | ||
import java.text.SimpleDateFormat | ||
import java.util.* | ||
|
||
@RunWith(RobolectricTestRunner::class) | ||
class LoginWithOtpApiRequestHandlerTest { | ||
@Test | ||
fun `should throw when missing otp`() { | ||
val options = hashMapOf("mfaToken" to "test-mfa-token") | ||
val handler = LoginWithOtpApiRequestHandler() | ||
val mockApi = mock<AuthenticationAPIClient>() | ||
val mockAccount = mock<Auth0>() | ||
val mockResult = mock<Result>() | ||
val request = MethodCallRequest(account = mockAccount, options) | ||
|
||
val exception = Assert.assertThrows(IllegalArgumentException::class.java) { | ||
handler.handle( | ||
mockApi, | ||
request, | ||
mockResult | ||
) | ||
} | ||
|
||
assertThat( | ||
exception.message, | ||
equalTo("Required property 'otp' is not provided.") | ||
) | ||
} | ||
|
||
@Test | ||
fun `should throw when missing mfaToken`() { | ||
val options = hashMapOf("otp" to "test-otp") | ||
val handler = LoginWithOtpApiRequestHandler() | ||
val mockApi = mock<AuthenticationAPIClient>() | ||
val mockAccount = mock<Auth0>() | ||
val mockResult = mock<Result>() | ||
val request = MethodCallRequest(account = mockAccount, options) | ||
|
||
val exception = Assert.assertThrows(IllegalArgumentException::class.java) { | ||
handler.handle( | ||
mockApi, | ||
request, | ||
mockResult | ||
) | ||
} | ||
|
||
assertThat( | ||
exception.message, | ||
equalTo("Required property 'mfaToken' is not provided.") | ||
) | ||
} | ||
|
||
@Test | ||
fun `should call loginWithOTp with the correct parameters`() { | ||
val options = hashMapOf( | ||
"otp" to "test-otp", | ||
"mfaToken" to "test-mfaToken" | ||
) | ||
val handler = LoginWithOtpApiRequestHandler() | ||
val mockLoginBuilder = mock<AuthenticationRequest>() | ||
val mockApi = mock<AuthenticationAPIClient>() | ||
val mockAccount = mock<Auth0>() | ||
val mockResult = mock<Result>() | ||
val request = MethodCallRequest(account = mockAccount, options) | ||
|
||
doReturn(mockLoginBuilder).`when`(mockApi).loginWithOTP(any(), any()) | ||
|
||
handler.handle( | ||
mockApi, | ||
request, | ||
mockResult | ||
) | ||
|
||
verify(mockApi).loginWithOTP("test-mfaToken", "test-otp") | ||
verify(mockLoginBuilder).start(any()) | ||
} | ||
|
||
@Test | ||
fun `should call result error on failure`() { | ||
val options = hashMapOf( | ||
"otp" to "test-otp", | ||
"mfaToken" to "test-mfaToken" | ||
) | ||
val handler = LoginWithOtpApiRequestHandler() | ||
val mockLoginBuilder = mock<AuthenticationRequest>() | ||
val mockApi = mock<AuthenticationAPIClient>() | ||
val mockAccount = mock<Auth0>() | ||
val mockResult = mock<Result>() | ||
val request = MethodCallRequest(account = mockAccount, options) | ||
val exception = | ||
AuthenticationException(code = "test-code", description = "test-description") | ||
|
||
doReturn(mockLoginBuilder).`when`(mockApi).loginWithOTP(any(), any()) | ||
doAnswer { | ||
val ob = it.getArgument<Callback<Credentials, AuthenticationException>>(0) | ||
ob.onFailure(exception) | ||
}.`when`(mockLoginBuilder).start(any()) | ||
|
||
handler.handle( | ||
mockApi, | ||
request, | ||
mockResult | ||
) | ||
|
||
verify(mockResult).error(eq("test-code"), eq("test-description"), any()) | ||
} | ||
|
||
@Test | ||
fun `should call result success on success`() { | ||
val options = hashMapOf( | ||
"otp" to "test-otp", | ||
"mfaToken" to "test-mfaToken" | ||
) | ||
val handler = LoginWithOtpApiRequestHandler() | ||
val mockLoginBuilder = mock<AuthenticationRequest>() | ||
val mockApi = mock<AuthenticationAPIClient>() | ||
val mockAccount = mock<Auth0>() | ||
val mockResult = mock<Result>() | ||
val request = MethodCallRequest(account = mockAccount, options) | ||
val idToken = JwtTestUtils.createJwt(claims = mapOf("name" to "John Doe")) | ||
val credentials = Credentials(idToken, "test", "", null, Date(), "scope1 scope2") | ||
|
||
doReturn(mockLoginBuilder).`when`(mockApi).loginWithOTP(any(), any()) | ||
doAnswer { | ||
val ob = it.getArgument<Callback<Credentials, AuthenticationException>>(0) | ||
ob.onSuccess(credentials) | ||
}.`when`(mockLoginBuilder).start(any()) | ||
|
||
handler.handle( | ||
mockApi, | ||
request, | ||
mockResult | ||
) | ||
|
||
val captor = argumentCaptor<() -> Map<String, *>>() | ||
verify(mockResult).success(captor.capture()) | ||
|
||
val sdf = | ||
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()) | ||
|
||
val formattedDate = sdf.format(credentials.expiresAt) | ||
|
||
assertThat((captor.firstValue as Map<*, *>)["accessToken"], equalTo(credentials.accessToken)) | ||
assertThat((captor.firstValue as Map<*, *>)["idToken"], equalTo(credentials.idToken)) | ||
assertThat((captor.firstValue as Map<*, *>)["refreshToken"], equalTo(credentials.refreshToken)) | ||
assertThat((captor.firstValue as Map<*, *>)["expiresAt"] as String, equalTo(formattedDate)) | ||
assertThat((captor.firstValue as Map<*, *>)["scopes"], equalTo(listOf("scope1", "scope2"))) | ||
assertThat(((captor.firstValue as Map<*, *>)["userProfile"] as Map<*, *>)["name"], equalTo("John Doe")) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.