Skip to content

Commit e3e87da

Browse files
committed
feat: fetch-Compatible API
1 parent f23e807 commit e3e87da

13 files changed

+256
-104
lines changed

.readme-partials.yaml

+62-89
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,13 @@ body: |-
4040
* Instead of specifying the type of client you'd like to use (JWT, OAuth2, etc)
4141
* this library will automatically choose the right client based on the environment.
4242
*/
43-
async function main() {
44-
const auth = new GoogleAuth({
45-
scopes: 'https://www.googleapis.com/auth/cloud-platform'
46-
});
47-
const client = await auth.getClient();
48-
const projectId = await auth.getProjectId();
49-
const url = `https://dns.googleapis.com/dns/v1/projects/${projectId}`;
50-
const res = await client.request({ url });
51-
console.log(res.data);
52-
}
53-
54-
main().catch(console.error);
43+
const auth = new GoogleAuth({
44+
scopes: 'https://www.googleapis.com/auth/cloud-platform'
45+
});
46+
const projectId = await auth.getProjectId();
47+
const url = `https://dns.googleapis.com/dns/v1/projects/${projectId}`;
48+
const res = await auth.fetch(url);
49+
console.log(res.data);
5550
```
5651
5752
## OAuth2
@@ -81,10 +76,11 @@ body: |-
8176
*/
8277
async function main() {
8378
const oAuth2Client = await getAuthenticatedClient();
84-
// Make a simple request to the People API using our pre-authenticated client. The `request()` method
85-
// takes an GaxiosOptions object. Visit https://github.com/JustinBeckwith/gaxios.
79+
// Make a simple request to the People API using our pre-authenticated client. The `fetch` and
80+
// `request` methods accept a [`GaxiosOptions`](https://github.com/googleapis/gaxios)
81+
// object.
8682
const url = 'https://people.googleapis.com/v1/people/me?personFields=names';
87-
const res = await oAuth2Client.request({url});
83+
const res = await oAuth2Client.fetch(url);
8884
console.log(res.data);
8985
9086
// After acquiring an access_token, you may want to check on the audience, expiration,
@@ -156,6 +152,7 @@ body: |-
156152
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:
157153
158154
```js
155+
const auth = new GoogleAuth();
159156
const client = await auth.getClient();
160157
161158
client.on('tokens', (tokens) => {
@@ -166,9 +163,10 @@ body: |-
166163
console.log(tokens.access_token);
167164
});
168165
166+
const projectId = await auth.getProjectId();
169167
const url = `https://dns.googleapis.com/dns/v1/projects/${projectId}`;
170-
const res = await client.request({ url });
171168
// The `tokens` event would now be raised if this was the first request
169+
const res = await client.fetch(url);
172170
```
173171
174172
#### Retrieve access token
@@ -241,18 +239,14 @@ body: |-
241239
const {JWT} = require('google-auth-library');
242240
const keys = require('./jwt.keys.json');
243241
244-
async function main() {
245-
const client = new JWT({
246-
email: keys.client_email,
247-
key: keys.private_key,
248-
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
249-
});
250-
const url = `https://dns.googleapis.com/dns/v1/projects/${keys.project_id}`;
251-
const res = await client.request({url});
252-
console.log(res.data);
253-
}
254-
255-
main().catch(console.error);
242+
const client = new JWT({
243+
email: keys.client_email,
244+
key: keys.private_key,
245+
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
246+
});
247+
const url = `https://dns.googleapis.com/dns/v1/projects/${keys.project_id}`;
248+
const res = await client.fetch(url);
249+
console.log(res.data);
256250
```
257251
258252
The parameters for the JWT auth client including how to use it with a `.pem` file are explained in [samples/jwt.js](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/jwt.js).
@@ -288,16 +282,12 @@ body: |-
288282
}
289283
const keys = JSON.parse(keysEnvVar);
290284
291-
async function main() {
292-
// load the JWT or UserRefreshClient from the keys
293-
const client = auth.fromJSON(keys);
294-
client.scopes = ['https://www.googleapis.com/auth/cloud-platform'];
295-
const url = `https://dns.googleapis.com/dns/v1/projects/${keys.project_id}`;
296-
const res = await client.request({url});
297-
console.log(res.data);
298-
}
299-
300-
main().catch(console.error);
285+
// load the JWT or UserRefreshClient from the keys
286+
const client = auth.fromJSON(keys);
287+
client.scopes = ['https://www.googleapis.com/auth/cloud-platform'];
288+
const url = `https://dns.googleapis.com/dns/v1/projects/${keys.project_id}`;
289+
const res = await client.fetch(url);
290+
console.log(res.data);
301291
```
302292
303293
**Important**: If you accept a credential configuration (credential JSON/File/Stream) from an external source for authentication to Google Cloud, you must validate it before providing it to any Google API or library. Providing an unvalidated credential configuration to Google APIs can compromise the security of your systems and data. For more information, refer to [Validate credential configurations from external sources](https://cloud.google.com/docs/authentication/external/externally-sourced-credentials).
@@ -313,18 +303,14 @@ body: |-
313303
``` js
314304
const {auth, Compute} = require('google-auth-library');
315305
316-
async function main() {
317-
const client = new Compute({
318-
// Specifying the service account email is optional.
319-
serviceAccountEmail: '[email protected]'
320-
});
321-
const projectId = await auth.getProjectId();
322-
const url = `https://dns.googleapis.com/dns/v1/projects/${projectId}`;
323-
const res = await client.request({url});
324-
console.log(res.data);
325-
}
326-
327-
main().catch(console.error);
306+
const client = new Compute({
307+
// Specifying the service account email is optional.
308+
serviceAccountEmail: '[email protected]'
309+
});
310+
const projectId = await auth.getProjectId();
311+
const url = `https://dns.googleapis.com/dns/v1/projects/${projectId}`;
312+
const res = await client.fetch(url);
313+
console.log(res.data);
328314
```
329315
330316
## Workload Identity Federation
@@ -1023,17 +1009,14 @@ body: |-
10231009
The library can now automatically choose the right type of client and initialize credentials from the context provided in the configuration file.
10241010
10251011
```js
1026-
async function main() {
1027-
const auth = new GoogleAuth({
1028-
scopes: 'https://www.googleapis.com/auth/cloud-platform'
1029-
});
1030-
const client = await auth.getClient();
1031-
const projectId = await auth.getProjectId();
1032-
// List all buckets in a project.
1033-
const url = `https://storage.googleapis.com/storage/v1/b?project=${projectId}`;
1034-
const res = await client.request({ url });
1035-
console.log(res.data);
1036-
}
1012+
const auth = new GoogleAuth({
1013+
scopes: 'https://www.googleapis.com/auth/cloud-platform'
1014+
});
1015+
const projectId = await auth.getProjectId();
1016+
// List all buckets in a project.
1017+
const url = `https://storage.googleapis.com/storage/v1/b?project=${projectId}`;
1018+
const res = await client.fetch(url);
1019+
console.log(res.data);
10371020
```
10381021
10391022
When using external identities with Application Default Credentials in Node.js, the `roles/browser` role needs to be granted to the service account.
@@ -1056,14 +1039,12 @@ body: |-
10561039
const {ExternalAccountClient} = require('google-auth-library');
10571040
const jsonConfig = require('/path/to/config.json');
10581041
1059-
async function main() {
1060-
const client = ExternalAccountClient.fromJSON(jsonConfig);
1061-
client.scopes = ['https://www.googleapis.com/auth/cloud-platform'];
1062-
// List all buckets in a project.
1063-
const url = `https://storage.googleapis.com/storage/v1/b?project=${projectId}`;
1064-
const res = await client.request({url});
1065-
console.log(res.data);
1066-
}
1042+
const client = ExternalAccountClient.fromJSON(jsonConfig);
1043+
client.scopes = ['https://www.googleapis.com/auth/cloud-platform'];
1044+
// List all buckets in a project.
1045+
const url = `https://storage.googleapis.com/storage/v1/b?project=${projectId}`;
1046+
const res = await client.fetch(url);
1047+
console.log(res.data);
10671048
```
10681049
10691050
#### Security Considerations
@@ -1087,15 +1068,11 @@ body: |-
10871068
// Make a request to a protected Cloud Run service.
10881069
const {GoogleAuth} = require('google-auth-library');
10891070
1090-
async function main() {
1091-
const url = 'https://cloud-run-1234-uc.a.run.app';
1092-
const auth = new GoogleAuth();
1093-
const client = await auth.getIdTokenClient(url);
1094-
const res = await client.request({url});
1095-
console.log(res.data);
1096-
}
1097-
1098-
main().catch(console.error);
1071+
const url = 'https://cloud-run-1234-uc.a.run.app';
1072+
const auth = new GoogleAuth();
1073+
const client = await auth.getIdTokenClient(url);
1074+
const res = await client.fetch(url);
1075+
console.log(res.data);
10991076
```
11001077
11011078
A complete example can be found in [`samples/idtokens-serverless.js`](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/idtokens-serverless.js).
@@ -1107,16 +1084,12 @@ body: |-
11071084
// Make a request to a protected Cloud Identity-Aware Proxy (IAP) resource
11081085
const {GoogleAuth} = require('google-auth-library');
11091086
1110-
async function main()
1111-
const targetAudience = 'iap-client-id';
1112-
const url = 'https://iap-url.com';
1113-
const auth = new GoogleAuth();
1114-
const client = await auth.getIdTokenClient(targetAudience);
1115-
const res = await client.request({url});
1116-
console.log(res.data);
1117-
}
1118-
1119-
main().catch(console.error);
1087+
const targetAudience = 'iap-client-id';
1088+
const url = 'https://iap-url.com';
1089+
const auth = new GoogleAuth();
1090+
const client = await auth.getIdTokenClient(targetAudience);
1091+
const res = await client.fetch(url);
1092+
console.log(res.data);
11201093
```
11211094
11221095
A complete example can be found in [`samples/idtokens-iap.js`](https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/idtokens-iap.js).
@@ -1189,7 +1162,7 @@ body: |-
11891162
11901163
// Use impersonated credentials:
11911164
const url = 'https://www.googleapis.com/storage/v1/b?project=anotherProjectID'
1192-
const resp = await targetClient.request({ url });
1165+
const resp = await targetClient.fetch(url);
11931166
for (const bucket of resp.data.items) {
11941167
console.log(bucket.name);
11951168
}

samples/adc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async function main() {
2828
const client = await auth.getClient();
2929
const projectId = await auth.getProjectId();
3030
const url = `https://dns.googleapis.com/dns/v1/projects/${projectId}`;
31-
const res = await client.request({url});
31+
const res = await client.fetch(url);
3232
console.log('DNS Info:');
3333
console.log(res.data);
3434
}

samples/compute.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async function main() {
2828
});
2929
const projectId = await auth.getProjectId();
3030
const url = `https://dns.googleapis.com/dns/v1/projects/${projectId}`;
31-
const res = await client.request({url});
31+
const res = await client.fetch(url);
3232
console.log(res.data);
3333
}
3434

samples/credentials.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ async function main() {
4343
const client = await auth.getClient();
4444
const projectId = await auth.getProjectId();
4545
const url = `https://dns.googleapis.com/dns/v1/projects/${projectId}`;
46-
const res = await client.request({url});
46+
const res = await client.fetch(url);
4747
console.log('DNS Info:');
4848
console.log(res.data);
4949
}

samples/idtokens-iap.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function main(
3535
async function request() {
3636
console.info(`request IAP ${url} with target audience ${targetAudience}`);
3737
const client = await auth.getIdTokenClient(targetAudience);
38-
const res = await client.request({url});
38+
const res = await client.fetch(url);
3939
console.info(res.data);
4040
}
4141

samples/idtokens-serverless.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ function main(
6565

6666
// Alternatively, one can use `client.idTokenProvider.fetchIdToken`
6767
// to return the ID Token.
68-
const res = await client.request({url});
68+
const res = await client.fetch(url);
6969
console.info(res.data);
7070
}
7171

samples/jwt.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ async function main(
3838
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
3939
});
4040
const url = `https://dns.googleapis.com/dns/v1/projects/${keys.project_id}`;
41-
const res = await client.request({url});
41+
const res = await client.fetch(url);
4242
console.log('DNS Info:');
4343
console.log(res.data);
4444

samples/keyfile.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ async function main(
3333
const client = await auth.getClient();
3434
const projectId = await auth.getProjectId();
3535
const url = `https://dns.googleapis.com/dns/v1/projects/${projectId}`;
36-
const res = await client.request({url});
36+
const res = await client.fetch(url);
3737
console.log('DNS Info:');
3838
console.log(res.data);
3939
}

src/auth/authclient.ts

+70-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ import {OriginalAndCamel, originalOrCamelOptions} from '../util';
2020

2121
import {PRODUCT_NAME, USER_AGENT} from '../shared.cjs';
2222

23+
/**
24+
* An interface for enforcing `fetch`-type compliance.
25+
*
26+
* @remarks
27+
*
28+
* This provides type guarantees during build-time, ensuring the `fetch` method is 1:1
29+
* compatible with the `Gaxios#fetch` API.
30+
*/
31+
interface GaxiosFetchCompliance {
32+
fetch: typeof fetch | Gaxios['fetch'];
33+
}
34+
2335
/**
2436
* Base auth configurations (e.g. from JWT or `.json` files) with conventional
2537
* camelCased options.
@@ -192,7 +204,7 @@ export declare interface AuthClient {
192204

193205
export abstract class AuthClient
194206
extends EventEmitter
195-
implements CredentialsClient
207+
implements CredentialsClient, GaxiosFetchCompliance
196208
{
197209
apiKey?: string;
198210
projectId?: string | null;
@@ -238,9 +250,66 @@ export abstract class AuthClient
238250
this.forceRefreshOnFailure = opts.forceRefreshOnFailure ?? false;
239251
}
240252

253+
/**
254+
* A {@link fetch `fetch`} compliant API for {@link AuthClient}.
255+
*
256+
* @see {@link AuthClient.request} for the classic method.
257+
*
258+
* @remarks
259+
*
260+
* This is useful as a drop-in replacement for `fetch` API usage.
261+
*
262+
* @example
263+
*
264+
* ```ts
265+
* const authClient = new AuthClient();
266+
* const fetchWithAuthClient: typeof fetch = (...args) => authClient.fetch(...args);
267+
* await fetchWithAuthClient('https://example.com');
268+
* ```
269+
*
270+
* @param args `fetch` API or {@link Gaxios.fetch `Gaxios#fetch`} parameters
271+
* @returns the {@link GaxiosResponse} with Gaxios-added properties
272+
*/
273+
fetch<T>(...args: Parameters<Gaxios['fetch']>): GaxiosPromise<T> {
274+
// Up to 2 parameters in either overload
275+
const input = args[0];
276+
const init = args[1];
277+
278+
let url: URL | undefined = undefined;
279+
const headers = new Headers();
280+
281+
// prepare URL
282+
if (typeof input === 'string') {
283+
url = new URL(input);
284+
} else if (input instanceof URL) {
285+
url = input;
286+
} else if (input && input.url) {
287+
url = new URL(input.url);
288+
}
289+
290+
// prepare headers
291+
if (input && typeof input === 'object' && 'headers' in input) {
292+
Gaxios.mergeHeaders(headers, input.headers);
293+
}
294+
if (init) {
295+
Gaxios.mergeHeaders(headers, new Headers(init.headers));
296+
}
297+
298+
// prepare request
299+
if (typeof input === 'object' && !(input instanceof URL)) {
300+
// input must have been a non-URL object
301+
return this.request({...init, ...input, headers, url});
302+
} else {
303+
// input must have been a string or URL
304+
return this.request({...init, headers, url});
305+
}
306+
}
307+
241308
/**
242309
* The public request API in which credentials may be added to the request.
243310
*
311+
* @see {@link AuthClient.fetch} for the modern method.
312+
*
244313
* @param options options for `gaxios`
245314
*/
246315
abstract request<T>(options: GaxiosOptions): GaxiosPromise<T>;

0 commit comments

Comments
 (0)