scopes,
+ @Nullable EnvironmentProvider environmentProvider) {
+ super(
+ transportFactory,
+ audience,
+ subjectTokenType,
+ tokenUrl,
+ credentialSource,
+ tokenInfoUrl,
+ serviceAccountImpersonationUrl,
+ quotaProjectId,
+ clientId,
+ clientSecret,
+ scopes,
+ environmentProvider);
+ }
+
@Override
public AccessToken refreshAccessToken() {
return new AccessToken("accessToken", new Date());
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java
new file mode 100644
index 000000000..52b6ca269
--- /dev/null
+++ b/oauth2_http/javatests/com/google/auth/oauth2/ITWorkloadIdentityFederationTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * * Neither the name of Google LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.google.auth.oauth2;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.api.client.http.GenericUrl;
+import com.google.api.client.http.HttpRequest;
+import com.google.api.client.http.HttpRequestFactory;
+import com.google.api.client.http.HttpResponse;
+import com.google.api.client.http.UrlEncodedContent;
+import com.google.api.client.http.javanet.NetHttpTransport;
+import com.google.api.client.json.GenericJson;
+import com.google.api.client.json.JsonFactory;
+import com.google.api.client.json.JsonObjectParser;
+import com.google.api.client.json.gson.GsonFactory;
+import com.google.api.client.util.GenericData;
+import com.google.auth.http.HttpCredentialsAdapter;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Integration tests for Workload Identity Federation.
+ *
+ * The only requirements for this test suite to run is to set the environment variable
+ * GOOGLE_APPLICATION_CREDENTIALS to point to the same service account keys used in the setup script
+ * (workloadidentityfederation-setup). These tests call GCS to get bucket information. The bucket
+ * name must be provided through the GCS_BUCKET environment variable.
+ */
+public final class ITWorkloadIdentityFederationTest {
+
+ // Copy output from workloadidentityfederation-setup.
+ private static final String AUDIENCE_PREFIX =
+ "//iam.googleapis.com/projects/1016721519174/locations/global/workloadIdentityPools/pool-1/providers/";
+ private static final String AWS_ROLE_NAME = "ci-java-test";
+ private static final String AWS_ROLE_ARN = "arn:aws:iam::027472800722:role/ci-java-test";
+
+ private static final String AWS_AUDIENCE = AUDIENCE_PREFIX + "aws-1";
+ private static final String OIDC_AUDIENCE = AUDIENCE_PREFIX + "oidc-1";
+
+ private String clientEmail;
+
+ @Before
+ public void setup() throws IOException {
+ GenericJson keys = getServiceAccountKeyFileAsJson();
+ clientEmail = (String) keys.get("client_email");
+ }
+
+ /**
+ * IdentityPoolCredentials (OIDC provider): Uses the service account to generate a Google ID token
+ * using the iamcredentials generateIdToken API. This will use the service account client ID as
+ * the sub field of the token. This OIDC token will be used as the external subject token to be
+ * exchanged for a GCP access token via GCP STS endpoint and then to impersonate the original
+ * service account key.
+ */
+ @Test
+ public void identityPoolCredentials() throws IOException {
+ IdentityPoolCredentials identityPoolCredentials =
+ (IdentityPoolCredentials)
+ ExternalAccountCredentials.fromJson(
+ buildIdentityPoolCredentialConfig(), OAuth2Utils.HTTP_TRANSPORT_FACTORY);
+
+ callGcs(identityPoolCredentials);
+ }
+
+ /**
+ * AwsCredentials (AWS provider): Uses the service account keys to generate a Google ID token
+ * using the iamcredentials generateIdToken API. Exchanges the OIDC ID token for AWS security keys
+ * using AWS STS AssumeRoleWithWebIdentity API. These values will be set as AWS environment
+ * variables to simulate an AWS VM. The Auth library can now read these variables and create a
+ * signed request to AWS GetCallerIdentity. This will be used as the external subject token to be
+ * exchanged for a GCP access token via GCP STS endpoint and then to impersonate the original
+ * service account key.
+ */
+ @Test
+ public void awsCredentials() throws Exception {
+ String idToken = generateGoogleIdToken(AWS_AUDIENCE);
+
+ String url =
+ String.format(
+ "https://sts.amazonaws.com/?Action=AssumeRoleWithWebIdentity"
+ + "&Version=2011-06-15&DurationSeconds=3600&RoleSessionName=%s"
+ + "&RoleArn=%s&WebIdentityToken=%s",
+ AWS_ROLE_NAME, AWS_ROLE_ARN, idToken);
+
+ HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory();
+ HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(url));
+
+ JsonObjectParser parser = new JsonObjectParser(GsonFactory.getDefaultInstance());
+ request.setParser(parser);
+
+ HttpResponse response = request.execute();
+ String rawXml = response.parseAsString();
+
+ String awsAccessKeyId = getXmlValueByTagName(rawXml, "AccessKeyId");
+ String awsSecretAccessKey = getXmlValueByTagName(rawXml, "SecretAccessKey");
+ String awsSessionToken = getXmlValueByTagName(rawXml, "SessionToken");
+
+ AwsCredentials awsCredentialWithoutEnvProvider =
+ (AwsCredentials)
+ AwsCredentials.fromJson(buildAwsCredentialConfig(), OAuth2Utils.HTTP_TRANSPORT_FACTORY);
+ TestEnvironmentProvider testEnvironmentProvider = new TestEnvironmentProvider();
+ testEnvironmentProvider
+ .setEnv("AWS_ACCESS_KEY_ID", awsAccessKeyId)
+ .setEnv("AWS_SECRET_ACCESS_KEY", awsSecretAccessKey)
+ .setEnv("Token", awsSessionToken)
+ .setEnv("AWS_REGION", "us-east-2");
+
+ AwsCredentials awsCredential =
+ (AwsCredentials)
+ AwsCredentials.newBuilder(awsCredentialWithoutEnvProvider)
+ .setEnvironmentProvider(testEnvironmentProvider)
+ .build();
+
+ callGcs(awsCredential);
+ }
+
+ private GenericJson buildIdentityPoolCredentialConfig() throws IOException {
+ String idToken = generateGoogleIdToken(OIDC_AUDIENCE);
+
+ File file =
+ File.createTempFile(
+ "ITWorkloadIdentityFederation", /* suffix= */ null, /* directory= */ null);
+ file.deleteOnExit();
+ OAuth2Utils.writeInputStreamToFile(
+ new ByteArrayInputStream(idToken.getBytes(StandardCharsets.UTF_8)), file.getAbsolutePath());
+
+ GenericJson config = new GenericJson();
+ config.put("type", "external_account");
+ config.put("audience", OIDC_AUDIENCE);
+ config.put("subject_token_type", "urn:ietf:params:oauth:token-type:jwt");
+ config.put("token_url", "https://sts.googleapis.com/v1beta/token");
+ config.put(
+ "service_account_impersonation_url",
+ String.format(
+ "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken",
+ clientEmail));
+
+ GenericJson credentialSource = new GenericJson();
+ credentialSource.put("file", file.getAbsolutePath());
+ config.put("credential_source", credentialSource);
+
+ return config;
+ }
+
+ private GenericJson buildAwsCredentialConfig() {
+ GenericJson config = new GenericJson();
+ config.put("type", "external_account");
+ config.put("audience", AWS_AUDIENCE);
+ config.put("subject_token_type", "urn:ietf:params:aws:token-type:aws4_request");
+ config.put("token_url", "https://sts.googleapis.com/v1beta/token");
+ config.put(
+ "service_account_impersonation_url",
+ String.format(
+ "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken",
+ clientEmail));
+
+ GenericJson credentialSource = new GenericJson();
+ credentialSource.put("environment_id", "aws1");
+ credentialSource.put(
+ "regional_cred_verification_url",
+ "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15");
+ config.put("credential_source", credentialSource);
+
+ return config;
+ }
+
+ private void callGcs(GoogleCredentials credentials) throws IOException {
+ String bucketName = System.getenv("GCS_BUCKET");
+ if (bucketName == null) {
+ fail("GCS bucket name not set through GCS_BUCKET env variable.");
+ }
+
+ String url = "https://storage.googleapis.com/storage/v1/b/" + bucketName;
+
+ HttpCredentialsAdapter credentialsAdapter = new HttpCredentialsAdapter(credentials);
+ HttpRequestFactory requestFactory =
+ new NetHttpTransport().createRequestFactory(credentialsAdapter);
+ HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(url));
+
+ JsonObjectParser parser = new JsonObjectParser(GsonFactory.getDefaultInstance());
+ request.setParser(parser);
+
+ HttpResponse response = request.execute();
+ assertTrue(response.isSuccessStatusCode());
+ }
+
+ /**
+ * Generates a Google ID token using the iamcredentials generateIdToken API.
+ * https://cloud.google.com/iam/docs/creating-short-lived-service-account-credentials#sa-credentials-oidc
+ */
+ private String generateGoogleIdToken(String audience) throws IOException {
+ GoogleCredentials googleCredentials =
+ GoogleCredentials.getApplicationDefault()
+ .createScoped("https://www.googleapis.com/auth/cloud-platform");
+
+ HttpCredentialsAdapter credentialsAdapter = new HttpCredentialsAdapter(googleCredentials);
+ HttpRequestFactory requestFactory =
+ new NetHttpTransport().createRequestFactory(credentialsAdapter);
+
+ String url =
+ String.format(
+ "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateIdToken",
+ clientEmail);
+
+ GenericData data = new GenericData();
+ data.set("audience", audience);
+ data.set("includeEmail", true);
+ UrlEncodedContent content = new UrlEncodedContent(data);
+
+ HttpRequest request = requestFactory.buildPostRequest(new GenericUrl(url), content);
+ JsonObjectParser parser = new JsonObjectParser(GsonFactory.getDefaultInstance());
+ request.setParser(parser);
+
+ HttpResponse response = request.execute();
+ GenericData responseData = response.parseAs(GenericData.class);
+ return (String) responseData.get("token");
+ }
+
+ private GenericJson getServiceAccountKeyFileAsJson() throws IOException {
+ String credentialsPath = System.getenv(DefaultCredentialsProvider.CREDENTIAL_ENV_VAR);
+ JsonFactory jsonFactory = OAuth2Utils.JSON_FACTORY;
+ JsonObjectParser parser = new JsonObjectParser(jsonFactory);
+ return parser.parseAndClose(
+ new FileInputStream(credentialsPath), StandardCharsets.UTF_8, GenericJson.class);
+ }
+
+ private String getXmlValueByTagName(String rawXml, String tagName) {
+ int startIndex = rawXml.indexOf("<" + tagName + ">");
+ int endIndex = rawXml.indexOf("" + tagName + ">", startIndex);
+
+ if (startIndex >= 0 && endIndex > startIndex) {
+ return rawXml.substring(startIndex + tagName.length() + 2, endIndex);
+ }
+ return null;
+ }
+}
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java
index 4095edcad..871edb043 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/IdentityPoolCredentialsTest.java
@@ -104,8 +104,7 @@ public void createdScoped_clonedCredentialWithAddedScopes() {
List newScopes = Arrays.asList("scope1", "scope2");
- IdentityPoolCredentials newCredentials =
- (IdentityPoolCredentials) credentials.createScoped(newScopes);
+ IdentityPoolCredentials newCredentials = credentials.createScoped(newScopes);
assertEquals(credentials.getAudience(), newCredentials.getAudience());
assertEquals(credentials.getSubjectTokenType(), newCredentials.getSubjectTokenType());
@@ -444,6 +443,7 @@ public void builder() {
assertEquals(credentials.getClientId(), "clientId");
assertEquals(credentials.getClientSecret(), "clientSecret");
assertEquals(credentials.getScopes(), scopes);
+ assertEquals(credentials.getEnvironmentProvider(), SystemEnvironmentProvider.getInstance());
}
static InputStream writeIdentityPoolCredentialsStream(
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java b/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java
index 904093154..604f68c60 100644
--- a/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java
+++ b/oauth2_http/javatests/com/google/auth/oauth2/ServiceAccountCredentialsTest.java
@@ -479,12 +479,12 @@ public void createdScoped_defaultScopes() throws IOException {
}
@Test
- public void createScopedRequired_emptyScopes_false() throws IOException {
+ public void createScopedRequired_emptyScopes() throws IOException {
GoogleCredentials credentials =
ServiceAccountCredentials.fromPkcs8(
CLIENT_ID, CLIENT_EMAIL, PRIVATE_KEY_PKCS8, PRIVATE_KEY_ID, EMPTY_SCOPES);
- assertFalse(credentials.createScopedRequired());
+ assertTrue(credentials.createScopedRequired());
}
@Test
diff --git a/oauth2_http/javatests/com/google/auth/oauth2/TestEnvironmentProvider.java b/oauth2_http/javatests/com/google/auth/oauth2/TestEnvironmentProvider.java
new file mode 100644
index 000000000..9e6de1108
--- /dev/null
+++ b/oauth2_http/javatests/com/google/auth/oauth2/TestEnvironmentProvider.java
@@ -0,0 +1,19 @@
+package com.google.auth.oauth2;
+
+import java.util.HashMap;
+import java.util.Map;
+
+final class TestEnvironmentProvider implements EnvironmentProvider {
+
+ private final Map environmentVariables = new HashMap<>();
+
+ @Override
+ public String getEnv(String name) {
+ return environmentVariables.get(name);
+ }
+
+ public TestEnvironmentProvider setEnv(String name, String value) {
+ environmentVariables.put(name, value);
+ return this;
+ }
+}
diff --git a/oauth2_http/pom.xml b/oauth2_http/pom.xml
index fc81672fd..8e3fa8ff2 100644
--- a/oauth2_http/pom.xml
+++ b/oauth2_http/pom.xml
@@ -5,7 +5,7 @@
com.google.auth
google-auth-library-parent
- 0.25.0
+ 0.25.1
../pom.xml
@@ -51,13 +51,63 @@
org.apache.maven.plugins
maven-jar-plugin
-
+
com.google.auth.oauth2
-
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ 3.0.0-M4
+
+ 1200
+ sponge_log
+
+ **/IT*.java
+
+
+
+
+ org.apache.maven.surefire
+ surefire-junit47
+ 3.0.0-M4
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0-M5
+
+
+
+ **/IT*.java
+
+ sponge_log
+
+
+
+ org.apache.maven.surefire
+ surefire-junit47
+ 3.0.0-M5
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+
+
+ integration-test
+ verify
+
+
+
+
@@ -92,5 +142,4 @@
test
-
diff --git a/pom.xml b/pom.xml
index 8635a00ae..6b48cf952 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.google.auth
google-auth-library-parent
- 0.25.0
+ 0.25.1
pom
Google Auth Library for Java
Client libraries providing authentication and
diff --git a/scripts/workloadidentityfederation-setup.sh b/scripts/workloadidentityfederation-setup.sh
new file mode 100755
index 000000000..2d7692d6d
--- /dev/null
+++ b/scripts/workloadidentityfederation-setup.sh
@@ -0,0 +1,153 @@
+#!/bin/bash
+
+# Copyright 2021 Google LLC
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google LLC nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# This script is used to generate the project configurations needed to
+# end-to-end test workload identity pools in the Auth library, specifically
+# OIDC-based credentials and AWS credentials. This script only needs to be ran once.
+#
+# In order to run this script, the GOOGLE_APPLICATION_CREDENTIALS environment
+# variable needs to be set to point to a service account key file.
+# Additional fields must be provided in this file.
+# Detailed instructions are documented below.
+#
+# GCP project changes:
+# --------------------
+# The following IAM roles need to be set on the service account:
+# 1. IAM Workload Identity Pool Admin (needed to create resources for workload
+# identity pools).
+# 2. Security Admin (needed to get and set IAM policies).
+# 3. Service Account Token Creator (needed to generate Google ID tokens and
+# access tokens).
+#
+# The following APIs need to be enabled on the project:
+# 1. Identity and Access Management (IAM) API.
+# 2. IAM Service Account Credentials API.
+# 3. Cloud Resource Manager API.
+# 4. The API being accessed in the test, eg. DNS.
+#
+# AWS developer account changes:
+# ------------------------------
+# For testing AWS credentials, the following are needed:
+# 1. An AWS developer account is needed. The account ID will need to
+# be provided in the configuration object below.
+# 2. A role for web identity federation. This will also need to be provided
+# in the configuration object below.
+# - An OIDC Google identity provider needs to be created with the following:
+# issuer: accounts.google.com
+# audience: Use the client_id of the service account.
+# - A role for OIDC web identity federation is needed with the created
+# Google provider as a trusted entity:
+# "accounts.google.com:aud": "$CLIENT_ID"
+# The role creation steps are documented at:
+# https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html
+#
+# This script needs to be run once. It will do the following:
+# 1. Create a random workload identity pool.
+# 2. Create a random OIDC provider in that pool which uses the
+# 3. Enable OIDC tokens generated by the current service account to impersonate
+# the service account. (Identified by the OIDC token sub field which is the
+# service account client ID).
+# 4. Create a random AWS provider in that pool which uses the provided AWS
+# account ID.
+# 5. Enable AWS provider to impersonate the service account. (Principal is
+# identified by the AWS role name).
+# 6. Print out the STS audience fields associated with the created providers
+# and AWS role name/arn after the setup completes successfully so that
+# they can be used in the tests.
+#
+# The same service account used for this setup script should be used for
+# the test script.
+#
+# It is safe to run the setup script again. A new pool is created and new
+# audiences are printed. If run multiple times, it is advisable to delete
+# unused pools. Note that deleted pools are soft deleted and may remain for
+# a while before they are completely deleted. The old pool ID cannot be used
+# in the meantime.
+
+suffix=""
+
+function generate_random_string () {
+ local valid_chars=abcdefghijklmnopqrstuvwxyz0123456789
+ for i in {1..8} ; do
+ suffix+="${valid_chars:RANDOM%${#valid_chars}:1}"
+ done
+}
+
+generate_random_string
+
+pool_id="pool-"${suffix}
+oidc_provider_id="oidc-"${suffix}
+aws_provider_id="aws-"${suffix}
+
+# Fill in.
+project_id=""
+project_number=""
+aws_account_id=""
+aws_role_name=""
+service_account_email=""
+sub=""; # client_id from service account key file
+
+oidc_aud="//iam.googleapis.com/projects/${project_number}/locations/global/workloadIdentityPools/${pool_id}/providers/${oidc_provider_id}"
+aws_aud="//iam.googleapis.com/projects/${project_number}/locations/global/workloadIdentityPools/${pool_id}/providers/${aws_provider_id}"
+
+gcloud config set project ${project_id}
+
+# Create the Workload Identity Pool.
+gcloud beta iam workload-identity-pools create ${pool_id} \
+ --location="global" \
+ --description="Test pool" \
+ --display-name="Test pool for Java"
+
+# Create the OIDC Provider.
+gcloud beta iam workload-identity-pools providers create-oidc ${oidc_provider_id} \
+ --workload-identity-pool=${pool_id} \
+ --issuer-uri="https://accounts.google.com" \
+ --location="global" \
+ --attribute-mapping="google.subject=assertion.sub"
+
+# Create the AWS Provider.
+gcloud beta iam workload-identity-pools providers create-aws ${aws_provider_id} \
+ --workload-identity-pool=${pool_id} \
+ --account-id=${aws_account_id} \
+ --location="global"
+
+# Give permission to impersonate the service account.
+gcloud iam service-accounts add-iam-policy-binding ${service_account_email} \
+--role roles/iam.workloadIdentityUser \
+--member "principal://iam.googleapis.com/projects/${project_number}/locations/global/workloadIdentityPools/${pool_id}/subject/${sub}"
+
+gcloud iam service-accounts add-iam-policy-binding ${service_account_email} \
+ --role roles/iam.workloadIdentityUser \
+ --member "principalSet://iam.googleapis.com/projects/${project_number}/locations/global/workloadIdentityPools/${pool_id}/attribute.aws_role/arn:aws:sts::${aws_account_id}:assumed-role/${aws_role_name}"
+
+echo "OIDC audience:"${oidc_aud}
+echo "AWS audience:"${aws_aud}
+echo "AWS role name:"${aws_role_name}
+echo "AWS role ARN: arn:aws:iam::${aws_account_id}:role/${aws_role_name}"
diff --git a/versions.txt b/versions.txt
index c4f1a6169..fcb8e9d22 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,9 +1,9 @@
# Format:
# module:released-version:current-version
-google-auth-library:0.25.0:0.25.0
-google-auth-library-bom:0.25.0:0.25.0
-google-auth-library-parent:0.25.0:0.25.0
-google-auth-library-appengine:0.25.0:0.25.0
-google-auth-library-credentials:0.25.0:0.25.0
-google-auth-library-oauth2-http:0.25.0:0.25.0
+google-auth-library:0.25.1:0.25.1
+google-auth-library-bom:0.25.1:0.25.1
+google-auth-library-parent:0.25.1:0.25.1
+google-auth-library-appengine:0.25.1:0.25.1
+google-auth-library-credentials:0.25.1:0.25.1
+google-auth-library-oauth2-http:0.25.1:0.25.1