Skip to content

Commit 8bbaacd

Browse files
committed
BOM manages AWS Sdk dependencies. Multiple scopes demonstrated for Okta. Doc update.
1 parent 78cc927 commit 8bbaacd

File tree

3 files changed

+78
-75
lines changed

3 files changed

+78
-75
lines changed

samples/snippets/pom.xml

Lines changed: 7 additions & 20 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,34 +69,14 @@
6269
<groupId>com.google.cloud</groupId>
6370
<artifactId>google-cloud-storage</artifactId>
6471
</dependency>
65-
66-
<!-- AWS SDK for Java V2 -->
6772
<dependency>
6873
<groupId>software.amazon.awssdk</groupId>
6974
<artifactId>auth</artifactId>
70-
<version>2.20.27</version>
7175
</dependency>
7276
<dependency>
7377
<groupId>software.amazon.awssdk</groupId>
7478
<artifactId>regions</artifactId>
75-
<version>2.20.27</version>
76-
</dependency>
77-
78-
79-
<!-- Test dependencies-->
80-
<dependency>
81-
<groupId>junit</groupId>
82-
<artifactId>junit</artifactId>
83-
<version>4.13.2</version>
84-
<scope>test</scope>
8579
</dependency>
86-
<dependency>
87-
<artifactId>truth</artifactId>
88-
<groupId>com.google.truth</groupId>
89-
<scope>test</scope>
90-
<version>1.4.4</version>
91-
</dependency>
92-
9380
</dependencies>
9481

9582
</project>

samples/snippets/src/main/java/CustomCredentialSupplierAwsWorkload.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public static void main(String[] args) {
4545
// 2. GCP_WORKLOAD_AUDIENCE:
4646
// The audience for the workload identity federation. This is the full resource name of the
4747
// Workload Identity Pool Provider, in the following format:
48-
// //iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/<pool-id>/providers/<provider-id>
48+
// `//iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/<pool-id>/providers/<provider-id>`
4949
String gcpWorkloadAudience = System.getenv("GCP_WORKLOAD_AUDIENCE");
5050

5151
// 3. GCP_SERVICE_ACCOUNT_IMPERSONATION_URL (optional):

samples/snippets/src/main/java/CustomCredentialSupplierOktaWorkload.java

Lines changed: 70 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.cloud.storage.Bucket;
2424
import com.google.cloud.storage.Storage;
2525
import com.google.cloud.storage.StorageOptions;
26+
import java.time.Instant;
2627
import java.io.BufferedReader;
2728
import java.io.DataOutputStream;
2829
import java.io.IOException;
@@ -44,7 +45,7 @@ public static void main(String[] args) {
4445
// 1. GCP_WORKLOAD_AUDIENCE:
4546
// The audience for the workload identity federation. This is the full resource name of the
4647
// Workload Identity Pool Provider, in the following format:
47-
// //iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/<pool-id>/providers/<provider-id>
48+
// `//iam.googleapis.com/projects/<project-number>/locations/global/workloadIdentityPools/<pool-id>/providers/<provider-id>`
4849
String gcpWorkloadAudience = System.getenv("GCP_WORKLOAD_AUDIENCE");
4950

5051
// 2. GCP_SERVICE_ACCOUNT_IMPERSONATION_URL (optional):
@@ -58,7 +59,11 @@ public static void main(String[] args) {
5859
String gcsBucketName = System.getenv("GCS_BUCKET_NAME");
5960

6061
// 4. Okta Configuration:
61-
// To set up the Okta application for this flow:
62+
// To set up the Okta application for this flow, refer:
63+
// https://developer.okta.com/docs/guides/implement-grant-type/clientcreds/main/
64+
// https://developer.okta.com/docs/guides/customize-authz-server/main/
65+
//
66+
// Steps:
6267
// a. In your Okta developer console, create a new Application of type "Machine-to-Machine
6368
// (M2M)".
6469
// b. Under the "General" tab, ensure that "Client Credentials" is an allowed grant type.
@@ -138,11 +143,13 @@ public static void customCredentialSupplierOktaWorkload(
138143
*/
139144
private static class OktaClientCredentialsSupplier implements IdentityPoolSubjectTokenSupplier {
140145

146+
private static final long TOKEN_REFRESH_BUFFER_SECONDS = 60;
147+
141148
private final String oktaTokenUrl;
142149
private final String clientId;
143150
private final String clientSecret;
144151
private String accessToken;
145-
private long expiryTime;
152+
private Instant expiryTime;
146153

147154
public OktaClientCredentialsSupplier(String domain, String clientId, String clientSecret) {
148155
this.oktaTokenUrl = domain + "/oauth2/default/v1/token";
@@ -159,7 +166,8 @@ public OktaClientCredentialsSupplier(String domain, String clientId, String clie
159166
public String getSubjectToken(ExternalAccountSupplierContext context) throws IOException {
160167
// Check if the current token is still valid (with a 60-second buffer).
161168
boolean isTokenValid =
162-
this.accessToken != null && System.currentTimeMillis() < this.expiryTime - 60 * 1000;
169+
this.accessToken != null
170+
&& Instant.now().isBefore(this.expiryTime.minusSeconds(TOKEN_REFRESH_BUFFER_SECONDS));
163171

164172
if (isTokenValid) {
165173
System.out.println("[Supplier] Returning cached Okta Access token.");
@@ -178,59 +186,67 @@ public String getSubjectToken(ExternalAccountSupplierContext context) throws IOE
178186
*/
179187
private void fetchOktaAccessToken() throws IOException {
180188
URL url = new URL(this.oktaTokenUrl);
181-
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
182-
conn.setRequestMethod("POST");
183-
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
184-
185-
// The client_id and client_secret are sent in a Basic Auth header, as required by the
186-
// OAuth 2.0 Client Credentials grant specification. The credentials are Base64 encoded.
187-
String auth = this.clientId + ":" + this.clientSecret;
188-
String encodedAuth =
189-
Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
190-
conn.setRequestProperty("Authorization", "Basic " + encodedAuth);
191-
192-
conn.setDoOutput(true);
193-
try (DataOutputStream out = new DataOutputStream(conn.getOutputStream())) {
194-
// For the Client Credentials grant, scopes are optional and define the permissions
195-
// the access token will have. Replace "gcp.test.read" with the scopes defined in your
196-
// Okta authorization server. Multiple scopes can be requested by space-separating them
197-
// (e.g., "scope1 scope2").
198-
String params = "grant_type=client_credentials&scope=gcp.test.read";
199-
out.writeBytes(params);
200-
out.flush();
201-
}
202-
203-
int responseCode = conn.getResponseCode();
204-
if (responseCode == HttpURLConnection.HTTP_OK) {
205-
try (BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
206-
StringBuilder response = new StringBuilder();
207-
String line;
208-
while ((line = in.readLine()) != null) {
209-
response.append(line);
210-
}
189+
HttpURLConnection conn = null;
190+
try {
191+
conn = (HttpURLConnection) url.openConnection();
192+
conn.setRequestMethod("POST");
193+
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
194+
195+
// The client_id and client_secret are sent in a Basic Auth header, as required by the
196+
// OAuth 2.0 Client Credentials grant specification. The credentials are Base64 encoded.
197+
String auth = this.clientId + ":" + this.clientSecret;
198+
String encodedAuth =
199+
Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
200+
conn.setRequestProperty("Authorization", "Basic " + encodedAuth);
201+
202+
conn.setDoOutput(true);
203+
try (DataOutputStream out = new DataOutputStream(conn.getOutputStream())) {
204+
// For the Client Credentials grant, scopes are optional and define the permissions
205+
// the access token will have. Replace "gcp.test.read" with the scopes defined in your
206+
// Okta authorization server. Multiple scopes can be requested by space-separating them.
207+
// In application/x-www-form-urlencoded, a space is represented by '+' or '%20'.
208+
// e.g., "scope1%20scope2" or "scope1+scope2".
209+
String params = "grant_type=client_credentials&scope=gcp.test.read%20gcp.bucket.read";
210+
out.writeBytes(params);
211+
out.flush();
212+
}
211213

212-
GenericJson jsonObject =
213-
GsonFactory.getDefaultInstance()
214-
.createJsonParser(response.toString())
215-
.parse(GenericJson.class);
216-
217-
if (jsonObject.containsKey("access_token") && jsonObject.containsKey("expires_in")) {
218-
this.accessToken = (String) jsonObject.get("access_token");
219-
Number expiresInNumber = (Number) jsonObject.get("expires_in");
220-
int expiresIn = expiresInNumber.intValue();
221-
this.expiryTime = System.currentTimeMillis() + expiresIn * 1000L;
222-
System.out.println(
223-
"[Supplier] Successfully received Access Token from Okta. Expires in "
224-
+ expiresIn
225-
+ " seconds.");
226-
} else {
227-
throw new IOException("Access token or expires_in not found in Okta response.");
214+
int responseCode = conn.getResponseCode();
215+
if (responseCode == HttpURLConnection.HTTP_OK) {
216+
try (BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
217+
StringBuilder response = new StringBuilder();
218+
String line;
219+
while ((line = in.readLine()) != null) {
220+
response.append(line);
221+
}
222+
223+
GenericJson jsonObject =
224+
GsonFactory.getDefaultInstance()
225+
.createJsonParser(response.toString())
226+
.parse(GenericJson.class);
227+
228+
if (jsonObject.containsKey("access_token") && jsonObject.containsKey("expires_in")) {
229+
this.accessToken = (String) jsonObject.get("access_token");
230+
Number expiresInNumber = (Number) jsonObject.get("expires_in");
231+
int expiresIn = expiresInNumber.intValue();
232+
this.expiryTime = Instant.now().plusSeconds(expiresIn);
233+
System.out.println(
234+
"[Supplier] Successfully received Access Token from Okta. Expires in "
235+
+ expiresIn
236+
+ " seconds.");
237+
} else {
238+
throw new IOException("Access token or expires_in not found in Okta response.");
239+
}
228240
}
241+
} else {
242+
throw new IOException(
243+
"Failed to authenticate with Okta using Client Credentials grant. Response code: "
244+
+ responseCode);
245+
}
246+
} finally {
247+
if (conn != null) {
248+
conn.disconnect();
229249
}
230-
} else {
231-
throw new IOException(
232-
"Failed to authenticate with Okta using Client Credentials grant. Response code: "
233-
+ responseCode);
234250
}
235251
}
236252
}

0 commit comments

Comments
 (0)