Skip to content
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

Add Logging for Requests/Responses and Uri's. #40

Merged
merged 5 commits into from
Nov 30, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions auth0/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,15 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.0.1'
compile 'com.squareup.okhttp:okhttp:2.5.0'
compile "com.google.code.gson:gson:2.6.2"
compile 'com.squareup.okhttp:okhttp:2.7.5'
compile 'com.squareup.okhttp:logging-interceptor:2.7.5'
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.auth0.android:jwtdecode:1.0.0'

testCompile 'junit:junit:4.12'
testCompile 'org.hamcrest:java-hamcrest:2.0.0.0'
testCompile 'org.mockito:mockito-core:1.10.19'
testCompile 'com.squareup.okhttp:mockwebserver:2.5.0'
testCompile 'com.squareup.okhttp:mockwebserver:2.7.5'
testCompile 'com.jayway.awaitility:awaitility:1.6.4'
testCompile 'org.robolectric:robolectric:3.1.2'
testCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import com.google.gson.Gson;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.logging.HttpLoggingInterceptor;

import java.util.Map;

Expand Down Expand Up @@ -91,6 +92,7 @@ public class AuthenticationAPIClient {

private final Auth0 auth0;
private final OkHttpClient client;
private final HttpLoggingInterceptor logInterceptor;
private final Gson gson;
private final com.auth0.android.request.internal.RequestFactory factory;
private final ErrorBuilder<AuthenticationException> authErrorBuilder;
Expand All @@ -116,13 +118,15 @@ public AuthenticationAPIClient(Context context) {
}

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

private AuthenticationAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, Gson gson) {
this.auth0 = auth0;
this.client = client;
this.logInterceptor = new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.NONE);
this.client.interceptors().add(logInterceptor);
this.gson = gson;
this.factory = factory;
this.authErrorBuilder = new AuthenticationErrorBuilder();
Expand All @@ -132,6 +136,21 @@ private AuthenticationAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClien
}
}

/**
* Log every Request and Response made by this client.
* You shouldn't enable logging in release builds as it may leak sensitive information.
*/
public void setLoggingEnabled(boolean enabled) {
logInterceptor.setLevel(enabled ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE);
}

/**
* Getter for the current client logger enabled state.
*/
public boolean isLoggingEnabled() {
return logInterceptor.getLevel() == HttpLoggingInterceptor.Level.BODY;
}

public String getClientId() {
return auth0.getClientId();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import com.google.gson.reflect.TypeToken;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.logging.HttpLoggingInterceptor;

import java.util.List;
import java.util.Map;
Expand All @@ -67,6 +68,7 @@ public class UsersAPIClient {

private final Auth0 auth0;
private final OkHttpClient client;
private final HttpLoggingInterceptor logInterceptor;
private final Gson gson;
private final RequestFactory factory;
private final ErrorBuilder<ManagementException> mgmtErrorBuilder;
Expand All @@ -93,13 +95,15 @@ public UsersAPIClient(Context context, String token) {
}

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

private UsersAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client, Gson gson) {
this.auth0 = auth0;
this.client = client;
this.logInterceptor = new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.NONE);
this.client.interceptors().add(logInterceptor);
this.gson = gson;
this.factory = factory;
this.mgmtErrorBuilder = new ManagementErrorBuilder();
Expand All @@ -109,6 +113,14 @@ private UsersAPIClient(Auth0 auth0, RequestFactory factory, OkHttpClient client,
}
}

/**
* Log every Request and Response made by this client.
* You shouldn't enable logging in release builds as it may leak sensitive information.
*/
public void enableLogging() {
logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
}

public String getClientId() {
return auth0.getClientId();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,16 @@ byte[] getSHA256(byte[] input) {
return signature;
}

public String generateCodeVerifier() {
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;
return Base64.encodeToString(code, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
}

public String generateCodeChallenge(@NonNull String codeVerifier) {
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;
return getBase64String(signature);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public class WebAuthProvider {
private Map<String, String> parameters;

private static WebAuthProvider providerInstance;
private boolean loggingEnabled;

@VisibleForTesting
WebAuthProvider(@NonNull Auth0 account) {
Expand All @@ -105,6 +106,7 @@ public static class Builder {
private boolean useBrowser;
private boolean useFullscreen;
private PKCE pkce;
private boolean loggingEnabled;

Builder(Auth0 account) {
this.account = account;
Expand Down Expand Up @@ -253,6 +255,15 @@ public Builder withConnection(@NonNull String connectionName) {
return this;
}

/**
* Log every Request and Response made by this provider.
* You shouldn't enable logging in release builds as it may leak sensitive information.
*/
public Builder enableLogging() {
this.loggingEnabled = true;
return this;
}

@VisibleForTesting
Builder withPKCE(PKCE pkce) {
this.pkce = pkce;
Expand All @@ -276,6 +287,7 @@ public void start(@NonNull Activity activity, @NonNull AuthCallback callback, in
webAuth.useFullscreen = useFullscreen;
webAuth.parameters = values;
webAuth.pkce = pkce;
webAuth.loggingEnabled = loggingEnabled;

providerInstance = webAuth;

Expand Down Expand Up @@ -368,6 +380,7 @@ private boolean authorize(@NonNull AuthorizeResult data) {
Log.w(TAG, "The response didn't contain any of these values: code, state, id_token, access_token, token_type, refresh_token");
return false;
}
logDebug("The parsed CallbackURI contains the following values: " + values);

if (values.containsKey(KEY_ERROR)) {
Log.e(TAG, "Error, access denied. Check that the required Permissions are granted and that the Application has this Connection configured in Auth0 Dashboard.");
Expand Down Expand Up @@ -507,12 +520,18 @@ Uri buildAuthorizeUri() {
builder.appendQueryParameter(entry.getKey(), entry.getValue());
}
Uri uri = builder.build();
Log.d(TAG, "The final Authorize Uri is " + uri.toString());
logDebug("The parsed CallbackURI contains the following values: " + "Using the following AuthorizeURI: " + uri.toString());
return uri;
}

private PKCE createPKCE(String redirectUri) {
return pkce == null ? new PKCE(new AuthenticationAPIClient(account), redirectUri) : pkce;
if (pkce == null) {
final AuthenticationAPIClient client = new AuthenticationAPIClient(account);
client.setLoggingEnabled(loggingEnabled);
return new PKCE(client, redirectUri);
} else {
return pkce;
}
}

@VisibleForTesting
Expand All @@ -525,6 +544,14 @@ private String getState() {
return parameters.containsKey(KEY_STATE) ? parameters.get(KEY_STATE) : secureRandomString();
}

String getScope() {
return this.parameters.get(KEY_SCOPE) != null ? this.parameters.get(KEY_SCOPE) : SCOPE_TYPE_OPENID;
}

boolean isLoggingEnabled() {
return loggingEnabled;
}

private String getNonce() {
return parameters.containsKey(KEY_NONCE) ? parameters.get(KEY_NONCE) : secureRandomString();
}
Expand All @@ -543,4 +570,10 @@ private String secureRandomString() {
sr.nextBytes(randomBytes);
return Base64.encodeToString(randomBytes, Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
}

private void logDebug(String message) {
if (loggingEnabled) {
Log.d(TAG, message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,20 @@
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.squareup.okhttp.HttpUrl;
import com.squareup.okhttp.Interceptor;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.logging.HttpLoggingInterceptor;
import com.squareup.okhttp.mockwebserver.RecordedRequest;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

Expand All @@ -64,6 +69,7 @@
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasKey;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
Expand Down Expand Up @@ -110,8 +116,10 @@ public void tearDown() throws Exception {

@Test
public void shouldSetUserAgent() throws Exception {
Auth0 account = mock(Auth0.class);
RequestFactory factory = mock(RequestFactory.class);
AuthenticationAPIClient client = new AuthenticationAPIClient(new Auth0(CLIENT_ID, DOMAIN), factory);
OkHttpClient okClient = mock(OkHttpClient.class);
AuthenticationAPIClient client = new AuthenticationAPIClient(account, factory, okClient);
client.setUserAgent("nexus-5x");
verify(factory).setUserAgent("nexus-5x");
}
Expand All @@ -121,21 +129,88 @@ public void shouldSetTelemetryIfPresent() throws Exception {
final Telemetry telemetry = mock(Telemetry.class);
when(telemetry.getValue()).thenReturn("the-telemetry-data");
RequestFactory factory = mock(RequestFactory.class);
OkHttpClient okClient = mock(OkHttpClient.class);
Auth0 auth0 = new Auth0(CLIENT_ID, DOMAIN);
auth0.setTelemetry(telemetry);
new AuthenticationAPIClient(auth0, factory);
new AuthenticationAPIClient(auth0, factory, okClient);
verify(factory).setClientInfo("the-telemetry-data");
}

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

@SuppressWarnings("unchecked")
@Test
public void shouldEnableHttpLogging() throws Exception {
Auth0 account = mock(Auth0.class);
RequestFactory factory = mock(RequestFactory.class);
OkHttpClient okClient = mock(OkHttpClient.class);
List list = mock(List.class);
when(okClient.interceptors()).thenReturn(list);

ArgumentCaptor<Interceptor> interceptorCaptor = ArgumentCaptor.forClass(Interceptor.class);
AuthenticationAPIClient client = new AuthenticationAPIClient(account, factory, okClient);
client.setLoggingEnabled(true);

verify(okClient).interceptors();
verify(list).add(interceptorCaptor.capture());

assertThat(interceptorCaptor.getValue(), is(notNullValue()));
assertThat(interceptorCaptor.getValue(), is(instanceOf(HttpLoggingInterceptor.class)));
assertThat(((HttpLoggingInterceptor) interceptorCaptor.getValue()).getLevel(), is(HttpLoggingInterceptor.Level.BODY));
assertThat(client.isLoggingEnabled(), is(true));
}

@SuppressWarnings("unchecked")
@Test
public void shouldDisableHttpLogging() throws Exception {
Auth0 account = mock(Auth0.class);
RequestFactory factory = mock(RequestFactory.class);
OkHttpClient okClient = mock(OkHttpClient.class);
List list = mock(List.class);
when(okClient.interceptors()).thenReturn(list);

ArgumentCaptor<Interceptor> interceptorCaptor = ArgumentCaptor.forClass(Interceptor.class);
AuthenticationAPIClient client = new AuthenticationAPIClient(account, factory, okClient);
client.setLoggingEnabled(false);

verify(okClient).interceptors();
verify(list).add(interceptorCaptor.capture());

assertThat(interceptorCaptor.getValue(), is(notNullValue()));
assertThat(interceptorCaptor.getValue(), is(instanceOf(HttpLoggingInterceptor.class)));
assertThat(((HttpLoggingInterceptor) interceptorCaptor.getValue()).getLevel(), is(HttpLoggingInterceptor.Level.NONE));
assertThat(client.isLoggingEnabled(), is(false));
}

@SuppressWarnings("unchecked")
@Test
public void shouldHaveHttpLoggingDisabledByDefault() throws Exception {
Auth0 account = mock(Auth0.class);
RequestFactory factory = mock(RequestFactory.class);
OkHttpClient okClient = mock(OkHttpClient.class);
List list = mock(List.class);
when(okClient.interceptors()).thenReturn(list);

ArgumentCaptor<Interceptor> interceptorCaptor = ArgumentCaptor.forClass(Interceptor.class);
new AuthenticationAPIClient(account, factory, okClient);

verify(okClient).interceptors();
verify(list).add(interceptorCaptor.capture());

assertThat(interceptorCaptor.getValue(), is(notNullValue()));
assertThat(interceptorCaptor.getValue(), is(instanceOf(HttpLoggingInterceptor.class)));
assertThat(((HttpLoggingInterceptor) interceptorCaptor.getValue()).getLevel(), is(HttpLoggingInterceptor.Level.NONE));
assertThat(client.isLoggingEnabled(), is(false));
}

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