firebase-functions 3.24.1 → 4.0.0-rc.0
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.
- package/README.md +7 -9
- package/lib/bin/firebase-functions.js +13 -15
- package/lib/common/app.d.ts +14 -0
- package/lib/common/app.js +62 -0
- package/lib/common/change.d.ts +0 -5
- package/lib/common/change.js +5 -7
- package/lib/common/config.d.ts +6 -0
- package/lib/common/config.js +49 -0
- package/lib/common/debug.js +2 -2
- package/lib/common/encoding.d.ts +0 -8
- package/lib/common/encoding.js +6 -29
- package/lib/common/options.d.ts +14 -0
- package/lib/common/options.js +44 -0
- package/lib/common/params.d.ts +32 -0
- package/lib/common/params.js +23 -0
- package/lib/common/providers/database.d.ts +17 -19
- package/lib/common/providers/database.js +68 -39
- package/lib/common/providers/https.d.ts +11 -52
- package/lib/common/providers/https.js +115 -110
- package/lib/common/providers/identity.d.ts +8 -8
- package/lib/common/providers/identity.js +71 -79
- package/lib/common/providers/tasks.d.ts +11 -10
- package/lib/common/providers/tasks.js +10 -9
- package/lib/common/timezone.js +538 -538
- package/lib/common/trace.d.ts +14 -0
- package/lib/common/trace.js +69 -0
- package/lib/{encoder.d.ts → common/utilities/encoder.d.ts} +0 -0
- package/lib/{encoder.js → common/utilities/encoder.js} +1 -1
- package/lib/{utilities → common/utilities}/path-pattern.d.ts +0 -0
- package/lib/{utilities → common/utilities}/path-pattern.js +17 -16
- package/lib/{utilities → common/utilities}/path.d.ts +0 -0
- package/lib/{utilities → common/utilities}/path.js +5 -7
- package/lib/{utils.d.ts → common/utilities/utils.d.ts} +1 -1
- package/lib/{utils.js → common/utilities/utils.js} +18 -12
- package/lib/logger/common.d.ts +1 -3
- package/lib/logger/common.js +9 -14
- package/lib/logger/compat.js +9 -9
- package/lib/logger/index.d.ts +1 -1
- package/lib/logger/index.js +24 -30
- package/lib/{v2/params → params}/index.d.ts +22 -9
- package/lib/{v2/params → params}/index.js +22 -14
- package/lib/params/types.d.ts +180 -0
- package/lib/params/types.js +289 -0
- package/lib/runtime/loader.js +12 -14
- package/lib/runtime/manifest.d.ts +56 -24
- package/lib/runtime/manifest.js +114 -4
- package/lib/v1/cloud-functions.d.ts +232 -0
- package/lib/v1/cloud-functions.js +205 -0
- package/lib/v1/config.d.ts +8 -0
- package/lib/v1/config.js +75 -0
- package/lib/{function-builder.d.ts → v1/function-builder.d.ts} +16 -16
- package/lib/{function-builder.js → v1/function-builder.js} +56 -46
- package/lib/v1/function-configuration.d.ts +197 -0
- package/lib/v1/function-configuration.js +70 -0
- package/lib/v1/index.d.ts +20 -0
- package/lib/{index.js → v1/index.js} +5 -10
- package/lib/{providers → v1/providers}/analytics.d.ts +5 -11
- package/lib/{providers → v1/providers}/analytics.js +49 -43
- package/lib/{providers → v1/providers}/auth.d.ts +28 -18
- package/lib/{providers → v1/providers}/auth.js +45 -34
- package/lib/{providers → v1/providers}/database.d.ts +25 -42
- package/lib/{providers → v1/providers}/database.js +46 -46
- package/lib/{providers → v1/providers}/firestore.d.ts +15 -33
- package/lib/{providers → v1/providers}/firestore.js +40 -44
- package/lib/{providers → v1/providers}/https.d.ts +3 -8
- package/lib/{providers → v1/providers}/https.js +14 -22
- package/lib/{providers → v1/providers}/pubsub.d.ts +8 -16
- package/lib/{providers → v1/providers}/pubsub.js +22 -26
- package/lib/{providers → v1/providers}/remoteConfig.d.ts +2 -11
- package/lib/{providers → v1/providers}/remoteConfig.js +9 -9
- package/lib/{providers → v1/providers}/storage.d.ts +11 -26
- package/lib/{providers → v1/providers}/storage.js +24 -29
- package/lib/{providers → v1/providers}/tasks.d.ts +6 -8
- package/lib/{providers → v1/providers}/tasks.js +12 -17
- package/lib/{providers → v1/providers}/testLab.d.ts +86 -46
- package/lib/{providers → v1/providers}/testLab.js +14 -14
- package/lib/v2/core.d.ts +4 -5
- package/lib/v2/index.d.ts +16 -14
- package/lib/v2/index.js +5 -1
- package/lib/v2/options.d.ts +52 -32
- package/lib/v2/options.js +32 -47
- package/lib/v2/providers/alerts/alerts.d.ts +20 -18
- package/lib/v2/providers/alerts/alerts.js +9 -6
- package/lib/v2/providers/alerts/appDistribution.d.ts +28 -26
- package/lib/v2/providers/alerts/appDistribution.js +8 -7
- package/lib/v2/providers/alerts/billing.d.ts +5 -5
- package/lib/v2/providers/alerts/billing.js +5 -4
- package/lib/v2/providers/alerts/crashlytics.d.ts +32 -30
- package/lib/v2/providers/alerts/crashlytics.js +10 -9
- package/lib/v2/providers/alerts/index.d.ts +5 -5
- package/lib/v2/providers/alerts/performance.d.ts +3 -3
- package/lib/v2/providers/alerts/performance.js +5 -7
- package/lib/v2/providers/database.d.ts +41 -38
- package/lib/v2/providers/database.js +22 -17
- package/lib/v2/providers/eventarc.d.ts +25 -23
- package/lib/v2/providers/eventarc.js +10 -7
- package/lib/v2/providers/https.d.ts +43 -33
- package/lib/v2/providers/https.js +18 -56
- package/lib/v2/providers/identity.d.ts +25 -23
- package/lib/v2/providers/identity.js +10 -7
- package/lib/v2/providers/pubsub.d.ts +25 -23
- package/lib/v2/providers/pubsub.js +11 -27
- package/lib/v2/providers/remoteConfig.d.ts +63 -0
- package/lib/v2/providers/remoteConfig.js +65 -0
- package/lib/v2/providers/scheduler.d.ts +14 -12
- package/lib/v2/providers/scheduler.js +16 -14
- package/lib/v2/providers/storage.d.ts +32 -30
- package/lib/v2/providers/storage.js +16 -32
- package/lib/v2/providers/tasks.d.ts +30 -26
- package/lib/v2/providers/tasks.js +14 -31
- package/lib/v2/providers/testLab.d.ts +110 -0
- package/lib/v2/providers/testLab.js +65 -0
- package/lib/v2/trace.d.ts +4 -0
- package/lib/v2/trace.js +21 -0
- package/package.json +61 -66
- package/lib/apps.d.ts +0 -35
- package/lib/apps.js +0 -112
- package/lib/cloud-functions.d.ts +0 -231
- package/lib/cloud-functions.js +0 -251
- package/lib/config.d.ts +0 -24
- package/lib/config.js +0 -117
- package/lib/function-configuration.d.ts +0 -108
- package/lib/function-configuration.js +0 -71
- package/lib/handler-builder.d.ts +0 -256
- package/lib/handler-builder.js +0 -349
- package/lib/index.d.ts +0 -19
- package/lib/setup.d.ts +0 -1
- package/lib/setup.js +0 -60
- package/lib/v2/params/types.d.ts +0 -118
- package/lib/v2/params/types.js +0 -196
|
@@ -26,7 +26,9 @@ 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
|
-
const
|
|
29
|
+
const app_check_1 = require("firebase-admin/app-check");
|
|
30
|
+
const auth_1 = require("firebase-admin/auth");
|
|
31
|
+
const app_1 = require("../app");
|
|
30
32
|
const debug_1 = require("../debug");
|
|
31
33
|
const JWT_REGEX = /^[a-zA-Z0-9\-_=]+?\.[a-zA-Z0-9\-_=]+?\.([a-zA-Z0-9\-_=]+)?$/;
|
|
32
34
|
/**
|
|
@@ -39,23 +41,23 @@ const JWT_REGEX = /^[a-zA-Z0-9\-_=]+?\.[a-zA-Z0-9\-_=]+?\.([a-zA-Z0-9\-_=]+)?$/;
|
|
|
39
41
|
* supported set.
|
|
40
42
|
*/
|
|
41
43
|
const errorCodeMap = {
|
|
42
|
-
ok: { canonicalName:
|
|
43
|
-
cancelled: { canonicalName:
|
|
44
|
-
unknown: { canonicalName:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
unauthenticated: { canonicalName:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
aborted: { canonicalName:
|
|
54
|
-
|
|
55
|
-
unimplemented: { canonicalName:
|
|
56
|
-
internal: { canonicalName:
|
|
57
|
-
unavailable: { canonicalName:
|
|
58
|
-
|
|
44
|
+
ok: { canonicalName: "OK", status: 200 },
|
|
45
|
+
cancelled: { canonicalName: "CANCELLED", status: 499 },
|
|
46
|
+
unknown: { canonicalName: "UNKNOWN", status: 500 },
|
|
47
|
+
"invalid-argument": { canonicalName: "INVALID_ARGUMENT", status: 400 },
|
|
48
|
+
"deadline-exceeded": { canonicalName: "DEADLINE_EXCEEDED", status: 504 },
|
|
49
|
+
"not-found": { canonicalName: "NOT_FOUND", status: 404 },
|
|
50
|
+
"already-exists": { canonicalName: "ALREADY_EXISTS", status: 409 },
|
|
51
|
+
"permission-denied": { canonicalName: "PERMISSION_DENIED", status: 403 },
|
|
52
|
+
unauthenticated: { canonicalName: "UNAUTHENTICATED", status: 401 },
|
|
53
|
+
"resource-exhausted": { canonicalName: "RESOURCE_EXHAUSTED", status: 429 },
|
|
54
|
+
"failed-precondition": { canonicalName: "FAILED_PRECONDITION", status: 400 },
|
|
55
|
+
aborted: { canonicalName: "ABORTED", status: 409 },
|
|
56
|
+
"out-of-range": { canonicalName: "OUT_OF_RANGE", status: 400 },
|
|
57
|
+
unimplemented: { canonicalName: "UNIMPLEMENTED", status: 501 },
|
|
58
|
+
internal: { canonicalName: "INTERNAL", status: 500 },
|
|
59
|
+
unavailable: { canonicalName: "UNAVAILABLE", status: 503 },
|
|
60
|
+
"data-loss": { canonicalName: "DATA_LOSS", status: 500 },
|
|
59
61
|
};
|
|
60
62
|
/**
|
|
61
63
|
* An explicit error that can be thrown from a handler to send an error to the
|
|
@@ -90,51 +92,51 @@ exports.HttpsError = HttpsError;
|
|
|
90
92
|
function isValidRequest(req) {
|
|
91
93
|
// The body must not be empty.
|
|
92
94
|
if (!req.body) {
|
|
93
|
-
logger.warn(
|
|
95
|
+
logger.warn("Request is missing body.");
|
|
94
96
|
return false;
|
|
95
97
|
}
|
|
96
98
|
// Make sure it's a POST.
|
|
97
|
-
if (req.method !==
|
|
98
|
-
logger.warn(
|
|
99
|
+
if (req.method !== "POST") {
|
|
100
|
+
logger.warn("Request has invalid method.", req.method);
|
|
99
101
|
return false;
|
|
100
102
|
}
|
|
101
103
|
// Check that the Content-Type is JSON.
|
|
102
|
-
let contentType = (req.header(
|
|
104
|
+
let contentType = (req.header("Content-Type") || "").toLowerCase();
|
|
103
105
|
// If it has a charset, just ignore it for now.
|
|
104
|
-
const semiColon = contentType.indexOf(
|
|
106
|
+
const semiColon = contentType.indexOf(";");
|
|
105
107
|
if (semiColon >= 0) {
|
|
106
108
|
contentType = contentType.slice(0, semiColon).trim();
|
|
107
109
|
}
|
|
108
|
-
if (contentType !==
|
|
109
|
-
logger.warn(
|
|
110
|
+
if (contentType !== "application/json") {
|
|
111
|
+
logger.warn("Request has incorrect Content-Type.", contentType);
|
|
110
112
|
return false;
|
|
111
113
|
}
|
|
112
114
|
// The body must have data.
|
|
113
|
-
if (typeof req.body.data ===
|
|
114
|
-
logger.warn(
|
|
115
|
+
if (typeof req.body.data === "undefined") {
|
|
116
|
+
logger.warn("Request body is missing data.", req.body);
|
|
115
117
|
return false;
|
|
116
118
|
}
|
|
117
119
|
// TODO(klimt): Allow only specific http headers.
|
|
118
120
|
// Verify that the body does not have any extra fields.
|
|
119
|
-
const extraKeys = Object.keys(req.body).filter((field) => field !==
|
|
121
|
+
const extraKeys = Object.keys(req.body).filter((field) => field !== "data");
|
|
120
122
|
if (extraKeys.length !== 0) {
|
|
121
|
-
logger.warn(
|
|
123
|
+
logger.warn("Request body has extra fields: ", extraKeys.join(", "));
|
|
122
124
|
return false;
|
|
123
125
|
}
|
|
124
126
|
return true;
|
|
125
127
|
}
|
|
126
128
|
exports.isValidRequest = isValidRequest;
|
|
127
129
|
/** @hidden */
|
|
128
|
-
const LONG_TYPE =
|
|
130
|
+
const LONG_TYPE = "type.googleapis.com/google.protobuf.Int64Value";
|
|
129
131
|
/** @hidden */
|
|
130
|
-
const UNSIGNED_LONG_TYPE =
|
|
132
|
+
const UNSIGNED_LONG_TYPE = "type.googleapis.com/google.protobuf.UInt64Value";
|
|
131
133
|
/**
|
|
132
134
|
* Encodes arbitrary data in our special format for JSON.
|
|
133
135
|
* This is exposed only for testing.
|
|
134
136
|
*/
|
|
135
137
|
/** @hidden */
|
|
136
138
|
function encode(data) {
|
|
137
|
-
if (data === null || typeof data ===
|
|
139
|
+
if (data === null || typeof data === "undefined") {
|
|
138
140
|
return null;
|
|
139
141
|
}
|
|
140
142
|
if (data instanceof Number) {
|
|
@@ -145,16 +147,16 @@ function encode(data) {
|
|
|
145
147
|
// without any loss of precision.
|
|
146
148
|
return data;
|
|
147
149
|
}
|
|
148
|
-
if (typeof data ===
|
|
150
|
+
if (typeof data === "boolean") {
|
|
149
151
|
return data;
|
|
150
152
|
}
|
|
151
|
-
if (typeof data ===
|
|
153
|
+
if (typeof data === "string") {
|
|
152
154
|
return data;
|
|
153
155
|
}
|
|
154
156
|
if (Array.isArray(data)) {
|
|
155
157
|
return data.map(encode);
|
|
156
158
|
}
|
|
157
|
-
if (typeof data ===
|
|
159
|
+
if (typeof data === "object" || typeof data === "function") {
|
|
158
160
|
// Sadly we don't have Object.fromEntries in Node 10, so we can't use a single
|
|
159
161
|
// list comprehension
|
|
160
162
|
const obj = {};
|
|
@@ -164,8 +166,8 @@ function encode(data) {
|
|
|
164
166
|
return obj;
|
|
165
167
|
}
|
|
166
168
|
// If we got this far, the data is not encodable.
|
|
167
|
-
logger.error(
|
|
168
|
-
throw new Error(
|
|
169
|
+
logger.error("Data cannot be encoded in JSON.", data);
|
|
170
|
+
throw new Error(`Data cannot be encoded in JSON: ${data}`);
|
|
169
171
|
}
|
|
170
172
|
exports.encode = encode;
|
|
171
173
|
/**
|
|
@@ -177,8 +179,8 @@ function decode(data) {
|
|
|
177
179
|
if (data === null) {
|
|
178
180
|
return data;
|
|
179
181
|
}
|
|
180
|
-
if (data[
|
|
181
|
-
switch (data[
|
|
182
|
+
if (data["@type"]) {
|
|
183
|
+
switch (data["@type"]) {
|
|
182
184
|
case LONG_TYPE:
|
|
183
185
|
// Fall through and handle this the same as unsigned.
|
|
184
186
|
case UNSIGNED_LONG_TYPE: {
|
|
@@ -187,21 +189,21 @@ function decode(data) {
|
|
|
187
189
|
// worth all the extra code to detect that case.
|
|
188
190
|
const value = parseFloat(data.value);
|
|
189
191
|
if (isNaN(value)) {
|
|
190
|
-
logger.error(
|
|
191
|
-
throw new Error(
|
|
192
|
+
logger.error("Data cannot be decoded from JSON.", data);
|
|
193
|
+
throw new Error(`Data cannot be decoded from JSON: ${data}`);
|
|
192
194
|
}
|
|
193
195
|
return value;
|
|
194
196
|
}
|
|
195
197
|
default: {
|
|
196
|
-
logger.error(
|
|
197
|
-
throw new Error(
|
|
198
|
+
logger.error("Data cannot be decoded from JSON.", data);
|
|
199
|
+
throw new Error(`Data cannot be decoded from JSON: ${data}`);
|
|
198
200
|
}
|
|
199
201
|
}
|
|
200
202
|
}
|
|
201
203
|
if (Array.isArray(data)) {
|
|
202
204
|
return data.map(decode);
|
|
203
205
|
}
|
|
204
|
-
if (typeof data ===
|
|
206
|
+
if (typeof data === "object") {
|
|
205
207
|
const obj = {};
|
|
206
208
|
for (const [k, v] of Object.entries(data)) {
|
|
207
209
|
obj[k] = decode(v);
|
|
@@ -217,18 +219,18 @@ function unsafeDecodeToken(token) {
|
|
|
217
219
|
if (!JWT_REGEX.test(token)) {
|
|
218
220
|
return {};
|
|
219
221
|
}
|
|
220
|
-
const components = token
|
|
221
|
-
.split('.')
|
|
222
|
-
.map((s) => Buffer.from(s, 'base64').toString());
|
|
222
|
+
const components = token.split(".").map((s) => Buffer.from(s, "base64").toString());
|
|
223
223
|
let payload = components[1];
|
|
224
|
-
if (typeof payload ===
|
|
224
|
+
if (typeof payload === "string") {
|
|
225
225
|
try {
|
|
226
226
|
const obj = JSON.parse(payload);
|
|
227
|
-
if (typeof obj ===
|
|
227
|
+
if (typeof obj === "object") {
|
|
228
228
|
payload = obj;
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
|
-
catch (e) {
|
|
231
|
+
catch (e) {
|
|
232
|
+
// ignore error
|
|
233
|
+
}
|
|
232
234
|
}
|
|
233
235
|
return payload;
|
|
234
236
|
}
|
|
@@ -267,13 +269,13 @@ exports.unsafeDecodeAppCheckToken = unsafeDecodeAppCheckToken;
|
|
|
267
269
|
*
|
|
268
270
|
* @param {Request} req - Request sent to the Callable function.
|
|
269
271
|
* @param {CallableContext} ctx - Context to be sent to callable function handler.
|
|
270
|
-
* @
|
|
272
|
+
* @returns {CallableTokenStatus} Status of the token verifications.
|
|
271
273
|
*/
|
|
272
274
|
/** @internal */
|
|
273
275
|
async function checkTokens(req, ctx) {
|
|
274
276
|
const verifications = {
|
|
275
|
-
app:
|
|
276
|
-
auth:
|
|
277
|
+
app: "INVALID",
|
|
278
|
+
auth: "INVALID",
|
|
277
279
|
};
|
|
278
280
|
await Promise.all([
|
|
279
281
|
Promise.resolve().then(async () => {
|
|
@@ -285,83 +287,77 @@ async function checkTokens(req, ctx) {
|
|
|
285
287
|
]);
|
|
286
288
|
const logPayload = {
|
|
287
289
|
verifications,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
+
"logging.googleapis.com/labels": {
|
|
291
|
+
"firebase-log-type": "callable-request-verification",
|
|
290
292
|
},
|
|
291
293
|
};
|
|
292
294
|
const errs = [];
|
|
293
|
-
if (verifications.app ===
|
|
294
|
-
errs.push(
|
|
295
|
+
if (verifications.app === "INVALID") {
|
|
296
|
+
errs.push("AppCheck token was rejected.");
|
|
295
297
|
}
|
|
296
|
-
if (verifications.auth ===
|
|
297
|
-
errs.push(
|
|
298
|
+
if (verifications.auth === "INVALID") {
|
|
299
|
+
errs.push("Auth token was rejected.");
|
|
298
300
|
}
|
|
299
|
-
if (errs.length
|
|
300
|
-
logger.
|
|
301
|
+
if (errs.length === 0) {
|
|
302
|
+
logger.debug("Callable request verification passed", logPayload);
|
|
301
303
|
}
|
|
302
304
|
else {
|
|
303
|
-
logger.warn(`Callable request verification failed: ${errs.join(
|
|
305
|
+
logger.warn(`Callable request verification failed: ${errs.join(" ")}`, logPayload);
|
|
304
306
|
}
|
|
305
307
|
return verifications;
|
|
306
308
|
}
|
|
307
309
|
/** @interanl */
|
|
308
310
|
async function checkAuthToken(req, ctx) {
|
|
309
|
-
const authorization = req.header(
|
|
311
|
+
const authorization = req.header("Authorization");
|
|
310
312
|
if (!authorization) {
|
|
311
|
-
return
|
|
313
|
+
return "MISSING";
|
|
312
314
|
}
|
|
313
|
-
const match = authorization.match(/^Bearer (.*)$/);
|
|
314
|
-
if (match) {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
authToken = await (0, apps_1.apps)()
|
|
323
|
-
.admin.auth()
|
|
324
|
-
.verifyIdToken(idToken);
|
|
325
|
-
}
|
|
326
|
-
ctx.auth = {
|
|
327
|
-
uid: authToken.uid,
|
|
328
|
-
token: authToken,
|
|
329
|
-
};
|
|
330
|
-
return 'VALID';
|
|
315
|
+
const match = authorization.match(/^Bearer (.*)$/i);
|
|
316
|
+
if (!match) {
|
|
317
|
+
return "INVALID";
|
|
318
|
+
}
|
|
319
|
+
const idToken = match[1];
|
|
320
|
+
try {
|
|
321
|
+
let authToken;
|
|
322
|
+
if ((0, debug_1.isDebugFeatureEnabled)("skipTokenVerification")) {
|
|
323
|
+
authToken = unsafeDecodeIdToken(idToken);
|
|
331
324
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
return 'INVALID';
|
|
325
|
+
else {
|
|
326
|
+
authToken = await (0, auth_1.getAuth)((0, app_1.getApp)()).verifyIdToken(idToken);
|
|
335
327
|
}
|
|
328
|
+
ctx.auth = {
|
|
329
|
+
uid: authToken.uid,
|
|
330
|
+
token: authToken,
|
|
331
|
+
};
|
|
332
|
+
return "VALID";
|
|
333
|
+
}
|
|
334
|
+
catch (err) {
|
|
335
|
+
logger.warn("Failed to validate auth token.", err);
|
|
336
|
+
return "INVALID";
|
|
336
337
|
}
|
|
337
338
|
}
|
|
338
339
|
exports.checkAuthToken = checkAuthToken;
|
|
339
340
|
/** @internal */
|
|
340
341
|
async function checkAppCheckToken(req, ctx) {
|
|
341
|
-
const appCheck = req.header(
|
|
342
|
+
const appCheck = req.header("X-Firebase-AppCheck");
|
|
342
343
|
if (!appCheck) {
|
|
343
|
-
return
|
|
344
|
+
return "MISSING";
|
|
344
345
|
}
|
|
345
346
|
try {
|
|
346
|
-
if (!(0, apps_1.apps)().admin.appCheck) {
|
|
347
|
-
throw new Error('Cannot validate AppCheck token. Please update Firebase Admin SDK to >= v9.8.0');
|
|
348
|
-
}
|
|
349
347
|
let appCheckData;
|
|
350
|
-
if ((0, debug_1.isDebugFeatureEnabled)(
|
|
348
|
+
if ((0, debug_1.isDebugFeatureEnabled)("skipTokenVerification")) {
|
|
351
349
|
const decodedToken = unsafeDecodeAppCheckToken(appCheck);
|
|
352
350
|
appCheckData = { appId: decodedToken.app_id, token: decodedToken };
|
|
353
351
|
}
|
|
354
352
|
else {
|
|
355
|
-
appCheckData = await (0,
|
|
356
|
-
.admin.appCheck()
|
|
357
|
-
.verifyToken(appCheck);
|
|
353
|
+
appCheckData = await (0, app_check_1.getAppCheck)((0, app_1.getApp)()).verifyToken(appCheck);
|
|
358
354
|
}
|
|
359
355
|
ctx.app = appCheckData;
|
|
360
|
-
return
|
|
356
|
+
return "VALID";
|
|
361
357
|
}
|
|
362
358
|
catch (err) {
|
|
363
|
-
logger.warn(
|
|
364
|
-
return
|
|
359
|
+
logger.warn("Failed to validate AppCheck token.", err);
|
|
360
|
+
return "INVALID";
|
|
365
361
|
}
|
|
366
362
|
}
|
|
367
363
|
/** @internal */
|
|
@@ -369,7 +365,7 @@ function onCallHandler(options, handler) {
|
|
|
369
365
|
const wrapped = wrapOnCallHandler(options, handler);
|
|
370
366
|
return (req, res) => {
|
|
371
367
|
return new Promise((resolve) => {
|
|
372
|
-
res.on(
|
|
368
|
+
res.on("finish", resolve);
|
|
373
369
|
cors(options.cors)(req, res, () => {
|
|
374
370
|
resolve(wrapped(req, res));
|
|
375
371
|
});
|
|
@@ -382,24 +378,32 @@ function wrapOnCallHandler(options, handler) {
|
|
|
382
378
|
return async (req, res) => {
|
|
383
379
|
try {
|
|
384
380
|
if (!isValidRequest(req)) {
|
|
385
|
-
logger.error(
|
|
386
|
-
throw new HttpsError(
|
|
381
|
+
logger.error("Invalid request, unable to process.");
|
|
382
|
+
throw new HttpsError("invalid-argument", "Bad Request");
|
|
387
383
|
}
|
|
388
384
|
const context = { rawRequest: req };
|
|
389
385
|
const tokenStatus = await checkTokens(req, context);
|
|
390
|
-
if (tokenStatus.auth ===
|
|
391
|
-
throw new HttpsError(
|
|
386
|
+
if (tokenStatus.auth === "INVALID") {
|
|
387
|
+
throw new HttpsError("unauthenticated", "Unauthenticated");
|
|
388
|
+
}
|
|
389
|
+
if (tokenStatus.app === "INVALID") {
|
|
390
|
+
if (options.enforceAppCheck) {
|
|
391
|
+
throw new HttpsError("unauthenticated", "Unauthenticated");
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
logger.warn("Allowing request with invalid AppCheck token because enforcement is disabled");
|
|
395
|
+
}
|
|
392
396
|
}
|
|
393
|
-
if (tokenStatus.app ===
|
|
394
|
-
throw new HttpsError(
|
|
397
|
+
if (tokenStatus.app === "MISSING" && options.enforceAppCheck) {
|
|
398
|
+
throw new HttpsError("unauthenticated", "Unauthenticated");
|
|
395
399
|
}
|
|
396
|
-
const instanceId = req.header(
|
|
400
|
+
const instanceId = req.header("Firebase-Instance-ID-Token");
|
|
397
401
|
if (instanceId) {
|
|
398
402
|
// Validating the token requires an http request, so we don't do it.
|
|
399
403
|
// If the user wants to use it for something, it will be validated then.
|
|
400
404
|
// Currently, the only real use case for this token is for sending
|
|
401
405
|
// pushes with FCM. In that case, the FCM APIs will validate the token.
|
|
402
|
-
context.instanceIdToken = req.header(
|
|
406
|
+
context.instanceIdToken = req.header("Firebase-Instance-ID-Token");
|
|
403
407
|
}
|
|
404
408
|
const data = decode(req.body.data);
|
|
405
409
|
let result;
|
|
@@ -422,13 +426,14 @@ function wrapOnCallHandler(options, handler) {
|
|
|
422
426
|
res.status(200).send(responseBody);
|
|
423
427
|
}
|
|
424
428
|
catch (err) {
|
|
429
|
+
let httpErr = err;
|
|
425
430
|
if (!(err instanceof HttpsError)) {
|
|
426
431
|
// This doesn't count as an 'explicit' error.
|
|
427
|
-
logger.error(
|
|
428
|
-
|
|
432
|
+
logger.error("Unhandled error", err);
|
|
433
|
+
httpErr = new HttpsError("internal", "INTERNAL");
|
|
429
434
|
}
|
|
430
|
-
const { status } =
|
|
431
|
-
const body = { error:
|
|
435
|
+
const { status } = httpErr.httpErrorCode;
|
|
436
|
+
const body = { error: httpErr.toJSON() };
|
|
432
437
|
res.status(status).send(body);
|
|
433
438
|
}
|
|
434
439
|
};
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { EventContext } from
|
|
3
|
-
import { HttpsError } from
|
|
1
|
+
import * as auth from "firebase-admin/auth";
|
|
2
|
+
import { EventContext } from "../../v1/cloud-functions";
|
|
3
|
+
import { HttpsError } from "./https";
|
|
4
4
|
export { HttpsError };
|
|
5
5
|
/**
|
|
6
6
|
* Shorthand auth blocking events from GCIP.
|
|
7
7
|
* @hidden
|
|
8
8
|
* @alpha
|
|
9
9
|
*/
|
|
10
|
-
export declare type AuthBlockingEventType =
|
|
10
|
+
export declare type AuthBlockingEventType = "beforeCreate" | "beforeSignIn";
|
|
11
11
|
/**
|
|
12
12
|
* The UserRecord passed to Cloud Functions is the same UserRecord that is returned by the Firebase Admin
|
|
13
13
|
* SDK.
|
|
14
14
|
*/
|
|
15
|
-
export declare type UserRecord =
|
|
15
|
+
export declare type UserRecord = auth.UserRecord;
|
|
16
16
|
/**
|
|
17
17
|
* UserInfo that is part of the UserRecord
|
|
18
18
|
*/
|
|
19
|
-
export declare type UserInfo =
|
|
19
|
+
export declare type UserInfo = auth.UserInfo;
|
|
20
20
|
/**
|
|
21
21
|
* Helper class to create the user metadata in a UserRecord object
|
|
22
22
|
*/
|
|
23
|
-
export declare class UserRecordMetadata implements
|
|
23
|
+
export declare class UserRecordMetadata implements auth.UserMetadata {
|
|
24
24
|
creationTime: string;
|
|
25
25
|
lastSignInTime: string;
|
|
26
26
|
constructor(creationTime: string, lastSignInTime: string);
|
|
@@ -32,7 +32,7 @@ export declare class UserRecordMetadata implements firebase.auth.UserMetadata {
|
|
|
32
32
|
* @param wireData data sent over the wire
|
|
33
33
|
* @returns an instance of UserRecord with correct toJSON functions
|
|
34
34
|
*/
|
|
35
|
-
export declare function userRecordConstructor(wireData:
|
|
35
|
+
export declare function userRecordConstructor(wireData: Record<string, unknown>): UserRecord;
|
|
36
36
|
/**
|
|
37
37
|
* User info that is part of the AuthUserRecord
|
|
38
38
|
*/
|