firebase-functions 3.18.1 → 3.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/lib/apps.js +1 -1
  2. package/lib/bin/firebase-functions.js +1 -1
  3. package/lib/cloud-functions.js +12 -12
  4. package/lib/common/providers/https.d.ts +20 -49
  5. package/lib/common/providers/https.js +10 -50
  6. package/lib/common/providers/identity.d.ts +187 -4
  7. package/lib/common/providers/identity.js +553 -27
  8. package/lib/common/providers/tasks.d.ts +58 -0
  9. package/lib/common/providers/tasks.js +77 -0
  10. package/lib/function-builder.d.ts +4 -1
  11. package/lib/function-builder.js +6 -1
  12. package/lib/handler-builder.d.ts +13 -2
  13. package/lib/handler-builder.js +18 -5
  14. package/lib/index.d.ts +2 -1
  15. package/lib/index.js +4 -2
  16. package/lib/logger/compat.js +1 -1
  17. package/lib/providers/analytics.js +1 -1
  18. package/lib/providers/auth.js +2 -2
  19. package/lib/providers/database.js +12 -12
  20. package/lib/providers/firestore.js +7 -7
  21. package/lib/providers/https.d.ts +2 -44
  22. package/lib/providers/https.js +8 -56
  23. package/lib/providers/pubsub.js +2 -2
  24. package/lib/providers/remoteConfig.js +1 -1
  25. package/lib/providers/storage.js +2 -2
  26. package/lib/providers/tasks.d.ts +46 -0
  27. package/lib/providers/tasks.js +75 -0
  28. package/lib/providers/testLab.js +1 -1
  29. package/lib/runtime/manifest.d.ts +5 -3
  30. package/lib/runtime/manifest.js +21 -0
  31. package/lib/setup.js +3 -3
  32. package/lib/v2/index.d.ts +3 -1
  33. package/lib/v2/index.js +5 -1
  34. package/lib/v2/options.d.ts +2 -2
  35. package/lib/v2/options.js +23 -14
  36. package/lib/v2/providers/alerts/alerts.js +2 -2
  37. package/lib/v2/providers/alerts/appDistribution.js +1 -1
  38. package/lib/v2/providers/alerts/billing.d.ts +2 -2
  39. package/lib/v2/providers/alerts/billing.js +6 -6
  40. package/lib/v2/providers/alerts/crashlytics.js +1 -1
  41. package/lib/v2/providers/eventarc.d.ts +32 -0
  42. package/lib/v2/providers/eventarc.js +65 -0
  43. package/lib/v2/providers/https.d.ts +2 -20
  44. package/lib/v2/providers/https.js +4 -43
  45. package/lib/v2/providers/pubsub.js +1 -1
  46. package/lib/v2/providers/storage.js +3 -5
  47. package/lib/v2/providers/tasks.d.ts +22 -0
  48. package/lib/v2/providers/tasks.js +89 -0
  49. package/package.json +29 -15
package/lib/apps.js CHANGED
@@ -103,7 +103,7 @@ exports.apps = apps;
103
103
  this._emulatedAdminApp = app;
104
104
  }
105
105
  get firebaseArgs() {
106
- return _.assign({}, (0, config_1.firebaseConfig)(), {
106
+ return _.assign({}, config_1.firebaseConfig(), {
107
107
  credential: firebase.credential.applicationDefault(),
108
108
  });
109
109
  }
@@ -31,7 +31,7 @@ app.post('/__/quitquitquit', handleQuitquitquit);
31
31
  if (process.env.FUNCTIONS_CONTROL_API === 'true') {
32
32
  app.get('/__/functions.yaml', async (req, res) => {
33
33
  try {
34
- const stack = await (0, loader_1.loadStack)(functionsDir);
34
+ const stack = await loader_1.loadStack(functionsDir);
35
35
  res.setHeader('content-type', 'text/yaml');
36
36
  res.send(JSON.stringify(stack));
37
37
  }
@@ -135,7 +135,7 @@ function makeCloudFunction({ after = () => { }, before = () => { }, contextOnlyH
135
135
  promise = handler(dataOrChange, context);
136
136
  }
137
137
  if (typeof promise === 'undefined') {
138
- (0, logger_1.warn)('Function returned undefined, expected Promise or value');
138
+ logger_1.warn('Function returned undefined, expected Promise or value');
139
139
  }
140
140
  return Promise.resolve(promise)
141
141
  .then((result) => {
@@ -252,8 +252,8 @@ function _detectAuthType(event) {
252
252
  /** @hidden */
253
253
  function optionsToTrigger(options) {
254
254
  const trigger = {};
255
- (0, encoding_1.copyIfPresent)(trigger, options, 'regions', 'schedule', 'minInstances', 'maxInstances', 'ingressSettings', 'vpcConnectorEgressSettings', 'vpcConnector', 'labels', 'secrets');
256
- (0, encoding_1.convertIfPresent)(trigger, options, 'failurePolicy', 'failurePolicy', (policy) => {
255
+ encoding_1.copyIfPresent(trigger, options, 'regions', 'schedule', 'minInstances', 'maxInstances', 'ingressSettings', 'vpcConnectorEgressSettings', 'vpcConnector', 'labels', 'secrets');
256
+ encoding_1.convertIfPresent(trigger, options, 'failurePolicy', 'failurePolicy', (policy) => {
257
257
  if (policy === false) {
258
258
  return undefined;
259
259
  }
@@ -264,8 +264,8 @@ function optionsToTrigger(options) {
264
264
  return policy;
265
265
  }
266
266
  });
267
- (0, encoding_1.convertIfPresent)(trigger, options, 'timeout', 'timeoutSeconds', encoding_1.durationFromSeconds);
268
- (0, encoding_1.convertIfPresent)(trigger, options, 'availableMemoryMb', 'memory', (mem) => {
267
+ encoding_1.convertIfPresent(trigger, options, 'timeout', 'timeoutSeconds', encoding_1.durationFromSeconds);
268
+ encoding_1.convertIfPresent(trigger, options, 'availableMemoryMb', 'memory', (mem) => {
269
269
  const memoryLookup = {
270
270
  '128MB': 128,
271
271
  '256MB': 256,
@@ -277,21 +277,21 @@ function optionsToTrigger(options) {
277
277
  };
278
278
  return memoryLookup[mem];
279
279
  });
280
- (0, encoding_1.convertIfPresent)(trigger, options, 'serviceAccountEmail', 'serviceAccount', encoding_1.serviceAccountFromShorthand);
280
+ encoding_1.convertIfPresent(trigger, options, 'serviceAccountEmail', 'serviceAccount', encoding_1.serviceAccountFromShorthand);
281
281
  return trigger;
282
282
  }
283
283
  exports.optionsToTrigger = optionsToTrigger;
284
284
  function optionsToEndpoint(options) {
285
285
  const endpoint = {};
286
- (0, encoding_1.copyIfPresent)(endpoint, options, 'minInstances', 'maxInstances', 'ingressSettings', 'labels', 'timeoutSeconds');
287
- (0, encoding_1.convertIfPresent)(endpoint, options, 'region', 'regions');
288
- (0, encoding_1.convertIfPresent)(endpoint, options, 'serviceAccountEmail', 'serviceAccount', (sa) => sa);
289
- (0, encoding_1.convertIfPresent)(endpoint, options, 'secretEnvironmentVariables', 'secrets', (secrets) => secrets.map((secret) => ({ secret, key: secret })));
286
+ encoding_1.copyIfPresent(endpoint, options, 'minInstances', 'maxInstances', 'ingressSettings', 'labels', 'timeoutSeconds');
287
+ encoding_1.convertIfPresent(endpoint, options, 'region', 'regions');
288
+ encoding_1.convertIfPresent(endpoint, options, 'serviceAccountEmail', 'serviceAccount', (sa) => sa);
289
+ encoding_1.convertIfPresent(endpoint, options, 'secretEnvironmentVariables', 'secrets', (secrets) => secrets.map((secret) => ({ secret, key: secret })));
290
290
  if (options === null || options === void 0 ? void 0 : options.vpcConnector) {
291
291
  endpoint.vpc = { connector: options.vpcConnector };
292
- (0, encoding_1.convertIfPresent)(endpoint.vpc, options, 'egressSettings', 'vpcConnectorEgressSettings');
292
+ encoding_1.convertIfPresent(endpoint.vpc, options, 'egressSettings', 'vpcConnectorEgressSettings');
293
293
  }
294
- (0, encoding_1.convertIfPresent)(endpoint, options, 'availableMemoryMb', 'memory', (mem) => {
294
+ encoding_1.convertIfPresent(endpoint, options, 'availableMemoryMb', 'memory', (mem) => {
295
295
  const memoryLookup = {
296
296
  '128MB': 128,
297
297
  '256MB': 256,
@@ -1,6 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import * as express from 'express';
3
3
  import * as firebase from 'firebase-admin';
4
+ import { TaskContext } from './tasks';
4
5
  /** @hidden */
5
6
  export interface Request extends express.Request {
6
7
  rawBody: Buffer;
@@ -107,55 +108,6 @@ export interface CallableRequest<T = any> {
107
108
  */
108
109
  rawRequest: Request;
109
110
  }
110
- /** How a task should be retried in the event of a non-2xx return. */
111
- export interface TaskRetryConfig {
112
- /**
113
- * Maximum number of times a request should be attempted.
114
- * If left unspecified, will default to 3.
115
- */
116
- maxAttempts?: number;
117
- /**
118
- * The maximum amount of time to wait between attempts.
119
- * If left unspecified will default to 1hr.
120
- */
121
- maxBackoffSeconds?: number;
122
- /**
123
- * The maximum number of times to double the backoff between
124
- * retries. If left unspecified will default to 16.
125
- */
126
- maxDoublings?: number;
127
- /**
128
- * The minimum time to wait between attempts. If left unspecified
129
- * will default to 100ms.
130
- */
131
- minBackoffSeconds?: number;
132
- }
133
- /** How congestion control should be applied to the function. */
134
- export interface TaskRateLimits {
135
- maxBurstSize?: number;
136
- maxConcurrentDispatches?: number;
137
- maxDispatchesPerSecond?: number;
138
- }
139
- /** Metadata about a call to a Task Queue function. */
140
- export interface TaskContext {
141
- /**
142
- * The result of decoding and verifying an ODIC token.
143
- */
144
- auth?: AuthData;
145
- }
146
- /**
147
- * The request used to call a Task Queue function.
148
- */
149
- export interface TaskRequest<T = any> {
150
- /**
151
- * The parameters used by a client when calling this function.
152
- */
153
- data: T;
154
- /**
155
- * The result of decoding and verifying an ODIC token.
156
- */
157
- auth?: AuthData;
158
- }
159
111
  /**
160
112
  * The set of Firebase Functions status codes. The codes are the same at the
161
113
  * ones exposed by gRPC here:
@@ -232,6 +184,14 @@ export declare class HttpsError extends Error {
232
184
  constructor(code: FunctionsErrorCode, message: string, details?: unknown);
233
185
  toJSON(): HttpErrorWireFormat;
234
186
  }
187
+ /** @hidden */
188
+ interface HttpRequest extends Request {
189
+ body: {
190
+ data: any;
191
+ };
192
+ }
193
+ /** @hidden */
194
+ export declare function isValidRequest(req: Request): req is HttpRequest;
235
195
  /**
236
196
  * Encodes arbitrary data in our special format for JSON.
237
197
  * This is exposed only for testing.
@@ -244,4 +204,15 @@ export declare function encode(data: any): any;
244
204
  */
245
205
  /** @hidden */
246
206
  export declare function decode(data: any): any;
207
+ /**
208
+ * Be careful when changing token status values.
209
+ *
210
+ * Users are encouraged to setup log-based metric based on these values, and
211
+ * changing their values may cause their metrics to break.
212
+ *
213
+ */
214
+ /** @hidden */
215
+ declare type TokenStatus = 'MISSING' | 'VALID' | 'INVALID';
216
+ /** @interanl */
217
+ export declare function checkAuthToken(req: Request, ctx: CallableContext | TaskContext): Promise<TokenStatus>;
247
218
  export {};
@@ -21,13 +21,13 @@
21
21
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  // SOFTWARE.
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.onDispatchHandler = exports.onCallHandler = exports.unsafeDecodeAppCheckToken = exports.unsafeDecodeIdToken = exports.decode = exports.encode = exports.HttpsError = void 0;
24
+ exports.onCallHandler = exports.checkAuthToken = exports.unsafeDecodeAppCheckToken = exports.unsafeDecodeIdToken = exports.decode = exports.encode = exports.isValidRequest = exports.HttpsError = void 0;
25
25
  const cors = require("cors");
26
26
  const logger = require("../../logger");
27
27
  // TODO(inlined): Decide whether we want to un-version apps or whether we want a
28
28
  // different strategy
29
29
  const apps_1 = require("../../apps");
30
- const debug_1 = require("../../common/debug");
30
+ const debug_1 = require("../debug");
31
31
  const JWT_REGEX = /^[a-zA-Z0-9\-_=]+?\.[a-zA-Z0-9\-_=]+?\.([a-zA-Z0-9\-_=]+)?$/;
32
32
  /**
33
33
  * Standard error codes and HTTP statuses for different ways a request can fail,
@@ -100,7 +100,7 @@ function isValidRequest(req) {
100
100
  // If it has a charset, just ignore it for now.
101
101
  const semiColon = contentType.indexOf(';');
102
102
  if (semiColon >= 0) {
103
- contentType = contentType.substr(0, semiColon).trim();
103
+ contentType = contentType.slice(0, semiColon).trim();
104
104
  }
105
105
  if (contentType !== 'application/json') {
106
106
  logger.warn('Request has incorrect Content-Type.', contentType);
@@ -120,6 +120,7 @@ function isValidRequest(req) {
120
120
  }
121
121
  return true;
122
122
  }
123
+ exports.isValidRequest = isValidRequest;
123
124
  /** @hidden */
124
125
  const LONG_TYPE = 'type.googleapis.com/google.protobuf.Int64Value';
125
126
  /** @hidden */
@@ -309,11 +310,11 @@ async function checkAuthToken(req, ctx) {
309
310
  const idToken = match[1];
310
311
  try {
311
312
  let authToken;
312
- if ((0, debug_1.isDebugFeatureEnabled)('skipTokenVerification')) {
313
+ if (debug_1.isDebugFeatureEnabled('skipTokenVerification')) {
313
314
  authToken = unsafeDecodeIdToken(idToken);
314
315
  }
315
316
  else {
316
- authToken = await (0, apps_1.apps)()
317
+ authToken = await apps_1.apps()
317
318
  .admin.auth()
318
319
  .verifyIdToken(idToken);
319
320
  }
@@ -329,6 +330,7 @@ async function checkAuthToken(req, ctx) {
329
330
  }
330
331
  }
331
332
  }
333
+ exports.checkAuthToken = checkAuthToken;
332
334
  /** @internal */
333
335
  async function checkAppCheckToken(req, ctx) {
334
336
  const appCheck = req.header('X-Firebase-AppCheck');
@@ -336,16 +338,16 @@ async function checkAppCheckToken(req, ctx) {
336
338
  return 'MISSING';
337
339
  }
338
340
  try {
339
- if (!(0, apps_1.apps)().admin.appCheck) {
341
+ if (!apps_1.apps().admin.appCheck) {
340
342
  throw new Error('Cannot validate AppCheck token. Please update Firebase Admin SDK to >= v9.8.0');
341
343
  }
342
344
  let appCheckData;
343
- if ((0, debug_1.isDebugFeatureEnabled)('skipTokenVerification')) {
345
+ if (debug_1.isDebugFeatureEnabled('skipTokenVerification')) {
344
346
  const decodedToken = unsafeDecodeAppCheckToken(appCheck);
345
347
  appCheckData = { appId: decodedToken.app_id, token: decodedToken };
346
348
  }
347
349
  else {
348
- appCheckData = await (0, apps_1.apps)()
350
+ appCheckData = await apps_1.apps()
349
351
  .admin.appCheck()
350
352
  .verifyToken(appCheck);
351
353
  }
@@ -426,45 +428,3 @@ function wrapOnCallHandler(options, handler) {
426
428
  }
427
429
  };
428
430
  }
429
- /** @internal */
430
- function onDispatchHandler(handler) {
431
- return async (req, res) => {
432
- try {
433
- if (!isValidRequest(req)) {
434
- logger.error('Invalid request, unable to process.');
435
- throw new HttpsError('invalid-argument', 'Bad Request');
436
- }
437
- const context = {};
438
- const status = await checkAuthToken(req, context);
439
- // Note: this should never happen since task queue functions are guarded by IAM.
440
- if (status === 'INVALID') {
441
- throw new HttpsError('unauthenticated', 'Unauthenticated');
442
- }
443
- const data = decode(req.body.data);
444
- if (handler.length === 2) {
445
- await handler(data, context);
446
- }
447
- else {
448
- const arg = {
449
- ...context,
450
- data,
451
- };
452
- // For some reason the type system isn't picking up that the handler
453
- // is a one argument function.
454
- await handler(arg);
455
- }
456
- res.status(204).end();
457
- }
458
- catch (err) {
459
- if (!(err instanceof HttpsError)) {
460
- // This doesn't count as an 'explicit' error.
461
- logger.error('Unhandled error', err);
462
- err = new HttpsError('internal', 'INTERNAL');
463
- }
464
- const { status } = err.httpErrorCode;
465
- const body = { error: err.toJSON() };
466
- res.status(status).send(body);
467
- }
468
- };
469
- }
470
- exports.onDispatchHandler = onDispatchHandler;
@@ -1,4 +1,7 @@
1
1
  import * as firebase from 'firebase-admin';
2
+ import { HttpsError } from './https';
3
+ import { EventContext } from '../../cloud-functions';
4
+ export { HttpsError };
2
5
  /**
3
6
  * The UserRecord passed to Cloud Functions is the same UserRecord that is returned by the Firebase Admin
4
7
  * SDK.
@@ -16,10 +19,7 @@ export declare class UserRecordMetadata implements firebase.auth.UserMetadata {
16
19
  lastSignInTime: string;
17
20
  constructor(creationTime: string, lastSignInTime: string);
18
21
  /** Returns a plain JavaScript object with the properties of UserRecordMetadata. */
19
- toJSON(): {
20
- creationTime: string;
21
- lastSignInTime: string;
22
- };
22
+ toJSON(): AuthUserMetadata;
23
23
  }
24
24
  /**
25
25
  * Helper function that creates a UserRecord Class from data sent over the wire.
@@ -27,3 +27,186 @@ export declare class UserRecordMetadata implements firebase.auth.UserMetadata {
27
27
  * @returns an instance of UserRecord with correct toJSON functions
28
28
  */
29
29
  export declare function userRecordConstructor(wireData: Object): UserRecord;
30
+ /**
31
+ * User info that is part of the AuthUserRecord
32
+ */
33
+ export interface AuthUserInfo {
34
+ /**
35
+ * The user identifier for the linked provider.
36
+ */
37
+ uid: string;
38
+ /**
39
+ * The display name for the linked provider.
40
+ */
41
+ displayName: string;
42
+ /**
43
+ * The email for the linked provider.
44
+ */
45
+ email: string;
46
+ /**
47
+ * The photo URL for the linked provider.
48
+ */
49
+ photoURL: string;
50
+ /**
51
+ * The linked provider ID (for example, "google.com" for the Google provider).
52
+ */
53
+ providerId: string;
54
+ /**
55
+ * The phone number for the linked provider.
56
+ */
57
+ phoneNumber: string;
58
+ }
59
+ /**
60
+ * Additional metadata about the user.
61
+ */
62
+ export interface AuthUserMetadata {
63
+ /**
64
+ * The date the user was created, formatted as a UTC string.
65
+ */
66
+ creationTime: string;
67
+ /**
68
+ * The date the user last signed in, formatted as a UTC string.
69
+ */
70
+ lastSignInTime: string;
71
+ }
72
+ /**
73
+ * Interface representing the common properties of a user-enrolled second factor.
74
+ */
75
+ export interface AuthMultiFactorInfo {
76
+ /**
77
+ * The ID of the enrolled second factor. This ID is unique to the user.
78
+ */
79
+ uid: string;
80
+ /**
81
+ * The optional display name of the enrolled second factor.
82
+ */
83
+ displayName?: string;
84
+ /**
85
+ * The type identifier of the second factor. For SMS second factors, this is `phone`.
86
+ */
87
+ factorId: string;
88
+ /**
89
+ * The optional date the second factor was enrolled, formatted as a UTC string.
90
+ */
91
+ enrollmentTime?: string;
92
+ /**
93
+ * The phone number associated with a phone second factor.
94
+ */
95
+ phoneNumber?: string;
96
+ }
97
+ /**
98
+ * The multi-factor related properties for the current user, if available.
99
+ */
100
+ export interface AuthMultiFactorSettings {
101
+ /**
102
+ * List of second factors enrolled with the current user.
103
+ */
104
+ enrolledFactors: AuthMultiFactorInfo[];
105
+ }
106
+ /**
107
+ * The UserRecord passed to auth blocking Cloud Functions from the identity platform.
108
+ */
109
+ export interface AuthUserRecord {
110
+ /**
111
+ * The user's `uid`.
112
+ */
113
+ uid: string;
114
+ /**
115
+ * The user's primary email, if set.
116
+ */
117
+ email?: string;
118
+ /**
119
+ * Whether or not the user's primary email is verified.
120
+ */
121
+ emailVerified: boolean;
122
+ /**
123
+ * The user's display name.
124
+ */
125
+ displayName?: string;
126
+ /**
127
+ * The user's photo URL.
128
+ */
129
+ photoURL?: string;
130
+ /**
131
+ * The user's primary phone number, if set.
132
+ */
133
+ phoneNumber?: string;
134
+ /**
135
+ * Whether or not the user is disabled: `true` for disabled; `false` for
136
+ * enabled.
137
+ */
138
+ disabled: boolean;
139
+ /**
140
+ * Additional metadata about the user.
141
+ */
142
+ metadata: AuthUserMetadata;
143
+ /**
144
+ * An array of providers (for example, Google, Facebook) linked to the user.
145
+ */
146
+ providerData: AuthUserInfo[];
147
+ /**
148
+ * The user's hashed password (base64-encoded).
149
+ */
150
+ passwordHash?: string;
151
+ /**
152
+ * The user's password salt (base64-encoded).
153
+ */
154
+ passwordSalt?: string;
155
+ /**
156
+ * The user's custom claims object if available, typically used to define
157
+ * user roles and propagated to an authenticated user's ID token.
158
+ */
159
+ customClaims?: Record<string, any>;
160
+ /**
161
+ * The ID of the tenant the user belongs to, if available.
162
+ */
163
+ tenantId?: string | null;
164
+ /**
165
+ * The date the user's tokens are valid after, formatted as a UTC string.
166
+ */
167
+ tokensValidAfterTime?: string;
168
+ /**
169
+ * The multi-factor related properties for the current user, if available.
170
+ */
171
+ multiFactor?: AuthMultiFactorSettings;
172
+ }
173
+ /** The additional user info component of the auth event context */
174
+ export interface AdditionalUserInfo {
175
+ providerId: string;
176
+ profile?: any;
177
+ username?: string;
178
+ isNewUser: boolean;
179
+ }
180
+ /** The credential component of the auth event context */
181
+ export interface Credential {
182
+ claims?: {
183
+ [key: string]: any;
184
+ };
185
+ idToken?: string;
186
+ accessToken?: string;
187
+ refreshToken?: string;
188
+ expirationTime?: string;
189
+ secret?: string;
190
+ providerId: string;
191
+ signInMethod: string;
192
+ }
193
+ /** Defines the auth event context for blocking events */
194
+ export interface AuthEventContext extends EventContext {
195
+ locale?: string;
196
+ ipAddress: string;
197
+ userAgent: string;
198
+ additionalUserInfo?: AdditionalUserInfo;
199
+ credential?: Credential;
200
+ }
201
+ /** The handler response type for beforeCreate blocking events */
202
+ export interface BeforeCreateResponse {
203
+ displayName?: string;
204
+ disabled?: boolean;
205
+ emailVerified?: boolean;
206
+ photoURL?: string;
207
+ customClaims?: object;
208
+ }
209
+ /** The handler response type for beforeSignIn blocking events */
210
+ export interface BeforeSignInResponse extends BeforeCreateResponse {
211
+ sessionClaims?: object;
212
+ }