@@ -377,6 +377,143 @@ ExternalAccountCredentials credentials =
377
377
ExternalAccountCredentials . fromStream(new FileInputStream (" /path/to/credentials.json" ));
378
378
```
379
379
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
+
380
517
## Configuring a Proxy
381
518
382
519
For HTTP clients, a basic proxy can be configured by using ` http.proxyHost ` and related system properties as documented
0 commit comments