Skip to content

Commit

Permalink
Merge pull request #28 from auth0/improve-tests
Browse files Browse the repository at this point in the history
Improve tests coverage
  • Loading branch information
hzalaz authored Sep 27, 2016
2 parents e9df83d + d734b25 commit 37792a0
Show file tree
Hide file tree
Showing 28 changed files with 1,521 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;

import com.auth0.android.Auth0;
import com.auth0.android.authentication.request.DatabaseConnectionRequest;
Expand Down Expand Up @@ -98,7 +99,7 @@ public class AuthenticationAPIClient {
* @param auth0 account information
*/
public AuthenticationAPIClient(@NonNull Auth0 auth0) {
this(auth0, new OkHttpClient(), GsonProvider.buildGson());
this(auth0, new RequestFactory(), new OkHttpClient(), GsonProvider.buildGson());
}

/**
Expand All @@ -111,11 +112,16 @@ public AuthenticationAPIClient(Context context) {
this(new Auth0(context));
}

private AuthenticationAPIClient(Auth0 auth0, OkHttpClient client, Gson gson) {
@VisibleForTesting
AuthenticationAPIClient(Auth0 auth0, RequestFactory factory) {
this(auth0, factory, new OkHttpClient(), GsonProvider.buildGson());
}

private AuthenticationAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, Gson gson) {
this.auth0 = auth0;
this.client = client;
this.gson = gson;
this.factory = new RequestFactory();
this.factory = factory;
this.authErrorBuilder = new AuthenticationErrorBuilder();
final Telemetry telemetry = auth0.getTelemetry();
if (telemetry != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@


import android.content.Context;
import android.support.annotation.VisibleForTesting;

import com.auth0.android.Auth0;
import com.auth0.android.authentication.ParameterBuilder;
Expand Down Expand Up @@ -77,7 +78,7 @@ public class UsersAPIClient {
* @param token of the primary identity
*/
public UsersAPIClient(Auth0 auth0, String token) {
this(auth0, token, new OkHttpClient(), GsonProvider.buildGson());
this(auth0, new RequestFactory(token), new OkHttpClient(), GsonProvider.buildGson());
}

/**
Expand All @@ -91,11 +92,16 @@ public UsersAPIClient(Context context, String token) {
this(new Auth0(context), token);
}

private UsersAPIClient(Auth0 auth0, String token, OkHttpClient client, Gson gson) {
@VisibleForTesting
UsersAPIClient(Auth0 auth0, RequestFactory factory) {
this(auth0, factory, new OkHttpClient(), GsonProvider.buildGson());
}

private UsersAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, Gson gson) {
this.auth0 = auth0;
this.client = client;
this.gson = gson;
this.factory = new RequestFactory(token);
this.factory = factory;
this.mgmtErrorBuilder = new ManagementErrorBuilder();
final Telemetry telemetry = auth0.getTelemetry();
if (telemetry != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.auth0.android.provider;

import android.support.annotation.NonNull;
import android.util.Base64;
import android.util.Log;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

class AlgorithmHelper {

private static final String TAG = AlgorithmHelper.class.getSimpleName();

private static final String US_ASCII = "US-ASCII";
private static final String SHA_256 = "SHA-256";

private String getBase64String(byte[] source) {
return Base64.encodeToString(source, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
}

byte[] getASCIIBytes(String value) {
byte[] input;
try {
input = value.getBytes(US_ASCII);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Could not convert string to an ASCII byte array", e);
throw new IllegalStateException("Could not convert string to an ASCII byte array", e);
}
return input;
}

byte[] getSHA256(byte[] input) {
byte[] signature;
try {
MessageDigest md = MessageDigest.getInstance(SHA_256);
md.update(input, 0, input.length);
signature = md.digest();
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "Failed to get SHA-256 signature", e);
throw new IllegalStateException("Failed to get SHA-256 signature", e);
}
return signature;
}

public String generateCodeVerifier() {
SecureRandom sr = new SecureRandom();
byte[] code = new byte[32];
sr.nextBytes(code);
String verifier = Base64.encodeToString(code, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
Log.d(TAG, "Generated code verifier is " + verifier);
return verifier;
}

public String generateCodeChallenge(@NonNull String codeVerifier) {
byte[] input = getASCIIBytes(codeVerifier);
byte[] signature = getSHA256(input);
String challenge = getBase64String(signature);
Log.d(TAG, "Generated code challenge is " + challenge);
return challenge;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
import android.util.Log;

import com.auth0.android.auth0.R;
Expand Down Expand Up @@ -173,6 +174,11 @@ private void requestPermissions(Activity activity, int requestCode) {
handler.requestPermissions(activity, permissions, requestCode);
}

@VisibleForTesting
PermissionHandler getPermissionHandler(){
return handler;
}

/**
* Should be called from the activity that initiated the Android Manifest.permission request,
* when the method #onRequestPermissionResult is called on that activity. If all the permissions
Expand Down
72 changes: 17 additions & 55 deletions auth0/src/main/java/com/auth0/android/provider/PKCE.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,18 @@

import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.util.Base64;
import android.util.Log;

import com.auth0.android.authentication.AuthenticationAPIClient;
import com.auth0.android.authentication.AuthenticationException;
import com.auth0.android.callback.BaseCallback;
import com.auth0.android.result.Credentials;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
* Performs code exchange according to Proof Key for Code Exchange (PKCE) spec.
*/
class PKCE {
private static final String TAG = PKCE.class.getSimpleName();
private static final String US_ASCII = "US-ASCII";
private static final String SHA_256 = "SHA-256";

private final AuthenticationAPIClient apiClient;
private final String codeVerifier;
Expand All @@ -62,28 +54,26 @@ class PKCE {
* @see #isAvailable()
*/
public PKCE(@NonNull AuthenticationAPIClient apiClient, String redirectUri) {
this(apiClient, redirectUri, generateCodeVerifier());
this(apiClient, new AlgorithmHelper(), redirectUri);
}

PKCE(@NonNull AuthenticationAPIClient apiClient, @NonNull String redirectUri, @NonNull String codeVerifier) {
@VisibleForTesting
PKCE(@NonNull AuthenticationAPIClient apiClient, @NonNull AlgorithmHelper algorithmHelper, @NonNull String redirectUri) {
this.apiClient = apiClient;
this.redirectUri = redirectUri;
this.codeVerifier = codeVerifier;
this.codeChallenge = generateCodeChallenge();
this.codeVerifier = algorithmHelper.generateCodeVerifier();
this.codeChallenge = algorithmHelper.generateCodeChallenge(codeVerifier);
}

/**
* Returns the Code Challenge generated using a Code Verifier.
*
* @return the Code Challenge for this session.
*/
public String getCodeChallenge() {
return codeChallenge;
}

private String generateCodeChallenge() {
byte[] input = asASCIIBytes(codeVerifier);
byte[] signature = SHA256(input);
String challenge = Base64.encodeToString(signature, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
Log.d(TAG, "Generated code challenge is " + challenge);
return challenge;
}

/**
* Performs a request to the Auth0 API to get the OAuth Token and end the PKCE flow.
* The instance of this class must be disposed after this method is called.
Expand Down Expand Up @@ -117,45 +107,17 @@ public void onFailure(AuthenticationException error) {
* @return if this device can use PKCE flow or not.
*/
public static boolean isAvailable() {
return isAvailable(new AlgorithmHelper());
}

@VisibleForTesting
static boolean isAvailable(@NonNull AlgorithmHelper algorithmHelper) {
try {
byte[] input = asASCIIBytes("test");
SHA256(input);
byte[] input = algorithmHelper.getASCIIBytes("test");
algorithmHelper.getSHA256(input);
} catch (Exception ignored) {
return false;
}
return true;
}

private static byte[] asASCIIBytes(String value) {
byte[] input;
try {
input = value.getBytes(US_ASCII);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Could not convert string to an ASCII byte array", e);
throw new IllegalStateException("Could not convert string to an ASCII byte array", e);
}
return input;
}

private static byte[] SHA256(byte[] input) {
byte[] signature;
try {
MessageDigest md = MessageDigest.getInstance(SHA_256);
md.update(input, 0, input.length);
signature = md.digest();
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "Failed to get SHA-256 signature", e);
throw new IllegalStateException("Failed to get SHA-256 signature", e);
}
return signature;
}

private static String generateCodeVerifier() {
SecureRandom sr = new SecureRandom();
byte[] code = new byte[32];
sr.nextBytes(code);
String verifier = Base64.encodeToString(code, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
Log.d(TAG, "Generated code verifier is " + verifier);
return verifier;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import android.app.Activity;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.PermissionChecker;
Expand Down Expand Up @@ -127,4 +128,9 @@ public List<String> parseRequestResult(int requestCode, @NonNull String[] permis
}
return declinedPermissions;
}

@VisibleForTesting
int getLastRequestCode(){
return lastRequestCode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
import android.content.res.Resources;

import com.auth0.android.Auth0;
import com.auth0.android.request.internal.RequestFactory;
import com.auth0.android.result.Authentication;
import com.auth0.android.result.Credentials;
import com.auth0.android.result.DatabaseUser;
import com.auth0.android.result.Delegation;
import com.auth0.android.result.UserProfile;
import com.auth0.android.util.AuthenticationAPI;
import com.auth0.android.util.MockAuthenticationCallback;
import com.auth0.android.util.Telemetry;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
Expand Down Expand Up @@ -66,8 +68,12 @@
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class AuthenticationAPIClientTest {
Expand Down Expand Up @@ -102,6 +108,34 @@ public void tearDown() throws Exception {
mockAPI.shutdown();
}

@Test
public void shouldSetUserAgent() throws Exception {
RequestFactory factory = mock(RequestFactory.class);
AuthenticationAPIClient client = new AuthenticationAPIClient(new Auth0(CLIENT_ID, DOMAIN), factory);
client.setUserAgent("nexus-5x");
verify(factory).setUserAgent("nexus-5x");
}

@Test
public void shouldSetTelemetryIfPresent() throws Exception {
final Telemetry telemetry = mock(Telemetry.class);
when(telemetry.getValue()).thenReturn("the-telemetry-data");
RequestFactory factory = mock(RequestFactory.class);
Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN);
auth0.setTelemetry(telemetry);
new AuthenticationAPIClient(auth0, factory);
verify(factory).setClientInfo("the-telemetry-data");
}

@Test
public void shouldNotSetTelemetryIfMissing() throws Exception {
RequestFactory factory = mock(RequestFactory.class);
Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN);
auth0.doNotSendTelemetry();
new AuthenticationAPIClient(auth0, factory);
verify(factory, never()).setClientInfo(any(String.class));
}

@Test
public void shouldCreateClientWithAccountInfo() throws Exception {
AuthenticationAPIClient client = new AuthenticationAPIClient(new Auth0(CLIENT_ID, DOMAIN));
Expand Down
Loading

0 comments on commit 37792a0

Please sign in to comment.