UNPKG

30.6 kBJavaScriptView Raw
1"use strict";
2// Copyright 2019 Google LLC
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15Object.defineProperty(exports, "__esModule", { value: true });
16exports.OAuth2Client = exports.CertificateFormat = exports.CodeChallengeMethod = void 0;
17const querystring = require("querystring");
18const stream = require("stream");
19const formatEcdsa = require("ecdsa-sig-formatter");
20const crypto_1 = require("../crypto/crypto");
21const authclient_1 = require("./authclient");
22const loginticket_1 = require("./loginticket");
23var CodeChallengeMethod;
24(function (CodeChallengeMethod) {
25 CodeChallengeMethod["Plain"] = "plain";
26 CodeChallengeMethod["S256"] = "S256";
27})(CodeChallengeMethod = exports.CodeChallengeMethod || (exports.CodeChallengeMethod = {}));
28var CertificateFormat;
29(function (CertificateFormat) {
30 CertificateFormat["PEM"] = "PEM";
31 CertificateFormat["JWK"] = "JWK";
32})(CertificateFormat = exports.CertificateFormat || (exports.CertificateFormat = {}));
33class OAuth2Client extends authclient_1.AuthClient {
34 constructor(optionsOrClientId, clientSecret, redirectUri) {
35 super();
36 this.certificateCache = {};
37 this.certificateExpiry = null;
38 this.certificateCacheFormat = CertificateFormat.PEM;
39 this.refreshTokenPromises = new Map();
40 const opts = optionsOrClientId && typeof optionsOrClientId === 'object'
41 ? optionsOrClientId
42 : { clientId: optionsOrClientId, clientSecret, redirectUri };
43 this._clientId = opts.clientId;
44 this._clientSecret = opts.clientSecret;
45 this.redirectUri = opts.redirectUri;
46 this.eagerRefreshThresholdMillis =
47 opts.eagerRefreshThresholdMillis || 5 * 60 * 1000;
48 this.forceRefreshOnFailure = !!opts.forceRefreshOnFailure;
49 }
50 /**
51 * Generates URL for consent page landing.
52 * @param opts Options.
53 * @return URL to consent page.
54 */
55 generateAuthUrl(opts = {}) {
56 if (opts.code_challenge_method && !opts.code_challenge) {
57 throw new Error('If a code_challenge_method is provided, code_challenge must be included.');
58 }
59 opts.response_type = opts.response_type || 'code';
60 opts.client_id = opts.client_id || this._clientId;
61 opts.redirect_uri = opts.redirect_uri || this.redirectUri;
62 // Allow scopes to be passed either as array or a string
63 if (opts.scope instanceof Array) {
64 opts.scope = opts.scope.join(' ');
65 }
66 const rootUrl = OAuth2Client.GOOGLE_OAUTH2_AUTH_BASE_URL_;
67 return (rootUrl +
68 '?' +
69 querystring.stringify(opts));
70 }
71 generateCodeVerifier() {
72 // To make the code compatible with browser SubtleCrypto we need to make
73 // this method async.
74 throw new Error('generateCodeVerifier is removed, please use generateCodeVerifierAsync instead.');
75 }
76 /**
77 * Convenience method to automatically generate a code_verifier, and its
78 * resulting SHA256. If used, this must be paired with a S256
79 * code_challenge_method.
80 *
81 * For a full example see:
82 * https://github.com/googleapis/google-auth-library-nodejs/blob/main/samples/oauth2-codeVerifier.js
83 */
84 async generateCodeVerifierAsync() {
85 // base64 encoding uses 6 bits per character, and we want to generate128
86 // characters. 6*128/8 = 96.
87 const crypto = crypto_1.createCrypto();
88 const randomString = crypto.randomBytesBase64(96);
89 // The valid characters in the code_verifier are [A-Z]/[a-z]/[0-9]/
90 // "-"/"."/"_"/"~". Base64 encoded strings are pretty close, so we're just
91 // swapping out a few chars.
92 const codeVerifier = randomString
93 .replace(/\+/g, '~')
94 .replace(/=/g, '_')
95 .replace(/\//g, '-');
96 // Generate the base64 encoded SHA256
97 const unencodedCodeChallenge = await crypto.sha256DigestBase64(codeVerifier);
98 // We need to use base64UrlEncoding instead of standard base64
99 const codeChallenge = unencodedCodeChallenge
100 .split('=')[0]
101 .replace(/\+/g, '-')
102 .replace(/\//g, '_');
103 return { codeVerifier, codeChallenge };
104 }
105 getToken(codeOrOptions, callback) {
106 const options = typeof codeOrOptions === 'string' ? { code: codeOrOptions } : codeOrOptions;
107 if (callback) {
108 this.getTokenAsync(options).then(r => callback(null, r.tokens, r.res), e => callback(e, null, e.response));
109 }
110 else {
111 return this.getTokenAsync(options);
112 }
113 }
114 async getTokenAsync(options) {
115 const url = OAuth2Client.GOOGLE_OAUTH2_TOKEN_URL_;
116 const values = {
117 code: options.code,
118 client_id: options.client_id || this._clientId,
119 client_secret: this._clientSecret,
120 redirect_uri: options.redirect_uri || this.redirectUri,
121 grant_type: 'authorization_code',
122 code_verifier: options.codeVerifier,
123 };
124 const res = await this.transporter.request({
125 method: 'POST',
126 url,
127 data: querystring.stringify(values),
128 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
129 });
130 const tokens = res.data;
131 if (res.data && res.data.expires_in) {
132 tokens.expiry_date = new Date().getTime() + res.data.expires_in * 1000;
133 delete tokens.expires_in;
134 }
135 this.emit('tokens', tokens);
136 return { tokens, res };
137 }
138 /**
139 * Refreshes the access token.
140 * @param refresh_token Existing refresh token.
141 * @private
142 */
143 async refreshToken(refreshToken) {
144 if (!refreshToken) {
145 return this.refreshTokenNoCache(refreshToken);
146 }
147 // If a request to refresh using the same token has started,
148 // return the same promise.
149 if (this.refreshTokenPromises.has(refreshToken)) {
150 return this.refreshTokenPromises.get(refreshToken);
151 }
152 const p = this.refreshTokenNoCache(refreshToken).then(r => {
153 this.refreshTokenPromises.delete(refreshToken);
154 return r;
155 }, e => {
156 this.refreshTokenPromises.delete(refreshToken);
157 throw e;
158 });
159 this.refreshTokenPromises.set(refreshToken, p);
160 return p;
161 }
162 async refreshTokenNoCache(refreshToken) {
163 if (!refreshToken) {
164 throw new Error('No refresh token is set.');
165 }
166 const url = OAuth2Client.GOOGLE_OAUTH2_TOKEN_URL_;
167 const data = {
168 refresh_token: refreshToken,
169 client_id: this._clientId,
170 client_secret: this._clientSecret,
171 grant_type: 'refresh_token',
172 };
173 // request for new token
174 const res = await this.transporter.request({
175 method: 'POST',
176 url,
177 data: querystring.stringify(data),
178 headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
179 });
180 const tokens = res.data;
181 // TODO: de-duplicate this code from a few spots
182 if (res.data && res.data.expires_in) {
183 tokens.expiry_date = new Date().getTime() + res.data.expires_in * 1000;
184 delete tokens.expires_in;
185 }
186 this.emit('tokens', tokens);
187 return { tokens, res };
188 }
189 refreshAccessToken(callback) {
190 if (callback) {
191 this.refreshAccessTokenAsync().then(r => callback(null, r.credentials, r.res), callback);
192 }
193 else {
194 return this.refreshAccessTokenAsync();
195 }
196 }
197 async refreshAccessTokenAsync() {
198 const r = await this.refreshToken(this.credentials.refresh_token);
199 const tokens = r.tokens;
200 tokens.refresh_token = this.credentials.refresh_token;
201 this.credentials = tokens;
202 return { credentials: this.credentials, res: r.res };
203 }
204 getAccessToken(callback) {
205 if (callback) {
206 this.getAccessTokenAsync().then(r => callback(null, r.token, r.res), callback);
207 }
208 else {
209 return this.getAccessTokenAsync();
210 }
211 }
212 async getAccessTokenAsync() {
213 const shouldRefresh = !this.credentials.access_token || this.isTokenExpiring();
214 if (shouldRefresh) {
215 if (!this.credentials.refresh_token) {
216 if (this.refreshHandler) {
217 const refreshedAccessToken = await this.processAndValidateRefreshHandler();
218 if (refreshedAccessToken === null || refreshedAccessToken === void 0 ? void 0 : refreshedAccessToken.access_token) {
219 this.setCredentials(refreshedAccessToken);
220 return { token: this.credentials.access_token };
221 }
222 }
223 else {
224 throw new Error('No refresh token or refresh handler callback is set.');
225 }
226 }
227 const r = await this.refreshAccessTokenAsync();
228 if (!r.credentials || (r.credentials && !r.credentials.access_token)) {
229 throw new Error('Could not refresh access token.');
230 }
231 return { token: r.credentials.access_token, res: r.res };
232 }
233 else {
234 return { token: this.credentials.access_token };
235 }
236 }
237 /**
238 * The main authentication interface. It takes an optional url which when
239 * present is the endpoint being accessed, and returns a Promise which
240 * resolves with authorization header fields.
241 *
242 * In OAuth2Client, the result has the form:
243 * { Authorization: 'Bearer <access_token_value>' }
244 * @param url The optional url being authorized
245 */
246 async getRequestHeaders(url) {
247 const headers = (await this.getRequestMetadataAsync(url)).headers;
248 return headers;
249 }
250 async getRequestMetadataAsync(
251 // eslint-disable-next-line @typescript-eslint/no-unused-vars
252 url) {
253 const thisCreds = this.credentials;
254 if (!thisCreds.access_token &&
255 !thisCreds.refresh_token &&
256 !this.apiKey &&
257 !this.refreshHandler) {
258 throw new Error('No access, refresh token, API key or refresh handler callback is set.');
259 }
260 if (thisCreds.access_token && !this.isTokenExpiring()) {
261 thisCreds.token_type = thisCreds.token_type || 'Bearer';
262 const headers = {
263 Authorization: thisCreds.token_type + ' ' + thisCreds.access_token,
264 };
265 return { headers: this.addSharedMetadataHeaders(headers) };
266 }
267 // If refreshHandler exists, call processAndValidateRefreshHandler().
268 if (this.refreshHandler) {
269 const refreshedAccessToken = await this.processAndValidateRefreshHandler();
270 if (refreshedAccessToken === null || refreshedAccessToken === void 0 ? void 0 : refreshedAccessToken.access_token) {
271 this.setCredentials(refreshedAccessToken);
272 const headers = {
273 Authorization: 'Bearer ' + this.credentials.access_token,
274 };
275 return { headers: this.addSharedMetadataHeaders(headers) };
276 }
277 }
278 if (this.apiKey) {
279 return { headers: { 'X-Goog-Api-Key': this.apiKey } };
280 }
281 let r = null;
282 let tokens = null;
283 try {
284 r = await this.refreshToken(thisCreds.refresh_token);
285 tokens = r.tokens;
286 }
287 catch (err) {
288 const e = err;
289 if (e.response &&
290 (e.response.status === 403 || e.response.status === 404)) {
291 e.message = `Could not refresh access token: ${e.message}`;
292 }
293 throw e;
294 }
295 const credentials = this.credentials;
296 credentials.token_type = credentials.token_type || 'Bearer';
297 tokens.refresh_token = credentials.refresh_token;
298 this.credentials = tokens;
299 const headers = {
300 Authorization: credentials.token_type + ' ' + tokens.access_token,
301 };
302 return { headers: this.addSharedMetadataHeaders(headers), res: r.res };
303 }
304 /**
305 * Generates an URL to revoke the given token.
306 * @param token The existing token to be revoked.
307 */
308 static getRevokeTokenUrl(token) {
309 const parameters = querystring.stringify({ token });
310 return `${OAuth2Client.GOOGLE_OAUTH2_REVOKE_URL_}?${parameters}`;
311 }
312 revokeToken(token, callback) {
313 const opts = {
314 url: OAuth2Client.getRevokeTokenUrl(token),
315 method: 'POST',
316 };
317 if (callback) {
318 this.transporter
319 .request(opts)
320 .then(r => callback(null, r), callback);
321 }
322 else {
323 return this.transporter.request(opts);
324 }
325 }
326 revokeCredentials(callback) {
327 if (callback) {
328 this.revokeCredentialsAsync().then(res => callback(null, res), callback);
329 }
330 else {
331 return this.revokeCredentialsAsync();
332 }
333 }
334 async revokeCredentialsAsync() {
335 const token = this.credentials.access_token;
336 this.credentials = {};
337 if (token) {
338 return this.revokeToken(token);
339 }
340 else {
341 throw new Error('No access token to revoke.');
342 }
343 }
344 request(opts, callback) {
345 if (callback) {
346 this.requestAsync(opts).then(r => callback(null, r), e => {
347 return callback(e, e.response);
348 });
349 }
350 else {
351 return this.requestAsync(opts);
352 }
353 }
354 async requestAsync(opts, retry = false) {
355 let r2;
356 try {
357 const r = await this.getRequestMetadataAsync(opts.url);
358 opts.headers = opts.headers || {};
359 if (r.headers && r.headers['x-goog-user-project']) {
360 opts.headers['x-goog-user-project'] = r.headers['x-goog-user-project'];
361 }
362 if (r.headers && r.headers.Authorization) {
363 opts.headers.Authorization = r.headers.Authorization;
364 }
365 if (this.apiKey) {
366 opts.headers['X-Goog-Api-Key'] = this.apiKey;
367 }
368 r2 = await this.transporter.request(opts);
369 }
370 catch (e) {
371 const res = e.response;
372 if (res) {
373 const statusCode = res.status;
374 // Retry the request for metadata if the following criteria are true:
375 // - We haven't already retried. It only makes sense to retry once.
376 // - The response was a 401 or a 403
377 // - The request didn't send a readableStream
378 // - An access_token and refresh_token were available, but either no
379 // expiry_date was available or the forceRefreshOnFailure flag is set.
380 // The absent expiry_date case can happen when developers stash the
381 // access_token and refresh_token for later use, but the access_token
382 // fails on the first try because it's expired. Some developers may
383 // choose to enable forceRefreshOnFailure to mitigate time-related
384 // errors.
385 // Or the following criteria are true:
386 // - We haven't already retried. It only makes sense to retry once.
387 // - The response was a 401 or a 403
388 // - The request didn't send a readableStream
389 // - No refresh_token was available
390 // - An access_token and a refreshHandler callback were available, but
391 // either no expiry_date was available or the forceRefreshOnFailure
392 // flag is set. The access_token fails on the first try because it's
393 // expired. Some developers may choose to enable forceRefreshOnFailure
394 // to mitigate time-related errors.
395 const mayRequireRefresh = this.credentials &&
396 this.credentials.access_token &&
397 this.credentials.refresh_token &&
398 (!this.credentials.expiry_date || this.forceRefreshOnFailure);
399 const mayRequireRefreshWithNoRefreshToken = this.credentials &&
400 this.credentials.access_token &&
401 !this.credentials.refresh_token &&
402 (!this.credentials.expiry_date || this.forceRefreshOnFailure) &&
403 this.refreshHandler;
404 const isReadableStream = res.config.data instanceof stream.Readable;
405 const isAuthErr = statusCode === 401 || statusCode === 403;
406 if (!retry && isAuthErr && !isReadableStream && mayRequireRefresh) {
407 await this.refreshAccessTokenAsync();
408 return this.requestAsync(opts, true);
409 }
410 else if (!retry &&
411 isAuthErr &&
412 !isReadableStream &&
413 mayRequireRefreshWithNoRefreshToken) {
414 const refreshedAccessToken = await this.processAndValidateRefreshHandler();
415 if (refreshedAccessToken === null || refreshedAccessToken === void 0 ? void 0 : refreshedAccessToken.access_token) {
416 this.setCredentials(refreshedAccessToken);
417 }
418 return this.requestAsync(opts, true);
419 }
420 }
421 throw e;
422 }
423 return r2;
424 }
425 verifyIdToken(options, callback) {
426 // This function used to accept two arguments instead of an options object.
427 // Check the types to help users upgrade with less pain.
428 // This check can be removed after a 2.0 release.
429 if (callback && typeof callback !== 'function') {
430 throw new Error('This method accepts an options object as the first parameter, which includes the idToken, audience, and maxExpiry.');
431 }
432 if (callback) {
433 this.verifyIdTokenAsync(options).then(r => callback(null, r), callback);
434 }
435 else {
436 return this.verifyIdTokenAsync(options);
437 }
438 }
439 async verifyIdTokenAsync(options) {
440 if (!options.idToken) {
441 throw new Error('The verifyIdToken method requires an ID Token');
442 }
443 const response = await this.getFederatedSignonCertsAsync();
444 const login = await this.verifySignedJwtWithCertsAsync(options.idToken, response.certs, options.audience, OAuth2Client.ISSUERS_, options.maxExpiry);
445 return login;
446 }
447 /**
448 * Obtains information about the provisioned access token. Especially useful
449 * if you want to check the scopes that were provisioned to a given token.
450 *
451 * @param accessToken Required. The Access Token for which you want to get
452 * user info.
453 */
454 async getTokenInfo(accessToken) {
455 const { data } = await this.transporter.request({
456 method: 'POST',
457 headers: {
458 'Content-Type': 'application/x-www-form-urlencoded',
459 Authorization: `Bearer ${accessToken}`,
460 },
461 url: OAuth2Client.GOOGLE_TOKEN_INFO_URL,
462 });
463 const info = Object.assign({
464 expiry_date: new Date().getTime() + data.expires_in * 1000,
465 scopes: data.scope.split(' '),
466 }, data);
467 delete info.expires_in;
468 delete info.scope;
469 return info;
470 }
471 getFederatedSignonCerts(callback) {
472 if (callback) {
473 this.getFederatedSignonCertsAsync().then(r => callback(null, r.certs, r.res), callback);
474 }
475 else {
476 return this.getFederatedSignonCertsAsync();
477 }
478 }
479 async getFederatedSignonCertsAsync() {
480 const nowTime = new Date().getTime();
481 const format = crypto_1.hasBrowserCrypto()
482 ? CertificateFormat.JWK
483 : CertificateFormat.PEM;
484 if (this.certificateExpiry &&
485 nowTime < this.certificateExpiry.getTime() &&
486 this.certificateCacheFormat === format) {
487 return { certs: this.certificateCache, format };
488 }
489 let res;
490 let url;
491 switch (format) {
492 case CertificateFormat.PEM:
493 url = OAuth2Client.GOOGLE_OAUTH2_FEDERATED_SIGNON_PEM_CERTS_URL_;
494 break;
495 case CertificateFormat.JWK:
496 url = OAuth2Client.GOOGLE_OAUTH2_FEDERATED_SIGNON_JWK_CERTS_URL_;
497 break;
498 default:
499 throw new Error(`Unsupported certificate format ${format}`);
500 }
501 try {
502 res = await this.transporter.request({ url });
503 }
504 catch (e) {
505 e.message = `Failed to retrieve verification certificates: ${e.message}`;
506 throw e;
507 }
508 const cacheControl = res ? res.headers['cache-control'] : undefined;
509 let cacheAge = -1;
510 if (cacheControl) {
511 const pattern = new RegExp('max-age=([0-9]*)');
512 const regexResult = pattern.exec(cacheControl);
513 if (regexResult && regexResult.length === 2) {
514 // Cache results with max-age (in seconds)
515 cacheAge = Number(regexResult[1]) * 1000; // milliseconds
516 }
517 }
518 let certificates = {};
519 switch (format) {
520 case CertificateFormat.PEM:
521 certificates = res.data;
522 break;
523 case CertificateFormat.JWK:
524 for (const key of res.data.keys) {
525 certificates[key.kid] = key;
526 }
527 break;
528 default:
529 throw new Error(`Unsupported certificate format ${format}`);
530 }
531 const now = new Date();
532 this.certificateExpiry =
533 cacheAge === -1 ? null : new Date(now.getTime() + cacheAge);
534 this.certificateCache = certificates;
535 this.certificateCacheFormat = format;
536 return { certs: certificates, format, res };
537 }
538 getIapPublicKeys(callback) {
539 if (callback) {
540 this.getIapPublicKeysAsync().then(r => callback(null, r.pubkeys, r.res), callback);
541 }
542 else {
543 return this.getIapPublicKeysAsync();
544 }
545 }
546 async getIapPublicKeysAsync() {
547 let res;
548 const url = OAuth2Client.GOOGLE_OAUTH2_IAP_PUBLIC_KEY_URL_;
549 try {
550 res = await this.transporter.request({ url });
551 }
552 catch (e) {
553 e.message = `Failed to retrieve verification certificates: ${e.message}`;
554 throw e;
555 }
556 return { pubkeys: res.data, res };
557 }
558 verifySignedJwtWithCerts() {
559 // To make the code compatible with browser SubtleCrypto we need to make
560 // this method async.
561 throw new Error('verifySignedJwtWithCerts is removed, please use verifySignedJwtWithCertsAsync instead.');
562 }
563 /**
564 * Verify the id token is signed with the correct certificate
565 * and is from the correct audience.
566 * @param jwt The jwt to verify (The ID Token in this case).
567 * @param certs The array of certs to test the jwt against.
568 * @param requiredAudience The audience to test the jwt against.
569 * @param issuers The allowed issuers of the jwt (Optional).
570 * @param maxExpiry The max expiry the certificate can be (Optional).
571 * @return Returns a promise resolving to LoginTicket on verification.
572 */
573 async verifySignedJwtWithCertsAsync(jwt, certs, requiredAudience, issuers, maxExpiry) {
574 const crypto = crypto_1.createCrypto();
575 if (!maxExpiry) {
576 maxExpiry = OAuth2Client.MAX_TOKEN_LIFETIME_SECS_;
577 }
578 const segments = jwt.split('.');
579 if (segments.length !== 3) {
580 throw new Error('Wrong number of segments in token: ' + jwt);
581 }
582 const signed = segments[0] + '.' + segments[1];
583 let signature = segments[2];
584 let envelope;
585 let payload;
586 try {
587 envelope = JSON.parse(crypto.decodeBase64StringUtf8(segments[0]));
588 }
589 catch (err) {
590 err.message = `Can't parse token envelope: ${segments[0]}': ${err.message}`;
591 throw err;
592 }
593 if (!envelope) {
594 throw new Error("Can't parse token envelope: " + segments[0]);
595 }
596 try {
597 payload = JSON.parse(crypto.decodeBase64StringUtf8(segments[1]));
598 }
599 catch (err) {
600 err.message = `Can't parse token payload '${segments[0]}`;
601 throw err;
602 }
603 if (!payload) {
604 throw new Error("Can't parse token payload: " + segments[1]);
605 }
606 if (!Object.prototype.hasOwnProperty.call(certs, envelope.kid)) {
607 // If this is not present, then there's no reason to attempt verification
608 throw new Error('No pem found for envelope: ' + JSON.stringify(envelope));
609 }
610 const cert = certs[envelope.kid];
611 if (envelope.alg === 'ES256') {
612 signature = formatEcdsa.joseToDer(signature, 'ES256').toString('base64');
613 }
614 const verified = await crypto.verify(cert, signed, signature);
615 if (!verified) {
616 throw new Error('Invalid token signature: ' + jwt);
617 }
618 if (!payload.iat) {
619 throw new Error('No issue time in token: ' + JSON.stringify(payload));
620 }
621 if (!payload.exp) {
622 throw new Error('No expiration time in token: ' + JSON.stringify(payload));
623 }
624 const iat = Number(payload.iat);
625 if (isNaN(iat))
626 throw new Error('iat field using invalid format');
627 const exp = Number(payload.exp);
628 if (isNaN(exp))
629 throw new Error('exp field using invalid format');
630 const now = new Date().getTime() / 1000;
631 if (exp >= now + maxExpiry) {
632 throw new Error('Expiration time too far in future: ' + JSON.stringify(payload));
633 }
634 const earliest = iat - OAuth2Client.CLOCK_SKEW_SECS_;
635 const latest = exp + OAuth2Client.CLOCK_SKEW_SECS_;
636 if (now < earliest) {
637 throw new Error('Token used too early, ' +
638 now +
639 ' < ' +
640 earliest +
641 ': ' +
642 JSON.stringify(payload));
643 }
644 if (now > latest) {
645 throw new Error('Token used too late, ' +
646 now +
647 ' > ' +
648 latest +
649 ': ' +
650 JSON.stringify(payload));
651 }
652 if (issuers && issuers.indexOf(payload.iss) < 0) {
653 throw new Error('Invalid issuer, expected one of [' +
654 issuers +
655 '], but got ' +
656 payload.iss);
657 }
658 // Check the audience matches if we have one
659 if (typeof requiredAudience !== 'undefined' && requiredAudience !== null) {
660 const aud = payload.aud;
661 let audVerified = false;
662 // If the requiredAudience is an array, check if it contains token
663 // audience
664 if (requiredAudience.constructor === Array) {
665 audVerified = requiredAudience.indexOf(aud) > -1;
666 }
667 else {
668 audVerified = aud === requiredAudience;
669 }
670 if (!audVerified) {
671 throw new Error('Wrong recipient, payload audience != requiredAudience');
672 }
673 }
674 return new loginticket_1.LoginTicket(envelope, payload);
675 }
676 /**
677 * Returns a promise that resolves with AccessTokenResponse type if
678 * refreshHandler is defined.
679 * If not, nothing is returned.
680 */
681 async processAndValidateRefreshHandler() {
682 if (this.refreshHandler) {
683 const accessTokenResponse = await this.refreshHandler();
684 if (!accessTokenResponse.access_token) {
685 throw new Error('No access token is returned by the refreshHandler callback.');
686 }
687 return accessTokenResponse;
688 }
689 return;
690 }
691 /**
692 * Returns true if a token is expired or will expire within
693 * eagerRefreshThresholdMillismilliseconds.
694 * If there is no expiry time, assumes the token is not expired or expiring.
695 */
696 isTokenExpiring() {
697 const expiryDate = this.credentials.expiry_date;
698 return expiryDate
699 ? expiryDate <= new Date().getTime() + this.eagerRefreshThresholdMillis
700 : false;
701 }
702}
703exports.OAuth2Client = OAuth2Client;
704OAuth2Client.GOOGLE_TOKEN_INFO_URL = 'https://oauth2.googleapis.com/tokeninfo';
705/**
706 * The base URL for auth endpoints.
707 */
708OAuth2Client.GOOGLE_OAUTH2_AUTH_BASE_URL_ = 'https://accounts.google.com/o/oauth2/v2/auth';
709/**
710 * The base endpoint for token retrieval.
711 */
712OAuth2Client.GOOGLE_OAUTH2_TOKEN_URL_ = 'https://oauth2.googleapis.com/token';
713/**
714 * The base endpoint to revoke tokens.
715 */
716OAuth2Client.GOOGLE_OAUTH2_REVOKE_URL_ = 'https://oauth2.googleapis.com/revoke';
717/**
718 * Google Sign on certificates in PEM format.
719 */
720OAuth2Client.GOOGLE_OAUTH2_FEDERATED_SIGNON_PEM_CERTS_URL_ = 'https://www.googleapis.com/oauth2/v1/certs';
721/**
722 * Google Sign on certificates in JWK format.
723 */
724OAuth2Client.GOOGLE_OAUTH2_FEDERATED_SIGNON_JWK_CERTS_URL_ = 'https://www.googleapis.com/oauth2/v3/certs';
725/**
726 * Google Sign on certificates in JWK format.
727 */
728OAuth2Client.GOOGLE_OAUTH2_IAP_PUBLIC_KEY_URL_ = 'https://www.gstatic.com/iap/verify/public_key';
729/**
730 * Clock skew - five minutes in seconds
731 */
732OAuth2Client.CLOCK_SKEW_SECS_ = 300;
733/**
734 * Max Token Lifetime is one day in seconds
735 */
736OAuth2Client.MAX_TOKEN_LIFETIME_SECS_ = 86400;
737/**
738 * The allowed oauth token issuers.
739 */
740OAuth2Client.ISSUERS_ = [
741 'accounts.google.com',
742 'https://accounts.google.com',
743];
744//# sourceMappingURL=oauth2client.js.map
\No newline at end of file