From d878ec02d32e1bd3f652bcde5bb26281b05a7271 Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Tue, 22 Jan 2019 17:44:32 -0300 Subject: [PATCH] update README.md and javadoc with exception explanations --- README.md | 17 +++++++++++++---- .../storage/SecureCredentialsManager.java | 9 +++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1d353b1b9..a098e17cf 100644 --- a/README.md +++ b/README.md @@ -589,11 +589,11 @@ manager.clearCredentials(); ### Encryption enforced (Min API 21) -This version expands the minimum version and adds encryption to the data storage. In those devices where a Secure LockScreen has been configured it can require the user authentication before letting them obtain the stored credentials. The class is called `SecureCredentialsManager` and requires at minimum Android API 21. +This version expands the minimum version and adds encryption to the data storage. Additionally, in those devices where a Secure Lock Screen has been configured it can require the user authentication before letting them obtain the stored credentials. The class is called `SecureCredentialsManager` and requires at minimum Android API 21. #### Usage -The usage is similar to the previous version, with the slight difference that the manager now requires a valid `Context` as shown below: +The usage is similar to the previous version, with the slight difference that the manager now requires a valid android `Context` as shown below: ```java AuthenticationAPIClient authentication = new AuthenticationAPIClient(account); @@ -603,7 +603,7 @@ SecureCredentialsManager manager = new SecureCredentialsManager(this, authentica #### Requiring Authentication -You can require the user authentication to obtain credentials. This will make the manager prompt the user with the device's configured LockScreen, which they must pass correctly in order to obtain the credentials. **This feature is only available on devices where the user has setup a secured LockScreen** (PIN, Pattern, Password or Fingerprint). +You can require the user authentication to obtain credentials. This will make the manager prompt the user with the device's configured Lock Screen, which they must pass correctly in order to obtain the credentials. **This feature is only available on devices where the user has setup a secured Lock Screen** (PIN, Pattern, Password or Fingerprint). To enable authentication you must call the `requireAuthentication` method passing a valid _Activity_ context, a Request Code that represents the authentication call, and the title and description to display in the LockScreen. As seen in the snippet below, you can leave these last two parameters with `null` to use the system default resources. @@ -614,7 +614,7 @@ private static final int AUTH_REQ_CODE = 11; manager.requireAuthentication(this, AUTH_REQ_CODE, null, null); ``` -When the above conditions are met and the manager requires the user authentication, it will use the activity context to launch a new activity for the result. The outcome of getting approved or rejected by the LockScreen is given back to the activity in the `onActivityResult` method, which your activity must override to redirect the data to the manager using the `checkAuthenticationResult` method. +When the above conditions are met and the manager requires the user authentication, it will use the activity context to launch a new activity for the result. The outcome of getting approved or rejected by the Lock Screen is given back to the activity in the `onActivityResult` method, which your activity must override to redirect the data to the manager using the `checkAuthenticationResult` method. ```java @Override @@ -629,6 +629,15 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { The `checkAuthenticationResult` method will continue the retrieval of credentials on a successful authentication, and the decrypted credentials will be delivered to the callback passed on the `getCredentials` call. +#### Handling exceptions + +In the event that something happened while trying to save or retrieve the credentials, a `CredentialsManagerException` will be thrown. These are some of the expected failure scenarios: + +- Invalid Credentials format or values. e.g. when it's missing the `access_token`, the `id_token` or the `expires_at` values. +- Tokens have expired but no `refresh_token` is available to perform a refresh credentials request. +- Device's Lock Screen security settings have changed (e.g. the PIN code was changed). Even when `hasCredentials` returns true, the encryption keys will be deemed invalid and until `saveCredentials` is called again it won't be possible to decrypt any previously existing content, since they keys used back then are not the same as the new ones. +- Device is not compatible with some of the algorithms required by the `SecureCredentialsManager` class. This is considered a catastrophic event and might happen when the OEM has modified the Android ROM removing some of the officially included algorithms. Nevertheless, it can be checked in the exception instance itself by calling `isDeviceIncompatible`. By doing so you can decide the fallback for storing the credentials, such as using the regular `CredentialsManager`. + ## FAQ * Why is the Android Lint _error_ `'InvalidPackage'` considered a _warning_? diff --git a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.java b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.java index e2a007942..ab6bb691c 100644 --- a/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.java +++ b/auth0/src/main/java/com/auth0/android/authentication/storage/SecureCredentialsManager.java @@ -13,6 +13,7 @@ import android.util.Base64; import android.util.Log; +import com.auth0.android.Auth0Exception; import com.auth0.android.authentication.AuthenticationAPIClient; import com.auth0.android.authentication.AuthenticationException; import com.auth0.android.callback.AuthenticationCallback; @@ -130,7 +131,8 @@ public boolean checkAuthenticationResult(int requestCode, int resultCode) { * Saves the given credentials in the Storage. * * @param credentials the credentials to save. - * @throws CredentialsManagerException if the credentials couldn't be encrypted. If the class is not supported by this device the cause will be a {@link IncompatibleDeviceException}. + * @throws CredentialsManagerException if the credentials couldn't be encrypted. Some devices are not compatible at all with the cryptographic + * implementation and will have {@link CredentialsManagerException#isDeviceIncompatible()} return true. */ public void saveCredentials(@NonNull Credentials credentials) throws CredentialsManagerException { if ((isEmpty(credentials.getAccessToken()) && isEmpty(credentials.getIdToken())) || credentials.getExpiresAt() == null) { @@ -162,7 +164,10 @@ public void saveCredentials(@NonNull Credentials credentials) throws Credentials } /** - * Tries to obtain the credentials from the Storage. + * Tries to obtain the credentials from the Storage. The callback's {@link BaseCallback#onSuccess(Object)} method will be called with the result. + * If something unexpected happens, the {@link BaseCallback#onFailure(Auth0Exception)} method will be called with the error. Some devices are not compatible + * at all with the cryptographic implementation and will have {@link CredentialsManagerException#isDeviceIncompatible()} return true. + *

* If a LockScreen is setup and {@link #requireAuthentication(Activity, int, String, String)} was called, the user will be asked to authenticate before accessing * the credentials. Your activity must override the {@link Activity#onActivityResult(int, int, Intent)} method and call * {@link #checkAuthenticationResult(int, int)} with the received values.