Create a downscoped short-lived credential Stay organized with collections Save and categorize content based on your preferences.
This page explains how to use Credential Access Boundaries to create an OAuth2.0 access token with downscoped Cloud Storage permissions.
The process for creating a token with downscoped permissions includes thefollowing steps:
- Grant the appropriate IAM roles to a useror service account.
- Define a Credential Access Boundary that sets an upperbound on the permissions that are available to the user or service account.
- Create an OAuth 2.0 access token for the user orservice account.
- Exchange the OAuth 2.0 access token for a new tokenthat respects the Credential Access Boundary.
You can then use the new downscoped OAuth 2.0 access token to authenticaterequests to Cloud Storage.
Before you begin
Before you use Credential Access Boundaries, make sure you meet the followingrequirements:
You need to downscope permissions only for Cloud Storage, not for otherGoogle Cloud services.
If you need to downscope permissions for additional Google Cloudservices, you can create multipleservice accounts andgrant a different set of roles to each service account.
You can useOAuth 2.0 access tokens for authentication.Other types of short-lived credentials don't support Credential AccessBoundaries.
Also, you must enable the required APIs:
Enable the IAM and Security Token Service APIs.
Roles required to enable APIs
To enable APIs, you need the Service Usage Admin IAM role (
roles/serviceusage.serviceUsageAdmin), which contains theserviceusage.services.enablepermission.Learn how to grant roles.
Grant IAM roles
A Credential Access Boundary sets an upper bound on the available permissionsfor a resource. It can subtract permissions from a principal, but it can't addpermissions that the principal does not already have.
As a result, you must also grant roles to the principal that provide thepermissions they need, eitheron a Cloud Storage bucket oron a higher-level resource, such as the project.
For example, suppose you need to create a downscoped short-lived credential thatallows a service account to create objects in a bucket:
- At a minimum, you must grant a role to the service account that includes the
storage.objects.createpermission, such as the Storage Object Creator role(roles/storage.objectCreator). The Credential Access Boundary must alsoinclude this permission. - You can also grant a role that includes more permissions, such as the StorageObject Admin role (
roles/storage.objectAdmin). The service account can useonly the permissions that appear in both the role grant and the CredentialAccess Boundary.
To learn about predefined roles for Cloud Storage, seeCloud Storage roles.
Define the Credential Access Boundary
A Credential Access Boundary is an object that contains a list ofaccessboundary rules. The rules are made up of parameters that specify the upperbound of the permissions that are available to the user or service account. Todefine a Credential Access Boundary, create a JSON object that lists the accessboundary rules and their parameters.
The following is an example of a Credential Access Boundary:
{"accessBoundary":{"accessBoundaryRules":[{"availablePermissions":["inRole:ROLE_ID"],"availableResource":"//storage.googleapis.com/projects/_/buckets/BUCKET_NAME""availabilityCondition":{"expression":"CONDITION"}]}}Replace the following:
ROLE_ID: The ID of a predefined or custom rolethat defines the upper bound on the available permissions for the resource.For example,roles/storage.objectViewer. To specify multiple roles, add anew line with aninRole:prefix followed by the role ID. Only the permissionsin the specified roles will be available.BUCKET_NAME: The name of theCloud Storage bucket that the rule applies to.CONDITION: Optional. A condition expression thatspecifies the Cloud Storage objects where permissions are available.For example, the following condition makes permissions available for objectswhose name starts withcustomer-a:resource.name.startsWith('projects/_/buckets/example-bucket/objects/customer-a')
To learn more about how to create and customize credential access boundaries,seeComponents of a Credential Access Boundary.
For examples of potential use cases for Credential Access Boundaries, seeExamples of Credential Access Boundaries.
Create an OAuth 2.0 access token
Before you create a downscoped short-lived credential, you must create a normalOAuth 2.0 access token. You can then exchange the normal credential for adownscoped credential. When you create the access token, use the OAuth 2.0 scopehttps://www.googleapis.com/auth/cloud-platform.
To create an access token for a service account, you cancomplete the server-to-server OAuth 2.0 flow, or youcan use the Service Account Credentials API togenerate an OAuth 2.0 access token.
To create an access token for a user, seeObtaining OAuth 2.0 access tokens. You can also usetheOAuth 2.0 Playground to create an access token for yourown Google Account.
Exchange the OAuth 2.0 access token
After you create an OAuth 2.0 access token, you can exchange the access tokenfor a downscoped token that respects the Credential Access Boundary. Thisprocess typically involves atoken broker and atoken consumer:
Thetoken broker is responsible for defining the Credential Access Boundaryand exchanging an access token for a downscoped token.
The token broker can use a supported authentication library toexchange access tokens automatically, or it cancall the Security Token Service to exchange tokens manually.
Thetoken consumer requests a downscoped access token from the token broker,then uses the downscoped access token to perform another action.
The token consumer can use a supported authentication library toautomatically refresh access tokens before theyexpire. Alternatively, it can refresh tokens manually, or it can allowtokens to expire without refreshing them.
There are two ways to exchange the access token for a downscoped token:
Client-side token exchange (recommended): Clients obtain cryptographicmaterials from the Security Token Service API server. The cryptographic materials allowclients to generate downscoped tokens with varying Credential Access Boundaryrules independently on the client side for a set duration (for example, 1 hour).This approach reduces latency and improves efficiency, especially for clientsrequiring frequent Credential Access Boundary rule updates. It's also moreefficient for applications that need to generate many unique downscoped tokens.This is the recommended approach because it provides better performance,scalability, and future feature compatibility.
Server-side token exchange: Clients request a new downscoped token fromthe Security Token Service API server whenever a Credential Access Boundary rule changes.This approach is straightforward but requires a round trip to the Security Token ServiceAPI server for each downscoped token request. This approach is recommended onlyfor customers who require a client library that doesn't support client-sidetoken exchange, due to the round trip to the Security Token Service API for eachdownscoped token request.
Client-side token exchange
If you create the token broker and token consumer with the followinglanguage, you can use Google's authentication library to exchange and refreshtokens automatically through the client-side approach.
Java
For Java, you can exchange and refresh tokens automatically with version 1.32.1or later of thecom.google.auth:google-auth-library-cab-token-generator artifact.
To check your artifact version, run the following Maven command in yourapplication directory:
mvndependency:list-DincludeArtifactIds=google-auth-library-cab-token-generatorThe following example shows how a token broker can generate downscoped tokens:
importcom.google.auth.credentialaccessboundary.ClientSideCredentialAccessBoundaryFactory;importcom.google.auth.oauth2.AccessToken;importcom.google.auth.oauth2.CredentialAccessBoundary;importcom.google.auth.oauth2.GoogleCredentials;importdev.cel.common.CelValidationException;importjava.io.IOException;importjava.security.GeneralSecurityException;publicstaticAccessTokengetTokenFromBroker(StringbucketName,StringobjectPrefix)throwsIOException{// Retrieve the source credentials from ADC.GoogleCredentialssourceCredentials=GoogleCredentials.getApplicationDefault().createScoped("https://www.googleapis.com/auth/cloud-platform");// Initialize the Credential Access Boundary rules.StringavailableResource="//storage.googleapis.com/projects/_/buckets/"+bucketName;// Downscoped credentials will have readonly access to the resource.StringavailablePermission="inRole:roles/storage.objectViewer";// Only objects starting with the specified prefix string in the object name will be allowed// read access.Stringexpression="resource.name.startsWith('projects/_/buckets/"+bucketName+"/objects/"+objectPrefix+"')";// Build the AvailabilityCondition.CredentialAccessBoundary.AccessBoundaryRule.AvailabilityConditionavailabilityCondition=CredentialAccessBoundary.AccessBoundaryRule.AvailabilityCondition.newBuilder().setExpression(expression).build();// Define the single access boundary rule using the above properties.CredentialAccessBoundary.AccessBoundaryRulerule=CredentialAccessBoundary.AccessBoundaryRule.newBuilder().setAvailableResource(availableResource).addAvailablePermission(availablePermission).setAvailabilityCondition(availabilityCondition).build();// Define the Credential Access Boundary with all the relevant rules.CredentialAccessBoundarycredentialAccessBoundary=CredentialAccessBoundary.newBuilder().addRule(rule).build();// Create an instance of ClientSideCredentialAccessBoundaryFactory.ClientSideCredentialAccessBoundaryFactoryfactory=ClientSideCredentialAccessBoundaryFactory.newBuilder().setSourceCredential(sourceCredentials).build();// Generate the token and pass it to the Token Consumer.try{returnfactory.generateToken(credentialAccessBoundary);}catch(GeneralSecurityException|CelValidationExceptione){thrownewIOException("Error generating downscoped token",e);}}The following example shows how a token consumer can use a refresh handler toautomatically obtain and refresh downscoped tokens:
importcom.google.auth.oauth2.AccessToken;importcom.google.auth.oauth2.OAuth2CredentialsWithRefresh;importcom.google.cloud.storage.Blob;importcom.google.cloud.storage.Storage;importcom.google.cloud.storage.StorageOptions;importjava.io.IOException;publicstaticStringretrieveBlobWithDownscopedToken(finalStringbucketName,finalStringobjectName)throwsIOException{// You can pass an `OAuth2RefreshHandler` to `OAuth2CredentialsWithRefresh` which will allow the// library to seamlessly handle downscoped token refreshes on expiration.OAuth2CredentialsWithRefresh.OAuth2RefreshHandlerhandler=newOAuth2CredentialsWithRefresh.OAuth2RefreshHandler(){@OverridepublicAccessTokenrefreshAccessToken()throwsIOException{// The common pattern of usage is to have a token broker pass the downscoped short-lived// access tokens to a token consumer via some secure authenticated channel.// For illustration purposes, we are generating the downscoped token locally.// We want to test the ability to limit access to objects with a certain prefix string// in the resource bucket. objectName.substring(0, 3) is the prefix here. This field is// not required if access to all bucket resources are allowed. If access to limited// resources in the bucket is needed, this mechanism can be used.returnDownscopedAccessTokenGenerator.getTokenFromBroker(bucketName,objectName);}};AccessTokendownscopedToken=handler.refreshAccessToken();OAuth2CredentialsWithRefreshcredentials=OAuth2CredentialsWithRefresh.newBuilder().setAccessToken(downscopedToken).setRefreshHandler(handler).build();StorageOptionsoptions=StorageOptions.newBuilder().setCredentials(credentials).build();Storagestorage=options.getService();Blobblob=storage.get(bucketName,objectName);if(blob==null){returnnull;}returnnewString(blob.getContent());}Server-side token exchange
This section describes the following methods that you can use to exchange tokensthrough the service side approach:
Exchange and refresh the access token automatically using server-side approach
If you create the token broker and token consumer with one of the followinglanguages, you can use Google's authentication library to exchange and refreshtokens automatically using the server-side token generation approach:
Go
For Go, you can exchange and refresh tokens automatically with versionv0.0.0-20210819190943-2bc19b11175f or later of thegolang.org/x/oauth2 package.
To check your package version, run the following commandin your application directory:
golist-mgolang.org/x/oauth2The following example shows how a token broker can generate downscoped tokens:
import("context""fmt""golang.org/x/oauth2""golang.org/x/oauth2/google""golang.org/x/oauth2/google/downscope")// createDownscopedToken would be run on the token broker in order to generate// a downscoped access token that only grants access to objects whose name begins with prefix.// The token broker would then pass the newly created token to the requesting token consumer for use.funccreateDownscopedToken(bucketNamestring,prefixstring)error{// bucketName := "foo"// prefix := "profile-picture-"ctx:=context.Background()// A condition can optionally be provided to further restrict access permissions.condition:=downscope.AvailabilityCondition{Expression:"resource.name.startsWith('projects/_/buckets/"+bucketName+"/objects/"+prefix+"')",Title:prefix+" Only",Description:"Restricts a token to only be able to access objects that start with `"+prefix+"`",}// Initializes an accessBoundary with one Rule which restricts the downscoped// token to only be able to access the bucket "bucketName" and only grants it the// permission "storage.objectViewer".accessBoundary:=[]downscope.AccessBoundaryRule{{AvailableResource:"//storage.googleapis.com/projects/_/buckets/"+bucketName,AvailablePermissions:[]string{"inRole:roles/storage.objectViewer"},Condition:&condition,// Optional},}// This Source can be initialized in multiple ways; the following example uses// Application Default Credentials.varrootSourceoauth2.TokenSource// You must provide the "https://www.googleapis.com/auth/cloud-platform" scope.rootSource,err:=google.DefaultTokenSource(ctx,"https://www.googleapis.com/auth/cloud-platform")iferr!=nil{returnfmt.Errorf("failed to generate rootSource: %w",err)}// downscope.NewTokenSource constructs the token source with the configuration provided.dts,err:=downscope.NewTokenSource(ctx,downscope.DownscopingConfig{RootSource:rootSource,Rules:accessBoundary})iferr!=nil{returnfmt.Errorf("failed to generate downscoped token source: %w",err)}// Token() uses the previously declared TokenSource to generate a downscoped token.tok,err:=dts.Token()iferr!=nil{returnfmt.Errorf("failed to generate token: %w",err)}// Pass this token back to the token consumer._=tokreturnnil}The following example shows how a token consumer can use a refresh handler toautomatically obtain and refresh downscoped tokens:
import("context""fmt""io""golang.org/x/oauth2/google""golang.org/x/oauth2/google/downscope""cloud.google.com/go/storage""golang.org/x/oauth2""google.golang.org/api/option")// A token consumer should define their own tokenSource. In the Token() method,// it should send a query to a token broker requesting a downscoped token.// The token broker holds the root credential that is used to generate the// downscoped token.typelocalTokenSourcestruct{ctxcontext.ContextbucketNamestringbrokerURLstring}func(ltslocalTokenSource)Token()(*oauth2.Token,error){varremoteToken*oauth2.Token// Usually you would now retrieve remoteToken, an oauth2.Token, from token broker.// This snippet performs the same functionality locally.accessBoundary:=[]downscope.AccessBoundaryRule{{AvailableResource:"//storage.googleapis.com/projects/_/buckets/"+lts.bucketName,AvailablePermissions:[]string{"inRole:roles/storage.objectViewer"},},}rootSource,err:=google.DefaultTokenSource(lts.ctx,"https://www.googleapis.com/auth/cloud-platform")iferr!=nil{returnnil,fmt.Errorf("failed to generate rootSource: %w",err)}dts,err:=downscope.NewTokenSource(lts.ctx,downscope.DownscopingConfig{RootSource:rootSource,Rules:accessBoundary})iferr!=nil{returnnil,fmt.Errorf("failed to generate downscoped token source: %w",err)}// Token() uses the previously declared TokenSource to generate a downscoped token.remoteToken,err=dts.Token()iferr!=nil{returnnil,fmt.Errorf("failed to generate token: %w",err)}returnremoteToken,nil}// getObjectContents will read the contents of an object in Google Storage// named objectName, contained in the bucket "bucketName".funcgetObjectContents(outputio.Writer,bucketNamestring,objectNamestring)error{// bucketName := "foo"// prefix := "profile-picture-"ctx:=context.Background()thisTokenSource:=localTokenSource{ctx:ctx,bucketName:bucketName,brokerURL:"yourURL.com/internal/broker",}// Wrap the TokenSource in an oauth2.ReuseTokenSource to enable automatic refreshing.refreshableTS:=oauth2.ReuseTokenSource(nil,thisTokenSource)// You can now use the token source to access Google Cloud Storage resources as follows.storageClient,err:=storage.NewClient(ctx,option.WithTokenSource(refreshableTS))iferr!=nil{returnfmt.Errorf("failed to create the storage client: %w",err)}deferstorageClient.Close()bkt:=storageClient.Bucket(bucketName)obj:=bkt.Object(objectName)rc,err:=obj.NewReader(ctx)iferr!=nil{returnfmt.Errorf("failed to retrieve the object: %w",err)}deferrc.Close()data,err:=io.ReadAll(rc)iferr!=nil{returnfmt.Errorf("could not read the object's contents: %w",err)}// Data now contains the contents of the requested object.output.Write(data)returnnil}Java
For Java, you can exchange and refresh tokens automatically with version 1.1.0or later of thecom.google.auth:google-auth-library-oauth2-http artifact.
To check your artifact version, run the following Maven command in yourapplication directory:
mvndependency:list-DincludeArtifactIds=google-auth-library-oauth2-httpThe following example shows how a token broker can generate downscoped tokens:
publicstaticAccessTokengetTokenFromBroker(StringbucketName,StringobjectPrefix)throwsIOException{// Retrieve the source credentials from ADC.GoogleCredentialssourceCredentials=GoogleCredentials.getApplicationDefault().createScoped("https://www.googleapis.com/auth/cloud-platform");// Initialize the Credential Access Boundary rules.StringavailableResource="//storage.googleapis.com/projects/_/buckets/"+bucketName;// Downscoped credentials will have readonly access to the resource.StringavailablePermission="inRole:roles/storage.objectViewer";// Only objects starting with the specified prefix string in the object name will be allowed// read access.Stringexpression="resource.name.startsWith('projects/_/buckets/"+bucketName+"/objects/"+objectPrefix+"')";// Build the AvailabilityCondition.CredentialAccessBoundary.AccessBoundaryRule.AvailabilityConditionavailabilityCondition=CredentialAccessBoundary.AccessBoundaryRule.AvailabilityCondition.newBuilder().setExpression(expression).build();// Define the single access boundary rule using the above properties.CredentialAccessBoundary.AccessBoundaryRulerule=CredentialAccessBoundary.AccessBoundaryRule.newBuilder().setAvailableResource(availableResource).addAvailablePermission(availablePermission).setAvailabilityCondition(availabilityCondition).build();// Define the Credential Access Boundary with all the relevant rules.CredentialAccessBoundarycredentialAccessBoundary=CredentialAccessBoundary.newBuilder().addRule(rule).build();// Create the downscoped credentials.DownscopedCredentialsdownscopedCredentials=DownscopedCredentials.newBuilder().setSourceCredential(sourceCredentials).setCredentialAccessBoundary(credentialAccessBoundary).build();// Retrieve the token.// This will need to be passed to the Token Consumer.AccessTokenaccessToken=downscopedCredentials.refreshAccessToken();returnaccessToken;}The following example shows how a token consumer can use a refresh handler toautomatically obtain and refresh downscoped tokens:
publicstaticvoidtokenConsumer(finalStringbucketName,finalStringobjectName)throwsIOException{// You can pass an `OAuth2RefreshHandler` to `OAuth2CredentialsWithRefresh` which will allow the// library to seamlessly handle downscoped token refreshes on expiration.OAuth2CredentialsWithRefresh.OAuth2RefreshHandlerhandler=newOAuth2CredentialsWithRefresh.OAuth2RefreshHandler(){@OverridepublicAccessTokenrefreshAccessToken()throwsIOException{// The common pattern of usage is to have a token broker pass the downscoped short-lived// access tokens to a token consumer via some secure authenticated channel.// For illustration purposes, we are generating the downscoped token locally.// We want to test the ability to limit access to objects with a certain prefix string// in the resource bucket. objectName.substring(0, 3) is the prefix here. This field is// not required if access to all bucket resources are allowed. If access to limited// resources in the bucket is needed, this mechanism can be used.returngetTokenFromBroker(bucketName,objectName.substring(0,3));}};// Downscoped token retrieved from token broker.AccessTokendownscopedToken=handler.refreshAccessToken();// Create the OAuth2CredentialsWithRefresh from the downscoped token and pass a refresh handler// which will handle token expiration.// This will allow the consumer to seamlessly obtain new downscoped tokens on demand every time// token expires.OAuth2CredentialsWithRefreshcredentials=OAuth2CredentialsWithRefresh.newBuilder().setAccessToken(downscopedToken).setRefreshHandler(handler).build();// Use the credentials with the Cloud Storage SDK.StorageOptionsoptions=StorageOptions.newBuilder().setCredentials(credentials).build();Storagestorage=options.getService();// Call Cloud Storage APIs.Blobblob=storage.get(bucketName,objectName);Stringcontent=newString(blob.getContent());System.out.println("Retrieved object, "+objectName+", from bucket,"+bucketName+", with content: "+content);}Node.js
For Node.js, you can exchange and refresh tokens automatically with version7.9.0 or later of thegoogle-auth-library package.
To check your package version, run the following commandin your application directory:
npmlistgoogle-auth-libraryThe following example shows how a token broker can generate downscoped tokens:
// Imports the Google Auth libraries.const{GoogleAuth,DownscopedClient}=require('google-auth-library');/** * Simulates token broker generating downscoped tokens for specified bucket. * * @param bucketName The name of the Cloud Storage bucket. * @param objectPrefix The prefix string of the object name. This is used * to ensure access is restricted to only objects starting with this * prefix string. */asyncfunctiongetTokenFromBroker(bucketName,objectPrefix){constgoogleAuth=newGoogleAuth({scopes:'https://www.googleapis.com/auth/cloud-platform',});// Define the Credential Access Boundary object.constcab={// Define the access boundary.accessBoundary:{// Define the single access boundary rule.accessBoundaryRules:[{availableResource:`//storage.googleapis.com/projects/_/buckets/${bucketName}`,// Downscoped credentials will have readonly access to the resource.availablePermissions:['inRole:roles/storage.objectViewer'],// Only objects starting with the specified prefix string in the object name// will be allowed read access.availabilityCondition:{expression:"resource.name.startsWith('projects/_/buckets/"+`${bucketName}/objects/${objectPrefix}')`,},},],},};// Obtain an authenticated client via ADC.constclient=awaitgoogleAuth.getClient();// Use the client to create a DownscopedClient.constcabClient=newDownscopedClient(client,cab);// Refresh the tokens.constrefreshedAccessToken=awaitcabClient.getAccessToken();// This will need to be passed to the token consumer.returnrefreshedAccessToken;}The following example shows how a token consumer can provide a refresh handlerthat automatically obtains and refreshes downscoped tokens:
// Imports the Google Auth and Google Cloud libraries.const{OAuth2Client}=require('google-auth-library');const{Storage}=require('@google-cloud/storage');/** * Simulates token consumer generating calling GCS APIs using generated * downscoped tokens for specified bucket. * * @param bucketName The name of the Cloud Storage bucket. * @param objectName The name of the object in the Cloud Storage bucket * to read. */asyncfunctiontokenConsumer(bucketName,objectName){// Create the OAuth credentials (the consumer).constoauth2Client=newOAuth2Client();// We are defining a refresh handler instead of a one-time access// token/expiry pair.// This will allow the consumer to obtain new downscoped tokens on// demand every time a token is expired, without any additional code// changes.oauth2Client.refreshHandler=async()=>{// The common pattern of usage is to have a token broker pass the// downscoped short-lived access tokens to a token consumer via some// secure authenticated channel. For illustration purposes, we are// generating the downscoped token locally. We want to test the ability// to limit access to objects with a certain prefix string in the// resource bucket. objectName.substring(0, 3) is the prefix here. This// field is not required if access to all bucket resources are allowed.// If access to limited resources in the bucket is needed, this mechanism// can be used.constrefreshedAccessToken=awaitgetTokenFromBroker(bucketName,objectName.substring(0,3));return{access_token:refreshedAccessToken.token,expiry_date:refreshedAccessToken.expirationTime,};};conststorageOptions={projectId:process.env.GOOGLE_CLOUD_PROJECT,authClient:oauth2Client,};conststorage=newStorage(storageOptions);constdownloadFile=awaitstorage.bucket(bucketName).file(objectName).download();console.log(downloadFile.toString('utf8'));}Python
For Python, you can exchange and refresh tokens automatically with version 2.0.0or later of thegoogle-auth package.
To check your package version, run the following commandin the environment where the package is installed:
pipshowgoogle-authThe following example shows how a token broker can generate downscoped tokens:
importgoogle.authfromgoogle.authimportdownscopedfromgoogle.auth.transportimportrequestsdefget_token_from_broker(bucket_name,object_prefix):"""Simulates token broker generating downscoped tokens for specified bucket. Args: bucket_name (str): The name of the Cloud Storage bucket. object_prefix (str): The prefix string of the object name. This is used to ensure access is restricted to only objects starting with this prefix string. Returns: Tuple[str, datetime.datetime]: The downscoped access token and its expiry date. """# Initialize the Credential Access Boundary rules.available_resource=f"//storage.googleapis.com/projects/_/buckets/{bucket_name}"# Downscoped credentials will have readonly access to the resource.available_permissions=["inRole:roles/storage.objectViewer"]# Only objects starting with the specified prefix string in the object name# will be allowed read access.availability_expression=("resource.name.startsWith('projects/_/buckets/{}/objects/{}')".format(bucket_name,object_prefix))availability_condition=downscoped.AvailabilityCondition(availability_expression)# Define the single access boundary rule using the above properties.rule=downscoped.AccessBoundaryRule(available_resource=available_resource,available_permissions=available_permissions,availability_condition=availability_condition,)# Define the Credential Access Boundary with all the relevant rules.credential_access_boundary=downscoped.CredentialAccessBoundary(rules=[rule])# Retrieve the source credentials via ADC.source_credentials,_=google.auth.default()ifsource_credentials.requires_scopes:source_credentials=source_credentials.with_scopes(["https://www.googleapis.com/auth/cloud-platform"])# Create the downscoped credentials.downscoped_credentials=downscoped.Credentials(source_credentials=source_credentials,credential_access_boundary=credential_access_boundary,)# Refresh the tokens.downscoped_credentials.refresh(requests.Request())# These values will need to be passed to the token consumer.access_token=downscoped_credentials.tokenexpiry=downscoped_credentials.expiryreturn(access_token,expiry)The following example shows how a token consumer can provide a refresh handlerthat automatically obtains and refreshes downscoped tokens:
fromgoogle.cloudimportstoragefromgoogle.oauth2importcredentialsdeftoken_consumer(bucket_name,object_name):"""Tests token consumer readonly access to the specified object. Args: bucket_name (str): The name of the Cloud Storage bucket. object_name (str): The name of the object in the Cloud Storage bucket to read. """# Create the OAuth credentials from the downscoped token and pass a# refresh handler to handle token expiration. We are passing a# refresh_handler instead of a one-time access token/expiry pair.# This will allow the consumer to obtain new downscoped tokens on# demand every time a token is expired, without any additional code# changes.defrefresh_handler(request,scopes=None):# The common pattern of usage is to have a token broker pass the# downscoped short-lived access tokens to a token consumer via some# secure authenticated channel.# For illustration purposes, we are generating the downscoped token# locally.# We want to test the ability to limit access to objects with a certain# prefix string in the resource bucket. object_name[0:3] is the prefix# here. This field is not required if access to all bucket resources are# allowed. If access to limited resources in the bucket is needed, this# mechanism can be used.returnget_token_from_broker(bucket_name,object_prefix=object_name[0:3])creds=credentials.Credentials(None,scopes=["https://www.googleapis.com/auth/cloud-platform"],refresh_handler=refresh_handler,)# Initialize a Cloud Storage client with the oauth2 credentials.storage_client=storage.Client(credentials=creds)# The token broker has readonly access to the specified bucket object.bucket=storage_client.bucket(bucket_name)blob=bucket.blob(object_name)print(blob.download_as_bytes().decode("utf-8"))Exchange and refresh the access token manually
A token broker can use the Security Token Service API to exchange an accesstoken for a downscoped access token. It can then provide the downscoped tokento a token consumer.
To exchange the access token, use the following HTTP method and URL:
POST https://sts.googleapis.com/v1/token
Set theContent-Type header in the request toapplication/x-www-form-urlencoded. Include the following fields in the requestbody:
| Fields | |
|---|---|
grant_type |
Use the value |
options |
A JSON-formatted Credential Access Boundary, encoded withpercent encoding. |
requested_token_type |
Use the value |
subject_token |
The OAuth 2.0 access token that you want to exchange. |
subject_token_type |
Use the value |
The response is a JSON object that contains the following fields:
| Fields | |
|---|---|
access_token |
A downscoped OAuth 2.0 access token that respects the Credential Access Boundary. |
expires_in |
The amount of time until the downscoped token expires, in seconds. This field is included only if the original access token represents a service account. When this field is not included, the downscoped token has the same time to expire as the original access token. |
issued_token_type |
Contains the value |
token_type |
Contains the value |
For example, if a JSON-formatted Credential Access Boundary is stored in the file./access-boundary.json, you can use the followingcurl command to exchange the access token. Replaceoriginal-token with the original access token:
curl-H"Content-Type:application/x-www-form-urlencoded"\-XPOST\https://sts.googleapis.com/v1/token\-d"grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token_type=urn:ietf:params:oauth:token-type:access_token&requested_token_type=urn:ietf:params:oauth:token-type:access_token&subject_token=original-token"\--data-urlencode"options=$(cat./access-boundary.json)"
The response is similar to the following example:
{"access_token":"ya29.dr.AbCDeFg-123456...","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600}When a token consumer requests a downscoped token, the token broker respondswith both the downscoped token and the number of seconds until it expires. Ifthe token has expired, the server rejects the request. To refresh thedownscoped token, the consumer can request a downscoped token from the brokerbefore the existing token expires.
What's next
- Learn aboutaccess control for Cloud Storage.
- Create ashort-lived service account credential.
- Create an OAuth 2.0 access token for a service account with one of the following methods:
- Create an OAuth 2.0 access tokenfor a user.
- See thepermissions in each predefined role.
- Learn aboutcustom roles.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2026-02-18 UTC.