Skip to content

Commit

Permalink
feat: adds README, samples and integration tests for downscoping with…
Browse files Browse the repository at this point in the history
… CAB (#1311)

* Add description, samples and integration tests for CAB

* fix lints

* fix copyright format and import

* add storage dependency

* try on header fix

* fix some comments

* update readme

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* change yaml and fixes comments

* 🦉 Updates from OwlBot

See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md

* revertbucketName and objectName

* revert logic in try blocks

* tweak child exec process

* fix lint

* set bucket name and object name environment variable

Co-authored-by: Leo <39062083+lsirac@users.noreply.github.com>
Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed Dec 15, 2021
1 parent e91472a commit 4549116
Show file tree
Hide file tree
Showing 7 changed files with 509 additions and 1 deletion.
113 changes: 113 additions & 0 deletions .readme-partials.yaml
Expand Up @@ -9,6 +9,7 @@ body: |-
- [Google Compute](#compute) - Directly use a service account on Google Cloud Platform. Useful for server->server or server->API communication.
- [Workload Identity Federation](#workload-identity-federation) - Use workload identity federation to access Google Cloud resources from Amazon Web Services (AWS), Microsoft Azure or any identity provider that supports OpenID Connect (OIDC).
- [Impersonated Credentials Client](#impersonated-credentials-client) - access protected resources on behalf of another service account.
- [Downscoped Client](#downscoped-client) - Use Downscoped Client with Credential Access Boundary to generate a short-lived credential with downscoped, restricted IAM permissions that can use for Cloud Storage.
## Application Default Credentials
This library provides an implementation of [Application Default Credentials](https://cloud.google.com/docs/authentication/getting-started)for Node.js. The [Application Default Credentials](https://cloud.google.com/docs/authentication/getting-started) provide a simple way to get authorization credentials for use in calling Google APIs.
Expand Down Expand Up @@ -676,3 +677,115 @@ body: |-
main();
```
## Downscoped Client
[Downscoping with Credential Access Boundaries](https://cloud.google.com/iam/docs/downscoping-short-lived-credentials) is used to restrict the Identity and Access Management (IAM) permissions that a short-lived credential can use.
The `DownscopedClient` class can be used to produce a downscoped access token from a
`CredentialAccessBoundary` and a source credential. The Credential Access Boundary specifies which resources the newly created credential can access, as well as an upper bound on the permissions that are available on each resource. Using downscoped credentials ensures tokens in flight always have the least privileges, e.g. Principle of Least Privilege.
> Notice: Only Cloud Storage supports Credential Access Boundaries for now.
### Sample Usage
There are two entities needed to generate and use credentials generated from
Downscoped Client with Credential Access Boundaries.
- Token broker: This is the entity with elevated permissions. This entity has the permissions needed to generate downscoped tokens. The common pattern of usage is to have a token broker with elevated access generate these downscoped credentials from higher access source credentials and pass the downscoped short-lived access tokens to a token consumer via some secure authenticated channel for limited access to Google Cloud Storage resources.
``` js
const {GoogleAuth, DownscopedClient} = require('google-auth-library');
// Define CAB rules which will restrict the downscoped token to have readonly
// access to objects starting with "customer-a" in bucket "bucket_name".
const cabRules = {
accessBoundary: {
accessBoundaryRules: [
{
availableResource: `//storage.googleapis.com/projects/_/buckets/bucket_name`,
availablePermissions: ['inRole:roles/storage.objectViewer'],
availabilityCondition: {
expression:
`resource.name.startsWith('projects/_/buckets/` +
`bucket_name/objects/customer-a)`
}
},
],
},
};
// This will use ADC to get the credentials used for the downscoped client.
const googleAuth = new GoogleAuth({
scopes: ['https://www.googleapis.com/auth/cloud-platform']
});
// Obtain an authenticated client via ADC.
const client = await googleAuth.getClient();
// Use the client to create a DownscopedClient.
const cabClient = new DownscopedClient(client, cab);
// Refresh the tokens.
const refreshedAccessToken = await cabClient.getAccessToken();
// This will need to be passed to the token consumer.
access_token = refreshedAccessToken.token;
expiry_date = refreshedAccessToken.expirationTime;
```
A token broker can be set up on a server in a private network. Various workloads
(token consumers) in the same network will send authenticated requests to that broker for downscoped tokens to access or modify specific google cloud storage buckets.
The broker will instantiate downscoped credentials instances that can be used to generate short lived downscoped access tokens which will be passed to the token consumer.
- Token consumer: This is the consumer of the downscoped tokens. This entity does not have the direct ability to generate access tokens and instead relies on the token broker to provide it with downscoped tokens to run operations on GCS buckets. It is assumed that the downscoped token consumer may have its own mechanism to authenticate itself with the token broker.
``` js
const {OAuth2Client} = require('google-auth-library');
const {Storage} = require('@google-cloud/storage');
// Create the OAuth credentials (the consumer).
const oauth2Client = new OAuth2Client();
// 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.
const refreshedAccessToken = await cabClient.getAccessToken();
return {
access_token: refreshedAccessToken.token,
expiry_date: refreshedAccessToken.expirationTime,
}
};
// Use the consumer client to define storageOptions and create a GCS object.
const storageOptions = {
projectId: 'my_project_id',
authClient: {
sign: () => Promise.reject('unsupported'),
getCredentials: () => Promise.reject(),
request: (opts, callback) => {
return oauth2Client.request(opts, callback);
},
authorizeRequest: async (opts) => {
opts = opts || {};
const url = opts.url || opts.uri;
const headers = await oauth2Client.getRequestHeaders(url);
opts.headers = Object.assign(opts.headers || {}, headers);
return opts;
},
},
};
const storage = new Storage(storageOptions);
const downloadFile = await storage
.bucket('bucket_name')
.file('customer-a-data.txt')
.download();
console.log(downloadFile.toString('utf8'));
main().catch(console.error);
```
115 changes: 115 additions & 0 deletions README.md
Expand Up @@ -54,6 +54,7 @@ This library provides a variety of ways to authenticate to your Google services.
- [Google Compute](#compute) - Directly use a service account on Google Cloud Platform. Useful for server->server or server->API communication.
- [Workload Identity Federation](#workload-identity-federation) - Use workload identity federation to access Google Cloud resources from Amazon Web Services (AWS), Microsoft Azure or any identity provider that supports OpenID Connect (OIDC).
- [Impersonated Credentials Client](#impersonated-credentials-client) - access protected resources on behalf of another service account.
- [Downscoped Client](#downscoped-client) - Use Downscoped Client with Credential Access Boundary to generate a short-lived credential with downscoped, restricted IAM permissions that can use for Cloud Storage.

## Application Default Credentials
This library provides an implementation of [Application Default Credentials](https://cloud.google.com/docs/authentication/getting-started)for Node.js. The [Application Default Credentials](https://cloud.google.com/docs/authentication/getting-started) provide a simple way to get authorization credentials for use in calling Google APIs.
Expand Down Expand Up @@ -721,6 +722,119 @@ async function main() {
main();
```

## Downscoped Client

[Downscoping with Credential Access Boundaries](https://cloud.google.com/iam/docs/downscoping-short-lived-credentials) is used to restrict the Identity and Access Management (IAM) permissions that a short-lived credential can use.

The `DownscopedClient` class can be used to produce a downscoped access token from a
`CredentialAccessBoundary` and a source credential. The Credential Access Boundary specifies which resources the newly created credential can access, as well as an upper bound on the permissions that are available on each resource. Using downscoped credentials ensures tokens in flight always have the least privileges, e.g. Principle of Least Privilege.

> Notice: Only Cloud Storage supports Credential Access Boundaries for now.
### Sample Usage
There are two entities needed to generate and use credentials generated from
Downscoped Client with Credential Access Boundaries.

- Token broker: This is the entity with elevated permissions. This entity has the permissions needed to generate downscoped tokens. The common pattern of usage is to have a token broker with elevated access generate these downscoped credentials from higher access source credentials and pass the downscoped short-lived access tokens to a token consumer via some secure authenticated channel for limited access to Google Cloud Storage resources.

``` js
const {GoogleAuth, DownscopedClient} = require('google-auth-library');
// Define CAB rules which will restrict the downscoped token to have readonly
// access to objects starting with "customer-a" in bucket "bucket_name".
const cabRules = {
accessBoundary: {
accessBoundaryRules: [
{
availableResource: `//storage.googleapis.com/projects/_/buckets/bucket_name`,
availablePermissions: ['inRole:roles/storage.objectViewer'],
availabilityCondition: {
expression:
`resource.name.startsWith('projects/_/buckets/` +
`bucket_name/objects/customer-a)`
}
},
],
},
};

// This will use ADC to get the credentials used for the downscoped client.
const googleAuth = new GoogleAuth({
scopes: ['https://www.googleapis.com/auth/cloud-platform']
});

// Obtain an authenticated client via ADC.
const client = await googleAuth.getClient();

// Use the client to create a DownscopedClient.
const cabClient = new DownscopedClient(client, cab);

// Refresh the tokens.
const refreshedAccessToken = await cabClient.getAccessToken();

// This will need to be passed to the token consumer.
access_token = refreshedAccessToken.token;
expiry_date = refreshedAccessToken.expirationTime;
```

A token broker can be set up on a server in a private network. Various workloads
(token consumers) in the same network will send authenticated requests to that broker for downscoped tokens to access or modify specific google cloud storage buckets.

The broker will instantiate downscoped credentials instances that can be used to generate short lived downscoped access tokens which will be passed to the token consumer.

- Token consumer: This is the consumer of the downscoped tokens. This entity does not have the direct ability to generate access tokens and instead relies on the token broker to provide it with downscoped tokens to run operations on GCS buckets. It is assumed that the downscoped token consumer may have its own mechanism to authenticate itself with the token broker.

``` js
const {OAuth2Client} = require('google-auth-library');
const {Storage} = require('@google-cloud/storage');

// Create the OAuth credentials (the consumer).
const oauth2Client = new OAuth2Client();
// 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.
const refreshedAccessToken = await cabClient.getAccessToken();
return {
access_token: refreshedAccessToken.token,
expiry_date: refreshedAccessToken.expirationTime,
}
};

// Use the consumer client to define storageOptions and create a GCS object.
const storageOptions = {
projectId: 'my_project_id',
authClient: {
sign: () => Promise.reject('unsupported'),
getCredentials: () => Promise.reject(),
request: (opts, callback) => {
return oauth2Client.request(opts, callback);
},
authorizeRequest: async (opts) => {
opts = opts || {};
const url = opts.url || opts.uri;
const headers = await oauth2Client.getRequestHeaders(url);
opts.headers = Object.assign(opts.headers || {}, headers);
return opts;
},
},
};

const storage = new Storage(storageOptions);

const downloadFile = await storage
.bucket('bucket_name')
.file('customer-a-data.txt')
.download();
console.log(downloadFile.toString('utf8'));

main().catch(console.error);
```


## Samples

Expand All @@ -731,6 +845,7 @@ Samples are in the [`samples/`](https://github.com/googleapis/google-auth-librar
| Adc | [source code](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/adc.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/google-auth-library-nodejs&page=editor&open_in_editor=samples/adc.js,samples/README.md) |
| Compute | [source code](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/compute.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/google-auth-library-nodejs&page=editor&open_in_editor=samples/compute.js,samples/README.md) |
| Credentials | [source code](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/credentials.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/google-auth-library-nodejs&page=editor&open_in_editor=samples/credentials.js,samples/README.md) |
| Downscopedclient | [source code](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/downscopedclient.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/google-auth-library-nodejs&page=editor&open_in_editor=samples/downscopedclient.js,samples/README.md) |
| Headers | [source code](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/headers.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/google-auth-library-nodejs&page=editor&open_in_editor=samples/headers.js,samples/README.md) |
| ID Tokens for Identity-Aware Proxy (IAP) | [source code](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/idtokens-iap.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/google-auth-library-nodejs&page=editor&open_in_editor=samples/idtokens-iap.js,samples/README.md) |
| ID Tokens for Serverless | [source code](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/idtokens-serverless.js) | [![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/google-auth-library-nodejs&page=editor&open_in_editor=samples/idtokens-serverless.js,samples/README.md) |
Expand Down
18 changes: 18 additions & 0 deletions samples/README.md
Expand Up @@ -15,6 +15,7 @@ This is Google's officially supported [node.js](http://nodejs.org/) client libra
* [Adc](#adc)
* [Compute](#compute)
* [Credentials](#credentials)
* [Downscopedclient](#downscopedclient)
* [Headers](#headers)
* [ID Tokens for Identity-Aware Proxy (IAP)](#id-tokens-for-identity-aware-proxy-iap)
* [ID Tokens for Serverless](#id-tokens-for-serverless)
Expand Down Expand Up @@ -93,6 +94,23 @@ __Usage:__



### Downscopedclient

View the [source code](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/downscopedclient.js).

[![Open in Cloud Shell][shell_img]](https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/googleapis/google-auth-library-nodejs&page=editor&open_in_editor=samples/downscopedclient.js,samples/README.md)

__Usage:__


`node samples/downscopedclient.js`


-----




### Headers

View the [source code](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/headers.js).
Expand Down
106 changes: 106 additions & 0 deletions samples/downscopedclient.js
@@ -0,0 +1,106 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

/**
* Imports the Google Auth and Google Cloud libraries.
*/
const {
OAuth2Client,
GoogleAuth,
DownscopedClient,
} = require('google-auth-library');
const {Storage} = require('@google-cloud/storage');

/**
* The following sample demonstrates how to initialize a DownscopedClient using
* a credential access boundary and a client obtained via ADC. The
* DownscopedClient is used to create downscoped tokens which can be consumed
* via the OAuth2Client. A refresh handler is used to obtain new downscoped
* tokens seamlessly when they expire. Then the oauth2Client is used to define
* a cloud storage object and call GCS APIs to access specified object and
* print the contents.
*/
async function main() {
const bucketName = process.env.BUCKET_NAME;
const objectName = process.env.OBJECT_NAME;
// Defines a credential access boundary that grants objectViewer access in
// the specified bucket.
const cab = {
accessBoundary: {
accessBoundaryRules: [
{
availableResource: `//storage.googleapis.com/projects/_/buckets/${bucketName}`,
availablePermissions: ['inRole:roles/storage.objectViewer'],
availabilityCondition: {
expression:
"resource.name.startsWith('projects/_/buckets/" +
`${bucketName}/objects/${objectName}')`,
},
},
],
},
};

const oauth2Client = new OAuth2Client();
const googleAuth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/cloud-platform',
});
const projectId = await googleAuth.getProjectId();
// Obtain an authenticated client via ADC.
const client = await googleAuth.getClient();
// Use the client to generate a DownscopedClient.
const cabClient = new DownscopedClient(client, cab);
// Define a refreshHandler that will be used to refresh the downscoped token
// when it expires.
oauth2Client.refreshHandler = async () => {
const refreshedAccessToken = await cabClient.getAccessToken();
return {
access_token: refreshedAccessToken.token,
expiry_date: refreshedAccessToken.expirationTime,
};
};

const storageOptions = {
projectId,
authClient: {
getCredentials: async () => {
Promise.reject();
},
request: opts => {
return oauth2Client.request(opts);
},
sign: () => {
Promise.reject('unsupported');
},
authorizeRequest: async opts => {
opts = opts || {};
const url = opts.url || opts.uri;
const headers = await oauth2Client.getRequestHeaders(url);
opts.headers = Object.assign(opts.headers || {}, headers);
return opts;
},
},
};

const storage = new Storage(storageOptions);
const downloadFile = await storage
.bucket(bucketName)
.file(objectName)
.download();
console.log(downloadFile.toString('utf8'));
}

main().catch(console.error);

0 comments on commit 4549116

Please sign in to comment.