Downscoping with Credential Access Boundaries

This page explains how to use Credential Access Boundaries to downscope, or restrict, the Identity and Access Management (IAM) permissions that a short-lived credential can use.

To downscope permissions, you define a Credential Access Boundary that specifies which resources the short-lived credential can access, as well as an upper bound on the permissions that are available on each resource. You can then create a short-lived credential, then exchange it for a new credential that respects the Credential Access Boundary.

The following example shows a simple Credential Access Boundary. It applies to the Cloud Storage bucket example-bucket, and it sets the upper bound to the permissions included in the Storage Object Viewer role (roles/storage.objectViewer):

{
  "accessBoundaryRules": [
    {
      "availablePermissions": [
        "inRole:roles/storage.objectViewer"
      ],
      "availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket"
    }
  ]
}

If you need to give members a distinct set of permissions for each session, using Credential Access Boundaries can be more efficient than creating many different service accounts and granting each service account a different set of roles. For example, if one of your customers needs to access Cloud Storage data that you control, you can create a service account that can access every Cloud Storage bucket that you own, then apply a Credential Access Boundary that only allows access to the bucket with your customer's data.

Before you begin

Before you use Credential Access Boundaries, make sure you meet the following requirements:

  • You need to downscope permissions only for Cloud Storage, not for other Google Cloud services.

    If you need to downscope permissions for additional Google Cloud services, you can create multiple service accounts and grant a different set of roles to each service account.

  • You need to downscope permissions at the bucket level, not at the object level.

  • You use uniform bucket-level access to manage access to your Cloud Storage resources.

  • You can use OAuth 2.0 access tokens for authentication. Other types of short-lived credentials do not support Credential Access Boundaries.

Creating a downscoped short-lived credential

To create an OAuth 2.0 access token with downscoped permissions, follow these steps:

  1. Grant the appropriate IAM roles to a user or service account.
  2. Define a Credential Access Boundary that sets an upper bound on the permissions that are available to the user or service account.
  3. Create an OAuth 2.0 access token for the user or service account.
  4. Exchange the OAuth 2.0 access token for a new token that respects the Credential Access Boundary.

You can then use the new, downscoped OAuth 2.0 access token to authenticate requests to Cloud Storage.

Granting IAM roles

A Credential Access Boundary sets an upper bound on the available permissions for a resource. It can subtract permissions from a member, but it cannot add permissions that the member does not already have.

As a result, you must also grant roles to the member that provide the permissions they need, either on a Cloud Storage bucket or on a higher-level resource, such as the project.

For example, suppose you need to create a downscoped short-lived credential that allows 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.create permission, such as the Storage Object Creator role (roles/storage.objectCreator). The Credential Access Boundary must also include this permission.
  • You can also grant a role that includes more permissions, such as the Storage Object Admin role (roles/storage.objectAdmin). The service account can use only the permissions that appear in both the role grant and the Credential Access Boundary.

Defining a Credential Access Boundary

A Credential Access Boundary is a JSON object that contains a list of access boundary rules. Each rule contains the following information:

  • The resource that the rule applies to
  • The upper bound of the permissions that are available on that resource

If you apply a Credential Access Boundary to a short-lived credential, then the credential can access only the resources in the Credential Access Boundary. No permissions are available on other resources.

A Credential Access Boundary can contain up to 10 access boundary rules. You can apply only one Credential Access Boundary to each short-lived credential.

A Credential Access Boundary contains the following fields:

Fields
accessBoundaryRules[]

object

A list of access boundary rules to apply to a short-lived credential.

accessBoundaryRules[].availablePermissions[]

string

A list that defines the upper bound on the available permissions for the resource.

Each value is the identifier for an IAM predefined role or custom role, with the prefix inRole:. For example: inRole:roles/storage.objectViewer. Only the permissions in these roles will be available.

accessBoundaryRules[].availableResource

string

The full resource name of the Cloud Storage bucket that the rule applies to. Use the format //storage.googleapis.com/projects/_/buckets/bucket-name.

The following example shows a Credential Access Boundary that includes rules for multiple resources:

  • The Cloud Storage bucket example-bucket-1. For this bucket, only the permissions in the Storage Object Viewer role (roles/storage.objectViewer) are available.
  • The Cloud Storage bucket example-bucket-2. For this bucket, only the permissions in the Storage Object Creator role (roles/storage.objectCreator) are available.
{
  "accessBoundaryRules": [
    {
      "availablePermissions": [
        "inRole:roles/storage.objectViewer"
      ],
      "availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket-1"
    },
    {
      "availablePermissions": [
        "inRole:roles/storage.objectCreator"
      ],
      "availableResource": "//storage.googleapis.com/projects/_/buckets/example-bucket-2"
    }
  ]
}

Create a JSON file that defines a Credential Access Boundary. You will use this file in a later step.

Creating an OAuth 2.0 access token

Before you create a downscoped short-lived credential, you must create a normal OAuth 2.0 access token. You can then exchange the normal credential for a downscoped credential. When you create the access token, use the OAuth 2.0 scope https://www.googleapis.com/auth/cloud-platform.

To create an access token for a service account, you can complete the server-to-server OAuth 2.0 flow, or you can use the Service Account Credentials API to generate an OAuth 2.0 access token.

To create an access token for a user, see Obtaining OAuth 2.0 access tokens. You can also use the OAuth 2.0 Playground to create an access token for your own Google Account.

Exchanging the OAuth 2.0 access token

After you create an OAuth 2.0 access token, you can exchange the access token for a new token that respects the Credential Access Boundary. You exchange the access token through the security token service, which is part of Identity Platform.

To exchange the access token, use the following HTTP method and URL:

POST https://securetoken.googleapis.com/v2beta1/token

Set the Content-Type header in the request to application/x-www-form-urlencoded. Include the following fields in the request body:

Fields
access_boundary

string

The percent-encoded Credential Access Boundary.

grant_type

string

Use the value urn:ietf:params:oauth:grant-type:token-exchange.

requested_token_type

string

Use the value urn:ietf:params:oauth:token-type:access_token.

subject_token

string

The OAuth 2.0 access token that you want to exchange.

subject_token_type

string

Use the value urn:ietf:params:oauth:token-type:access_token.

The response is a JSON object that contains the following fields:

Fields
access_token

string

A new OAuth 2.0 access token that respects the Credential Access Boundary.

expires_in

number

The amount of time until the new access token expires, in seconds.

This field is present only if the original access token represents a service account. When this field is not present, the new access token has the same time to expire as the original access token.

issued_token_type

string

Contains the value urn:ietf:params:oauth:token-type:access_token.

token_type

string

Contains the value Bearer.

For example, if the Credential Access Boundary is stored in the file ./access-boundary.json, you can use the following curl command to exchange the access token. Replace original-token with the original access token:

curl -H "Content-Type:application/x-www-form-urlencoded" \
    -X POST \
    https://securetoken.googleapis.com/v2beta1/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 "access_boundary=$(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
}

What's next