Skip to content

Commit e8fcf22

Browse files
committed
Added some samples for custom credentials. Including tests.
1 parent db6bef7 commit e8fcf22

File tree

6 files changed

+603
-1
lines changed

6 files changed

+603
-1
lines changed

auth/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,51 @@ You can then run `DownscopingExample` via:
6767

6868
mvn exec:java -Dexec.mainClass=com.google.cloud.auth.samples.DownscopingExample
6969

70+
## Custom Credential Suppliers
71+
72+
If you want to use external credentials (like AWS or Okta) that require custom retrieval logic not supported natively by the library, you can provide a custom supplier implementation.
73+
74+
### Authenticate with Okta (Custom Supplier)
75+
76+
This sample demonstrates how to use a custom `IdentityPoolSubjectTokenSupplier` to fetch an OIDC token from Okta using the Client Credentials flow and exchange it for Google Cloud credentials.
77+
78+
1. **Set required environment variables:**
79+
```bash
80+
export OKTA_DOMAIN="https://your-domain.okta.com"
81+
export OKTA_CLIENT_ID="your-client-id"
82+
export OKTA_CLIENT_SECRET="your-client-secret"
83+
export GCP_WORKLOAD_AUDIENCE="//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/my-pool/providers/my-provider"
84+
export GCS_BUCKET_NAME="your-bucket-name"
85+
# Optional:
86+
# export GCP_SERVICE_ACCOUNT_IMPERSONATION_URL="..."
87+
```
88+
89+
2. **Run the sample:**
90+
```bash
91+
mvn exec:java -Dexec.mainClass=com.google.cloud.auth.samples.CustomCredentialSupplierOktaWorkload
92+
```
93+
94+
### Authenticate with AWS (Custom Supplier)
95+
96+
This sample demonstrates how to use the **AWS SDK for Java (v2)** as a custom `AwsSecurityCredentialsSupplier` to bridge AWS credentials (from environment, `~/.aws/credentials`, or EKS/ECS metadata) to Google Cloud Workload Identity.
97+
98+
1. **Set required environment variables:**
99+
```bash
100+
# Google Cloud Config
101+
export GCP_WORKLOAD_AUDIENCE="//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/my-pool/providers/my-aws-provider"
102+
export GCS_BUCKET_NAME="your-bucket-name"
103+
104+
# AWS Credentials (or use ~/.aws/credentials)
105+
export AWS_ACCESS_KEY_ID="your-aws-key"
106+
export AWS_SECRET_ACCESS_KEY="your-aws-secret"
107+
export AWS_REGION="us-east-1"
108+
```
109+
110+
2. **Run the sample:**
111+
```bash
112+
mvn exec:java -Dexec.mainClass=com.google.cloud.auth.samples.CustomCredentialSupplierAwsWorkload
113+
```
114+
70115
## Tests
71116
Run all tests:
72117
```

auth/pom.xml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ limitations under the License.
3030
<parent>
3131
<groupId>com.google.cloud.samples</groupId>
3232
<artifactId>shared-configuration</artifactId>
33-
<version>1.2.0</version>
33+
<version>1.2.2</version>
3434
</parent>
3535

3636
<properties>
@@ -52,6 +52,13 @@ limitations under the License.
5252
<type>pom</type>
5353
<scope>import</scope>
5454
</dependency>
55+
<dependency>
56+
<groupId>software.amazon.awssdk</groupId>
57+
<artifactId>bom</artifactId>
58+
<version>2.25.41</version>
59+
<type>pom</type>
60+
<scope>import</scope>
61+
</dependency>
5562
</dependencies>
5663
</dependencyManagement>
5764

@@ -82,6 +89,14 @@ limitations under the License.
8289
<groupId>com.google.cloud</groupId>
8390
<artifactId>google-cloud-language</artifactId>
8491
</dependency>
92+
<dependency>
93+
<groupId>software.amazon.awssdk</groupId>
94+
<artifactId>auth</artifactId>
95+
</dependency>
96+
<dependency>
97+
<groupId>software.amazon.awssdk</groupId>
98+
<artifactId>regions</artifactId>
99+
</dependency>
85100
<!-- END dependencies -->
86101
<dependency>
87102
<groupId>junit</groupId>
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package com.google.cloud.auth.samples;
2+
3+
/*
4+
* Copyright 2025 Google LLC
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
// [START auth_custom_credential_supplier_aws]
20+
import com.google.auth.oauth2.AwsCredentials;
21+
import com.google.auth.oauth2.AwsSecurityCredentials;
22+
import com.google.auth.oauth2.AwsSecurityCredentialsSupplier;
23+
import com.google.auth.oauth2.ExternalAccountSupplierContext;
24+
import com.google.auth.oauth2.GoogleCredentials;
25+
import com.google.cloud.storage.Bucket;
26+
import com.google.cloud.storage.Storage;
27+
import com.google.cloud.storage.StorageOptions;
28+
import java.io.IOException;
29+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
30+
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
31+
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
32+
import software.amazon.awssdk.regions.Region;
33+
import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
34+
// [END auth_custom_credential_supplier_aws]
35+
36+
/**
37+
* This sample demonstrates how to use a custom AWS security credentials supplier to authenticate to
38+
* Google Cloud Storage using AWS Workload Identity Federation.
39+
*/
40+
public class CustomCredentialSupplierAwsWorkload {
41+
42+
public static void main(String[] args) throws IOException {
43+
// The audience for the workload identity federation.
44+
// Format:
45+
// //iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/<pool-id>/providers/<provider-id>
46+
String gcpWorkloadAudience = System.getenv("GCP_WORKLOAD_AUDIENCE");
47+
48+
// The bucket to fetch data from.
49+
String gcsBucketName = System.getenv("GCS_BUCKET_NAME");
50+
51+
// (Optional) The service account impersonation URL.
52+
String saImpersonationUrl = System.getenv("GCP_SERVICE_ACCOUNT_IMPERSONATION_URL");
53+
54+
if (gcpWorkloadAudience == null || gcsBucketName == null) {
55+
System.err.println(
56+
"Error: GCP_WORKLOAD_AUDIENCE and GCS_BUCKET_NAME environment variables are required.");
57+
return;
58+
}
59+
60+
System.out.println("Getting metadata for bucket: " + gcsBucketName + "...");
61+
Bucket bucket =
62+
authenticateWithAwsCredentials(gcpWorkloadAudience, saImpersonationUrl, gcsBucketName);
63+
64+
System.out.println(" --- SUCCESS! ---");
65+
System.out.printf("Bucket Name: %s%n", bucket.getName());
66+
System.out.printf("Bucket Location: %s%n", bucket.getLocation());
67+
}
68+
69+
/**
70+
* Authenticates using a custom AWS credential supplier and retrieves bucket metadata.
71+
*
72+
* @param gcpWorkloadAudience The WIF provider audience.
73+
* @param saImpersonationUrl Optional service account impersonation URL.
74+
* @param gcsBucketName The GCS bucket name.
75+
* @return The Bucket object containing metadata.
76+
* @throws IOException If authentication fails.
77+
*/
78+
// [START auth_custom_credential_supplier_aws]
79+
public static Bucket authenticateWithAwsCredentials(
80+
String gcpWorkloadAudience, String saImpersonationUrl, String gcsBucketName)
81+
throws IOException {
82+
83+
// 1. Instantiate the custom supplier.
84+
CustomAwsSupplier customSupplier = new CustomAwsSupplier();
85+
86+
// 2. Configure the AwsCredentials options.
87+
AwsCredentials.Builder credentialsBuilder =
88+
AwsCredentials.newBuilder()
89+
.setAudience(gcpWorkloadAudience)
90+
// This token type indicates that the subject token is an AWS Signature Version 4 signed
91+
// request. This is required for AWS Workload Identity Federation.
92+
.setSubjectTokenType("urn:ietf:params:aws:token-type:aws4_request")
93+
.setAwsSecurityCredentialsSupplier(customSupplier);
94+
95+
if (saImpersonationUrl != null) {
96+
credentialsBuilder.setServiceAccountImpersonationUrl(saImpersonationUrl);
97+
}
98+
99+
GoogleCredentials credentials = credentialsBuilder.build();
100+
101+
// 3. Use the credentials to make an authenticated request.
102+
Storage storage = StorageOptions.newBuilder().setCredentials(credentials).build().getService();
103+
104+
return storage.get(gcsBucketName);
105+
}
106+
107+
/**
108+
* Custom AWS Security Credentials Supplier.
109+
*
110+
* <p>This implementation resolves AWS credentials and regions using the default provider chains
111+
* from the AWS SDK (v2). This supports environment variables, ~/.aws/credentials, and EC2/EKS
112+
* metadata.
113+
*/
114+
private static class CustomAwsSupplier implements AwsSecurityCredentialsSupplier {
115+
private final AwsCredentialsProvider awsCredentialsProvider;
116+
private String region;
117+
118+
public CustomAwsSupplier() {
119+
// The AWS SDK handles memoization and refreshing internally.
120+
this.awsCredentialsProvider = DefaultCredentialsProvider.create();
121+
}
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+
throw new IllegalStateException(
129+
"Unable to resolve AWS region. Ensure AWS_REGION is set or configured.");
130+
}
131+
this.region = awsRegion.id();
132+
}
133+
return this.region;
134+
}
135+
136+
@Override
137+
public AwsSecurityCredentials getCredentials(ExternalAccountSupplierContext context) {
138+
software.amazon.awssdk.auth.credentials.AwsCredentials credentials =
139+
this.awsCredentialsProvider.resolveCredentials();
140+
141+
if (credentials == null) {
142+
throw new IllegalStateException("Unable to resolve AWS credentials.");
143+
}
144+
145+
String sessionToken = null;
146+
if (credentials instanceof AwsSessionCredentials) {
147+
sessionToken = ((AwsSessionCredentials) credentials).sessionToken();
148+
}
149+
150+
return new AwsSecurityCredentials(
151+
credentials.accessKeyId(), credentials.secretAccessKey(), sessionToken);
152+
}
153+
}
154+
// [END auth_custom_credential_supplier_aws]
155+
}

0 commit comments

Comments
 (0)