firebase-functions 6.5.0 → 7.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/lib/_virtual/rolldown_runtime.js +34 -0
- package/lib/bin/firebase-functions.js +78 -103
- package/lib/common/app.js +35 -55
- package/lib/common/change.js +54 -75
- package/lib/common/config.js +41 -41
- package/lib/common/debug.js +23 -47
- package/lib/common/encoding.js +59 -82
- package/lib/common/onInit.js +26 -28
- package/lib/common/options.js +22 -42
- package/lib/common/params.d.ts +6 -6
- package/lib/common/params.js +0 -23
- package/lib/common/providers/database.js +270 -300
- package/lib/common/providers/firestore.js +66 -92
- package/lib/common/providers/https.d.ts +0 -1
- package/lib/common/providers/https.js +537 -539
- package/lib/common/providers/identity.js +393 -444
- package/lib/common/providers/tasks.js +64 -98
- package/lib/common/timezone.js +544 -542
- package/lib/common/trace.d.ts +0 -1
- package/lib/common/trace.js +63 -55
- package/lib/common/utilities/assertions.d.ts +11 -0
- package/lib/common/utilities/assertions.js +18 -0
- package/lib/common/utilities/encoder.js +20 -37
- package/lib/common/utilities/path-pattern.js +106 -132
- package/lib/common/utilities/path.js +28 -27
- package/lib/common/utilities/utils.js +23 -45
- package/lib/esm/_virtual/rolldown_runtime.mjs +16 -0
- package/lib/esm/bin/firebase-functions.mjs +91 -0
- package/lib/esm/common/app.mjs +39 -0
- package/lib/esm/common/change.mjs +57 -0
- package/lib/esm/common/config.mjs +45 -0
- package/lib/esm/common/debug.mjs +28 -0
- package/lib/esm/common/encoding.mjs +69 -0
- package/lib/esm/common/onInit.mjs +33 -0
- package/lib/esm/common/options.mjs +22 -0
- package/lib/esm/common/params.mjs +1 -0
- package/lib/esm/common/providers/database.mjs +269 -0
- package/lib/esm/common/providers/firestore.mjs +78 -0
- package/lib/esm/common/providers/https.mjs +573 -0
- package/lib/esm/common/providers/identity.mjs +428 -0
- package/lib/esm/common/providers/tasks.mjs +67 -0
- package/lib/esm/common/timezone.mjs +544 -0
- package/lib/esm/common/trace.mjs +73 -0
- package/lib/esm/common/utilities/assertions.mjs +17 -0
- package/lib/esm/common/utilities/encoder.mjs +21 -0
- package/lib/esm/common/utilities/path-pattern.mjs +116 -0
- package/lib/esm/common/utilities/path.mjs +35 -0
- package/lib/esm/common/utilities/utils.mjs +29 -0
- package/lib/esm/function-configuration.mjs +1 -0
- package/lib/esm/logger/common.mjs +23 -0
- package/lib/esm/logger/compat.mjs +25 -0
- package/lib/esm/logger/index.mjs +131 -0
- package/lib/esm/params/index.mjs +160 -0
- package/lib/esm/params/types.mjs +400 -0
- package/lib/esm/runtime/loader.mjs +132 -0
- package/lib/esm/runtime/manifest.mjs +134 -0
- package/lib/esm/types/global.d.mjs +1 -0
- package/lib/esm/v1/cloud-functions.mjs +206 -0
- package/lib/esm/v1/config.mjs +14 -0
- package/lib/esm/v1/function-builder.mjs +252 -0
- package/lib/esm/v1/function-configuration.mjs +72 -0
- package/lib/esm/v1/index.mjs +27 -0
- package/lib/esm/v1/providers/analytics.mjs +212 -0
- package/lib/esm/v1/providers/auth.mjs +156 -0
- package/lib/esm/v1/providers/database.mjs +243 -0
- package/lib/esm/v1/providers/firestore.mjs +131 -0
- package/lib/esm/v1/providers/https.mjs +82 -0
- package/lib/esm/v1/providers/pubsub.mjs +175 -0
- package/lib/esm/v1/providers/remoteConfig.mjs +64 -0
- package/lib/esm/v1/providers/storage.mjs +163 -0
- package/lib/esm/v1/providers/tasks.mjs +63 -0
- package/lib/esm/v1/providers/testLab.mjs +94 -0
- package/lib/esm/v2/core.mjs +4 -0
- package/lib/esm/v2/index.mjs +28 -0
- package/lib/esm/v2/options.mjs +102 -0
- package/lib/esm/v2/providers/alerts/alerts.mjs +85 -0
- package/lib/esm/v2/providers/alerts/appDistribution.mjs +75 -0
- package/lib/esm/v2/providers/alerts/billing.mjs +51 -0
- package/lib/esm/v2/providers/alerts/crashlytics.mjs +122 -0
- package/lib/esm/v2/providers/alerts/index.mjs +22 -0
- package/lib/esm/v2/providers/alerts/performance.mjs +66 -0
- package/lib/esm/v2/providers/database.mjs +197 -0
- package/lib/esm/v2/providers/dataconnect.mjs +130 -0
- package/lib/esm/v2/providers/eventarc.mjs +51 -0
- package/lib/esm/v2/providers/firestore.mjs +294 -0
- package/lib/esm/v2/providers/https.mjs +210 -0
- package/lib/esm/v2/providers/identity.mjs +103 -0
- package/lib/esm/v2/providers/pubsub.mjs +148 -0
- package/lib/esm/v2/providers/remoteConfig.mjs +52 -0
- package/lib/esm/v2/providers/scheduler.mjs +84 -0
- package/lib/esm/v2/providers/storage.mjs +155 -0
- package/lib/esm/v2/providers/tasks.mjs +65 -0
- package/lib/esm/v2/providers/testLab.mjs +53 -0
- package/lib/esm/v2/trace.mjs +20 -0
- package/lib/function-configuration.d.ts +0 -0
- package/lib/function-configuration.js +0 -0
- package/lib/logger/common.js +21 -41
- package/lib/logger/compat.js +18 -33
- package/lib/logger/index.js +119 -130
- package/lib/params/index.d.ts +19 -4
- package/lib/params/index.js +153 -129
- package/lib/params/types.d.ts +17 -0
- package/lib/params/types.js +390 -382
- package/lib/runtime/loader.js +114 -148
- package/lib/runtime/manifest.js +106 -126
- package/lib/types/global.d.js +0 -0
- package/lib/v1/cloud-functions.d.ts +2 -2
- package/lib/v1/cloud-functions.js +193 -241
- package/lib/v1/config.d.ts +4 -7
- package/lib/v1/config.js +13 -75
- package/lib/v1/function-builder.js +239 -368
- package/lib/v1/function-configuration.js +70 -63
- package/lib/v1/index.js +118 -73
- package/lib/v1/providers/analytics.js +189 -210
- package/lib/v1/providers/auth.d.ts +2 -1
- package/lib/v1/providers/auth.js +159 -164
- package/lib/v1/providers/database.js +237 -242
- package/lib/v1/providers/firestore.js +131 -130
- package/lib/v1/providers/https.d.ts +2 -1
- package/lib/v1/providers/https.js +79 -86
- package/lib/v1/providers/pubsub.js +175 -172
- package/lib/v1/providers/remoteConfig.js +64 -68
- package/lib/v1/providers/storage.js +161 -163
- package/lib/v1/providers/tasks.d.ts +1 -1
- package/lib/v1/providers/tasks.js +65 -80
- package/lib/v1/providers/testLab.js +94 -94
- package/lib/v2/core.d.ts +1 -1
- package/lib/v2/core.js +5 -32
- package/lib/v2/index.d.ts +6 -3
- package/lib/v2/index.js +123 -75
- package/lib/v2/options.js +88 -114
- package/lib/v2/providers/alerts/alerts.js +76 -95
- package/lib/v2/providers/alerts/appDistribution.js +73 -78
- package/lib/v2/providers/alerts/billing.js +49 -53
- package/lib/v2/providers/alerts/crashlytics.js +110 -102
- package/lib/v2/providers/alerts/index.js +56 -53
- package/lib/v2/providers/alerts/performance.js +64 -74
- package/lib/v2/providers/database.js +177 -180
- package/lib/v2/providers/dataconnect.d.ts +95 -0
- package/lib/v2/providers/dataconnect.js +137 -0
- package/lib/v2/providers/eventarc.js +55 -77
- package/lib/v2/providers/firestore.js +262 -260
- package/lib/v2/providers/https.d.ts +3 -2
- package/lib/v2/providers/https.js +210 -247
- package/lib/v2/providers/identity.d.ts +2 -1
- package/lib/v2/providers/identity.js +96 -105
- package/lib/v2/providers/pubsub.js +149 -167
- package/lib/v2/providers/remoteConfig.js +54 -63
- package/lib/v2/providers/scheduler.js +84 -96
- package/lib/v2/providers/storage.js +147 -162
- package/lib/v2/providers/tasks.d.ts +1 -1
- package/lib/v2/providers/tasks.js +68 -95
- package/lib/v2/providers/testLab.js +55 -64
- package/lib/v2/trace.js +18 -19
- package/package.json +290 -226
- package/protos/compiledFirestore.mjs +3512 -0
- package/protos/update.sh +28 -7
|
@@ -1,494 +1,443 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
// copies of the Software, and to permit persons to whom the Software is
|
|
11
|
-
// furnished to do so, subject to the following conditions:
|
|
12
|
-
//
|
|
13
|
-
// The above copyright notice and this permission notice shall be included in all
|
|
14
|
-
// copies or substantial portions of the Software.
|
|
15
|
-
//
|
|
16
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
-
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
-
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
-
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
-
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
-
// SOFTWARE.
|
|
23
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.wrapHandler = exports.getUpdateMask = exports.validateAuthResponse = exports.parseAuthEventContext = exports.generateResponsePayload = exports.parseAuthUserRecord = exports.parseMultiFactor = exports.parseDate = exports.parseProviderData = exports.parseMetadata = exports.isValidRequest = exports.userRecordConstructor = exports.UserRecordMetadata = exports.HttpsError = void 0;
|
|
25
|
-
const auth = require("firebase-admin/auth");
|
|
26
|
-
const logger = require("../../logger");
|
|
27
|
-
const app_1 = require("../app");
|
|
28
|
-
const debug_1 = require("../debug");
|
|
29
|
-
const https_1 = require("./https");
|
|
30
|
-
Object.defineProperty(exports, "HttpsError", { enumerable: true, get: function () { return https_1.HttpsError; } });
|
|
1
|
+
const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.js');
|
|
2
|
+
const require_logger_index = require('../../logger/index.js');
|
|
3
|
+
const require_common_app = require('../app.js');
|
|
4
|
+
const require_common_debug = require('../debug.js');
|
|
5
|
+
const require_common_providers_https = require('./https.js');
|
|
6
|
+
let firebase_admin_auth = require("firebase-admin/auth");
|
|
7
|
+
firebase_admin_auth = require_rolldown_runtime.__toESM(firebase_admin_auth);
|
|
8
|
+
|
|
9
|
+
//#region src/common/providers/identity.ts
|
|
31
10
|
const DISALLOWED_CUSTOM_CLAIMS = [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
11
|
+
"acr",
|
|
12
|
+
"amr",
|
|
13
|
+
"at_hash",
|
|
14
|
+
"aud",
|
|
15
|
+
"auth_time",
|
|
16
|
+
"azp",
|
|
17
|
+
"cnf",
|
|
18
|
+
"c_hash",
|
|
19
|
+
"exp",
|
|
20
|
+
"iat",
|
|
21
|
+
"iss",
|
|
22
|
+
"jti",
|
|
23
|
+
"nbf",
|
|
24
|
+
"nonce",
|
|
25
|
+
"firebase"
|
|
47
26
|
];
|
|
48
|
-
const CLAIMS_MAX_PAYLOAD_SIZE =
|
|
27
|
+
const CLAIMS_MAX_PAYLOAD_SIZE = 1e3;
|
|
49
28
|
const EVENT_MAPPING = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
29
|
+
beforeCreate: "providers/cloud.auth/eventTypes/user.beforeCreate",
|
|
30
|
+
beforeSignIn: "providers/cloud.auth/eventTypes/user.beforeSignIn",
|
|
31
|
+
beforeSendEmail: "providers/cloud.auth/eventTypes/user.beforeSendEmail",
|
|
32
|
+
beforeSendSms: "providers/cloud.auth/eventTypes/user.beforeSendSms"
|
|
54
33
|
};
|
|
55
34
|
/**
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
exports.UserRecordMetadata = UserRecordMetadata;
|
|
35
|
+
* Helper class to create the user metadata in a `UserRecord` object.
|
|
36
|
+
*/
|
|
37
|
+
var UserRecordMetadata = class {
|
|
38
|
+
constructor(creationTime, lastSignInTime) {
|
|
39
|
+
this.creationTime = creationTime;
|
|
40
|
+
this.lastSignInTime = lastSignInTime;
|
|
41
|
+
}
|
|
42
|
+
/** Returns a plain JavaScript object with the properties of UserRecordMetadata. */
|
|
43
|
+
toJSON() {
|
|
44
|
+
return {
|
|
45
|
+
creationTime: this.creationTime,
|
|
46
|
+
lastSignInTime: this.lastSignInTime
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
};
|
|
72
50
|
/**
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
51
|
+
* Helper function that creates a `UserRecord` class from data sent over the wire.
|
|
52
|
+
* @param wireData data sent over the wire
|
|
53
|
+
* @returns an instance of `UserRecord` with correct toJSON functions
|
|
54
|
+
*/
|
|
77
55
|
function userRecordConstructor(wireData) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
56
|
+
const falseyValues = {
|
|
57
|
+
email: null,
|
|
58
|
+
emailVerified: false,
|
|
59
|
+
displayName: null,
|
|
60
|
+
photoURL: null,
|
|
61
|
+
phoneNumber: null,
|
|
62
|
+
disabled: false,
|
|
63
|
+
providerData: [],
|
|
64
|
+
customClaims: {},
|
|
65
|
+
passwordSalt: null,
|
|
66
|
+
passwordHash: null,
|
|
67
|
+
tokensValidAfterTime: null
|
|
68
|
+
};
|
|
69
|
+
const record = {
|
|
70
|
+
...falseyValues,
|
|
71
|
+
...wireData
|
|
72
|
+
};
|
|
73
|
+
const meta = record.metadata;
|
|
74
|
+
if (meta) {
|
|
75
|
+
record.metadata = new UserRecordMetadata(meta.createdAt || meta.creationTime, meta.lastSignedInAt || meta.lastSignInTime);
|
|
76
|
+
} else {
|
|
77
|
+
record.metadata = new UserRecordMetadata(null, null);
|
|
78
|
+
}
|
|
79
|
+
record.toJSON = () => {
|
|
80
|
+
const { uid, email, emailVerified, displayName, photoURL, phoneNumber, disabled, passwordHash, passwordSalt, tokensValidAfterTime } = record;
|
|
81
|
+
const json = {
|
|
82
|
+
uid,
|
|
83
|
+
email,
|
|
84
|
+
emailVerified,
|
|
85
|
+
displayName,
|
|
86
|
+
photoURL,
|
|
87
|
+
phoneNumber,
|
|
88
|
+
disabled,
|
|
89
|
+
passwordHash,
|
|
90
|
+
passwordSalt,
|
|
91
|
+
tokensValidAfterTime
|
|
92
|
+
};
|
|
93
|
+
json.metadata = record.metadata.toJSON();
|
|
94
|
+
json.customClaims = JSON.parse(JSON.stringify(record.customClaims));
|
|
95
|
+
json.providerData = record.providerData.map((entry) => {
|
|
96
|
+
const newEntry = { ...entry };
|
|
97
|
+
newEntry.toJSON = () => entry;
|
|
98
|
+
return newEntry;
|
|
99
|
+
});
|
|
100
|
+
return json;
|
|
101
|
+
};
|
|
102
|
+
return record;
|
|
124
103
|
}
|
|
125
|
-
exports.userRecordConstructor = userRecordConstructor;
|
|
126
104
|
/**
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
105
|
+
* Checks for a valid identity platform web request, otherwise throws an HttpsError.
|
|
106
|
+
* @internal
|
|
107
|
+
*/
|
|
130
108
|
function isValidRequest(req) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return true;
|
|
109
|
+
if (req.method !== "POST") {
|
|
110
|
+
require_logger_index.warn(`Request has invalid method "${req.method}".`);
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
const contentType = (req.header("Content-Type") || "").toLowerCase();
|
|
114
|
+
if (!contentType.includes("application/json")) {
|
|
115
|
+
require_logger_index.warn("Request has invalid header Content-Type.");
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (!req.body?.data?.jwt) {
|
|
119
|
+
require_logger_index.warn("Request has an invalid body.");
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
return true;
|
|
146
123
|
}
|
|
147
|
-
exports.isValidRequest = isValidRequest;
|
|
148
124
|
/**
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
125
|
+
* Decode, but not verify, an Auth Blocking token.
|
|
126
|
+
*
|
|
127
|
+
* Do not use in production. Token should always be verified using the Admin SDK.
|
|
128
|
+
*
|
|
129
|
+
* This is exposed only for testing.
|
|
130
|
+
*/
|
|
155
131
|
function unsafeDecodeAuthBlockingToken(token) {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
132
|
+
const decoded = require_common_providers_https.unsafeDecodeToken(token);
|
|
133
|
+
decoded.uid = decoded.sub;
|
|
134
|
+
return decoded;
|
|
159
135
|
}
|
|
160
136
|
/**
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
137
|
+
* Helper function to parse the decoded metadata object into a `UserMetaData` object
|
|
138
|
+
* @internal
|
|
139
|
+
*/
|
|
164
140
|
function parseMetadata(metadata) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
return {
|
|
172
|
-
creationTime,
|
|
173
|
-
lastSignInTime,
|
|
174
|
-
};
|
|
141
|
+
const creationTime = metadata?.creation_time ? new Date(metadata.creation_time).toUTCString() : null;
|
|
142
|
+
const lastSignInTime = metadata?.last_sign_in_time ? new Date(metadata.last_sign_in_time).toUTCString() : null;
|
|
143
|
+
return {
|
|
144
|
+
creationTime,
|
|
145
|
+
lastSignInTime
|
|
146
|
+
};
|
|
175
147
|
}
|
|
176
|
-
exports.parseMetadata = parseMetadata;
|
|
177
148
|
/**
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
149
|
+
* Helper function to parse the decoded user info array into an `AuthUserInfo` array.
|
|
150
|
+
* @internal
|
|
151
|
+
*/
|
|
181
152
|
function parseProviderData(providerData) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
153
|
+
const providers = [];
|
|
154
|
+
for (const provider of providerData) {
|
|
155
|
+
providers.push({
|
|
156
|
+
uid: provider.uid,
|
|
157
|
+
displayName: provider.display_name,
|
|
158
|
+
email: provider.email,
|
|
159
|
+
photoURL: provider.photo_url,
|
|
160
|
+
providerId: provider.provider_id,
|
|
161
|
+
phoneNumber: provider.phone_number
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return providers;
|
|
194
165
|
}
|
|
195
|
-
exports.parseProviderData = parseProviderData;
|
|
196
166
|
/**
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
167
|
+
* Helper function to parse the date into a UTC string.
|
|
168
|
+
* @internal
|
|
169
|
+
*/
|
|
200
170
|
function parseDate(tokensValidAfterTime) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
// ignore error
|
|
213
|
-
}
|
|
214
|
-
return null;
|
|
171
|
+
if (!tokensValidAfterTime) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
tokensValidAfterTime = tokensValidAfterTime * 1e3;
|
|
175
|
+
try {
|
|
176
|
+
const date = new Date(tokensValidAfterTime);
|
|
177
|
+
if (!isNaN(date.getTime())) {
|
|
178
|
+
return date.toUTCString();
|
|
179
|
+
}
|
|
180
|
+
} catch {}
|
|
181
|
+
return null;
|
|
215
182
|
}
|
|
216
|
-
exports.parseDate = parseDate;
|
|
217
183
|
/**
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
184
|
+
* Helper function to parse the decoded enrolled factors into a valid MultiFactorSettings
|
|
185
|
+
* @internal
|
|
186
|
+
*/
|
|
221
187
|
function parseMultiFactor(multiFactor) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
enrolledFactors: parsedEnrolledFactors,
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
return null;
|
|
188
|
+
if (!multiFactor) {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
const parsedEnrolledFactors = [];
|
|
192
|
+
for (const factor of multiFactor.enrolled_factors || []) {
|
|
193
|
+
if (!factor.uid) {
|
|
194
|
+
throw new require_common_providers_https.HttpsError("internal", "INTERNAL ASSERT FAILED: Invalid multi-factor info response");
|
|
195
|
+
}
|
|
196
|
+
const enrollmentTime = factor.enrollment_time ? new Date(factor.enrollment_time).toUTCString() : null;
|
|
197
|
+
parsedEnrolledFactors.push({
|
|
198
|
+
uid: factor.uid,
|
|
199
|
+
factorId: factor.phone_number ? factor.factor_id || "phone" : factor.factor_id,
|
|
200
|
+
displayName: factor.display_name,
|
|
201
|
+
enrollmentTime,
|
|
202
|
+
phoneNumber: factor.phone_number
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (parsedEnrolledFactors.length > 0) {
|
|
206
|
+
return { enrolledFactors: parsedEnrolledFactors };
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
247
209
|
}
|
|
248
|
-
exports.parseMultiFactor = parseMultiFactor;
|
|
249
210
|
/**
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
211
|
+
* Parses the decoded user record into a valid UserRecord for use in the handler
|
|
212
|
+
* @internal
|
|
213
|
+
*/
|
|
253
214
|
function parseAuthUserRecord(decodedJWTUserRecord) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
215
|
+
if (!decodedJWTUserRecord.uid) {
|
|
216
|
+
throw new require_common_providers_https.HttpsError("internal", "INTERNAL ASSERT FAILED: Invalid user response");
|
|
217
|
+
}
|
|
218
|
+
const disabled = decodedJWTUserRecord.disabled || false;
|
|
219
|
+
const metadata = parseMetadata(decodedJWTUserRecord.metadata);
|
|
220
|
+
const providerData = parseProviderData(decodedJWTUserRecord.provider_data);
|
|
221
|
+
const tokensValidAfterTime = parseDate(decodedJWTUserRecord.tokens_valid_after_time);
|
|
222
|
+
const multiFactor = parseMultiFactor(decodedJWTUserRecord.multi_factor);
|
|
223
|
+
return {
|
|
224
|
+
uid: decodedJWTUserRecord.uid,
|
|
225
|
+
email: decodedJWTUserRecord.email,
|
|
226
|
+
emailVerified: decodedJWTUserRecord.email_verified,
|
|
227
|
+
displayName: decodedJWTUserRecord.display_name,
|
|
228
|
+
photoURL: decodedJWTUserRecord.photo_url,
|
|
229
|
+
phoneNumber: decodedJWTUserRecord.phone_number,
|
|
230
|
+
disabled,
|
|
231
|
+
metadata,
|
|
232
|
+
providerData,
|
|
233
|
+
passwordHash: decodedJWTUserRecord.password_hash,
|
|
234
|
+
passwordSalt: decodedJWTUserRecord.password_salt,
|
|
235
|
+
customClaims: decodedJWTUserRecord.custom_claims,
|
|
236
|
+
tenantId: decodedJWTUserRecord.tenant_id,
|
|
237
|
+
tokensValidAfterTime,
|
|
238
|
+
multiFactor
|
|
239
|
+
};
|
|
279
240
|
}
|
|
280
|
-
exports.parseAuthUserRecord = parseAuthUserRecord;
|
|
281
241
|
/** Helper to get the `AdditionalUserInfo` from the decoded JWT */
|
|
282
242
|
function parseAdditionalUserInfo(decodedJWT) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
};
|
|
243
|
+
let profile;
|
|
244
|
+
let username;
|
|
245
|
+
if (decodedJWT.raw_user_info) {
|
|
246
|
+
try {
|
|
247
|
+
profile = JSON.parse(decodedJWT.raw_user_info);
|
|
248
|
+
} catch (err) {
|
|
249
|
+
require_logger_index.debug(`Parse Error: ${err.message}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (profile) {
|
|
253
|
+
if (decodedJWT.sign_in_method === "github.com") {
|
|
254
|
+
username = profile.login;
|
|
255
|
+
}
|
|
256
|
+
if (decodedJWT.sign_in_method === "twitter.com") {
|
|
257
|
+
username = profile.screen_name;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
providerId: decodedJWT.sign_in_method === "emailLink" ? "password" : decodedJWT.sign_in_method,
|
|
262
|
+
profile,
|
|
263
|
+
username,
|
|
264
|
+
isNewUser: decodedJWT.event_type === "beforeCreate" ? true : false,
|
|
265
|
+
recaptchaScore: decodedJWT.recaptcha_score,
|
|
266
|
+
email: decodedJWT.email,
|
|
267
|
+
phoneNumber: decodedJWT.phone_number
|
|
268
|
+
};
|
|
310
269
|
}
|
|
311
270
|
/**
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
271
|
+
* Helper to generate a response from the blocking function to the Firebase Auth backend.
|
|
272
|
+
* @internal
|
|
273
|
+
*/
|
|
315
274
|
function generateResponsePayload(authResponse) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
275
|
+
if (!authResponse) {
|
|
276
|
+
return {};
|
|
277
|
+
}
|
|
278
|
+
const { recaptchaActionOverride,...formattedAuthResponse } = authResponse;
|
|
279
|
+
const result = {};
|
|
280
|
+
const updateMask = getUpdateMask(formattedAuthResponse);
|
|
281
|
+
if (updateMask.length !== 0) {
|
|
282
|
+
result.userRecord = {
|
|
283
|
+
...formattedAuthResponse,
|
|
284
|
+
updateMask
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
if (recaptchaActionOverride !== undefined) {
|
|
288
|
+
result.recaptchaActionOverride = recaptchaActionOverride;
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
332
291
|
}
|
|
333
|
-
exports.generateResponsePayload = generateResponsePayload;
|
|
334
292
|
/** Helper to get the Credential from the decoded JWT */
|
|
335
293
|
function parseAuthCredential(decodedJWT, time) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
: undefined,
|
|
350
|
-
secret: decodedJWT.oauth_token_secret,
|
|
351
|
-
providerId: decodedJWT.sign_in_method === "emailLink" ? "password" : decodedJWT.sign_in_method,
|
|
352
|
-
signInMethod: decodedJWT.sign_in_method,
|
|
353
|
-
};
|
|
294
|
+
if (!decodedJWT.sign_in_attributes && !decodedJWT.oauth_id_token && !decodedJWT.oauth_access_token && !decodedJWT.oauth_refresh_token) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
claims: decodedJWT.sign_in_attributes,
|
|
299
|
+
idToken: decodedJWT.oauth_id_token,
|
|
300
|
+
accessToken: decodedJWT.oauth_access_token,
|
|
301
|
+
refreshToken: decodedJWT.oauth_refresh_token,
|
|
302
|
+
expirationTime: decodedJWT.oauth_expires_in ? new Date(time + decodedJWT.oauth_expires_in * 1e3).toUTCString() : undefined,
|
|
303
|
+
secret: decodedJWT.oauth_token_secret,
|
|
304
|
+
providerId: decodedJWT.sign_in_method === "emailLink" ? "password" : decodedJWT.sign_in_method,
|
|
305
|
+
signInMethod: decodedJWT.sign_in_method
|
|
306
|
+
};
|
|
354
307
|
}
|
|
355
308
|
/**
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
309
|
+
* Parses the decoded jwt into a valid AuthEventContext for use in the handler
|
|
310
|
+
* @internal
|
|
311
|
+
*/
|
|
359
312
|
function parseAuthEventContext(decodedJWT, projectId, time = new Date().getTime()) {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
emailType: decodedJWT.email_type,
|
|
380
|
-
smsType: decodedJWT.sms_type,
|
|
381
|
-
params: {},
|
|
382
|
-
};
|
|
313
|
+
const eventType = (EVENT_MAPPING[decodedJWT.event_type] || decodedJWT.event_type) + (decodedJWT.sign_in_method ? `:${decodedJWT.sign_in_method}` : "");
|
|
314
|
+
return {
|
|
315
|
+
locale: decodedJWT.locale,
|
|
316
|
+
ipAddress: decodedJWT.ip_address,
|
|
317
|
+
userAgent: decodedJWT.user_agent,
|
|
318
|
+
eventId: decodedJWT.event_id,
|
|
319
|
+
eventType,
|
|
320
|
+
authType: decodedJWT.user_record ? "USER" : "UNAUTHENTICATED",
|
|
321
|
+
resource: {
|
|
322
|
+
service: "identitytoolkit.googleapis.com",
|
|
323
|
+
name: decodedJWT.tenant_id ? `projects/${projectId}/tenants/${decodedJWT.tenant_id}` : `projects/${projectId}`
|
|
324
|
+
},
|
|
325
|
+
timestamp: new Date(decodedJWT.iat * 1e3).toUTCString(),
|
|
326
|
+
additionalUserInfo: parseAdditionalUserInfo(decodedJWT),
|
|
327
|
+
credential: parseAuthCredential(decodedJWT, time),
|
|
328
|
+
emailType: decodedJWT.email_type,
|
|
329
|
+
smsType: decodedJWT.sms_type,
|
|
330
|
+
params: {}
|
|
331
|
+
};
|
|
383
332
|
}
|
|
384
|
-
exports.parseAuthEventContext = parseAuthEventContext;
|
|
385
333
|
/**
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
334
|
+
* Checks the handler response for invalid customClaims & sessionClaims objects
|
|
335
|
+
* @internal
|
|
336
|
+
*/
|
|
389
337
|
function validateAuthResponse(eventType, authRequest) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
338
|
+
if (!authRequest) {
|
|
339
|
+
authRequest = {};
|
|
340
|
+
}
|
|
341
|
+
if (authRequest.customClaims) {
|
|
342
|
+
const invalidClaims = DISALLOWED_CUSTOM_CLAIMS.filter((claim) => authRequest.customClaims.hasOwnProperty(claim));
|
|
343
|
+
if (invalidClaims.length > 0) {
|
|
344
|
+
throw new require_common_providers_https.HttpsError("invalid-argument", `The customClaims claims "${invalidClaims.join(",")}" are reserved and cannot be specified.`);
|
|
345
|
+
}
|
|
346
|
+
if (JSON.stringify(authRequest.customClaims).length > CLAIMS_MAX_PAYLOAD_SIZE) {
|
|
347
|
+
throw new require_common_providers_https.HttpsError("invalid-argument", `The customClaims payload should not exceed ${CLAIMS_MAX_PAYLOAD_SIZE} characters.`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (eventType === "beforeSignIn" && authRequest.sessionClaims) {
|
|
351
|
+
const invalidClaims = DISALLOWED_CUSTOM_CLAIMS.filter((claim) => authRequest.sessionClaims.hasOwnProperty(claim));
|
|
352
|
+
if (invalidClaims.length > 0) {
|
|
353
|
+
throw new require_common_providers_https.HttpsError("invalid-argument", `The sessionClaims claims "${invalidClaims.join(",")}" are reserved and cannot be specified.`);
|
|
354
|
+
}
|
|
355
|
+
if (JSON.stringify(authRequest.sessionClaims).length > CLAIMS_MAX_PAYLOAD_SIZE) {
|
|
356
|
+
throw new require_common_providers_https.HttpsError("invalid-argument", `The sessionClaims payload should not exceed ${CLAIMS_MAX_PAYLOAD_SIZE} characters.`);
|
|
357
|
+
}
|
|
358
|
+
const combinedClaims = {
|
|
359
|
+
...authRequest.customClaims,
|
|
360
|
+
...authRequest.sessionClaims
|
|
361
|
+
};
|
|
362
|
+
if (JSON.stringify(combinedClaims).length > CLAIMS_MAX_PAYLOAD_SIZE) {
|
|
363
|
+
throw new require_common_providers_https.HttpsError("invalid-argument", `The customClaims and sessionClaims payloads should not exceed ${CLAIMS_MAX_PAYLOAD_SIZE} characters combined.`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
419
366
|
}
|
|
420
|
-
exports.validateAuthResponse = validateAuthResponse;
|
|
421
367
|
/**
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
368
|
+
* Helper function to generate the update mask for the identity platform changed values
|
|
369
|
+
* @internal
|
|
370
|
+
*/
|
|
425
371
|
function getUpdateMask(authResponse) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
372
|
+
if (!authResponse) {
|
|
373
|
+
return "";
|
|
374
|
+
}
|
|
375
|
+
const updateMask = [];
|
|
376
|
+
for (const key in authResponse) {
|
|
377
|
+
if (authResponse.hasOwnProperty(key) && typeof authResponse[key] !== "undefined") {
|
|
378
|
+
updateMask.push(key);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return updateMask.join(",");
|
|
436
382
|
}
|
|
437
|
-
exports.getUpdateMask = getUpdateMask;
|
|
438
383
|
/** @internal */
|
|
439
384
|
function wrapHandler(eventType, handler) {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
if (!(httpErr instanceof https_1.HttpsError)) {
|
|
483
|
-
// This doesn't count as an 'explicit' error.
|
|
484
|
-
logger.error("Unhandled error", err);
|
|
485
|
-
httpErr = new https_1.HttpsError("internal", "An unexpected error occurred.");
|
|
486
|
-
}
|
|
487
|
-
const { status } = httpErr.httpErrorCode;
|
|
488
|
-
const body = { error: httpErr.toJSON() };
|
|
489
|
-
res.setHeader("Content-Type", "application/json");
|
|
490
|
-
res.status(status).send(body);
|
|
491
|
-
}
|
|
492
|
-
};
|
|
385
|
+
return async (req, res) => {
|
|
386
|
+
try {
|
|
387
|
+
const projectId = process.env.GCLOUD_PROJECT;
|
|
388
|
+
if (!isValidRequest(req)) {
|
|
389
|
+
require_logger_index.error("Invalid request, unable to process");
|
|
390
|
+
throw new require_common_providers_https.HttpsError("invalid-argument", "Bad Request");
|
|
391
|
+
}
|
|
392
|
+
if (!firebase_admin_auth.getAuth(require_common_app.getApp())._verifyAuthBlockingToken) {
|
|
393
|
+
throw new Error("Cannot validate Auth Blocking token. Please update Firebase Admin SDK to >= v10.1.0");
|
|
394
|
+
}
|
|
395
|
+
const decodedPayload = require_common_debug.isDebugFeatureEnabled("skipTokenVerification") ? unsafeDecodeAuthBlockingToken(req.body.data.jwt) : handler.platform === "gcfv1" ? await firebase_admin_auth.getAuth(require_common_app.getApp())._verifyAuthBlockingToken(req.body.data.jwt) : await firebase_admin_auth.getAuth(require_common_app.getApp())._verifyAuthBlockingToken(req.body.data.jwt, "run.app");
|
|
396
|
+
let authUserRecord;
|
|
397
|
+
if (decodedPayload.event_type === "beforeCreate" || decodedPayload.event_type === "beforeSignIn") {
|
|
398
|
+
authUserRecord = parseAuthUserRecord(decodedPayload.user_record);
|
|
399
|
+
}
|
|
400
|
+
const authEventContext = parseAuthEventContext(decodedPayload, projectId);
|
|
401
|
+
let authResponse;
|
|
402
|
+
if (handler.platform === "gcfv1") {
|
|
403
|
+
authResponse = authUserRecord ? await handler(authUserRecord, authEventContext) || undefined : await handler(authEventContext) || undefined;
|
|
404
|
+
} else {
|
|
405
|
+
authResponse = await handler({
|
|
406
|
+
...authEventContext,
|
|
407
|
+
data: authUserRecord
|
|
408
|
+
}) || undefined;
|
|
409
|
+
}
|
|
410
|
+
validateAuthResponse(eventType, authResponse);
|
|
411
|
+
const result = generateResponsePayload(authResponse);
|
|
412
|
+
res.status(200);
|
|
413
|
+
res.setHeader("Content-Type", "application/json");
|
|
414
|
+
res.send(JSON.stringify(result));
|
|
415
|
+
} catch (err) {
|
|
416
|
+
let httpErr = err;
|
|
417
|
+
if (!(httpErr instanceof require_common_providers_https.HttpsError)) {
|
|
418
|
+
require_logger_index.error("Unhandled error", err);
|
|
419
|
+
httpErr = new require_common_providers_https.HttpsError("internal", "An unexpected error occurred.");
|
|
420
|
+
}
|
|
421
|
+
const { status } = httpErr.httpErrorCode;
|
|
422
|
+
const body = { error: httpErr.toJSON() };
|
|
423
|
+
res.setHeader("Content-Type", "application/json");
|
|
424
|
+
res.status(status).send(body);
|
|
425
|
+
}
|
|
426
|
+
};
|
|
493
427
|
}
|
|
494
|
-
|
|
428
|
+
|
|
429
|
+
//#endregion
|
|
430
|
+
exports.HttpsError = require_common_providers_https.HttpsError;
|
|
431
|
+
exports.UserRecordMetadata = UserRecordMetadata;
|
|
432
|
+
exports.generateResponsePayload = generateResponsePayload;
|
|
433
|
+
exports.getUpdateMask = getUpdateMask;
|
|
434
|
+
exports.isValidRequest = isValidRequest;
|
|
435
|
+
exports.parseAuthEventContext = parseAuthEventContext;
|
|
436
|
+
exports.parseAuthUserRecord = parseAuthUserRecord;
|
|
437
|
+
exports.parseDate = parseDate;
|
|
438
|
+
exports.parseMetadata = parseMetadata;
|
|
439
|
+
exports.parseMultiFactor = parseMultiFactor;
|
|
440
|
+
exports.parseProviderData = parseProviderData;
|
|
441
|
+
exports.userRecordConstructor = userRecordConstructor;
|
|
442
|
+
exports.validateAuthResponse = validateAuthResponse;
|
|
443
|
+
exports.wrapHandler = wrapHandler;
|