@@ -377,6 +377,143 @@ ExternalAccountCredentials credentials =
377377 ExternalAccountCredentials . fromStream(new FileInputStream (" /path/to/credentials.json" ));
378378```
379379
380+ ### Downscoping with Credential Access Boundaries
381+
382+ [ Downscoping with Credential Access Boundaries] ( https://cloud.google.com/iam/docs/downscoping-short-lived-credentials )
383+ enables the ability to downscope, or restrict, the Identity and Access Management (IAM) permissions
384+ that a short-lived credential can use for Cloud Storage.
385+
386+ The ` DownscopedCredentials ` class can be used to produce a downscoped access token from a
387+ ` CredentialAccessBoundary ` and a source credential. The Credential Access Boundary specifies which
388+ resources the newly created credential can access, as well as an upper bound on the permissions that
389+ are available on each resource. Using downscoped credentials ensures tokens in flight always have
390+ the least privileges (Principle of Least Privilege).
391+
392+ The snippet below shows how to initialize a CredentialAccessBoundary with one AccessBoundaryRule
393+ which specifies that the downscoped token will have readonly access to objects starting with
394+ "customer-a" in bucket "bucket-123":
395+ ``` java
396+ // Create the AccessBoundaryRule.
397+ String availableResource = " //storage.googleapis.com/projects/_/buckets/bucket-123" ;
398+ String availablePermission = " inRole:roles/storage.objectViewer" ;
399+ String expression = " resource.name.startsWith('projects/_/buckets/bucket-123/objects/customer-a')" ;
400+
401+ CredentialAccessBoundary . AccessBoundaryRule rule =
402+ CredentialAccessBoundary . AccessBoundaryRule . newBuilder()
403+ .setAvailableResource(availableResource)
404+ .addAvailablePermission(availablePermission)
405+ .setAvailabilityCondition(
406+ CredentialAccessBoundary . AccessBoundaryRule . AvailabilityCondition . newBuilder(). setExpression(expression). build())
407+ .build();
408+
409+ // Create the CredentialAccessBoundary with the rule.
410+ CredentialAccessBoundary credentialAccessBoundary =
411+ CredentialAccessBoundary . newBuilder(). addRule(rule). build();
412+ ```
413+
414+ The common pattern of usage is to have a token broker with elevated access generate these downscoped
415+ credentials from higher access source credentials and pass the downscoped short-lived access tokens
416+ to a token consumer via some secure authenticated channel for limited access to Google Cloud Storage
417+ resources.
418+
419+ Using the CredentialAccessBoundary created above in the Token Broker:
420+ ``` java
421+ // Retrieve the source credentials from ADC.
422+ GoogleCredentials sourceCredentials = GoogleCredentials . getApplicationDefault()
423+ .createScoped(" https://www.googleapis.com/auth/cloud-platform" );
424+
425+ // Initialize the DownscopedCredentials class.
426+ DownscopedCredentials downscopedCredentials =
427+ DownscopedCredentials . newBuilder()
428+ .setSourceCredential(credentials)
429+ .setCredentialAccessBoundary(credentialAccessBoundary)
430+ .build();
431+
432+ // Retrieve the downscoped access token.
433+ // This will need to be passed to the Token Consumer.
434+ AccessToken downscopedAccessToken = downscopedCredentials. refreshAccessToken();
435+ ```
436+
437+ A token broker can be set up on a server in a private network. Various workloads
438+ (token consumers) in the same network will send authenticated requests to that broker for downscoped
439+ tokens to access or modify specific google cloud storage buckets.
440+
441+ The broker will instantiate downscoped credentials instances that can be used to generate short
442+ lived downscoped access tokens which will be passed to the token consumer.
443+
444+ Putting it all together:
445+ ``` java
446+ // Retrieve the source credentials from ADC.
447+ GoogleCredentials sourceCredentials = GoogleCredentials . getApplicationDefault()
448+ .createScoped(" https://www.googleapis.com/auth/cloud-platform" );
449+
450+ // Create an Access Boundary Rule which will restrict the downscoped token to having readonly
451+ // access to objects starting with "customer-a" in bucket "bucket-123".
452+ String availableResource = " //storage.googleapis.com/projects/_/buckets/bucket-123" ;
453+ String availablePermission = " inRole:roles/storage.objectViewer" ;
454+ String expression = " resource.name.startsWith('projects/_/buckets/bucket-123/objects/customer-a')" ;
455+
456+ CredentialAccessBoundary . AccessBoundaryRule rule =
457+ CredentialAccessBoundary . AccessBoundaryRule . newBuilder()
458+ .setAvailableResource(availableResource)
459+ .addAvailablePermission(availablePermission)
460+ .setAvailabilityCondition(
461+ new AvailabilityCondition (expression, /* title= */ null , /* description= */ null ))
462+ .build();
463+
464+ // Initialize the DownscopedCredentials class.
465+ DownscopedCredentials downscopedCredentials =
466+ DownscopedCredentials . newBuilder()
467+ .setSourceCredential(credentials)
468+ .setCredentialAccessBoundary(CredentialAccessBoundary . newBuilder(). addRule(rule). build())
469+ .build();
470+
471+ // Retrieve the downscoped access token.
472+ // This will need to be passed to the Token Consumer.
473+ AccessToken downscopedAccessToken = downscopedCredentials. refreshAccessToken();
474+ ```
475+
476+ These downscoped access tokens can be used by the Token Consumer via ` OAuth2Credentials ` or
477+ ` OAuth2CredentialsWithRefresh ` . This credential can then be used to initialize a storage client
478+ instance to access Google Cloud Storage resources with restricted access.
479+
480+ ``` java
481+ // You can pass an `OAuth2RefreshHandler` to `OAuth2CredentialsWithRefresh` which will allow the
482+ // library to seamlessly handle downscoped token refreshes on expiration.
483+ OAuth2CredentialsWithRefresh . OAuth2RefreshHandler handler =
484+ new OAuth2CredentialsWithRefresh .OAuth2RefreshHandler () {
485+ @Override
486+ public AccessToken refreshAccessToken () {
487+ // Add the logic here that retrieves the token from your Token Broker.
488+ return accessToken;
489+ }
490+ };
491+
492+ // Downscoped token retrieved from token broker.
493+ AccessToken downscopedToken = handler. refreshAccessToken();
494+
495+ // Build the OAuth2CredentialsWithRefresh from the downscoped token and pass a refresh handler
496+ // to handle token expiration. Passing the original downscoped token or the expiry here is optional,
497+ // as the refresh_handler will generate the downscoped token on demand.
498+ OAuth2CredentialsWithRefresh credentials =
499+ OAuth2CredentialsWithRefresh . newBuilder()
500+ .setAccessToken(downscopedToken)
501+ .setRefreshHandler(handler)
502+ .build();
503+
504+ // Use the credentials with the Cloud Storage SDK.
505+ StorageOptions options = StorageOptions . newBuilder(). setCredentials(credentials). build();
506+ Storage storage = options. getService();
507+
508+ // Call GCS APIs.
509+ // Since we passed the downscoped credential, we will have have limited readonly access to objects
510+ // starting with "customer-a" in bucket "bucket-123".
511+ storage. get(... )
512+ ```
513+
514+ Note: Only Cloud Storage supports Credential Access Boundaries. Other Google Cloud services do not
515+ support this feature.
516+
380517## Configuring a Proxy
381518
382519For HTTP clients, a basic proxy can be configured by using ` http.proxyHost ` and related system properties as documented
0 commit comments