From 01dc65ab309816690d8ec745bbfe920c43803926 Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Thu, 29 Apr 2021 20:42:12 +0200 Subject: [PATCH 1/4] Use AuthenticationException and remove onCanceled method from callbacks --- .../auth0/android/lock/app/DemoActivity.kt | 13 +++--- .../android/lock/AuthenticationCallback.java | 40 +++++-------------- .../java/com/auth0/android/lock/Lock.java | 13 +++--- .../com/auth0/android/lock/LockCallback.java | 5 ++- .../auth0/android/lock/PasswordlessLock.java | 15 ++++--- .../lock/AuthenticationCallbackTest.java | 2 +- .../utils/AuthenticationCallbackMatcher.java | 4 +- .../android/lock/utils/MockLockCallback.java | 38 ++++-------------- 8 files changed, 44 insertions(+), 86 deletions(-) diff --git a/app/src/main/java/com/auth0/android/lock/app/DemoActivity.kt b/app/src/main/java/com/auth0/android/lock/app/DemoActivity.kt index ba54734bf..bf055ac00 100644 --- a/app/src/main/java/com/auth0/android/lock/app/DemoActivity.kt +++ b/app/src/main/java/com/auth0/android/lock/app/DemoActivity.kt @@ -34,7 +34,6 @@ import com.auth0.android.Auth0 import com.auth0.android.authentication.AuthenticationException import com.auth0.android.callback.Callback import com.auth0.android.lock.* -import com.auth0.android.lock.utils.LockException import com.auth0.android.provider.WebAuthProvider.login import com.auth0.android.provider.WebAuthProvider.logout import com.auth0.android.result.Credentials @@ -238,12 +237,12 @@ class DemoActivity : AppCompatActivity() { showResult("OK > " + credentials.accessToken) } - override fun onCanceled() { - showResult("User pressed back.") - } - - override fun onError(error: LockException) { - showResult(error.message.orEmpty()) + override fun onError(error: AuthenticationException) { + if (error.isCanceled) { + showResult("User pressed back.") + } else { + showResult(error.getDescription()) + } } } private val loginCallback: Callback = object : Callback { diff --git a/lib/src/main/java/com/auth0/android/lock/AuthenticationCallback.java b/lib/src/main/java/com/auth0/android/lock/AuthenticationCallback.java index 6379af312..5c2846b4d 100644 --- a/lib/src/main/java/com/auth0/android/lock/AuthenticationCallback.java +++ b/lib/src/main/java/com/auth0/android/lock/AuthenticationCallback.java @@ -25,12 +25,10 @@ package com.auth0.android.lock; import android.content.Intent; -import android.util.Log; import androidx.annotation.NonNull; import com.auth0.android.authentication.AuthenticationException; -import com.auth0.android.lock.utils.LockException; import com.auth0.android.result.Credentials; import java.util.Date; @@ -42,8 +40,6 @@ */ public abstract class AuthenticationCallback implements LockCallback { - private static final String TAG = AuthenticationCallback.class.getSimpleName(); - /** * Called when the authentication flow finished successfully. * @@ -51,23 +47,17 @@ public abstract class AuthenticationCallback implements LockCallback { */ public abstract void onAuthentication(@NonNull Credentials credentials); - /** - * Called when the user goes back and closes the activity, without using an Authentication flow. - */ - public abstract void onCanceled(); - @Override public void onEvent(@LockEvent int event, @NonNull Intent data) { - switch (event) { - case LockEvent.CANCELED: - onCanceled(); - break; - case LockEvent.AUTHENTICATION: - parseAuthentication(data); - break; - case LockEvent.RESET_PASSWORD: - case LockEvent.SIGN_UP: - break; + if (data.hasExtra(Constants.EXCEPTION_EXTRA)) { + onError((AuthenticationException) data.getSerializableExtra(Constants.EXCEPTION_EXTRA)); + return; + } + if (event == LockEvent.AUTHENTICATION) { + Credentials credentials = extractCredentials(data); + onAuthentication(credentials); + } else if (event == LockEvent.CANCELED) { + onError(new AuthenticationException("a0.authentication_canceled", "The user pressed back")); } } @@ -76,21 +66,13 @@ public void onEvent(@LockEvent int event, @NonNull Intent data) { * * @param data the intent received at the end of the login process. */ - private void parseAuthentication(Intent data) { - if (data.hasExtra(Constants.EXCEPTION_EXTRA)) { - AuthenticationException error = (AuthenticationException) data.getSerializableExtra(Constants.EXCEPTION_EXTRA); - onError(new LockException(error)); - return; - } + private Credentials extractCredentials(Intent data) { String idToken = data.getStringExtra(Constants.ID_TOKEN_EXTRA); String accessToken = data.getStringExtra(Constants.ACCESS_TOKEN_EXTRA); String tokenType = data.getStringExtra(Constants.TOKEN_TYPE_EXTRA); String refreshToken = data.getStringExtra(Constants.REFRESH_TOKEN_EXTRA); Date expiresAt = (Date) data.getSerializableExtra(Constants.EXPIRES_AT_EXTRA); String scope = data.getStringExtra(Constants.SCOPE_EXTRA); - Credentials credentials = new Credentials(idToken, accessToken, tokenType, refreshToken, expiresAt, scope); - - Log.d(TAG, "User authenticated!"); - onAuthentication(credentials); + return new Credentials(idToken, accessToken, tokenType, refreshToken, expiresAt, scope); } } diff --git a/lib/src/main/java/com/auth0/android/lock/Lock.java b/lib/src/main/java/com/auth0/android/lock/Lock.java index 7df524a1f..db8dd4ccb 100644 --- a/lib/src/main/java/com/auth0/android/lock/Lock.java +++ b/lib/src/main/java/com/auth0/android/lock/Lock.java @@ -41,7 +41,6 @@ import com.auth0.android.lock.internal.configuration.Options; import com.auth0.android.lock.internal.configuration.Theme; import com.auth0.android.lock.provider.AuthResolver; -import com.auth0.android.lock.utils.LockException; import com.auth0.android.lock.utils.SignUpField; import com.auth0.android.provider.AuthHandler; import com.auth0.android.provider.CustomTabsOptions; @@ -148,15 +147,15 @@ private void initialize(@NonNull Context context) { } private void processEvent(@NonNull Intent data) { + if (data.hasExtra(Constants.EXCEPTION_EXTRA)) { + callback.onError((AuthenticationException) data.getSerializableExtra(Constants.EXCEPTION_EXTRA)); + return; + } String action = data.getAction(); switch (action) { case Constants.AUTHENTICATION_ACTION: Log.v(TAG, "AUTHENTICATION action received in our BroadcastReceiver"); - if (data.hasExtra(Constants.EXCEPTION_EXTRA)) { - callback.onError(new LockException((AuthenticationException) data.getSerializableExtra(Constants.EXCEPTION_EXTRA))); - } else { - callback.onEvent(LockEvent.AUTHENTICATION, data); - } + callback.onEvent(LockEvent.AUTHENTICATION, data); break; case Constants.SIGN_UP_ACTION: Log.v(TAG, "SIGN_UP action received in our BroadcastReceiver"); @@ -168,7 +167,7 @@ private void processEvent(@NonNull Intent data) { break; case Constants.INVALID_CONFIGURATION_ACTION: Log.v(TAG, "INVALID_CONFIGURATION_ACTION action received in our BroadcastReceiver"); - callback.onError(new LockException(data.getStringExtra(Constants.ERROR_EXTRA))); + callback.onError(new AuthenticationException("a0.invalid_configuration", data.getStringExtra(Constants.ERROR_EXTRA))); break; } } diff --git a/lib/src/main/java/com/auth0/android/lock/LockCallback.java b/lib/src/main/java/com/auth0/android/lock/LockCallback.java index 10e015b98..2e7ce46c3 100644 --- a/lib/src/main/java/com/auth0/android/lock/LockCallback.java +++ b/lib/src/main/java/com/auth0/android/lock/LockCallback.java @@ -25,10 +25,11 @@ package com.auth0.android.lock; import android.content.Intent; + import androidx.annotation.IntDef; import androidx.annotation.NonNull; -import com.auth0.android.lock.utils.LockException; +import com.auth0.android.authentication.AuthenticationException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -68,5 +69,5 @@ public interface LockCallback { * * @param error describing what happened. */ - void onError(@NonNull LockException error); + void onError(@NonNull AuthenticationException error); } diff --git a/lib/src/main/java/com/auth0/android/lock/PasswordlessLock.java b/lib/src/main/java/com/auth0/android/lock/PasswordlessLock.java index c652cc805..30d8eb00b 100644 --- a/lib/src/main/java/com/auth0/android/lock/PasswordlessLock.java +++ b/lib/src/main/java/com/auth0/android/lock/PasswordlessLock.java @@ -41,7 +41,6 @@ import com.auth0.android.lock.internal.configuration.Options; import com.auth0.android.lock.internal.configuration.Theme; import com.auth0.android.lock.provider.AuthResolver; -import com.auth0.android.lock.utils.LockException; import com.auth0.android.provider.AuthHandler; import com.auth0.android.provider.CustomTabsOptions; import com.auth0.android.util.Auth0UserAgent; @@ -146,16 +145,16 @@ private void initialize(Context context) { lbm.registerReceiver(this.receiver, filter); } - private void processEvent(Intent data) { + private void processEvent(@NonNull Intent data) { + if (data.hasExtra(Constants.EXCEPTION_EXTRA)) { + callback.onError((AuthenticationException) data.getSerializableExtra(Constants.EXCEPTION_EXTRA)); + return; + } String action = data.getAction(); switch (action) { case Constants.AUTHENTICATION_ACTION: Log.v(TAG, "AUTHENTICATION action received in our BroadcastReceiver"); - if (data.hasExtra(Constants.EXCEPTION_EXTRA)) { - callback.onError(new LockException((AuthenticationException) data.getSerializableExtra(Constants.EXCEPTION_EXTRA))); - } else { - callback.onEvent(LockEvent.AUTHENTICATION, data); - } + callback.onEvent(LockEvent.AUTHENTICATION, data); break; case Constants.CANCELED_ACTION: Log.v(TAG, "CANCELED action received in our BroadcastReceiver"); @@ -163,7 +162,7 @@ private void processEvent(Intent data) { break; case Constants.INVALID_CONFIGURATION_ACTION: Log.v(TAG, "INVALID_CONFIGURATION_ACTION action received in our BroadcastReceiver"); - callback.onError(new LockException(data.getStringExtra(Constants.ERROR_EXTRA))); + callback.onError(new AuthenticationException("a0.invalid_configuration", data.getStringExtra(Constants.ERROR_EXTRA))); break; } } diff --git a/lib/src/test/java/com/auth0/android/lock/AuthenticationCallbackTest.java b/lib/src/test/java/com/auth0/android/lock/AuthenticationCallbackTest.java index f44537386..1d600e93b 100644 --- a/lib/src/test/java/com/auth0/android/lock/AuthenticationCallbackTest.java +++ b/lib/src/test/java/com/auth0/android/lock/AuthenticationCallbackTest.java @@ -98,7 +98,7 @@ public void shouldCallOnCanceled() { callback.onEvent(LockEvent.CANCELED, data); assertThat(callback, isCanceled()); - assertThat(callback, hasNoError()); + assertThat(callback, hasError()); } @Test diff --git a/lib/src/test/java/com/auth0/android/lock/utils/AuthenticationCallbackMatcher.java b/lib/src/test/java/com/auth0/android/lock/utils/AuthenticationCallbackMatcher.java index 9db3f4144..aa6b8c403 100644 --- a/lib/src/test/java/com/auth0/android/lock/utils/AuthenticationCallbackMatcher.java +++ b/lib/src/test/java/com/auth0/android/lock/utils/AuthenticationCallbackMatcher.java @@ -74,7 +74,7 @@ public void describeTo(Description description) { } public static AuthenticationCallbackMatcher isCanceled() { - return new AuthenticationCallbackMatcher(is(nullValue(Credentials.class)), equalTo(true), is(nullValue(Throwable.class))); + return new AuthenticationCallbackMatcher(is(nullValue(Credentials.class)), equalTo(true), is(notNullValue(Throwable.class))); } public static AuthenticationCallbackMatcher hasAuthentication() { @@ -82,7 +82,7 @@ public static AuthenticationCallbackMatcher hasAuthentication() { } public static AuthenticationCallbackMatcher hasError() { - return new AuthenticationCallbackMatcher(is(nullValue(Credentials.class)), equalTo(false), is(notNullValue(Throwable.class))); + return new AuthenticationCallbackMatcher(is(nullValue(Credentials.class)), any(Boolean.class), is(notNullValue(Throwable.class))); } public static AuthenticationCallbackMatcher hasNoError() { diff --git a/lib/src/test/java/com/auth0/android/lock/utils/MockLockCallback.java b/lib/src/test/java/com/auth0/android/lock/utils/MockLockCallback.java index be0314b9e..2d28e4c00 100644 --- a/lib/src/test/java/com/auth0/android/lock/utils/MockLockCallback.java +++ b/lib/src/test/java/com/auth0/android/lock/utils/MockLockCallback.java @@ -26,6 +26,7 @@ import androidx.annotation.NonNull; +import com.auth0.android.authentication.AuthenticationException; import com.auth0.android.lock.AuthenticationCallback; import com.auth0.android.result.Credentials; @@ -34,36 +35,18 @@ public class MockLockCallback extends AuthenticationCallback { private Credentials credentials; - private boolean canceled; - private LockException error; + private AuthenticationException error; public Callable authentication() { - return new Callable() { - @Override - @LockEvent - public Credentials call() { - return credentials; - } - }; + return () -> credentials; } public Callable canceled() { - return new Callable() { - @Override - @LockEvent - public Boolean call() { - return canceled; - } - }; + return () -> error != null && error.isCanceled(); } - public Callable error() { - return new Callable() { - @Override - public LockException call() { - return error; - } - }; + public Callable error() { + return () -> error; } @Override @@ -72,12 +55,7 @@ public void onAuthentication(@NonNull Credentials credentials) { } @Override - public void onCanceled() { - this.canceled = true; - } - - @Override - public void onError(@NonNull LockException error) { + public void onError(@NonNull AuthenticationException error) { this.error = error; } @@ -85,7 +63,7 @@ public Credentials getCredentials() { return this.credentials; } - public LockException getError() { + public AuthenticationException getError() { return error; } } From f48c1fc6f273b6d785bf7c6813083788a35b8dea Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Thu, 29 Apr 2021 20:43:58 +0200 Subject: [PATCH 2/4] document breaking change --- MIGRATION_GUIDE.md | 53 +++++++++++++++++++++++++++++++++++++++++----- README.md | 12 ++--------- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index a0fb3dabc..f09685f98 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -30,6 +30,15 @@ android { } ``` +## Changes to the AndroidManifest file +In the previous version you had to declare the Lock activities you planned to use. These activities are now declared internally by the library with intent filters configured using the Manifest Placeholders that you provide for the Domain and Scheme. The Manifest Merger tool will process these and include them as part of your Android application. + +If your `AndroidManifest.xml` file includes declarations for `LockActivity`, `PasswordlessLockActivity` or `CountryCodeActivity`, you should remove them to avoid duplicated intent filter declarations. + +If you are using a custom style for the theme or need to override the intent-filter declarations in any of these activities, you will have to declare an activity with the same component name and annotate it with `tools:node="replace"`. + +Find details about the merging rules that will be used in the [Android Manifest Merger article](https://developer.android.com/studio/build/manifest-merge). + ## Changes to the Public API As part of removing legacy APIs or authentication flows no longer recommended for mobile clients, the following features are no longer available: @@ -39,20 +48,54 @@ As part of removing legacy APIs or authentication flows no longer recommended fo Continue reading for the detail of classes and methods that were impacted. -## Changes to the AndroidManifest file -In the previous version you had to declare the Lock activities you planned to use. These activities are now declared internally by the library with intent filters configured using the Manifest Placeholders that you provide for the Domain and Scheme. The Manifest Merger tool will process these and include them as part of your Android application. +### Updated Callbacks +The widget requires a callback to receive the results in. The interface for this is `LockCallback`, which takes either an event or an error. The `onError` method got updated to receive an `AuthenticationException` instead of `LockException`. This change will help developers extract the *code* and *description* of the error and understand better what went wrong and how to recover from it. -If your `AndroidManifest.xml` file includes declarations for `LockActivity`, `PasswordlessLockActivity` or `CountryCodeActivity`, you should remove them to avoid duplicated intent filter declarations. +The change impacts the abstract subclass `AuthenticationCallback`. Additionally, this class no longer has an `onCanceled` method. If you need to handle this scenario you have two options: +- Implement `LockCallback` and handle the different event types, checking for `LockEvent.CANCELED`. +- Implement `AuthenticationCallback` and check the received exception using the `AuthenticationException#isCanceled()` method. -If you are using a custom style for the theme or need to override the intent-filter declarations in any of these activities, you will have to declare an activity with the same component name and annotate it with `tools:node="replace"`. +```kotlin +// Before +val callback: LockCallback = object : AuthenticationCallback() { + override fun onAuthentication(credentials: Credentials) { + // Authenticated + } -Find details about the merging rules that will be used in the [Android Manifest Merger article](https://developer.android.com/studio/build/manifest-merge). + override fun onCanceled() { + // Canceled + } + + override fun onError(error: LockException) { + // Another error. Check code & description. + } +} + +// After +val callback: LockCallback = object : AuthenticationCallback() { + override fun onAuthentication(credentials: Credentials) { + // Authenticated + } + + override fun onError(error: AuthenticationException) { + if (error.isCanceled) { + // Canceled + } else { + // Another error. Check code & description. + } + } +} +``` ### Removed classes - `VoidCallback` is no longer available. Please, use `Callback` instead. +- `LockException` is no longer available. This impacts the `LockCallback` and `AuthenticationCallback` classes. Please, use `AuthenticationException` instead. ### Removed methods +#### From class `AuthenticationCallback` +- Removed `public void onCanceled()`. Instead, an exception will be raised through the `public void onError(AuthenticationException)` method. Check for this scenario using the `AuthenticationException#isCanceled()` method. + #### From class `Lock.Builder` - Removed `public Builder useBrowser(boolean)`. The library will always use a third party browser app instead of a Web View to authenticate. No replacement is available. - Removed `public Builder useImplicitGrant(boolean)`. The library will always use the "Proof Key for Code Exchange" (PKCE) flow. Your application must be configured with the type "Native" and the "OIDC Conformant" switch ON. No replacement is available. diff --git a/README.md b/README.md index 9fc5ff4b8..325eb96ce 100644 --- a/README.md +++ b/README.md @@ -143,11 +143,7 @@ class MyActivity : AppCompatActivity() { // Authenticated } - override fun onCanceled() { - // User pressed back and closed Lock - } - - override fun onError(error: LockException) { + override fun onError(error: AuthenticationException) { // An exception occurred } } @@ -195,11 +191,7 @@ class MyActivity : AppCompatActivity() { // Authenticated } - override fun onCanceled() { - // User pressed back and closed Lock - } - - override fun onError(error: LockException) { + override fun onError(error: AuthenticationException) { // An exception occurred } } From d0fdb1c1aa3af98e481eb6ee221efc8668f570eb Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Thu, 29 Apr 2021 21:06:34 +0200 Subject: [PATCH 3/4] delete unused LockException --- .../android/lock/utils/LockException.java | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 lib/src/main/java/com/auth0/android/lock/utils/LockException.java diff --git a/lib/src/main/java/com/auth0/android/lock/utils/LockException.java b/lib/src/main/java/com/auth0/android/lock/utils/LockException.java deleted file mode 100644 index 0f8d8d732..000000000 --- a/lib/src/main/java/com/auth0/android/lock/utils/LockException.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * LockException.java - * - * Copyright (c) 2016 Auth0 (http://auth0.com) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package com.auth0.android.lock.utils; - -import androidx.annotation.NonNull; - -import com.auth0.android.authentication.AuthenticationException; - - -public class LockException extends Exception { - - public LockException(@NonNull String message) { - super(message); - } - - public LockException(@NonNull AuthenticationException exception) { - super(exception); - } - -} From 48117f5cd5f80113aaae7ce3b320fda7c469d0d1 Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Thu, 29 Apr 2021 21:31:38 +0200 Subject: [PATCH 4/4] reduce gradle max-workers to avoid OOM errors --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 29a3b1265..3180cb031 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,7 +20,7 @@ jobs: - /usr/local/android-sdk-linux/extras - run: name: Run checks - command: ./gradlew clean test lint --continue --console=plain --max-workers=4 + command: ./gradlew clean test lint --continue --console=plain --max-workers=3 - store_artifacts: path: lib/build/reports destination: reports