From b36a47abf41ab91b2c117da83a0f32ea3a73b8f2 Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Mon, 2 Jan 2017 12:48:16 -0300 Subject: [PATCH 1/3] send custom audience on login/signIn if is OIDC conformant --- .../com/auth0/android/lock/LockActivity.java | 29 ++++--- .../auth0/android/lock/LockActivityTest.java | 75 +++++++++++++++++-- 2 files changed, 88 insertions(+), 16 deletions(-) diff --git a/lib/src/main/java/com/auth0/android/lock/LockActivity.java b/lib/src/main/java/com/auth0/android/lock/LockActivity.java index 4be951e1d..970642b01 100644 --- a/lib/src/main/java/com/auth0/android/lock/LockActivity.java +++ b/lib/src/main/java/com/auth0/android/lock/LockActivity.java @@ -48,6 +48,7 @@ import com.auth0.android.authentication.AuthenticationAPIClient; import com.auth0.android.authentication.AuthenticationException; +import com.auth0.android.authentication.request.SignUpRequest; import com.auth0.android.callback.AuthenticationCallback; import com.auth0.android.lock.errors.AuthenticationError; import com.auth0.android.lock.errors.LoginErrorMessageBuilder; @@ -67,6 +68,7 @@ import com.auth0.android.provider.AuthCallback; import com.auth0.android.provider.AuthProvider; import com.auth0.android.provider.WebAuthProvider; +import com.auth0.android.request.AuthenticationRequest; import com.auth0.android.result.Credentials; import com.auth0.android.result.DatabaseUser; import com.squareup.okhttp.OkHttpClient; @@ -314,10 +316,13 @@ public void onOAuthAuthenticationRequest(OAuthLoginEvent event) { if (event.useActiveFlow()) { lockView.showProgress(true); Log.d(TAG, "Using the /ro endpoint for this OAuth Login Request"); - options.getAuthenticationAPIClient() + AuthenticationRequest request = options.getAuthenticationAPIClient() .login(event.getUsername(), event.getPassword(), connection) - .addAuthenticationParameters(options.getAuthenticationParameters()) - .start(authCallback); + .addAuthenticationParameters(options.getAuthenticationParameters()); + if (options.getAudience() != null && options.getAccount().isOIDCConformant()) { + request.setAudience(options.getAudience()); + } + request.start(authCallback); return; } @@ -349,9 +354,12 @@ public void onDatabaseAuthenticationRequest(DatabaseLoginEvent event) { parameters.put(KEY_VERIFICATION_CODE, event.getVerificationCode()); } final String connection = configuration.getDatabaseConnection().getName(); - apiClient.login(event.getUsernameOrEmail(), event.getPassword(), connection) - .addAuthenticationParameters(parameters) - .start(authCallback); + AuthenticationRequest request = apiClient.login(event.getUsernameOrEmail(), event.getPassword(), connection) + .addAuthenticationParameters(parameters); + if (options.getAudience() != null && options.getAccount().isOIDCConformant()) { + request.setAudience(options.getAudience()); + } + request.start(authCallback); } @SuppressWarnings("unused") @@ -368,9 +376,12 @@ public void onDatabaseAuthenticationRequest(DatabaseSignUpEvent event) { if (configuration.loginAfterSignUp()) { Map authParameters = new HashMap<>(options.getAuthenticationParameters()); - event.getSignUpRequest(apiClient, connection) - .addAuthenticationParameters(authParameters) - .start(authCallback); + SignUpRequest request = event.getSignUpRequest(apiClient, connection) + .addAuthenticationParameters(authParameters); + if (options.getAudience() != null && options.getAccount().isOIDCConformant()) { + request.setAudience(options.getAudience()); + } + request.start(authCallback); } else { event.getCreateUserRequest(apiClient, connection) .start(createCallback); diff --git a/lib/src/test/java/com/auth0/android/lock/LockActivityTest.java b/lib/src/test/java/com/auth0/android/lock/LockActivityTest.java index 3217f8852..7244e9873 100644 --- a/lib/src/test/java/com/auth0/android/lock/LockActivityTest.java +++ b/lib/src/test/java/com/auth0/android/lock/LockActivityTest.java @@ -27,6 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -74,14 +75,18 @@ public class LockActivityTest { Configuration configuration; @Mock ClassicLockView lockView; + @Captor + ArgumentCaptor mapCaptor; LockActivity activity; + HashMap basicParameters; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - HashMap basicParameters = new HashMap<>(Collections.singletonMap("extra", "value")); + basicParameters = new HashMap<>(Collections.singletonMap("extra", "value")); when(options.getAccount()).thenReturn(new Auth0("cliendId", "domain")); when(options.getAuthenticationAPIClient()).thenReturn(client); + when(options.getAudience()).thenReturn("aud"); when(options.getAuthenticationParameters()).thenReturn(basicParameters); when(client.login(anyString(), anyString(), anyString())).thenReturn(authRequest); @@ -120,13 +125,13 @@ public void shouldCallDatabaseLogin() throws Exception { DatabaseLoginEvent event = new DatabaseLoginEvent("username", "password"); activity.onDatabaseAuthenticationRequest(event); - ArgumentCaptor mapCaptor = ArgumentCaptor.forClass(Map.class); verify(lockView).showProgress(true); verify(options).getAuthenticationAPIClient(); verify(client).login(eq("username"), eq("password"), eq("connection")); verify(authRequest).addAuthenticationParameters(mapCaptor.capture()); verify(authRequest).start(any(BaseCallback.class)); + verify(authRequest, never()).setAudience("aud"); verify(configuration, atLeastOnce()).getDatabaseConnection(); Map reqParams = mapCaptor.getValue(); @@ -140,13 +145,13 @@ public void shouldCallDatabaseLoginWithVerificationCode() throws Exception { event.setVerificationCode("123456"); activity.onDatabaseAuthenticationRequest(event); - ArgumentCaptor mapCaptor = ArgumentCaptor.forClass(Map.class); verify(lockView).showProgress(true); verify(options).getAuthenticationAPIClient(); verify(client).login(eq("username"), eq("password"), eq("connection")); verify(authRequest).addAuthenticationParameters(mapCaptor.capture()); verify(authRequest).start(any(BaseCallback.class)); + verify(authRequest, never()).setAudience("aud"); verify(configuration, atLeastOnce()).getDatabaseConnection(); Map reqParams = mapCaptor.getValue(); @@ -155,6 +160,33 @@ public void shouldCallDatabaseLoginWithVerificationCode() throws Exception { assertThat(reqParams, hasEntry("mfa_code", "123456")); } + @Test + public void shouldCallDatabaseLoginWithCustomAudience() throws Exception { + Auth0 account = new Auth0("cliendId", "domain"); + account.setOIDCConformant(true); + Options options = mock(Options.class); + when(options.getAccount()).thenReturn(account); + when(options.getAuthenticationAPIClient()).thenReturn(client); + when(options.getAudience()).thenReturn("aud"); + when(options.getAuthenticationParameters()).thenReturn(basicParameters); + LockActivity activity = new LockActivity(configuration, options, lockView, webProvider); + + DatabaseLoginEvent event = new DatabaseLoginEvent("username", "password"); + activity.onDatabaseAuthenticationRequest(event); + + verify(lockView).showProgress(true); + verify(options).getAuthenticationAPIClient(); + verify(client).login(eq("username"), eq("password"), eq("connection")); + verify(authRequest).addAuthenticationParameters(mapCaptor.capture()); + verify(authRequest).setAudience("aud"); + verify(authRequest).start(any(BaseCallback.class)); + verify(configuration, atLeastOnce()).getDatabaseConnection(); + + Map reqParams = mapCaptor.getValue(); + assertThat(reqParams, is(notNullValue())); + assertThat(reqParams, hasEntry("extra", "value")); + } + @Test public void shouldFailDatabaseSignUpOnNullConnection() throws Exception { @@ -198,6 +230,36 @@ public void shouldCallDatabaseSignUp() throws Exception { verify(configuration, atLeastOnce()).getDatabaseConnection(); } + @Test + public void shouldCallDatabaseSignInWithCustomAudience() throws Exception { + Auth0 account = new Auth0("cliendId", "domain"); + account.setOIDCConformant(true); + Options options = mock(Options.class); + when(options.getAccount()).thenReturn(account); + when(options.getAuthenticationAPIClient()).thenReturn(client); + when(options.getAudience()).thenReturn("aud"); + when(options.getAuthenticationParameters()).thenReturn(basicParameters); + LockActivity activity = new LockActivity(configuration, options, lockView, webProvider); + + when(configuration.loginAfterSignUp()).thenReturn(true); + + DatabaseSignUpEvent event = new DatabaseSignUpEvent("email@domain.com", "password", "username"); + activity.onDatabaseAuthenticationRequest(event); + + + verify(lockView).showProgress(true); + verify(options).getAuthenticationAPIClient(); + verify(signUpRequest).addAuthenticationParameters(mapCaptor.capture()); + verify(signUpRequest).start(any(BaseCallback.class)); + verify(signUpRequest).setAudience("aud"); + verify(client).signUp(eq("email@domain.com"), eq("password"), eq("username"), eq("connection")); + verify(configuration, atLeastOnce()).getDatabaseConnection(); + + Map reqParams = mapCaptor.getValue(); + assertThat(reqParams, is(notNullValue())); + assertThat(reqParams, hasEntry("extra", "value")); + } + @Test public void shouldCallDatabaseSignInWithUsername() throws Exception { when(configuration.loginAfterSignUp()).thenReturn(true); @@ -205,12 +267,12 @@ public void shouldCallDatabaseSignInWithUsername() throws Exception { DatabaseSignUpEvent event = new DatabaseSignUpEvent("email@domain.com", "password", "username"); activity.onDatabaseAuthenticationRequest(event); - ArgumentCaptor mapCaptor = ArgumentCaptor.forClass(Map.class); verify(lockView).showProgress(true); verify(options).getAuthenticationAPIClient(); verify(signUpRequest).addAuthenticationParameters(mapCaptor.capture()); verify(signUpRequest).start(any(BaseCallback.class)); + verify(signUpRequest, never()).setAudience("aud"); verify(client).signUp(eq("email@domain.com"), eq("password"), eq("username"), eq("connection")); verify(configuration, atLeastOnce()).getDatabaseConnection(); @@ -226,12 +288,12 @@ public void shouldCallDatabaseSignIn() throws Exception { DatabaseSignUpEvent event = new DatabaseSignUpEvent("email", "password", null); activity.onDatabaseAuthenticationRequest(event); - ArgumentCaptor mapCaptor = ArgumentCaptor.forClass(Map.class); verify(lockView).showProgress(true); verify(options).getAuthenticationAPIClient(); verify(signUpRequest).addAuthenticationParameters(mapCaptor.capture()); verify(signUpRequest).start(any(BaseCallback.class)); + verify(signUpRequest, never()).setAudience("aud"); verify(client).signUp(eq("email"), eq("password"), eq("connection")); verify(configuration, atLeastOnce()).getDatabaseConnection(); @@ -274,12 +336,12 @@ public void shouldCallOAuthAuthenticationWithActiveFlow() throws Exception { OAuthLoginEvent event = new OAuthLoginEvent(connection, "email@domain.com", "password"); activity.onOAuthAuthenticationRequest(event); - ArgumentCaptor mapCaptor = ArgumentCaptor.forClass(Map.class); verify(lockView).showProgress(true); verify(options).getAuthenticationAPIClient(); verify(authRequest).addAuthenticationParameters(mapCaptor.capture()); verify(authRequest).start(any(BaseCallback.class)); + verify(authRequest, never()).setAudience("aud"); verify(client).login(eq("email@domain.com"), eq("password"), eq("my-connection")); Map reqParams = mapCaptor.getValue(); @@ -299,7 +361,6 @@ public void shouldCallOAuthAuthenticationWithCustomProvider() throws Exception { OAuthLoginEvent event = new OAuthLoginEvent(connection); activity.onOAuthAuthenticationRequest(event); - ArgumentCaptor mapCaptor = ArgumentCaptor.forClass(Map.class); verify(lockView, never()).showProgress(true); verify(customProvider).setParameters(mapCaptor.capture()); From 9902b03adb186414e5a61247b1257430d11dd606 Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Mon, 2 Jan 2017 12:54:16 -0300 Subject: [PATCH 2/3] add audience usage notes --- lib/src/main/java/com/auth0/android/lock/Lock.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d88272102..8d8170ea0 100644 --- a/lib/src/main/java/com/auth0/android/lock/Lock.java +++ b/lib/src/main/java/com/auth0/android/lock/Lock.java @@ -468,7 +468,7 @@ public Builder withScope(@NonNull String scope) { } /** - * Sets the Audience or API Identifier to request access to when performing the Authentication. + * Sets the Audience or API Identifier to request access to when performing the Authentication. This only applies if {@link com.auth0.android.Auth0#isOIDCConformant} is true. * * @param audience to use in the Authentication. * @return the current builder instance From 56cd2c41155b695ecfe2e82f948f4d721dac3915 Mon Sep 17 00:00:00 2001 From: Luciano Balmaceda Date: Mon, 2 Jan 2017 13:14:26 -0300 Subject: [PATCH 3/3] only send audience in webauth if OIDC is true --- .../com/auth0/android/lock/WebProvider.java | 2 +- .../auth0/android/lock/WebProviderTest.java | 35 ++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/lib/src/main/java/com/auth0/android/lock/WebProvider.java b/lib/src/main/java/com/auth0/android/lock/WebProvider.java index 7536e7e35..7fb1dc16e 100644 --- a/lib/src/main/java/com/auth0/android/lock/WebProvider.java +++ b/lib/src/main/java/com/auth0/android/lock/WebProvider.java @@ -48,7 +48,7 @@ public void start(Activity activity, String connection, AuthCallback callback, i builder.withScope(scope); } final String audience = options.getAudience(); - if (audience != null) { + if (audience != null && options.getAccount().isOIDCConformant()) { builder.withAudience(audience); } final String scheme = options.getScheme(); diff --git a/lib/src/test/java/com/auth0/android/lock/WebProviderTest.java b/lib/src/test/java/com/auth0/android/lock/WebProviderTest.java index 0b83a53f0..0f284ea9f 100644 --- a/lib/src/test/java/com/auth0/android/lock/WebProviderTest.java +++ b/lib/src/test/java/com/auth0/android/lock/WebProviderTest.java @@ -55,6 +55,37 @@ public void shouldStart() throws Exception { webProvider.start(activity, "my-connection", callback, 123); } + @Test + public void shouldStartWithCustomAudience() throws Exception { + Auth0 account = new Auth0("clientId", "domain.auth0.com"); + account.setOIDCConformant(true); + Options options = new Options(); + options.setAccount(account); + + options.setUseBrowser(true); + options.withAudience("https://me.auth0.com/myapi"); + + AuthCallback callback = mock(AuthCallback.class); + WebProvider webProvider = new WebProvider(options); + Activity activity = spy(Robolectric.buildActivity(Activity.class) + .create() + .start() + .resume() + .get()); + + webProvider.start(activity, "my-connection", callback, 123); + ArgumentCaptor intentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(activity).startActivity(intentCaptor.capture()); + + Intent intent = intentCaptor.getValue(); + assertThat(intent, is(notNullValue())); + assertThat(intent.getData(), hasHost("domain.auth0.com")); + assertThat(intent.getData(), hasParamWithValue("client_id", "clientId")); + assertThat(intent.getData(), hasParamWithValue("connection", "my-connection")); + assertThat(intent.getData(), hasParamWithValue("audience", "https://me.auth0.com/myapi")); + assertThat(intent, hasAction(Intent.ACTION_VIEW)); + } + @Test public void shouldStartBrowserWithOptions() throws Exception { Auth0 account = new Auth0("clientId", "domain.auth0.com"); @@ -68,7 +99,6 @@ public void shouldStartBrowserWithOptions() throws Exception { options.withScope("email profile photos"); options.withConnectionScope("my-connection", "the connection scope"); options.setUseBrowser(true); - options.withAudience("https://me.auth0.com/myapi"); options.withScheme("auth0"); AuthCallback callback = mock(AuthCallback.class); @@ -95,7 +125,6 @@ public void shouldStartBrowserWithOptions() throws Exception { assertThat(intent.getData(), hasParamWithValue("custom-param-2", "value-2")); assertThat(intent.getData(), hasParamWithValue("scope", "email profile photos")); assertThat(intent.getData(), hasParamWithValue("connection_scope", "the connection scope")); - assertThat(intent.getData(), hasParamWithValue("audience", "https://me.auth0.com/myapi")); assertThat(intent, hasAction(Intent.ACTION_VIEW)); } @@ -112,7 +141,6 @@ public void shouldStartWebViewWithOptions() throws Exception { options.withScope("email profile photos"); options.withConnectionScope("my-connection", "the connection scope"); options.setUseBrowser(false); - options.withAudience("https://me.auth0.com/myapi"); options.withScheme("auth0"); AuthCallback callback = mock(AuthCallback.class); @@ -139,7 +167,6 @@ public void shouldStartWebViewWithOptions() throws Exception { assertThat(intent.getData(), hasParamWithValue("custom-param-2", "value-2")); assertThat(intent.getData(), hasParamWithValue("scope", "email profile photos")); assertThat(intent.getData(), hasParamWithValue("connection_scope", "the connection scope")); - assertThat(intent.getData(), hasParamWithValue("audience", "https://me.auth0.com/myapi")); assertThat(intent, hasComponent(WebAuthActivity.class.getName())); }