Skip to content

Commit 73e9e44

Browse files
authored
doc: Samples for Documenting Custom Credential Suppliers (#1820)
* Included changes for Documenting Custom Credential Suppliers for AWS Workloads and Okta Workload. * Added documentation and formatting changes. * nit fixes. * Addressed PR comments. * Listed out imports. Added more user friendly documentation. * Added documentation for env variables. * Added documentation for env variables. * BOM manages AWS Sdk dependencies. Multiple scopes demonstrated for Okta. Doc update.
1 parent 6de75be commit 73e9e44

File tree

3 files changed

+426
-11
lines changed

3 files changed

+426
-11
lines changed

samples/snippets/pom.xml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@
3434
<type>pom</type>
3535
<scope>import</scope>
3636
</dependency>
37+
<dependency>
38+
<groupId>software.amazon.awssdk</groupId>
39+
<artifactId>bom</artifactId>
40+
<version>2.20.27</version>
41+
<type>pom</type>
42+
<scope>import</scope>
43+
</dependency>
3744
</dependencies>
3845
</dependencyManagement>
3946

@@ -62,21 +69,14 @@
6269
<groupId>com.google.cloud</groupId>
6370
<artifactId>google-cloud-storage</artifactId>
6471
</dependency>
65-
66-
<!-- Test dependencies-->
6772
<dependency>
68-
<groupId>junit</groupId>
69-
<artifactId>junit</artifactId>
70-
<version>4.13.2</version>
71-
<scope>test</scope>
73+
<groupId>software.amazon.awssdk</groupId>
74+
<artifactId>auth</artifactId>
7275
</dependency>
7376
<dependency>
74-
<artifactId>truth</artifactId>
75-
<groupId>com.google.truth</groupId>
76-
<scope>test</scope>
77-
<version>1.4.4</version>
77+
<groupId>software.amazon.awssdk</groupId>
78+
<artifactId>regions</artifactId>
7879
</dependency>
79-
8080
</dependencies>
8181

8282
</project>
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import com.google.auth.oauth2.AwsCredentials;
18+
import com.google.auth.oauth2.AwsSecurityCredentials;
19+
import com.google.auth.oauth2.AwsSecurityCredentialsSupplier;
20+
import com.google.auth.oauth2.ExternalAccountSupplierContext;
21+
import com.google.auth.oauth2.GoogleCredentials;
22+
import com.google.cloud.storage.Bucket;
23+
import com.google.cloud.storage.Storage;
24+
import com.google.cloud.storage.StorageOptions;
25+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
26+
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
27+
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
28+
import software.amazon.awssdk.regions.Region;
29+
import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
30+
31+
/**
32+
* This sample demonstrates how to use a custom AWS security credentials supplier to authenticate to
33+
* Google Cloud Storage.
34+
*/
35+
public class CustomCredentialSupplierAwsWorkload {
36+
37+
public static void main(String[] args) {
38+
// TODO(Developer): Set these environment variable values.
39+
40+
// 1. AWS Credentials:
41+
// If running on a local system, the user must set AWS_REGION, AWS_ACCESS_KEY_ID, and
42+
// AWS_SECRET_ACCESS_KEY as environment variables. If running in an AWS environment (e.g.,
43+
// ECS, EKS), these variables will be auto-detected.
44+
45+
// 2. GCP_WORKLOAD_AUDIENCE:
46+
// The audience for the workload identity federation. This is the full resource name of the
47+
// Workload Identity Pool Provider, in the following format:
48+
// `//iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/<pool-id>/providers/<provider-id>`
49+
String gcpWorkloadAudience = System.getenv("GCP_WORKLOAD_AUDIENCE");
50+
51+
// 3. GCP_SERVICE_ACCOUNT_IMPERSONATION_URL (optional):
52+
// The service account impersonation URL. It should follow the format:
53+
// https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/<service-account-email>:generateAccessToken
54+
// If not provided, you should grant access to the GCP bucket to the principal directly.
55+
String saImpersonationUrl = System.getenv("GCP_SERVICE_ACCOUNT_IMPERSONATION_URL");
56+
57+
// 4. GCS_BUCKET_NAME:
58+
// The name of the bucket that you wish to fetch data for.
59+
String gcsBucketName = System.getenv("GCS_BUCKET_NAME");
60+
61+
if (gcpWorkloadAudience == null || gcsBucketName == null) {
62+
System.out.println(
63+
"Missing required environment variables. Please check your environment settings. "
64+
+ "Required: GCP_WORKLOAD_AUDIENCE, GCS_BUCKET_NAME");
65+
return;
66+
}
67+
68+
customCredentialSupplierAwsWorkload(gcpWorkloadAudience, saImpersonationUrl, gcsBucketName);
69+
}
70+
71+
public static void customCredentialSupplierAwsWorkload(
72+
String gcpWorkloadAudience, String saImpersonationUrl, String gcsBucketName) {
73+
// 1. Instantiate the custom supplier.
74+
CustomAwsSupplier customSupplier = new CustomAwsSupplier();
75+
76+
// 2. Configure the AwsCredentials options.
77+
AwsCredentials.Builder credentialsBuilder =
78+
AwsCredentials.newBuilder()
79+
.setAudience(gcpWorkloadAudience)
80+
// This token type indicates that the subject token is an AWS Signature Version 4 signed
81+
// request. This is required for AWS Workload Identity Federation.
82+
.setSubjectTokenType("urn:ietf:params:aws:token-type:aws4_request")
83+
.setAwsSecurityCredentialsSupplier(customSupplier);
84+
85+
if (saImpersonationUrl != null) {
86+
credentialsBuilder.setServiceAccountImpersonationUrl(saImpersonationUrl);
87+
}
88+
89+
GoogleCredentials credentials = credentialsBuilder.build();
90+
91+
// 3. Use the credentials to make an authenticated request.
92+
Storage storage = StorageOptions.newBuilder().setCredentials(credentials).build().getService();
93+
94+
System.out.println("Getting metadata for bucket: " + gcsBucketName + "...");
95+
Bucket bucket = storage.get(gcsBucketName);
96+
System.out.println(" --- SUCCESS! ---");
97+
System.out.println("Successfully authenticated and retrieved bucket data:");
98+
System.out.println(bucket.toString());
99+
}
100+
101+
/**
102+
* Custom AWS Security Credentials Supplier.
103+
*
104+
* <p>This implementation resolves AWS credentials using the default provider chain from the AWS
105+
* SDK. This allows fetching credentials from environment variables, shared credential files
106+
* (~/.aws/credentials), or IAM roles for service accounts (IRSA) in EKS, etc.
107+
*/
108+
private static class CustomAwsSupplier implements AwsSecurityCredentialsSupplier {
109+
110+
private final AwsCredentialsProvider awsCredentialsProvider;
111+
private String region;
112+
113+
public CustomAwsSupplier() {
114+
// The AWS SDK handles memoization (caching) and proactive refreshing internally.
115+
this.awsCredentialsProvider = DefaultCredentialsProvider.create();
116+
}
117+
118+
/**
119+
* Returns the AWS region. This is required for signing the AWS request. It resolves the region
120+
* automatically by using the default AWS region provider chain, which searches for the region
121+
* in the standard locations (environment variables, AWS config file, etc.).
122+
*/
123+
@Override
124+
public String getRegion(ExternalAccountSupplierContext context) {
125+
if (this.region == null) {
126+
Region awsRegion = new DefaultAwsRegionProviderChain().getRegion();
127+
if (awsRegion != null) {
128+
this.region = awsRegion.id();
129+
}
130+
}
131+
if (this.region == null) {
132+
throw new IllegalStateException(
133+
"CustomAwsSupplier: Unable to resolve AWS region. Please set the AWS_REGION "
134+
+ "environment variable or configure it in your ~/.aws/config file.");
135+
}
136+
return this.region;
137+
}
138+
139+
/** Retrieves AWS security credentials using the AWS SDK's default provider chain. */
140+
@Override
141+
public AwsSecurityCredentials getCredentials(ExternalAccountSupplierContext context) {
142+
software.amazon.awssdk.auth.credentials.AwsCredentials credentials =
143+
this.awsCredentialsProvider.resolveCredentials();
144+
if (credentials == null
145+
|| credentials.accessKeyId() == null
146+
|| credentials.secretAccessKey() == null) {
147+
throw new IllegalStateException(
148+
"Unable to resolve AWS credentials from the default provider chain. "
149+
+ "Ensure your AWS CLI is configured, or AWS environment variables "
150+
+ "(like AWS_ACCESS_KEY_ID) are set.");
151+
}
152+
153+
String sessionToken = null;
154+
if (credentials instanceof AwsSessionCredentials) {
155+
sessionToken = ((AwsSessionCredentials) credentials).sessionToken();
156+
}
157+
158+
return new com.google.auth.oauth2.AwsSecurityCredentials(
159+
credentials.accessKeyId(), credentials.secretAccessKey(), sessionToken);
160+
}
161+
}
162+
}

0 commit comments

Comments
 (0)