Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
1 change: 1 addition & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
vNext
----------
- [MAJOR] Pass google id token to broker for enabling Sign in with Google (#2573)
- [MINOR] Organize browser selection classes and change signature for get AuthorizationStrategy (#2564)
- [MINOR] Add support for OneBox Environment (#2559)
- [MINOR] Add support for claims requests for native authentication (#2572)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ public static final class Broker {
*
* @see <a href="https://identitydivision.visualstudio.com/DevEx/_git/AuthLibrariesApiReview?path=/%5BAndroid%5D%20Broker%20API/broker_protocol_versions.md">Android Auth Broker Protocol Versions</a>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you merge the PR, could you also update the version in the link above?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes.

*/
public static final String LATEST_MSAL_TO_BROKER_PROTOCOL_VERSION_CODE = "16.0";
public static final String LATEST_MSAL_TO_BROKER_PROTOCOL_VERSION_CODE = "17.0";

/**
* The maximum msal-to-broker protocol version known by clients such as MSAL Android.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import androidx.annotation.Nullable;

import com.google.gson.annotations.SerializedName;
import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleCredential;
import com.microsoft.identity.common.java.authscheme.AbstractAuthenticationScheme;
import com.microsoft.identity.common.java.opentelemetry.SerializableSpanContext;
import com.microsoft.identity.common.java.providers.oauth2.OpenIdConnectPromptParameter;
Expand Down Expand Up @@ -79,6 +80,7 @@ private static final class SerializedNames {
final static String PREFERRED_AUTH_METHOD = "preferred_auth_method";
final static String ACCOUNT_TRANSFER_TOKEN = "account_transfer_token";
final static String SUPPRESS_ACCOUNT_PICKER = "suppress_account_picker";
final static String SIGN_IN_WITH_GOOGLE_CREDENTIAL = "sign_in_with_google_credential";
}

/**
Expand Down Expand Up @@ -266,4 +268,8 @@ private static final class SerializedNames {
*/
@SerializedName(SerializedNames.SUPPRESS_ACCOUNT_PICKER)
private boolean mSuppressAccountPicker;

@Nullable
@SerializedName(SerializedNames.SIGN_IN_WITH_GOOGLE_CREDENTIAL)
private SignInWithGoogleCredential mSignInWithGoogleCredential;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import android.app.Activity;

import com.microsoft.identity.common.internal.controllers.BrokerMsalController;
import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleCredential;
import com.microsoft.identity.common.java.commands.parameters.InteractiveTokenCommandParameters;

import lombok.EqualsAndHashCode;
Expand All @@ -39,6 +40,8 @@
@Getter
@SuperBuilder(toBuilder = true)
@EqualsAndHashCode(callSuper = true)
public class AndroidActivityInteractiveTokenCommandParameters extends InteractiveTokenCommandParameters {
public class AndroidInteractiveTokenCommandParameters extends InteractiveTokenCommandParameters {
private transient final Activity activity;

private final SignInWithGoogleCredential signInWithGoogleCredential;
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
import com.microsoft.identity.common.internal.cache.ClientActiveBrokerCache;
import com.microsoft.identity.common.internal.cache.HelloCache;
import com.microsoft.identity.common.internal.cache.HelloCacheResult;
import com.microsoft.identity.common.internal.commands.parameters.AndroidActivityInteractiveTokenCommandParameters;
import com.microsoft.identity.common.internal.commands.parameters.AndroidInteractiveTokenCommandParameters;
import com.microsoft.identity.common.internal.request.MsalBrokerRequestAdapter;
import com.microsoft.identity.common.internal.result.MsalBrokerResultAdapter;
import com.microsoft.identity.common.internal.telemetry.Telemetry;
Expand Down Expand Up @@ -358,8 +358,8 @@ public AcquireTokenResult acquireToken(final @NonNull InteractiveTokenCommandPar
final Intent interactiveRequestIntent = getBrokerAuthorizationIntent(parameters);

Activity activity = null;
if (parameters instanceof AndroidActivityInteractiveTokenCommandParameters) {
activity = ((AndroidActivityInteractiveTokenCommandParameters) parameters).getActivity();
if (parameters instanceof AndroidInteractiveTokenCommandParameters) {
activity = ((AndroidInteractiveTokenCommandParameters) parameters).getActivity();
}

//Pass this intent to the BrokerActivity which will be used to start this activity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,17 @@
// THE SOFTWARE.
package com.microsoft.identity.common.internal.msafederation

import com.google.gson.annotations.SerializedName
import java.util.AbstractMap

/**
* Represents credential artifact as result of successful sign in into a federated sign in provider
* (Google/Apple). It can contain id token and/or auth code. See implementations for more details.
*/
abstract class FederatedCredential(val federatedSignInProviderName: FederatedSignInProviderName)
abstract class FederatedCredential(@SerializedName("signInProviderName") val signInProviderName: FederatedSignInProviderName) {
fun getIdProviderExtraQueryParam(): Map.Entry<String, String> {
return AbstractMap.SimpleEntry(MsaFederationConstants.MSA_ID_PROVIDER_EXTRA_QUERY_PARAM_KEY, signInProviderName.getIdProviderName())
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ class SignInWithGoogleApi internal constructor(
instance ?: SignInWithGoogleApi(FederatedSignInProviderFactory).also { instance = it }
}
}

@JvmStatic
fun setInstance(api: SignInWithGoogleApi) {
instance = api
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,24 @@
// THE SOFTWARE.
package com.microsoft.identity.common.internal.msafederation.google

import com.microsoft.identity.common.internal.msafederation.MsaFederationConstants.MSA_ID_TOKEN_HEADER_KEY
import com.google.gson.annotations.SerializedName
import com.microsoft.identity.common.internal.msafederation.FederatedCredential
import com.microsoft.identity.common.internal.msafederation.FederatedSignInProviderName
import com.microsoft.identity.common.internal.msafederation.MsaFederationConstants.MSA_ID_TOKEN_HEADER_KEY
import java.io.Serializable

/**
* Represents credential artifact as result of successful sign in with google
* It can contain id token.
*/
data class SignInWithGoogleCredential internal constructor(internal val idToken: String) : FederatedCredential(FederatedSignInProviderName.GOOGLE) {
data class SignInWithGoogleCredential internal constructor(
@SerializedName("idToken") internal val idToken: String) : FederatedCredential(FederatedSignInProviderName.GOOGLE), Serializable {

/**
* Helper method to create header that can be used in MSA
* authorization. (Move to better place.)
*/
fun asHeaders(): Map<String, String> {
fun getIdProviderHeaders(): Map<String, String> {
return mapOf(MSA_ID_TOKEN_HEADER_KEY to idToken)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ public static Intent getAuthorizationActivityIntent(final Context context,
) throws ClientException {
// add header
final HashMap<String, String> requestHeadersWithGoogleAuthCredential = requestHeaders == null? new HashMap<>() : new HashMap<>(requestHeaders);
requestHeadersWithGoogleAuthCredential.putAll(signInWithGoogleCredential.asHeaders());
requestHeadersWithGoogleAuthCredential.putAll(signInWithGoogleCredential.getIdProviderHeaders());

// add id provider query parameter
String requestUrlWithIdProvider = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@

import com.microsoft.identity.common.adal.internal.AuthenticationConstants;
import com.microsoft.identity.common.internal.broker.BrokerRequest;
import com.microsoft.identity.common.internal.commands.parameters.AndroidInteractiveTokenCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.AcquirePrtSsoTokenCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.DeviceCodeFlowCommandParameters;
import com.microsoft.identity.common.java.commands.parameters.GenerateShrCommandParameters;
Expand Down Expand Up @@ -92,7 +93,7 @@ public BrokerRequest brokerRequestFromAcquireTokenParameters(@NonNull final Inte
final String extraOptions = parameters.getExtraOptions() != null ?
QueryParamsAdapter._toJson(parameters.getExtraOptions()) : null;

final BrokerRequest brokerRequest = BrokerRequest.builder()
final BrokerRequest.BrokerRequestBuilder brokerRequestBuilder = BrokerRequest.builder()
.authority(parameters.getAuthority().getAuthorityURL().toString())
.scope(TextUtils.join(" ", parameters.getScopes()))
.redirect(parameters.getRedirectUri())
Expand Down Expand Up @@ -127,10 +128,14 @@ public BrokerRequest brokerRequestFromAcquireTokenParameters(@NonNull final Inte
.preferredBrowser(parameters.getPreferredBrowser())
.preferredAuthMethod(parameters.getPreferredAuthMethod())
.accountTransferToken(parameters.getAccountTransferToken())
.suppressAccountPicker(parameters.isSuppressBrokerAccountPicker())
.build();
.suppressAccountPicker(parameters.isSuppressBrokerAccountPicker());

return brokerRequest;
if (parameters instanceof AndroidInteractiveTokenCommandParameters) {
final AndroidInteractiveTokenCommandParameters androidInteractiveTokenCommandParameters = (AndroidInteractiveTokenCommandParameters) parameters;
brokerRequestBuilder.signInWithGoogleCredential(androidInteractiveTokenCommandParameters.getSignInWithGoogleCredential());
}

return brokerRequestBuilder.build();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import com.google.gson.Gson
import com.google.gson.GsonBuilder

/**
* Tests for [SignInWithGoogleCredential].
Expand All @@ -39,11 +41,21 @@ class SignInWithGoogleCredentialTest {
fun testSignInWithGoogleCredential() {
val testIdToken = "test-id-token"
val credential = SignInWithGoogleCredential(testIdToken)
assertEquals(FederatedSignInProviderName.GOOGLE, credential.federatedSignInProviderName)
assertEquals(FederatedSignInProviderName.GOOGLE, credential.signInProviderName)
assertEquals(testIdToken, credential.idToken)

val headers = credential.asHeaders();
val headers = credential.getIdProviderHeaders();
assertEquals(1, headers.size)
assertEquals(testIdToken, headers[MsaFederationConstants.MSA_ID_TOKEN_HEADER_KEY])

val idProviderExtraQueryParam = credential.getIdProviderExtraQueryParam()
assertEquals(MsaFederationConstants.MSA_ID_PROVIDER_EXTRA_QUERY_PARAM_KEY, idProviderExtraQueryParam.key)
assertEquals(FederatedSignInProviderName.GOOGLE.getIdProviderName(), idProviderExtraQueryParam.value)

// serialize and deserialize credential using gson
val gson = Gson()
val json = gson.toJson(credential)
val deserializedCredential = gson.fromJson(json, SignInWithGoogleCredential::class.java)
assertEquals(credential, deserializedCredential)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,22 @@
import static com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.VERSION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;

import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleApi;
import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleCredential;
import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleParameters;
import com.microsoft.identity.common.java.ui.AuthorizationAgent;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;

Expand Down Expand Up @@ -105,4 +112,61 @@ public void testGetAuthorizationActivityIntent() {
assertNotNull(idTokenHeaderValue);
assertEquals(idToken, idTokenHeaderValue);
}

@SneakyThrows
@Test
public void testSignInWithGoogleAndGetAuthorizationActivityIntent() {
// Arrange
final Context context = RuntimeEnvironment.getApplication();
final Intent authIntent = new Intent();
final String requestUrl = "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id=123&response_type=code&redirect_uri=msauth%3A%2F%2Fexample.com%2Fredirect";
final String redirectUri = "msauth://example.com/redirect";
final HashMap<String, String> requestHeaders = new HashMap<>();
requestHeaders.put("header1", "value1");
final AuthorizationAgent authorizationAgent = AuthorizationAgent.WEBVIEW;
final boolean webViewZoomEnabled = true;
final boolean webViewZoomControlsEnabled = true;
final String sourceLibraryName = "TestLibrary";
final String sourceLibraryVersion = "1.0.0";
final String idToken = "idToken";
final Activity mockActivity = Robolectric.buildActivity(Activity.class).get();
// mock SignInWithGoogleApi using mockito
final SignInWithGoogleApi mockSignInWithGoogleApi = mock(SignInWithGoogleApi.class);
final SignInWithGoogleCredential mockCredential = new SignInWithGoogleCredential("idToken");
when(mockSignInWithGoogleApi.signInSync(any(SignInWithGoogleParameters.class))).thenReturn(mockCredential);

SignInWithGoogleApi.setInstance(mockSignInWithGoogleApi);
final SignInWithGoogleParameters siwgParams = new SignInWithGoogleParameters(mockActivity);
final Intent resultIntent = AuthorizationActivityFactory.signInWithGoogleAndGetAuthorizationActivityIntent(
context,
authIntent,
requestUrl,
redirectUri,
requestHeaders,
authorizationAgent,
webViewZoomEnabled,
webViewZoomControlsEnabled,
sourceLibraryName,
sourceLibraryVersion,
siwgParams
);

assertEquals(AuthorizationActivity.class.getName(), resultIntent.getComponent().getClassName());
assertEquals(authIntent, resultIntent.getParcelableExtra(AUTH_INTENT));
assertEquals(redirectUri, resultIntent.getStringExtra(REDIRECT_URI));
assertEquals(authorizationAgent, resultIntent.getSerializableExtra(AUTHORIZATION_AGENT));
assertEquals(webViewZoomEnabled, resultIntent.getBooleanExtra(WEB_VIEW_ZOOM_ENABLED, false));
assertEquals(webViewZoomControlsEnabled, resultIntent.getBooleanExtra(WEB_VIEW_ZOOM_CONTROLS_ENABLED, false));
assertEquals(sourceLibraryName, resultIntent.getStringExtra(PRODUCT));
assertEquals(sourceLibraryVersion, resultIntent.getStringExtra(VERSION));

final String receivedUrl = resultIntent.getStringExtra(REQUEST_URL);
final String expectedUrl = requestUrl + "&id_provider=google.com";
assertEquals(expectedUrl, receivedUrl);

final HashMap<String, String> receivedHeaders = (HashMap<String, String>) resultIntent.getSerializableExtra(REQUEST_HEADERS);
final String idTokenHeaderValue = receivedHeaders.get("x-ms-fidp-idtoken");
assertNotNull(idTokenHeaderValue);
assertEquals(idToken, idTokenHeaderValue);
}
}
Loading
Loading