diff --git a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AppDistributionEnvironment.kt b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AppDistributionEnvironment.kt index 9f8ef18299e..bcb728a0e74 100644 --- a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AppDistributionEnvironment.kt +++ b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AppDistributionEnvironment.kt @@ -16,11 +16,10 @@ package com.google.firebase.appdistribution.gradle -import com.google.api.client.googleapis.auth.oauth2.GoogleCredential -import com.google.api.client.http.HttpTransport +import com.google.auth.http.HttpCredentialsAdapter interface AppDistributionEnvironment { - fun getFirebaseCliLoginCredentials(transport: HttpTransport): GoogleCredential? + fun getFirebaseCliLoginCredentials(): HttpCredentialsAdapter? companion object { const val ENV_FIREBASE_TOKEN = "FIREBASE_TOKEN" diff --git a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AppDistributionEnvironmentImpl.kt b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AppDistributionEnvironmentImpl.kt index c09bd60dbcb..5354013a3bf 100644 --- a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AppDistributionEnvironmentImpl.kt +++ b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AppDistributionEnvironmentImpl.kt @@ -16,8 +16,7 @@ package com.google.firebase.appdistribution.gradle -import com.google.api.client.googleapis.auth.oauth2.GoogleCredential -import com.google.api.client.http.HttpTransport +import com.google.auth.http.HttpCredentialsAdapter import com.google.firebase.appdistribution.gradle.models.FirebaseCliConfig import com.google.gson.FieldNamingPolicy import com.google.gson.GsonBuilder @@ -28,7 +27,7 @@ import java.nio.file.Paths import org.gradle.api.logging.Logging class AppDistributionEnvironmentImpl : AppDistributionEnvironment { - override fun getFirebaseCliLoginCredentials(transport: HttpTransport): GoogleCredential? { + override fun getFirebaseCliLoginCredentials(): HttpCredentialsAdapter? { val gson = GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create() @@ -46,7 +45,7 @@ class AppDistributionEnvironmentImpl : AppDistributionEnvironment { // Step 3: Generate new credential using refresh token if (config?.tokens?.refreshToken != null) { - val refreshToken = RefreshToken(config.tokens.refreshToken, transport) + val refreshToken = RefreshToken(config.tokens.refreshToken) return refreshToken.generateNewCredentials() } } catch (e: IOException) { diff --git a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AuthenticatedHttpClient.kt b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AuthenticatedHttpClient.kt index 3082ac8a4b8..1e680ce71a1 100644 --- a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AuthenticatedHttpClient.kt +++ b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/AuthenticatedHttpClient.kt @@ -16,11 +16,11 @@ package com.google.firebase.appdistribution.gradle -import com.google.api.client.auth.oauth2.Credential import com.google.api.client.googleapis.GoogleUtils import com.google.api.client.http.GenericUrl import com.google.api.client.http.HttpContent import com.google.api.client.http.HttpRequestFactory +import com.google.api.client.http.HttpRequestInitializer import com.google.api.client.http.HttpTransport import com.google.api.client.http.javanet.NetHttpTransport import java.io.IOException @@ -34,8 +34,8 @@ private constructor( private val httpRequestFactory: HttpRequestFactory, ) { constructor( - credential: Credential - ) : this(newGoogleHttpTransport().createRequestFactory(credential)) + requestInitializer: HttpRequestInitializer + ) : this(newGoogleHttpTransport().createRequestFactory(requestInitializer)) constructor(httpTransport: HttpTransport) : this(httpTransport.createRequestFactory()) diff --git a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/CredentialsRetriever.kt b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/CredentialsRetriever.kt index 90c762f1a1d..5f67f1741e3 100644 --- a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/CredentialsRetriever.kt +++ b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/CredentialsRetriever.kt @@ -16,37 +16,32 @@ package com.google.firebase.appdistribution.gradle -import com.google.api.client.auth.oauth2.Credential -import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport -import com.google.api.client.http.HttpTransport import com.google.firebase.appdistribution.gradle.AppDistributionEnvironment.Companion.ENV_FIREBASE_TOKEN import com.google.firebase.appdistribution.gradle.AppDistributionEnvironment.Companion.ENV_GOOGLE_APPLICATION_CREDENTIALS import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.REFRESH_TOKEN_ERROR import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.SERVICE_CREDENTIALS_NOT_FOUND import com.google.firebase.appdistribution.gradle.OptionsUtils.ensureFileExists import com.google.firebase.appdistribution.gradle.models.ServiceAccountCredentials +import com.google.auth.http.HttpCredentialsAdapter +import com.google.auth.oauth2.GoogleCredentials import java.io.IOException import org.gradle.api.logging.Logging /** Helper class for retrieving auth credentials from one of the four possible auth mechanisms. */ class CredentialsRetriever( - private val httpTransport: HttpTransport = GoogleNetHttpTransport.newTrustedTransport(), private val appDistributionEnvironment: AppDistributionEnvironment = - AppDistributionEnvironmentImpl() + AppDistributionEnvironmentImpl(), + private val adcCredentialsProvider: () -> GoogleCredentials = { GoogleCredentials.getApplicationDefault() } ) { - internal constructor( - appDistributionEnvironment: AppDistributionEnvironment - ) : this(GoogleNetHttpTransport.newTrustedTransport(), appDistributionEnvironment) - /** Returns the auth credential, if found. Otherwise returns null. */ - fun getAuthCredential(serviceCredentialsPath: String? = null): Credential? { + fun getAuthCredential(serviceCredentialsPath: String? = null): HttpCredentialsAdapter? { // Check the explicitly passed in service credentials first if (serviceCredentialsPath != null) { val serviceCredentialsFile = ensureFileExists(serviceCredentialsPath, SERVICE_CREDENTIALS_NOT_FOUND) return try { - ServiceAccountCredentials.fromFile(serviceCredentialsFile).googleCredential + ServiceAccountCredentials.fromFile(serviceCredentialsFile).credentialsAdapter } catch (e: IOException) { throw AppDistributionException( SERVICE_CREDENTIALS_NOT_FOUND, @@ -64,7 +59,7 @@ class CredentialsRetriever( "Using credentials token specified by environment variable {}", ENV_FIREBASE_TOKEN ) - RefreshToken(envRefreshToken, httpTransport).generateNewCredentials() + RefreshToken(envRefreshToken).generateNewCredentials() } catch (e: Exception) { throw AppDistributionException( REFRESH_TOKEN_ERROR, @@ -75,8 +70,7 @@ class CredentialsRetriever( } // Then check for cached Firebase CLI tokens - val firebaseCliLoginCreds = - appDistributionEnvironment.getFirebaseCliLoginCredentials(httpTransport) + val firebaseCliLoginCreds = appDistributionEnvironment.getFirebaseCliLoginCredentials() if (firebaseCliLoginCreds != null) { logger.info("Using cached Firebase CLI credentials") return firebaseCliLoginCreds @@ -95,7 +89,7 @@ class CredentialsRetriever( return try { val envServiceAccountCredentials = ServiceAccountCredentials.fromFile(serviceCredentialsFile) - envServiceAccountCredentials.googleCredential + envServiceAccountCredentials.credentialsAdapter } catch (e: IOException) { throw AppDistributionException( SERVICE_CREDENTIALS_NOT_FOUND, @@ -105,6 +99,15 @@ class CredentialsRetriever( } } + // Lastly, check for standard Application Default Credentials (ADC) as a fallback + try { + val credentials = adcCredentialsProvider().createScoped(ApiEndpoints.SCOPES) + logger.info("Using Application Default Credentials (ADC)") + return HttpCredentialsAdapter(credentials) + } catch (e: IOException) { + logger.debug("Failed to load Application Default Credentials (ADC)", e) + } + // If we reach this point, we were unable to find valid credentials return null } diff --git a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/FirebaseAppDistribution.kt b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/FirebaseAppDistribution.kt index 58c2d75d1d3..32a0b207c44 100644 --- a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/FirebaseAppDistribution.kt +++ b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/FirebaseAppDistribution.kt @@ -16,7 +16,7 @@ package com.google.firebase.appdistribution.gradle -import com.google.api.client.auth.oauth2.Credential +import com.google.auth.http.HttpCredentialsAdapter import com.google.api.client.http.HttpResponseException import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.MISSING_CREDENTIALS import java.io.IOException @@ -76,9 +76,11 @@ object FirebaseAppDistribution { private fun getCredential(options: TesterManagementOptions) = options.credential ?: throw AppDistributionException(MISSING_CREDENTIALS) - private fun getAuthenticatedHttpClient(credential: Credential): AuthenticatedHttpClient { + private fun getAuthenticatedHttpClient( + credentialsAdapter: HttpCredentialsAdapter + ): AuthenticatedHttpClient { return try { - AuthenticatedHttpClient(credential) + AuthenticatedHttpClient(credentialsAdapter) } catch (e: Exception) { when (e) { is GeneralSecurityException, diff --git a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/RefreshToken.kt b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/RefreshToken.kt index 25a7accc824..df96c2c7647 100644 --- a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/RefreshToken.kt +++ b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/RefreshToken.kt @@ -16,41 +16,29 @@ package com.google.firebase.appdistribution.gradle -import com.google.api.client.googleapis.auth.oauth2.GoogleCredential -import com.google.api.client.googleapis.auth.oauth2.GoogleRefreshTokenRequest -import com.google.api.client.http.HttpTransport -import com.google.api.client.json.gson.GsonFactory -import java.io.IOException +import com.google.auth.http.HttpCredentialsAdapter +import com.google.auth.oauth2.UserCredentials /** - * This class encapsulates a refresh token that can be used for generate a new [GoogleCredential] to - * be used when authenticating requests. By using the refresh token the is returned after the oauth - * flow, we can generate new access tokens for future requests. + * This class encapsulates a refresh token that can be used for generate a new + * [HttpCredentialsAdapter] to be used when authenticating requests. By using the refresh token that + * is returned after the oauth flow, we can generate new access tokens for future requests. */ -class RefreshToken(private val refreshToken: String, private val transport: HttpTransport) { +class RefreshToken(private val refreshToken: String) { /** * Using the provided refresh token, this makes a request to generate a new access token that can * be used to authenticate requests * - * @return the credentials that were generated from the refresh token. Its worth noting that these - * credentials do not include the refresh token, only a new access token - * @throws IOException thrown if there was a problem making a request to the oauth provider + * @return the credentials that were generated from the refresh token. */ - fun generateNewCredentials(): GoogleCredential { - val response = - GoogleRefreshTokenRequest( - transport, - GsonFactory.getDefaultInstance(), - refreshToken, - CLIENT_ID, - CLIENT_SECRET - ) - .execute() - - val credential = GoogleCredential.Builder().build() - credential.setFromTokenResponse(response) - - return credential + fun generateNewCredentials(): HttpCredentialsAdapter { + return HttpCredentialsAdapter( + UserCredentials.newBuilder() + .setClientId(CLIENT_ID) + .setClientSecret(CLIENT_SECRET) + .setRefreshToken(refreshToken) + .build() + ) } companion object { @@ -58,8 +46,8 @@ class RefreshToken(private val refreshToken: String, private val transport: Http // In this type of application, the client secret is not treated as a secret. // See: https://developers.google.com/identity/protocols/OAuth2InstalledApp // TODO: Use separate ID's/secrets for each tool - private const val CLIENT_ID = + internal const val CLIENT_ID = "563584335869-fgrhgmd47bqnekij5i8b5pr03ho849e6.apps.googleusercontent.com" - private const val CLIENT_SECRET = "j9iVZfS8kkCEFUPaAeJV0sAi" + internal const val CLIENT_SECRET = "j9iVZfS8kkCEFUPaAeJV0sAi" } } diff --git a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/TesterManagementOptions.kt b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/TesterManagementOptions.kt index 5efee9ea878..6e84ecbe1cc 100644 --- a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/TesterManagementOptions.kt +++ b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/TesterManagementOptions.kt @@ -15,7 +15,7 @@ */ package com.google.firebase.appdistribution.gradle -import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport +import com.google.auth.http.HttpCredentialsAdapter import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.MISSING_PROJECT_NUMBER import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.MISSING_TESTER_EMAILS import com.google.firebase.appdistribution.gradle.OptionsUtils.getValueFromStringOrFile @@ -27,14 +27,11 @@ class TesterManagementOptions( emailsValue: String? = null, emailsFile: String? = null, serviceCredentialsFile: String? = null, - credentialsRetriever: CredentialsRetriever = - CredentialsRetriever( - GoogleNetHttpTransport.newTrustedTransport(), - AppDistributionEnvironmentImpl() - ) + credentialsRetriever: CredentialsRetriever = CredentialsRetriever() ) { val emails = splitCommaOrNewlineSeparatedString(getValueFromStringOrFile(emailsValue, emailsFile)) - val credential = credentialsRetriever.getAuthCredential(serviceCredentialsFile) + val credential: HttpCredentialsAdapter? = + credentialsRetriever.getAuthCredential(serviceCredentialsFile) init { // If project number is set it would be greater than 0, the default primitive value diff --git a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/UploadDistributionOptions.kt b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/UploadDistributionOptions.kt index b448975f83d..6a3dc65bd1c 100644 --- a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/UploadDistributionOptions.kt +++ b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/UploadDistributionOptions.kt @@ -16,9 +16,7 @@ package com.google.firebase.appdistribution.gradle -import com.google.api.client.auth.oauth2.Credential -import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport -import com.google.api.client.http.HttpTransport +import com.google.auth.http.HttpCredentialsAdapter import com.google.common.base.Splitter import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.Companion.binaryNotFoundError import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.INVALID_APP_ID @@ -47,7 +45,6 @@ internal constructor( val debug: Boolean = false, val testNonBlocking: Boolean = false, binaryPath: String, - transport: HttpTransport = GoogleNetHttpTransport.newTrustedTransport(), appDistributionEnvironment: AppDistributionEnvironment = AppDistributionEnvironmentImpl(), serviceCredentialsFile: String? = null, releaseNotesValue: String? = null, @@ -71,8 +68,8 @@ internal constructor( val testers = extractValues(testersValue, testersPath) val groups = extractValues(groupsValue, groupsPath) val testDevices = extractTestDevices(testDevicesValue, testDevicesPath) - val credential: Credential? = - CredentialsRetriever(transport, appDistributionEnvironment) + val credential: HttpCredentialsAdapter? = + CredentialsRetriever(appDistributionEnvironment) .getAuthCredential(serviceCredentialsFile) val testCases = extractValues(testCasesValue, testCasesPath) diff --git a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/models/ServiceAccountCredentials.kt b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/models/ServiceAccountCredentials.kt index f057e146456..f3a4ffab45a 100644 --- a/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/models/ServiceAccountCredentials.kt +++ b/firebase-appdistribution-gradle/src/main/java/com/google/firebase/appdistribution/gradle/models/ServiceAccountCredentials.kt @@ -16,17 +16,21 @@ package com.google.firebase.appdistribution.gradle.models -import com.google.api.client.googleapis.auth.oauth2.GoogleCredential +import com.google.auth.http.HttpCredentialsAdapter +import com.google.auth.oauth2.GoogleCredentials import com.google.firebase.appdistribution.gradle.ApiEndpoints import java.io.File import java.nio.file.Files -class ServiceAccountCredentials private constructor(val googleCredential: GoogleCredential) { +class ServiceAccountCredentials +private constructor(val credentialsAdapter: HttpCredentialsAdapter) { companion object { fun fromFile(credentials: File): ServiceAccountCredentials = ServiceAccountCredentials( - GoogleCredential.fromStream(Files.newInputStream(credentials.toPath())) - .createScoped(ApiEndpoints.SCOPES) + HttpCredentialsAdapter( + GoogleCredentials.fromStream(Files.newInputStream(credentials.toPath())) + .createScoped(ApiEndpoints.SCOPES) + ) ) } } diff --git a/firebase-appdistribution-gradle/src/test/fixtures/test-wif-credentials.json b/firebase-appdistribution-gradle/src/test/fixtures/test-wif-credentials.json new file mode 100644 index 00000000000..e755fc09e7f --- /dev/null +++ b/firebase-appdistribution-gradle/src/test/fixtures/test-wif-credentials.json @@ -0,0 +1,9 @@ +{ + "type": "external_account", + "audience": "//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/my-pool/providers/my-provider", + "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", + "token_url": "https://sts.googleapis.com/v1/token", + "credential_source": { + "file": "/var/run/secrets/token" + } +} diff --git a/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/CredentialsRetrieverTest.kt b/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/CredentialsRetrieverTest.kt index f1f04584a49..bdd0a5f1733 100644 --- a/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/CredentialsRetrieverTest.kt +++ b/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/CredentialsRetrieverTest.kt @@ -16,18 +16,20 @@ package com.google.firebase.appdistribution.gradle -import com.google.api.client.googleapis.auth.oauth2.GoogleCredential -import com.google.api.client.http.HttpTransport +import com.google.auth.http.HttpCredentialsAdapter +import com.google.auth.oauth2.ExternalAccountCredentials +import com.google.auth.oauth2.GoogleCredentials +import com.google.auth.oauth2.ServiceAccountCredentials as GoogleServiceAccountCredentials +import com.google.auth.oauth2.UserCredentials +import java.io.IOException import junit.framework.TestCase.assertEquals import junit.framework.TestCase.assertNull -import kotlin.test.assertContains import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.contrib.java.lang.system.EnvironmentVariables import org.junit.runner.RunWith import org.mockito.junit.MockitoJUnitRunner -import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @@ -42,44 +44,61 @@ class CredentialsRetrieverTest { "XDG_CONFIG_HOME", AppDistributionEnvironment.ENV_GOOGLE_APPLICATION_CREDENTIALS ) + // Disable automatic Google Cloud GCE metadata server detection in unit tests + environmentVariables.set("NO_GCE_CHECK", "true") + // Override Google Cloud SDK configuration directory to a non-existent path + // to prevent the library from naturally loading active local user credentials (via gcloud auth) + environmentVariables.set("CLOUDSDK_CONFIG", "/non-existent-path") } @Test fun testGetAuthCredential_usesServiceCredentialsJsonPassedIn() { val credentialsRetriever = CredentialsRetriever() - val credential = getAuthCredential(credentialsRetriever, TEST_SERVICE_CREDENTIALS_PATH) + val credential = + credentialsRetriever.getAuthCredential(TEST_SERVICE_CREDENTIALS_PATH)!! + val googleCreds = credential.credentials as GoogleServiceAccountCredentials - assertEquals( - "firebase-adminsdk@my-project.iam.gserviceaccount.com", - credential.serviceAccountId - ) - assertEquals("abcefg123456789", credential.serviceAccountPrivateKeyId) + assertEquals("firebase-adminsdk@my-project.iam.gserviceaccount.com", googleCreds.clientEmail) + assertEquals("abcefg123456789", googleCreds.privateKeyId) + } + + @Test + fun testGetAuthCredential_usesWifCredentialsJsonPassedIn() { + val credentialsRetriever = CredentialsRetriever() + + val credential = + credentialsRetriever.getAuthCredential(TEST_WIF_CREDENTIALS_PATH)!! + val googleCreds = credential.credentials + + assert(googleCreds is ExternalAccountCredentials) } @Test fun testGetAuthCredential_usesEnvFirebaseToken() { val expectedRefreshToken = "other-token" - val transport: HttpTransport = - MockVerifyRefreshToken(expectedRefreshToken, "{\"access_token\":\"some-access-token\"}") environmentVariables[AppDistributionEnvironment.ENV_FIREBASE_TOKEN] = expectedRefreshToken - val credentialsRetriever = CredentialsRetriever(transport) + val credentialsRetriever = CredentialsRetriever() - val credential = getAuthCredential(credentialsRetriever) + val credential = credentialsRetriever.getAuthCredential()!! + val userCreds = credential.credentials as UserCredentials - assertEquals("some-access-token", credential.accessToken) + assertEquals(expectedRefreshToken, userCreds.refreshToken) + assertEquals(RefreshToken.CLIENT_ID, userCreds.clientId) + assertEquals(RefreshToken.CLIENT_SECRET, userCreds.clientSecret) } @Test fun testGetAuthCredential_usesCachedFirebaseCliToken() { - val transport: HttpTransport = - MockVerifyRefreshToken("fake-refresh-token", "{\"access_token\":\"some-access-token\"}") environmentVariables["XDG_CONFIG_HOME"] = "src/test/fixtures" - val credentialsRetriever = CredentialsRetriever(transport) + val credentialsRetriever = CredentialsRetriever() - val credential = getAuthCredential(credentialsRetriever) + val credential = credentialsRetriever.getAuthCredential()!! + val userCreds = credential.credentials as UserCredentials - assertContains("some-access-token", credential.accessToken) + assertEquals("fake-refresh-token", userCreds.refreshToken) + assertEquals(RefreshToken.CLIENT_ID, userCreds.clientId) + assertEquals(RefreshToken.CLIENT_SECRET, userCreds.clientSecret) } @Test @@ -88,18 +107,16 @@ class CredentialsRetrieverTest { // Mock out the Firebase CLI login credentials and force it to return an empty optional. This // makes sure the test environment is set up correctly and doesn't fail erroneously on dev // machines that have cached CLI credentials. - whenever(testEnvironment.getFirebaseCliLoginCredentials(any())).thenReturn(null) + whenever(testEnvironment.getFirebaseCliLoginCredentials()).thenReturn(null) environmentVariables[AppDistributionEnvironment.ENV_GOOGLE_APPLICATION_CREDENTIALS] = TEST_SERVICE_CREDENTIALS_PATH val credentialsRetriever = CredentialsRetriever(testEnvironment) - val credential = getAuthCredential(credentialsRetriever) + val credential = credentialsRetriever.getAuthCredential()!! + val googleCreds = credential.credentials as GoogleServiceAccountCredentials - assertEquals( - "firebase-adminsdk@my-project.iam.gserviceaccount.com", - credential.serviceAccountId - ) - assertEquals("abcefg123456789", credential.serviceAccountPrivateKeyId) + assertEquals("firebase-adminsdk@my-project.iam.gserviceaccount.com", googleCreds.clientEmail) + assertEquals("abcefg123456789", googleCreds.privateKeyId) } @Test @@ -114,23 +131,17 @@ class CredentialsRetrieverTest { fun testGetAuthCredential_prefersServiceCredentialsJsonOverEnvFirebaseToken() { // Configure mock firebase token val expectedRefreshToken = "other-token" - val transport: HttpTransport = - MockVerifyRefreshToken(expectedRefreshToken, "{\"access_token\":\"some-access-token\"}") environmentVariables[AppDistributionEnvironment.ENV_FIREBASE_TOKEN] = expectedRefreshToken - val credentialsRetriever = CredentialsRetriever(transport) + val credentialsRetriever = CredentialsRetriever() // Also pass in service credentials json directly - val credential = getAuthCredential(credentialsRetriever, TEST_SERVICE_CREDENTIALS_PATH) + val credential = + credentialsRetriever.getAuthCredential(TEST_SERVICE_CREDENTIALS_PATH)!! + val googleCreds = credential.credentials as GoogleServiceAccountCredentials // Verify service credentials auth is configured - assertEquals( - "firebase-adminsdk@my-project.iam.gserviceaccount.com", - credential.serviceAccountId - ) - assertEquals("abcefg123456789", credential.serviceAccountPrivateKeyId) - - // Verify the firebase refresh token was not used - assertNull(credential.accessToken) + assertEquals("firebase-adminsdk@my-project.iam.gserviceaccount.com", googleCreds.clientEmail) + assertEquals("abcefg123456789", googleCreds.privateKeyId) } @Test @@ -139,45 +150,52 @@ class CredentialsRetrieverTest { environmentVariables[AppDistributionEnvironment.ENV_FIREBASE_TOKEN] = "env-firebase-token" // Also set the cached Firebase CLI token, which is "fake-refresh-token" environmentVariables["XDG_CONFIG_HOME"] = "src/test/fixtures" - // Configure transport for the env firebase token option - val transport: HttpTransport = - MockVerifyRefreshToken( - "env-firebase-token", - "{\"access_token\":\"access-token-for-env-firebase-token\"}" - ) - val credentialsRetriever = CredentialsRetriever(transport) + val credentialsRetriever = CredentialsRetriever() - val credential = getAuthCredential(credentialsRetriever) + val credential = credentialsRetriever.getAuthCredential()!! + val userCreds = credential.credentials as UserCredentials // Verify access token is the one set as the env firebase token - assertEquals("access-token-for-env-firebase-token", credential.accessToken) + assertEquals("env-firebase-token", userCreds.refreshToken) } @Test fun testGetAuthCredential_prefersCachedFirebaseCliTokenOverApplicationDefaultCredentials() { // Configure cached Firebase CLI token - val transport: HttpTransport = - MockVerifyRefreshToken("fake-refresh-token", "{\"access_token\":\"some-access-token\"}") environmentVariables["XDG_CONFIG_HOME"] = "src/test/fixtures" - val credentialsRetriever = CredentialsRetriever(transport) // Also configure the application default credentials environmentVariables[AppDistributionEnvironment.ENV_GOOGLE_APPLICATION_CREDENTIALS] = TEST_SERVICE_CREDENTIALS_PATH + val credentialsRetriever = CredentialsRetriever() - val credential = getAuthCredential(credentialsRetriever) + val credential = credentialsRetriever.getAuthCredential()!! + val userCreds = credential.credentials as UserCredentials // Verify Firebase CLI token is used, which also verifies that default credentials aren't used - // since it's not of type ServiceAccountCredentials - assertEquals("some-access-token", credential.accessToken) + assertEquals("fake-refresh-token", userCreds.refreshToken) } - private fun getAuthCredential( - credentialsRetriever: CredentialsRetriever, - serviceCredentialsPath: String? = null - ) = credentialsRetriever.getAuthCredential(serviceCredentialsPath) as GoogleCredential + @Test + fun testGetAuthCredential_usesApplicationDefaultCredentialsFallback() { + val testEnvironment: AppDistributionEnvironment = mock() + whenever(testEnvironment.getFirebaseCliLoginCredentials()).thenReturn(null) + + val mockAdcCredentials: GoogleCredentials = mock() + whenever(mockAdcCredentials.createScoped(ApiEndpoints.SCOPES)).thenReturn(mockAdcCredentials) + + val credentialsRetriever = CredentialsRetriever( + appDistributionEnvironment = testEnvironment, + adcCredentialsProvider = { mockAdcCredentials } + ) + + val credential = credentialsRetriever.getAuthCredential()!! + assertEquals(mockAdcCredentials, credential.credentials) + } companion object { private val TEST_SERVICE_CREDENTIALS_PATH = FixtureUtils.getFixtureAsFile("test-service-credentials.json").absolutePath + private val TEST_WIF_CREDENTIALS_PATH = + FixtureUtils.getFixtureAsFile("test-wif-credentials.json").absolutePath } } diff --git a/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/RefreshTokenTest.kt b/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/RefreshTokenTest.kt index 3f5fae86032..e4c4dc1898c 100644 --- a/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/RefreshTokenTest.kt +++ b/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/RefreshTokenTest.kt @@ -16,22 +16,22 @@ package com.google.firebase.appdistribution.gradle -import com.google.api.client.http.HttpTransport +import com.google.auth.oauth2.UserCredentials import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Test class RefreshTokenTest { - private var httpTransport: HttpTransport = - SuccessWithContent("{\"access_token\":\"access-token\"}") - @Test fun testGenerateCredentialFrom() { val refreshToken = "test-fresh-token" - val expectedAccessToken = "access-token" - val token = RefreshToken(refreshToken, httpTransport) + val token = RefreshToken(refreshToken) val credentials = token.generateNewCredentials() assertNotNull(credentials) - assertEquals(expectedAccessToken, credentials.accessToken) + + val userCreds = credentials.credentials as UserCredentials + assertEquals(refreshToken, userCreds.refreshToken) + assertEquals(RefreshToken.CLIENT_ID, userCreds.clientId) + assertEquals(RefreshToken.CLIENT_SECRET, userCreds.clientSecret) } } diff --git a/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/TesterManagementOptionsTest.kt b/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/TesterManagementOptionsTest.kt index f292bc8b2bb..1f900a059a3 100644 --- a/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/TesterManagementOptionsTest.kt +++ b/firebase-appdistribution-gradle/src/test/java/com/google/firebase/appdistribution/gradle/TesterManagementOptionsTest.kt @@ -15,7 +15,7 @@ */ package com.google.firebase.appdistribution.gradle -import com.google.api.client.auth.oauth2.Credential +import com.google.auth.http.HttpCredentialsAdapter import com.google.common.collect.ImmutableList import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.MISSING_PROJECT_NUMBER import com.google.firebase.appdistribution.gradle.AppDistributionException.Reason.MISSING_TESTER_EMAILS @@ -30,7 +30,7 @@ import org.mockito.kotlin.whenever @RunWith(MockitoJUnitRunner::class) class TesterManagementOptionsTest { private val mockCredentialsRetriever: CredentialsRetriever = mock() - private val mockCredential: Credential = mock() + private val mockCredential: HttpCredentialsAdapter = mock() @Test fun testValidateAndBuild_successfullyBuildsOptions() {