1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | Object.defineProperty(exports, "__esModule", { value: true });
|
16 | exports.JWT = void 0;
|
17 | const gtoken_1 = require("gtoken");
|
18 | const jwtaccess_1 = require("./jwtaccess");
|
19 | const oauth2client_1 = require("./oauth2client");
|
20 | const authclient_1 = require("./authclient");
|
21 | class JWT extends oauth2client_1.OAuth2Client {
|
22 | constructor(optionsOrEmail, keyFile, key, scopes, subject, keyId) {
|
23 | const opts = optionsOrEmail && typeof optionsOrEmail === 'object'
|
24 | ? optionsOrEmail
|
25 | : { email: optionsOrEmail, keyFile, key, keyId, scopes, subject };
|
26 | super(opts);
|
27 | this.email = opts.email;
|
28 | this.keyFile = opts.keyFile;
|
29 | this.key = opts.key;
|
30 | this.keyId = opts.keyId;
|
31 | this.scopes = opts.scopes;
|
32 | this.subject = opts.subject;
|
33 | this.additionalClaims = opts.additionalClaims;
|
34 |
|
35 |
|
36 | this.credentials = { refresh_token: 'jwt-placeholder', expiry_date: 1 };
|
37 | }
|
38 | |
39 |
|
40 |
|
41 |
|
42 |
|
43 | createScoped(scopes) {
|
44 | const jwt = new JWT(this);
|
45 | jwt.scopes = scopes;
|
46 | return jwt;
|
47 | }
|
48 | |
49 |
|
50 |
|
51 |
|
52 |
|
53 | async getRequestMetadataAsync(url) {
|
54 | url = this.defaultServicePath ? `https://${this.defaultServicePath}/` : url;
|
55 | const useSelfSignedJWT = (!this.hasUserScopes() && url) ||
|
56 | (this.useJWTAccessWithScope && this.hasAnyScopes()) ||
|
57 | this.universeDomain !== authclient_1.DEFAULT_UNIVERSE;
|
58 | if (this.subject && this.universeDomain !== authclient_1.DEFAULT_UNIVERSE) {
|
59 | throw new RangeError(`Service Account user is configured for the credential. Domain-wide delegation is not supported in universes other than ${authclient_1.DEFAULT_UNIVERSE}`);
|
60 | }
|
61 | if (!this.apiKey && useSelfSignedJWT) {
|
62 | if (this.additionalClaims &&
|
63 | this.additionalClaims.target_audience) {
|
64 | const { tokens } = await this.refreshToken();
|
65 | return {
|
66 | headers: this.addSharedMetadataHeaders({
|
67 | Authorization: `Bearer ${tokens.id_token}`,
|
68 | }),
|
69 | };
|
70 | }
|
71 | else {
|
72 |
|
73 |
|
74 | if (!this.access) {
|
75 | this.access = new jwtaccess_1.JWTAccess(this.email, this.key, this.keyId, this.eagerRefreshThresholdMillis);
|
76 | }
|
77 | let scopes;
|
78 | if (this.hasUserScopes()) {
|
79 | scopes = this.scopes;
|
80 | }
|
81 | else if (!url) {
|
82 | scopes = this.defaultScopes;
|
83 | }
|
84 | const useScopes = this.useJWTAccessWithScope ||
|
85 | this.universeDomain !== authclient_1.DEFAULT_UNIVERSE;
|
86 | const headers = await this.access.getRequestHeaders(url !== null && url !== void 0 ? url : undefined, this.additionalClaims,
|
87 |
|
88 |
|
89 |
|
90 | useScopes ? scopes : undefined);
|
91 | return { headers: this.addSharedMetadataHeaders(headers) };
|
92 | }
|
93 | }
|
94 | else if (this.hasAnyScopes() || this.apiKey) {
|
95 | return super.getRequestMetadataAsync(url);
|
96 | }
|
97 | else {
|
98 |
|
99 |
|
100 | return { headers: {} };
|
101 | }
|
102 | }
|
103 | |
104 |
|
105 |
|
106 |
|
107 | async fetchIdToken(targetAudience) {
|
108 |
|
109 | const gtoken = new gtoken_1.GoogleToken({
|
110 | iss: this.email,
|
111 | sub: this.subject,
|
112 | scope: this.scopes || this.defaultScopes,
|
113 | keyFile: this.keyFile,
|
114 | key: this.key,
|
115 | additionalClaims: { target_audience: targetAudience },
|
116 | transporter: this.transporter,
|
117 | });
|
118 | await gtoken.getToken({
|
119 | forceRefresh: true,
|
120 | });
|
121 | if (!gtoken.idToken) {
|
122 | throw new Error('Unknown error: Failed to fetch ID token');
|
123 | }
|
124 | return gtoken.idToken;
|
125 | }
|
126 | |
127 |
|
128 |
|
129 | hasUserScopes() {
|
130 | if (!this.scopes) {
|
131 | return false;
|
132 | }
|
133 | return this.scopes.length > 0;
|
134 | }
|
135 | |
136 |
|
137 |
|
138 | hasAnyScopes() {
|
139 | if (this.scopes && this.scopes.length > 0)
|
140 | return true;
|
141 | if (this.defaultScopes && this.defaultScopes.length > 0)
|
142 | return true;
|
143 | return false;
|
144 | }
|
145 | authorize(callback) {
|
146 | if (callback) {
|
147 | this.authorizeAsync().then(r => callback(null, r), callback);
|
148 | }
|
149 | else {
|
150 | return this.authorizeAsync();
|
151 | }
|
152 | }
|
153 | async authorizeAsync() {
|
154 | const result = await this.refreshToken();
|
155 | if (!result) {
|
156 | throw new Error('No result returned');
|
157 | }
|
158 | this.credentials = result.tokens;
|
159 | this.credentials.refresh_token = 'jwt-placeholder';
|
160 | this.key = this.gtoken.key;
|
161 | this.email = this.gtoken.iss;
|
162 | return result.tokens;
|
163 | }
|
164 | |
165 |
|
166 |
|
167 |
|
168 |
|
169 | async refreshTokenNoCache(
|
170 |
|
171 | refreshToken) {
|
172 | const gtoken = this.createGToken();
|
173 | const token = await gtoken.getToken({
|
174 | forceRefresh: this.isTokenExpiring(),
|
175 | });
|
176 | const tokens = {
|
177 | access_token: token.access_token,
|
178 | token_type: 'Bearer',
|
179 | expiry_date: gtoken.expiresAt,
|
180 | id_token: gtoken.idToken,
|
181 | };
|
182 | this.emit('tokens', tokens);
|
183 | return { res: null, tokens };
|
184 | }
|
185 | |
186 |
|
187 |
|
188 | createGToken() {
|
189 | if (!this.gtoken) {
|
190 | this.gtoken = new gtoken_1.GoogleToken({
|
191 | iss: this.email,
|
192 | sub: this.subject,
|
193 | scope: this.scopes || this.defaultScopes,
|
194 | keyFile: this.keyFile,
|
195 | key: this.key,
|
196 | additionalClaims: this.additionalClaims,
|
197 | transporter: this.transporter,
|
198 | });
|
199 | }
|
200 | return this.gtoken;
|
201 | }
|
202 | |
203 |
|
204 |
|
205 |
|
206 | fromJSON(json) {
|
207 | if (!json) {
|
208 | throw new Error('Must pass in a JSON object containing the service account auth settings.');
|
209 | }
|
210 | if (!json.client_email) {
|
211 | throw new Error('The incoming JSON object does not contain a client_email field');
|
212 | }
|
213 | if (!json.private_key) {
|
214 | throw new Error('The incoming JSON object does not contain a private_key field');
|
215 | }
|
216 |
|
217 | this.email = json.client_email;
|
218 | this.key = json.private_key;
|
219 | this.keyId = json.private_key_id;
|
220 | this.projectId = json.project_id;
|
221 | this.quotaProjectId = json.quota_project_id;
|
222 | this.universeDomain = json.universe_domain || this.universeDomain;
|
223 | }
|
224 | fromStream(inputStream, callback) {
|
225 | if (callback) {
|
226 | this.fromStreamAsync(inputStream).then(() => callback(), callback);
|
227 | }
|
228 | else {
|
229 | return this.fromStreamAsync(inputStream);
|
230 | }
|
231 | }
|
232 | fromStreamAsync(inputStream) {
|
233 | return new Promise((resolve, reject) => {
|
234 | if (!inputStream) {
|
235 | throw new Error('Must pass in a stream containing the service account auth settings.');
|
236 | }
|
237 | let s = '';
|
238 | inputStream
|
239 | .setEncoding('utf8')
|
240 | .on('error', reject)
|
241 | .on('data', chunk => (s += chunk))
|
242 | .on('end', () => {
|
243 | try {
|
244 | const data = JSON.parse(s);
|
245 | this.fromJSON(data);
|
246 | resolve();
|
247 | }
|
248 | catch (e) {
|
249 | reject(e);
|
250 | }
|
251 | });
|
252 | });
|
253 | }
|
254 | |
255 |
|
256 |
|
257 |
|
258 | fromAPIKey(apiKey) {
|
259 | if (typeof apiKey !== 'string') {
|
260 | throw new Error('Must provide an API Key string.');
|
261 | }
|
262 | this.apiKey = apiKey;
|
263 | }
|
264 | |
265 |
|
266 |
|
267 |
|
268 | async getCredentials() {
|
269 | if (this.key) {
|
270 | return { private_key: this.key, client_email: this.email };
|
271 | }
|
272 | else if (this.keyFile) {
|
273 | const gtoken = this.createGToken();
|
274 | const creds = await gtoken.getCredentials(this.keyFile);
|
275 | return { private_key: creds.privateKey, client_email: creds.clientEmail };
|
276 | }
|
277 | throw new Error('A key or a keyFile must be provided to getCredentials.');
|
278 | }
|
279 | }
|
280 | exports.JWT = JWT;
|