Skip to content

Commit

Permalink
refactor!: getOptions() no longer accepts GoogleAuthOptions (#749)
Browse files Browse the repository at this point in the history
  • Loading branch information
bcoe authored Jul 23, 2019
1 parent 9f31ed7 commit ba58e3b
Show file tree
Hide file tree
Showing 8 changed files with 86 additions and 47 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,22 @@ Before making your API call, you must be sure the API you're calling has been en

Rather than manually creating an OAuth2 client, JWT client, or Compute client, the auth library can create the correct credential type for you, depending upon the environment your code is running under.

For example, a JWT auth client will be created when your code is running on your local developer machine, and a Compute client will be created when the same code is running on Google Cloud Platform. If you need a specific set of scopes, you can pass those in the form of a string or an array into the `auth.getClient` method.
For example, a JWT auth client will be created when your code is running on your local developer machine, and a Compute client will be created when the same code is running on Google Cloud Platform. If you need a specific set of scopes, you can pass those in the form of a string or an array to the `GoogleAuth` constructor.

The code below shows how to retrieve a default credential type, depending upon the runtime environment.

```js
const {auth} = require('google-auth-library');
const {GoogleAuth} = require('google-auth-library');

/**
* Instead of specifying the type of client you'd like to use (JWT, OAuth2, etc)
* this library will automatically choose the right client based on the environment.
*/
async function main() {
const client = await auth.getClient({
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/cloud-platform'
});
const client = await auth.getClient();
const projectId = await auth.getProjectId();
const url = `https://www.googleapis.com/dns/v1/projects/${projectId}`;
const res = await client.request({ url });
Expand Down Expand Up @@ -168,6 +169,7 @@ main().catch(console.error);
```

#### Handling token events

This library will automatically obtain an `access_token`, and automatically refresh the `access_token` if a `refresh_token` is present. The `refresh_token` is only returned on the [first authorization](https://github.com/googleapis/google-api-nodejs-client/issues/750#issuecomment-304521450), so if you want to make sure you store it safely. An easy way to make sure you always store the most recent tokens is to use the `tokens` event:

```js
Expand Down
5 changes: 3 additions & 2 deletions samples/adc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,16 @@
/**
* Import the GoogleAuth library, and create a new GoogleAuth client.
*/
const {auth} = require('google-auth-library');
const {GoogleAuth} = require('google-auth-library');

/**
* Acquire a client, and make a request to an API that's enabled by default.
*/
async function main() {
const client = await auth.getClient({
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/cloud-platform',
});
const client = await auth.getClient();
const projectId = await auth.getProjectId();
const url = `https://www.googleapis.com/dns/v1/projects/${projectId}`;
const res = await client.request({url});
Expand Down
5 changes: 3 additions & 2 deletions samples/credentials.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
* Import the GoogleAuth library, and create a new GoogleAuth client.
*/
const {auth} = require('google-auth-library');
const {GoogleAuth} = require('google-auth-library');

/**
* This sample demonstrates passing a `credentials` object directly into the
Expand All @@ -33,13 +33,14 @@ async function main() {
this sample.
`);
}
const client = await auth.getClient({
const auth = new GoogleAuth({
credentials: {
client_email: clientEmail,
private_key: privateKey,
},
scopes: 'https://www.googleapis.com/auth/cloud-platform',
});
const client = await auth.getClient();
const projectId = await auth.getProjectId();
const url = `https://www.googleapis.com/dns/v1/projects/${projectId}`;
const res = await client.request({url});
Expand Down
11 changes: 6 additions & 5 deletions samples/headers.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
* Import the GoogleAuth library, and create a new GoogleAuth client.
*/
const {auth} = require('google-auth-library');
const {GoogleAuth} = require('google-auth-library');
const fetch = require('node-fetch');

/**
Expand All @@ -25,14 +25,15 @@ const fetch = require('node-fetch');
* node-fetch, but you could use any HTTP client you like.
*/
async function main() {
// create auth instance with custom scopes.
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/cloud-platform',
});
const projectId = await auth.getProjectId();
const url = `https://www.googleapis.com/dns/v1/projects/${projectId}`;

// obtain an authenticated client
const client = await auth.getClient({
scopes: 'https://www.googleapis.com/auth/cloud-platform',
});

const client = await auth.getClient();
// Use the client to get authenticated request headers
const headers = await client.getRequestHeaders();
console.log('Headers:');
Expand Down
5 changes: 3 additions & 2 deletions samples/keepalive.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@
/**
* Import the GoogleAuth library, and create a new GoogleAuth client.
*/
const {auth} = require('google-auth-library');
const {GoogleAuth} = require('google-auth-library');
const https = require('https');

/**
* Acquire a client, and make a request to an API that's enabled by default.
*/
async function main() {
const client = await auth.getClient({
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/cloud-platform',
});
const client = await auth.getClient();
const projectId = await auth.getProjectId();
const url = `https://www.googleapis.com/dns/v1/projects/${projectId}`;

Expand Down
5 changes: 3 additions & 2 deletions samples/keyfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
/**
* Import the GoogleAuth library, and create a new GoogleAuth client.
*/
const {auth} = require('google-auth-library');
const {GoogleAuth} = require('google-auth-library');

/**
* Acquire a client, and make a request to an API that's enabled by default.
Expand All @@ -25,10 +25,11 @@ async function main(
// Full path to the sevice account credential
keyFile = process.env.GOOGLE_APPLICATION_CREDENTIALS
) {
const client = await auth.getClient({
const auth = new GoogleAuth({
keyFile: keyFile,
scopes: 'https://www.googleapis.com/auth/cloud-platform',
});
const client = await auth.getClient();
const projectId = await auth.getProjectId();
const url = `https://www.googleapis.com/dns/v1/projects/${projectId}`;
const res = await client.request({url});
Expand Down
52 changes: 36 additions & 16 deletions src/auth/googleauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export interface CredentialCallback {
(err: Error | null, result?: UserRefreshClient | JWT): void;
}

interface DeprecatedGetClientOptions {}

export interface ADCCallback {
(
err: Error | null,
Expand Down Expand Up @@ -441,7 +443,6 @@ export class GoogleAuth {
'Must pass in a JSON object containing the Google auth settings.'
);
}
this.jsonContent = json;
options = options || {};
if (json.type === 'authorized_user') {
client = new UserRefreshClient(options);
Expand All @@ -453,6 +454,33 @@ export class GoogleAuth {
return client;
}

/**
* Return a JWT or UserRefreshClient from JavaScript object, caching both the
* object used to instantiate and the client.
* @param json The input object.
* @param options The JWT or UserRefresh options for the client
* @returns JWT or UserRefresh Client with data
*/
private _cacheClientFromJSON(
json: JWTInput,
options?: RefreshOptions
): JWT | UserRefreshClient {
let client: UserRefreshClient | JWT;
// create either a UserRefreshClient or JWT client.
options = options || {};
if (json.type === 'authorized_user') {
client = new UserRefreshClient(options);
} else {
(options as JWTOptions).scopes = this.scopes;
client = new JWT(options);
}
client.fromJSON(json);
// cache both raw data used to instantiate client and client itself.
this.jsonContent = json;
this.cachedCredential = client;
return this.cachedCredential;
}

/**
* Create a credentials instance using the given input stream.
* @param inputStream The input stream.
Expand Down Expand Up @@ -508,7 +536,7 @@ export class GoogleAuth {
.on('end', () => {
try {
const data = JSON.parse(s);
const r = this.fromJSON(data, options);
const r = this._cacheClientFromJSON(data, options);
return resolve(r);
} catch (err) {
return reject(err);
Expand Down Expand Up @@ -682,27 +710,19 @@ export class GoogleAuth {
* Automatically obtain a client based on the provided configuration. If no
* options were passed, use Application Default Credentials.
*/
async getClient(options?: GoogleAuthOptions) {
async getClient(options?: DeprecatedGetClientOptions) {
if (options) {
this.keyFilename =
options.keyFilename || options.keyFile || this.keyFilename;
this.scopes = options.scopes || this.scopes;
this.jsonContent = options.credentials || this.jsonContent;
this.clientOptions = options.clientOptions;
throw new Error(
'Passing options to getClient is forbidden in v5.0.0. Use new GoogleAuth(opts) instead.'
);
}
if (!this.cachedCredential) {
if (this.jsonContent) {
this.cachedCredential = await this.fromJSON(
this.jsonContent,
this.clientOptions
);
this._cacheClientFromJSON(this.jsonContent, this.clientOptions);
} else if (this.keyFilename) {
const filePath = path.resolve(this.keyFilename);
const stream = fs.createReadStream(filePath);
this.cachedCredential = await this.fromStreamAsync(
stream,
this.clientOptions
);
await this.fromStreamAsync(stream, this.clientOptions);
} else {
await this.getApplicationDefaultAsync(this.clientOptions);
}
Expand Down
42 changes: 27 additions & 15 deletions test/test.googleauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,16 @@ describe('googleauth', () => {
});
});

it('fromJson should not overwrite previous client configuration', async () => {
const auth = new GoogleAuth({keyFilename: './test/fixtures/private.json'});
auth.fromJSON({
client_email: 'batman@example.com',
private_key: 'abc123',
});
const client = (await auth.getClient()) as JWT;
assert.strictEqual(client.email, 'hello@youarecool.com');
});

it('fromAPIKey should error given an invalid api key', () => {
assert.throws(() => {
// Test verifies invalid parameter tests, which requires cast to any.
Expand Down Expand Up @@ -1124,7 +1134,7 @@ describe('googleauth', () => {

it('should use jsonContent if available', async () => {
const json = createJwtJSON();
auth.fromJSON(json);
const auth = new GoogleAuth({credentials: json});
// We know this returned a cached result if a nock scope isn't required
const body = await auth.getCredentials();
assert.notStrictEqual(body, null);
Expand All @@ -1138,10 +1148,8 @@ describe('googleauth', () => {
});

it('should error when invalid keyFilename passed to getClient', async () => {
await assertRejects(
auth.getClient({keyFilename: './funky/fresh.json'}),
/ENOENT: no such file or directory/
);
const auth = new GoogleAuth({keyFilename: './funky/fresh.json'});
await assertRejects(auth.getClient(), /ENOENT: no such file or directory/);
});

it('should accept credentials to get a client', async () => {
Expand All @@ -1165,21 +1173,24 @@ describe('googleauth', () => {
it('should allow passing scopes to get a client', async () => {
const scopes = ['http://examples.com/is/a/scope'];
const keyFilename = './test/fixtures/private.json';
const client = (await auth.getClient({scopes, keyFilename})) as JWT;
const auth = new GoogleAuth({scopes, keyFilename});
const client = (await auth.getClient()) as JWT;
assert.strictEqual(client.scopes, scopes);
});

it('should allow passing a scope to get a client', async () => {
const scopes = 'http://examples.com/is/a/scope';
const keyFilename = './test/fixtures/private.json';
const client = (await auth.getClient({scopes, keyFilename})) as JWT;
const auth = new GoogleAuth({scopes, keyFilename});
const client = (await auth.getClient()) as JWT;
assert.strictEqual(client.scopes, scopes);
});

it('should allow passing a scope to get a Compute client', async () => {
const scopes = ['http://examples.com/is/a/scope'];
const nockScopes = [nockIsGCE(), createGetProjectIdNock()];
const client = (await auth.getClient({scopes})) as Compute;
const auth = new GoogleAuth({scopes});
const client = (await auth.getClient()) as Compute;
assert.strictEqual(client.scopes, scopes);
nockScopes.forEach(x => x.done());
});
Expand Down Expand Up @@ -1348,13 +1359,6 @@ describe('googleauth', () => {
assert.strictEqual(count, 0);
});

it('should pass options to the JWT constructor via getClient', async () => {
const subject = 'science!';
const auth = new GoogleAuth({keyFilename: './test/fixtures/private.json'});
const client = (await auth.getClient({clientOptions: {subject}})) as JWT;
assert.strictEqual(client.subject, subject);
});

it('should pass options to the JWT constructor via constructor', async () => {
const subject = 'science!';
const auth = new GoogleAuth({
Expand All @@ -1373,4 +1377,12 @@ describe('googleauth', () => {
/Unable to detect a Project Id in the current environment/
);
});

it('should throw if options are passed to getClient()', async () => {
const auth = new GoogleAuth();
await assertRejects(
auth.getClient({hello: 'world'}),
/Passing options to getClient is forbidden in v5.0.0/
);
});
});

0 comments on commit ba58e3b

Please sign in to comment.