diff --git a/changelog.txt b/changelog.txt index 5a74408334..6a1f88ac14 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,6 @@ vNext ---------- +- [MINOR] Add Sign in With Google component for MSA federation (#2551) - [MINOR] Add SDMBroadcastReceiver for applications to register callbacks for SDM broadcasts (#2547) - [MINOR] Add switch_browser toMicrosoftStsAuthorizationRequest (#2550) - [MAJOR] Add suberror for network errors (#2537) diff --git a/common/build.gradle b/common/build.gradle index 572266b677..727e3fc370 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -170,6 +170,7 @@ dependencies { //needed for credentials support from play services, for devices running implementation "androidx.credentials:credentials-play-services-auth:$rootProject.ext.AndroidCredentialsVersion" implementation "com.google.android.gms:play-services-fido:$rootProject.ext.LegacyFidoApiVersion" + implementation "com.google.android.libraries.identity.googleid:googleid:$rootProject.ext.GoogleIdVersion" constraints { implementation ("com.squareup.okio:okio:3.4.0") { diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedCredential.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedCredential.kt new file mode 100644 index 0000000000..d05c01d162 --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedCredential.kt @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation + +/** + * 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) diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedSignInParameters.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedSignInParameters.kt new file mode 100644 index 0000000000..8a3f63fb71 --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedSignInParameters.kt @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation + +/** + * Parameters for Federated Sign In. e.g. SignInWithGoogleParameters for Google. + */ +abstract class FederatedSignInParameters { + abstract val providerName: FederatedSignInProviderName +} diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedSignInProviderFactory.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedSignInProviderFactory.kt new file mode 100644 index 0000000000..5f3a6a78a6 --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedSignInProviderFactory.kt @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation + +import com.microsoft.identity.common.internal.msafederation.google.GoogleSignInProvider +import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleParameters + +/** + * Factory class to get the Federated Sign In Provider based on provider type in parameters + * Currently only Google is supported. + */ +internal object FederatedSignInProviderFactory { + + /** + * Get the Federated Sign In Provider based on provider type in parameters. + */ + fun getProvider(parameters: FederatedSignInParameters): IFederatedSignInProvider { + return when (parameters.providerName) { + FederatedSignInProviderName.GOOGLE -> GoogleSignInProvider.create(parameters as SignInWithGoogleParameters, MsaFederationConstants.GOOGLE_MSA_WEB_CLIENT_ID) + + else -> { + throw IllegalArgumentException("Unsupported provider type") + } + } + } +} + diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedSignInProviderName.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedSignInProviderName.kt new file mode 100644 index 0000000000..c700c3bf6b --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/FederatedSignInProviderName.kt @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation + +/** + * Enum class for Federated Sign In Provider Name like Google, Apple + * Currently only Google is supported. + */ +enum class FederatedSignInProviderName(private val idProviderName: String) { + GOOGLE("google.com"), + APPLE("apple.com"); // would be used later + + fun getIdProviderName(): String { + return idProviderName + } +} diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/IFederatedCredentialCallback.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/IFederatedCredentialCallback.kt new file mode 100644 index 0000000000..caad790d83 --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/IFederatedCredentialCallback.kt @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation + +/** + * Interface for Federated Credential Callback. Helps calling sign methods + * async from java. + */ +interface IFederatedCredentialCallback { + /** + * Called when the sign in is successful. + */ + fun onSuccess(credential: R) +} diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/IFederatedSignInProvider.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/IFederatedSignInProvider.kt new file mode 100644 index 0000000000..4d8cae35c1 --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/IFederatedSignInProvider.kt @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation + +/** + * Internal interface for Federated Sign In Providers. + */ +internal interface IFederatedSignInProvider { + suspend fun signIn(): Result + suspend fun signOut() +} diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/MsaFederationConstants.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/MsaFederationConstants.kt new file mode 100644 index 0000000000..fb5c1fa7d9 --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/MsaFederationConstants.kt @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation + +/** + * Constants used in MSA federation. + */ +internal object MsaFederationConstants { + internal const val SIWG_TEST_WEBCLIENT_ID = "421268256362-r39ud27ddaajrcio0c8iq6snv3po43fb.apps.googleusercontent.com" + internal const val GOOGLE_MSA_WEB_CLIENT_ID = "1057459215779-l3uvdm899ucea09atcc09d9rq6uvkilv.apps.googleusercontent.com" + internal const val MSA_ID_TOKEN_HEADER_KEY = "x-ms-fidp-idtoken" + internal const val MSA_ID_PROVIDER_EXTRA_QUERY_PARAM_KEY = "id_provider" +} diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/GoogleSignInProvider.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/GoogleSignInProvider.kt new file mode 100644 index 0000000000..653c655b7a --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/GoogleSignInProvider.kt @@ -0,0 +1,208 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation.google + +import androidx.credentials.ClearCredentialStateRequest +import androidx.credentials.CredentialManager +import androidx.credentials.CustomCredential +import androidx.credentials.GetCredentialRequest +import androidx.credentials.GetCustomCredentialOption +import androidx.credentials.exceptions.GetCredentialException +import com.google.android.libraries.identity.googleid.GetGoogleIdOption +import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption +import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential +import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException +import com.microsoft.identity.common.internal.msafederation.IFederatedSignInProvider +import com.microsoft.identity.common.java.base64.Base64Util +import com.microsoft.identity.common.java.exception.ClientException +import com.microsoft.identity.common.logging.Logger +import java.security.SecureRandom + +/** + * GoogleSignInProvider is an implementation of the IFederatedSignInProvider interface + * that handles sign-in operations with Google using credential manager in Android. + * The class is internal and should not be used outside of the SDK. OneAuth should use + * [SignInWithGoogleApi] + * Call [GoogleSignInProvider.create] to create an instance of GoogleSignInProvider. + */ +internal class GoogleSignInProvider(private val credentialManager: CredentialManager, + private val parameters: SignInWithGoogleParameters, + private val webClientId: String +) : IFederatedSignInProvider { + + companion object { + private const val TAG = "GoogleSignInProvider" + + /** + * Creates an instance of GoogleSignInProvider. + * + * @param parameters The parameters required for signing in with Google. + * @param webClientId The web client ID for Google sign-in. + * @return A new instance of GoogleSignInProvider. Prod must use MSA client ID. + */ + @JvmStatic + fun create(parameters: SignInWithGoogleParameters, webClientId: String): GoogleSignInProvider { + return GoogleSignInProvider(CredentialManager.create(parameters.activity.applicationContext), parameters, webClientId) + } + } + + /** + * Signs in with Google using the specified parameters. + * if useBottomSheet is true, it will use GetGoogleIdOption based flow as + * suggested by google. Otherwise, it will use GetSignInWithGoogleOption based flow. + * + * @return A Result containing the SignInWithGoogleCredential on success, + * or an exception on failure. + */ + override suspend fun signIn(): Result { + return if (parameters.useBottomSheet) { + signInWithGoogleBottomSheet() + } else { + signInWithGoogle() + } + } + + /** + * Signs in with Google using a bottom sheet UI. + * + * @return A Result containing the SignInWithGoogleCredential on success, or an exception on failure. + */ + private suspend fun signInWithGoogleBottomSheet(): Result { + val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder() + .setFilterByAuthorizedAccounts(false) + .setServerClientId(webClientId) + .setAutoSelectEnabled(false) + .setNonce(generateNonce()) + .build() + + return getCredential(googleIdOption) + } + + /** + * Signs in with Google using a standard UI. + * + * @return A Result containing the SignInWithGoogleCredential on success, or an exception on failure. + */ + private suspend fun signInWithGoogle(): Result { + val signInWithGoogleOption = GetSignInWithGoogleOption.Builder(webClientId) + .setNonce(generateNonce()) + .build() + + return getCredential(signInWithGoogleOption) + } + + /** + * Signs out from Google for this app. + */ + override suspend fun signOut() { + credentialManager.clearCredentialState(ClearCredentialStateRequest()) + } + + /** + * Retrieves the credential using the specified option. + * + * @param option The GetCustomCredentialOption to use for retrieving the credential. + * @return A Result containing the SignInWithGoogleCredential on success, or an exception on failure. + */ + private suspend fun getCredential( + option: GetCustomCredentialOption + ) : Result { + val methodTag = "$TAG:getCredential" + val getCredentialRequest: GetCredentialRequest = GetCredentialRequest.Builder() + .addCredentialOption(option) + .build() + try { + val getCredentialResponse = credentialManager.getCredential( + request = getCredentialRequest, + context = parameters.activity + ) + + // handle the result + val credential = getCredentialResponse.credential + + if (credential is CustomCredential) { + // TYPE_GOOGLE_ID_TOKEN_SIWG_CREDENTIAL is documented but currently TYPE_GOOGLE_ID_TOKEN_CREDENTIAL is returned + if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL || + credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_SIWG_CREDENTIAL) { + try { + val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data) + return Result.success( + SignInWithGoogleCredential( + googleIdTokenCredential.idToken + ) + ) + } catch (e: GoogleIdTokenParsingException) { + // error parsing Google ID Token + Logger.warn(TAG, "Error parsing Google ID Token, $e.message") + val clientException = ClientException( + ClientException.SIGN_IN_WITH_GOOGLE_FAILED, + e.message, + e + ) + return Result.failure(clientException) + } + } else { + // unsupported credential type + val errorMessage = "Unsupported credential type, " + credential.type + Logger.warn(TAG, errorMessage) + val clientException = ClientException( + ClientException.SIGN_IN_WITH_GOOGLE_FAILED, + errorMessage + ) + return Result.failure(clientException) + } + } else { + // Unexpected credential type + val errorMessage = "Unexpected credential type" + credential.javaClass.simpleName + Logger.warn(TAG, errorMessage) + val clientException = ClientException( + ClientException.SIGN_IN_WITH_GOOGLE_FAILED, + errorMessage + ) + return Result.failure(clientException) + } + } catch (e: GetCredentialException) { + // failure + Logger.warn(TAG, "Error getting google id token credential, $e.message") + val clientException = ClientException( + ClientException.SIGN_IN_WITH_GOOGLE_FAILED, + e.message, + e + ) + return Result.failure(clientException) + } + } + + /** + * Generates a nonce for the sign-in request. + * + * @param size The size of the nonce to generate. Default is 16. + * @return A URL-safe base64 encoded nonce. + */ + private fun generateNonce(size: Int = 16): String { + val secureRandom = SecureRandom() + val nonceBytes = ByteArray(size) + secureRandom.nextBytes(nonceBytes) + return Base64Util.encodeUrlSafeString(nonceBytes) + } +} diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/ISignInWithGoogleCredentialCallback.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/ISignInWithGoogleCredentialCallback.kt new file mode 100644 index 0000000000..5867d07175 --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/ISignInWithGoogleCredentialCallback.kt @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation.google + +import com.microsoft.identity.common.internal.msafederation.IFederatedCredentialCallback + +/** + * Interface for Federated Credential Callback. Helps calling sign methods + * async from java. + */ +interface ISignInWithGoogleCredentialCallback : + IFederatedCredentialCallback { + override fun onSuccess(credential: SignInWithGoogleCredential) +} diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleApi.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleApi.kt new file mode 100644 index 0000000000..fe1ff47be5 --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleApi.kt @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation.google + +import android.app.Activity +import com.microsoft.identity.common.internal.msafederation.FederatedSignInProviderFactory +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext + +/** + * Entry point for signing in with Google into MSA. + */ +class SignInWithGoogleApi internal constructor( + private val federatedSignInProviderFactory: FederatedSignInProviderFactory){ + + companion object { + private const val TAG = "SignInWithGoogleApi" + + @Volatile + private var instance: SignInWithGoogleApi? = null + + @JvmStatic + fun getInstance(): SignInWithGoogleApi { + return instance ?: synchronized(this) { + instance ?: SignInWithGoogleApi(FederatedSignInProviderFactory).also { instance = it } + } + } + } + + /** + * Entry method to perform sign in with google. + * It creates a [GoogleSignInProvider] and calls [GoogleSignInProvider.signIn] + * to launch the sign in google flow. + * It return [SignInWithGoogleCredential] which represents the credentials as result of sign in + * and caller can use it further to authorization flow with MSA. + * @param signInWithGoogleParameters Parameters for signing in with Google. + * @return [SignInWithGoogleCredential] which represents credentials as result of successful sign in with google. + */ + suspend fun signIn(signInWithGoogleParameters: SignInWithGoogleParameters): SignInWithGoogleCredential { + val googleSignInProvider = federatedSignInProviderFactory.getProvider( + signInWithGoogleParameters + ) + val result = googleSignInProvider.signIn() as Result + + val signInWithGoogleCredential = result.getOrElse { throw it } + + return signInWithGoogleCredential + } + + /** + * Entry method to perform sign in with google synchronously. + * Refer [signIn] for more details. + */ + fun signInSync( + signInWithGoogleParameters: SignInWithGoogleParameters + ): SignInWithGoogleCredential { + return runBlocking { + signIn(signInWithGoogleParameters) + } + } + + /** + * Entry method to perform sign in with google asynchronously for java. + * Refer [signIn] for more details. + */ + fun signInAsync( + signInWithGoogleParameters: SignInWithGoogleParameters, + callback: ISignInWithGoogleCredentialCallback + ) { + CoroutineScope(Dispatchers.Default).launch { + val credential = signIn(signInWithGoogleParameters) + withContext(coroutineContext) { + callback.onSuccess(credential) + } + } + } + + suspend fun signOut(activity: Activity) { + val signInWithGoogleParameters = SignInWithGoogleParameters(activity) + val googleSignInProvider = federatedSignInProviderFactory.getProvider( + signInWithGoogleParameters + ) + googleSignInProvider.signOut() + } +} diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleCredential.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleCredential.kt new file mode 100644 index 0000000000..4cdd9d0086 --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleCredential.kt @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// 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.microsoft.identity.common.internal.msafederation.FederatedCredential +import com.microsoft.identity.common.internal.msafederation.FederatedSignInProviderName + +/** + * 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) { + + /** + * Helper method to create header that can be used in MSA + * authorization. (Move to better place.) + */ + fun asHeaders(): Map { + return mapOf(MSA_ID_TOKEN_HEADER_KEY to idToken) + } +} diff --git a/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleParameters.kt b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleParameters.kt new file mode 100644 index 0000000000..3355b5bff4 --- /dev/null +++ b/common/src/main/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleParameters.kt @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation.google + +import android.app.Activity +import com.microsoft.identity.common.internal.msafederation.FederatedSignInParameters +import com.microsoft.identity.common.internal.msafederation.FederatedSignInProviderName + +/** + * SignInWithGoogleParameters holds the parameters required for signing in with Google. + * + * @property activity The Activity context used for the sign-in process. + * UI for the sign-in process. + */ +data class SignInWithGoogleParameters( + internal val activity: Activity +) : FederatedSignInParameters() { + + /** + * Secondary constructor to initialize the parameters with an option to use a bottom sheet UI. + * Current requirement do not use bottom sheet UI, so this constructor is kept internal + * @param activity The Activity context used for the sign-in process. + * @param useBottomSheet A flag indicating whether to use a bottom sheet UI for the sign-in process. + */ + internal constructor(activity: Activity, useBottomSheet: Boolean) : this(activity) { + this.useBottomSheet = useBottomSheet + } + + /** + * A flag indicating whether to use a bottom sheet UI for the sign-in process. + */ + internal var useBottomSheet: Boolean = false + + /** + * The provider type for the federated sign-in, which is Google in this case. + */ + override val providerName: FederatedSignInProviderName + get() = FederatedSignInProviderName.GOOGLE +} diff --git a/common/src/main/java/com/microsoft/identity/common/internal/providers/oauth2/AuthorizationActivityFactory.java b/common/src/main/java/com/microsoft/identity/common/internal/providers/oauth2/AuthorizationActivityFactory.java index 799c1bd4ef..397513d88c 100644 --- a/common/src/main/java/com/microsoft/identity/common/internal/providers/oauth2/AuthorizationActivityFactory.java +++ b/common/src/main/java/com/microsoft/identity/common/internal/providers/oauth2/AuthorizationActivityFactory.java @@ -23,6 +23,17 @@ package com.microsoft.identity.common.internal.providers.oauth2; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.AUTHORIZATION_AGENT; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.AUTH_INTENT; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.REDIRECT_URI; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.REQUEST_HEADERS; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.REQUEST_URL; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.WEB_VIEW_ZOOM_CONTROLS_ENABLED; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.WEB_VIEW_ZOOM_ENABLED; +import static com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.PRODUCT; +import static com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.VERSION; +import static com.microsoft.identity.common.java.logging.DiagnosticContext.CORRELATION_ID; + import android.content.Context; import android.content.Intent; import android.os.Bundle; @@ -30,29 +41,26 @@ import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; -import com.microsoft.identity.common.internal.util.CommonMoshiJsonAdapter; -import com.microsoft.identity.common.java.configuration.LibraryConfiguration; +import com.microsoft.identity.common.internal.msafederation.FederatedSignInProviderName; +import com.microsoft.identity.common.internal.msafederation.MsaFederationConstants; +import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleCredential; +import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleParameters; +import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleApi; import com.microsoft.identity.common.internal.telemetry.Telemetry; import com.microsoft.identity.common.internal.telemetry.events.UiStartEvent; +import com.microsoft.identity.common.internal.util.CommonMoshiJsonAdapter; +import com.microsoft.identity.common.internal.util.ProcessUtil; +import com.microsoft.identity.common.java.configuration.LibraryConfiguration; +import com.microsoft.identity.common.java.exception.ClientException; +import com.microsoft.identity.common.java.logging.DiagnosticContext; import com.microsoft.identity.common.java.opentelemetry.SerializableSpanContext; import com.microsoft.identity.common.java.opentelemetry.SpanExtension; import com.microsoft.identity.common.java.ui.AuthorizationAgent; -import com.microsoft.identity.common.internal.util.ProcessUtil; -import com.microsoft.identity.common.java.logging.DiagnosticContext; +import com.microsoft.identity.common.java.util.CommonURIBuilder; +import java.net.URISyntaxException; import java.util.HashMap; -import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.AUTHORIZATION_AGENT; -import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.AUTH_INTENT; -import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.REDIRECT_URI; -import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.REQUEST_HEADERS; -import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.REQUEST_URL; -import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.WEB_VIEW_ZOOM_CONTROLS_ENABLED; -import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.WEB_VIEW_ZOOM_ENABLED; -import static com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.PRODUCT; -import static com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.VERSION; -import static com.microsoft.identity.common.java.logging.DiagnosticContext.CORRELATION_ID; - /** * Constructs intents and/or fragments for interactive requests based on library configuration and current request. */ @@ -112,13 +120,13 @@ public static Intent getAuthorizationActivityIntent(final Context context, if (ProcessUtil.isBrokerProcess(context)) { intent = new Intent(context, BrokerAuthorizationActivity.class); } else if (libraryConfig.isAuthorizationInCurrentTask() && !authorizationAgent.equals(AuthorizationAgent.WEBVIEW)) { - // We exclude the case when the authorization agent is already selected as WEBVIEW because of confusion - // that results from attempting to use the CurrentTaskAuthorizationActivity in that case, because as webview - // already uses the current task, attempting to manually simulate that behavior ends up supplying an incorrect - // Fragment to the activity. - intent = new Intent(context, CurrentTaskAuthorizationActivity.class); + // We exclude the case when the authorization agent is already selected as WEBVIEW because of confusion + // that results from attempting to use the CurrentTaskAuthorizationActivity in that case, because as webview + // already uses the current task, attempting to manually simulate that behavior ends up supplying an incorrect + // Fragment to the activity. + intent = new Intent(context, CurrentTaskAuthorizationActivity.class); } else { - intent = new Intent(context, AuthorizationActivity.class); + intent = new Intent(context, AuthorizationActivity.class); } intent.putExtra(AUTH_INTENT, authIntent); @@ -136,11 +144,11 @@ public static Intent getAuthorizationActivityIntent(final Context context, intent.putExtra(VERSION, sourceLibraryVersion); } intent.putExtra(SerializableSpanContext.SERIALIZABLE_SPAN_CONTEXT, new CommonMoshiJsonAdapter().toJson( - SerializableSpanContext.builder() - .traceId(SpanExtension.current().getSpanContext().getTraceId()) - .spanId(SpanExtension.current().getSpanContext().getSpanId()) - .traceFlags(SpanExtension.current().getSpanContext().getTraceFlags().asByte()) - .build() + SerializableSpanContext.builder() + .traceId(SpanExtension.current().getSpanContext().getTraceId()) + .spanId(SpanExtension.current().getSpanContext().getSpanId()) + .traceFlags(SpanExtension.current().getSpanContext().getTraceFlags().asByte()) + .build() ) ); return intent; @@ -197,4 +205,105 @@ public static Fragment getAuthorizationFragmentFromStartIntentWithState(@NonNull } return fragment; } + + /** + * This method first starts sign in with google flow displaying UX for user add/select a google account + * and after success creates intent with result obtained from successful google sign in and other input + * parameters. + * + * @param context Android application context + * @param authIntent Android intent used by the authorization activity to launch the specific implementation of authorization (BROWSER, EMBEDDED) + * @param requestUrl The authorization request in URL format + * @param redirectUri The expected redirect URI associated with the authorization request + * @param requestHeaders Additional HTTP headers included with the authorization request + * @param authorizationAgent The means by which authorization should be performed (EMBEDDED, WEBVIEW) NOTE: This should move to library configuration + * @param webViewZoomEnabled This parameter is specific to embedded and controls whether webview zoom is enabled... NOTE: Needs refactoring + * @param webViewZoomControlsEnabled This parameter is specific to embedded and controls whether webview zoom controls are enabled... NOTE: Needs refactoring + * @param sourceLibraryName Product name to be of library making the request + * @param sourceLibraryVersion Product version to be of library making the request + * @param signInWithGoogleParameters Parameters to first start sign in with google flow before creating the intent + * @return An android Intent which will be used by Android to create an AuthorizationActivity + */ + public static Intent signInWithGoogleAndGetAuthorizationActivityIntent(final Context context, + final Intent authIntent, + final String requestUrl, + final String redirectUri, + final HashMap requestHeaders, + final AuthorizationAgent authorizationAgent, + final boolean webViewZoomEnabled, + final boolean webViewZoomControlsEnabled, + final String sourceLibraryName, + final String sourceLibraryVersion, + @NonNull final SignInWithGoogleParameters signInWithGoogleParameters) throws ClientException { + final SignInWithGoogleCredential signInWithGoogleCredential = SignInWithGoogleApi.getInstance().signInSync(signInWithGoogleParameters); + return getAuthorizationActivityIntent( + context, + authIntent, + requestUrl, + redirectUri, + requestHeaders, + authorizationAgent, + webViewZoomEnabled, + webViewZoomControlsEnabled, + sourceLibraryName, + sourceLibraryVersion, + signInWithGoogleCredential + ); + } + + /** + * Return the correct authorization activity based on library configuration. + * + * @param context Android application context + * @param authIntent Android intent used by the authorization activity to launch the specific implementation of authorization (BROWSER, EMBEDDED) + * @param requestUrl The authorization request in URL format + * @param redirectUri The expected redirect URI associated with the authorization request + * @param requestHeaders Additional HTTP headers included with the authorization request + * @param authorizationAgent The means by which authorization should be performed (EMBEDDED, WEBVIEW) NOTE: This should move to library configuration + * @param webViewZoomEnabled This parameter is specific to embedded and controls whether webview zoom is enabled... NOTE: Needs refactoring + * @param webViewZoomControlsEnabled This parameter is specific to embedded and controls whether webview zoom controls are enabled... NOTE: Needs refactoring + * @param sourceLibraryName Product name to be of library making the request + * @param sourceLibraryVersion Product version to be of library making the request + * @param signInWithGoogleCredential object returned previously to caller as result of performing sign in with google flow (SignInWithGoogleApi.signIn) + * @return An android Intent which will be used by Android to create an AuthorizationActivity + */ + public static Intent getAuthorizationActivityIntent(final Context context, + final Intent authIntent, + final String requestUrl, + final String redirectUri, + final HashMap requestHeaders, + final AuthorizationAgent authorizationAgent, + final boolean webViewZoomEnabled, + final boolean webViewZoomControlsEnabled, + final String sourceLibraryName, + final String sourceLibraryVersion, + @NonNull final SignInWithGoogleCredential signInWithGoogleCredential + ) throws ClientException { + // add header + final HashMap requestHeadersWithGoogleAuthCredential = requestHeaders == null? new HashMap<>() : new HashMap<>(requestHeaders); + requestHeadersWithGoogleAuthCredential.putAll(signInWithGoogleCredential.asHeaders()); + + // add id provider query parameter + String requestUrlWithIdProvider = null; + try { + final CommonURIBuilder uriBuilder = new CommonURIBuilder(requestUrl); + uriBuilder.addParameterIfAbsent(MsaFederationConstants.MSA_ID_PROVIDER_EXTRA_QUERY_PARAM_KEY, FederatedSignInProviderName.GOOGLE.getIdProviderName()); + requestUrlWithIdProvider = uriBuilder.build().toString(); + } catch (final URISyntaxException e) { + throw new ClientException(ClientException.MALFORMED_URL, "Failed to add id provider query parameter to request URL", e); + } + + return getAuthorizationActivityIntent( + context, + authIntent, + requestUrlWithIdProvider, + redirectUri, + requestHeadersWithGoogleAuthCredential, + authorizationAgent, + webViewZoomEnabled, + webViewZoomControlsEnabled, + sourceLibraryName, + sourceLibraryVersion + ); + } } diff --git a/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/GoogleSignInProviderTest.kt b/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/GoogleSignInProviderTest.kt new file mode 100644 index 0000000000..d0ad7bed29 --- /dev/null +++ b/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/GoogleSignInProviderTest.kt @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation.google + +import android.app.Activity +import androidx.credentials.ClearCredentialStateRequest +import androidx.credentials.CredentialManager +import androidx.credentials.GetCredentialRequest +import androidx.credentials.GetCredentialResponse +import com.google.android.libraries.identity.googleid.GetGoogleIdOption +import com.google.android.libraries.identity.googleid.GetSignInWithGoogleOption +import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential +import com.microsoft.identity.common.internal.msafederation.MsaFederationConstants +import io.mockk.coEvery +import io.mockk.coVerify +import io.mockk.mockk +import io.mockk.slot +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertSame +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +/** + * Tests for [GoogleSignInProvider]. + */ +@RunWith(RobolectricTestRunner::class) +class GoogleSignInProviderTest { + + @Test + fun testSignIn() { + val mockCredentialManager = mockk() + val mockActivity = mockk() + val mockParameters = SignInWithGoogleParameters(mockActivity) + val webClientId = MsaFederationConstants.GOOGLE_MSA_WEB_CLIENT_ID + val mockIdToken = "mockIdToken" + val googleSignInProvider = GoogleSignInProvider(mockCredentialManager, mockParameters, webClientId) + + val mockCredential = GoogleIdTokenCredential( + GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL, + mockIdToken, + null, + null, + null, + null, + null) + val mockGetCredentialResponse = GetCredentialResponse(mockCredential) + val getCredentialRequestSlot = slot() + val activitySlot = slot() + coEvery { mockCredentialManager.getCredential(capture(activitySlot), capture(getCredentialRequestSlot)) } returns mockGetCredentialResponse + + val result = runBlocking { googleSignInProvider.signIn() } + assertTrue(result.isSuccess) + assertNotNull(result.getOrNull()) + assertEquals(mockIdToken, result.getOrNull()!!.idToken) + val capturedRequest = getCredentialRequestSlot.captured + assertNotNull(capturedRequest) + assertEquals(1, capturedRequest.credentialOptions.size) + assertEquals(webClientId, (capturedRequest.credentialOptions[0] as GetSignInWithGoogleOption).serverClientId) + val capturedActivity = activitySlot.captured + assertSame(mockActivity, capturedActivity) + } + + @Test + fun testSignInBottomSheet() { + val mockCredentialManager = mockk() + val mockActivity = mockk() + val mockParameters = SignInWithGoogleParameters(mockActivity, true) + val webClientId = MsaFederationConstants.GOOGLE_MSA_WEB_CLIENT_ID + val mockIdToken = "mockIdToken" + val googleSignInProvider = GoogleSignInProvider(mockCredentialManager, mockParameters, webClientId) + + val mockCredential = GoogleIdTokenCredential( + GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL, + mockIdToken, + null, + null, + null, + null, + null) + val mockGetCredentialResponse = GetCredentialResponse(mockCredential) + val getCredentialRequestSlot = slot() + val activitySlot = slot() + coEvery { mockCredentialManager.getCredential(capture(activitySlot), capture(getCredentialRequestSlot)) } returns mockGetCredentialResponse + + val result = runBlocking { googleSignInProvider.signIn() } + assertTrue(result.isSuccess) + assertNotNull(result.getOrNull()) + assertEquals(mockIdToken, result.getOrNull()!!.idToken) + val capturedRequest = getCredentialRequestSlot.captured + assertNotNull(capturedRequest) + assertEquals(1, capturedRequest.credentialOptions.size) + + val capturedGoogleIdOption = capturedRequest.credentialOptions[0] as GetGoogleIdOption + assertEquals(webClientId, capturedGoogleIdOption.serverClientId) + assertEquals(false, capturedGoogleIdOption.filterByAuthorizedAccounts) + assertEquals(false, capturedGoogleIdOption.autoSelectEnabled) + val capturedActivity = activitySlot.captured + assertSame(mockActivity, capturedActivity) + } + + @Test + fun testSignOut() { + val mockCredentialManager = mockk() + val mockActivity = mockk() + val mockParameters = SignInWithGoogleParameters(mockActivity) + val webClientId = MsaFederationConstants.GOOGLE_MSA_WEB_CLIENT_ID + val googleSignInProvider = GoogleSignInProvider(mockCredentialManager, mockParameters, webClientId) + + coEvery { mockCredentialManager.clearCredentialState(any()) } returns Unit + runBlocking { googleSignInProvider.signOut() } + coVerify { mockCredentialManager.clearCredentialState(any()) } + } +} diff --git a/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/MockGoogleSignInProvider.kt b/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/MockGoogleSignInProvider.kt new file mode 100644 index 0000000000..3b8553e8d0 --- /dev/null +++ b/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/MockGoogleSignInProvider.kt @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation.google + +import com.microsoft.identity.common.internal.msafederation.IFederatedSignInProvider + +class MockGoogleSignInProvider : IFederatedSignInProvider { + companion object { + private const val MOCK_ID_TOKEN = "mockIdToken" + } + override suspend fun signIn(): Result { + return Result.success(SignInWithGoogleCredential(MOCK_ID_TOKEN)) + } + + override suspend fun signOut() { + // No-op + } +} diff --git a/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleApiTest.kt b/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleApiTest.kt new file mode 100644 index 0000000000..1d09296662 --- /dev/null +++ b/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleApiTest.kt @@ -0,0 +1,89 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation.google + +import android.app.Activity +import com.microsoft.identity.common.internal.msafederation.FederatedSignInProviderFactory +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertNotNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner +import java.util.concurrent.CountDownLatch + +/** + * Tests for [SignInWithGoogleApi]. + */ +@RunWith(RobolectricTestRunner::class) +class SignInWithGoogleApiTest { + private lateinit var mockFederatedSignInProviderFactory: FederatedSignInProviderFactory + private lateinit var mockGoogleSignInProvider: MockGoogleSignInProvider + private lateinit var signInWithGoogleApi: SignInWithGoogleApi + private lateinit var mockActivity: Activity + private lateinit var mockParameters: SignInWithGoogleParameters + + @Before + fun setUp() { + mockGoogleSignInProvider = MockGoogleSignInProvider() + mockActivity = Robolectric.buildActivity(Activity::class.java).get() + mockParameters = SignInWithGoogleParameters(mockActivity) + mockFederatedSignInProviderFactory = mockk() + every { mockFederatedSignInProviderFactory.getProvider(any()) } returns mockGoogleSignInProvider + signInWithGoogleApi = SignInWithGoogleApi(mockFederatedSignInProviderFactory) + } + + @Test + fun testSignIn() { + val credential = runBlocking { + signInWithGoogleApi.signIn(mockParameters) + } + + assertNotNull(credential) + } + + @Test + fun testSignInSync() { + val credential = signInWithGoogleApi.signInSync(mockParameters) + + assertNotNull(credential) + } + + @Test + fun testSignInAsync() { + val latch = CountDownLatch(1) + var result: SignInWithGoogleCredential? = null + val callback = object : ISignInWithGoogleCredentialCallback { + override fun onSuccess(credential: SignInWithGoogleCredential) { + result = credential + latch.countDown() + } + } + signInWithGoogleApi.signInAsync(mockParameters, callback) + latch.await() + assertNotNull(result) + } +} diff --git a/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleCredentialTest.kt b/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleCredentialTest.kt new file mode 100644 index 0000000000..3f7a5715eb --- /dev/null +++ b/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleCredentialTest.kt @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation.google + +import com.microsoft.identity.common.internal.msafederation.MsaFederationConstants +import com.microsoft.identity.common.internal.msafederation.FederatedSignInProviderName +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +/** + * Tests for [SignInWithGoogleCredential]. + */ +@RunWith(JUnit4::class) +class SignInWithGoogleCredentialTest { + + @Test + fun testSignInWithGoogleCredential() { + val testIdToken = "test-id-token" + val credential = SignInWithGoogleCredential(testIdToken) + assertEquals(FederatedSignInProviderName.GOOGLE, credential.federatedSignInProviderName) + assertEquals(testIdToken, credential.idToken) + + val headers = credential.asHeaders(); + assertEquals(1, headers.size) + assertEquals(testIdToken, headers[MsaFederationConstants.MSA_ID_TOKEN_HEADER_KEY]) + } +} diff --git a/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleParametersTest.kt b/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleParametersTest.kt new file mode 100644 index 0000000000..127a42a2bd --- /dev/null +++ b/common/src/test/java/com/microsoft/identity/common/internal/msafederation/google/SignInWithGoogleParametersTest.kt @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.msafederation.google + +import android.app.Activity +import com.microsoft.identity.common.internal.msafederation.FederatedSignInProviderName +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.Robolectric +import org.robolectric.RobolectricTestRunner + +/** + * Tests for [SignInWithGoogleParameters]. + */ +@RunWith(RobolectricTestRunner::class) +class SignInWithGoogleParametersTest { + + @Test + fun testSignInWithGoogleParameters() { + val activity = Robolectric.buildActivity(Activity::class.java).get() + val signInWithGoogleParameters = SignInWithGoogleParameters(activity) + assertEquals(FederatedSignInProviderName.GOOGLE, signInWithGoogleParameters.providerName) + assertEquals(activity, signInWithGoogleParameters.activity) + assertFalse(signInWithGoogleParameters.useBottomSheet) + } + + @Test + fun testSignInWithGoogleParametersUseBottomSheet() { + val activity = Robolectric.buildActivity(Activity::class.java).get() + val signInWithGoogleParameters = SignInWithGoogleParameters(activity, true) + assertEquals(FederatedSignInProviderName.GOOGLE, signInWithGoogleParameters.providerName) + assertEquals(activity, signInWithGoogleParameters.activity) + assertTrue(signInWithGoogleParameters.useBottomSheet) + } +} diff --git a/common/src/test/java/com/microsoft/identity/common/internal/providers/oauth2/AuthorizationActivityFactoryTest.java b/common/src/test/java/com/microsoft/identity/common/internal/providers/oauth2/AuthorizationActivityFactoryTest.java new file mode 100644 index 0000000000..6b4a95714a --- /dev/null +++ b/common/src/test/java/com/microsoft/identity/common/internal/providers/oauth2/AuthorizationActivityFactoryTest.java @@ -0,0 +1,108 @@ +// Copyright (c) Microsoft Corporation. +// All rights reserved. +// +// This code is licensed under the MIT License. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.microsoft.identity.common.internal.providers.oauth2; + +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.AUTHORIZATION_AGENT; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.AUTH_INTENT; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.REDIRECT_URI; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.REQUEST_HEADERS; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.REQUEST_URL; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.WEB_VIEW_ZOOM_CONTROLS_ENABLED; +import static com.microsoft.identity.common.adal.internal.AuthenticationConstants.AuthorizationIntentKey.WEB_VIEW_ZOOM_ENABLED; +import static com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.PRODUCT; +import static com.microsoft.identity.common.java.AuthenticationConstants.SdkPlatformFields.VERSION; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.content.Context; +import android.content.Intent; + +import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleCredential; +import com.microsoft.identity.common.java.ui.AuthorizationAgent; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +import java.util.HashMap; + +import lombok.SneakyThrows; + +/** + * Tests for @link{AuthorizationActivityFactory}. + */ +@RunWith(RobolectricTestRunner.class) +public class AuthorizationActivityFactoryTest { + + @SneakyThrows + @Test + public void testGetAuthorizationActivityIntent() { + // 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 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 SignInWithGoogleCredential signInWithGoogleCredential = new SignInWithGoogleCredential(idToken); + + final Intent resultIntent = AuthorizationActivityFactory.getAuthorizationActivityIntent( + context, + authIntent, + requestUrl, + redirectUri, + requestHeaders, + authorizationAgent, + webViewZoomEnabled, + webViewZoomControlsEnabled, + sourceLibraryName, + sourceLibraryVersion, + signInWithGoogleCredential + ); + + 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 receivedHeaders = (HashMap) resultIntent.getSerializableExtra(REQUEST_HEADERS); + final String idTokenHeaderValue = receivedHeaders.get("x-ms-fidp-idtoken"); + assertNotNull(idTokenHeaderValue); + assertEquals(idToken, idTokenHeaderValue); + } +} diff --git a/common4j/src/main/com/microsoft/identity/common/java/exception/ClientException.java b/common4j/src/main/com/microsoft/identity/common/java/exception/ClientException.java index 746b737fb6..2c85974fc9 100644 --- a/common4j/src/main/com/microsoft/identity/common/java/exception/ClientException.java +++ b/common4j/src/main/com/microsoft/identity/common/java/exception/ClientException.java @@ -486,6 +486,11 @@ public class ClientException extends BaseException { */ public static final String KEY_LOAD_FAILURE = "key_load_failure"; + /** + * Error occurred while getting credential in sign in with google flow. + */ + public static final String SIGN_IN_WITH_GOOGLE_FAILED = "sign_in_with_google_failed"; + /** * Constructor of ClientException. * diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 515e0f9501..3922c73ecc 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -80,6 +80,7 @@ ext { lifecycleKtxVersion="2.5.1" AndroidCredentialsVersion="1.2.2" LegacyFidoApiVersion="20.1.0" + GoogleIdVersion="1.1.0" // microsoft-diagnostics-uploader app versions powerliftVersion = "0.14.7"