firebase-admin 9.12.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.
Files changed (84) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +90 -0
  3. package/lib/app-check/app-check-api-client-internal.js +197 -0
  4. package/lib/app-check/app-check.js +79 -0
  5. package/lib/app-check/index.d.ts +160 -0
  6. package/lib/app-check/index.js +19 -0
  7. package/lib/app-check/token-generator.js +161 -0
  8. package/lib/app-check/token-verifier.js +152 -0
  9. package/lib/auth/action-code-settings-builder.js +118 -0
  10. package/lib/auth/auth-api-request.js +1856 -0
  11. package/lib/auth/auth-config.js +636 -0
  12. package/lib/auth/auth.js +836 -0
  13. package/lib/auth/identifier.js +40 -0
  14. package/lib/auth/index.d.ts +1927 -0
  15. package/lib/auth/index.js +18 -0
  16. package/lib/auth/tenant-manager.js +140 -0
  17. package/lib/auth/tenant.js +171 -0
  18. package/lib/auth/token-generator.js +200 -0
  19. package/lib/auth/token-verifier.js +259 -0
  20. package/lib/auth/user-import-builder.js +387 -0
  21. package/lib/auth/user-record.js +346 -0
  22. package/lib/credential/credential-internal.js +391 -0
  23. package/lib/credential/credential.js +44 -0
  24. package/lib/credential/index.d.ts +169 -0
  25. package/lib/credential/index.js +23 -0
  26. package/lib/database/database-internal.js +266 -0
  27. package/lib/database/index.d.ts +89 -0
  28. package/lib/database/index.js +31 -0
  29. package/lib/default-namespace.js +31 -0
  30. package/lib/firebase-app.js +349 -0
  31. package/lib/firebase-namespace-api.d.ts +243 -0
  32. package/lib/firebase-namespace-api.js +18 -0
  33. package/lib/firebase-namespace.d.ts +31 -0
  34. package/lib/firebase-namespace.js +417 -0
  35. package/lib/firestore/firestore-internal.js +105 -0
  36. package/lib/firestore/index.d.ts +50 -0
  37. package/lib/firestore/index.js +47 -0
  38. package/lib/index.d.ts +24 -0
  39. package/lib/index.js +27 -0
  40. package/lib/installations/index.d.ts +81 -0
  41. package/lib/installations/index.js +18 -0
  42. package/lib/installations/installations-request-handler.js +117 -0
  43. package/lib/installations/installations.js +62 -0
  44. package/lib/instance-id/index.d.ts +83 -0
  45. package/lib/instance-id/index.js +18 -0
  46. package/lib/instance-id/instance-id.js +87 -0
  47. package/lib/machine-learning/index.d.ts +249 -0
  48. package/lib/machine-learning/index.js +18 -0
  49. package/lib/machine-learning/machine-learning-api-client.js +304 -0
  50. package/lib/machine-learning/machine-learning-utils.js +62 -0
  51. package/lib/machine-learning/machine-learning.js +364 -0
  52. package/lib/messaging/batch-request-internal.js +129 -0
  53. package/lib/messaging/index.d.ts +1174 -0
  54. package/lib/messaging/index.js +18 -0
  55. package/lib/messaging/messaging-api-request-internal.js +128 -0
  56. package/lib/messaging/messaging-errors-internal.js +106 -0
  57. package/lib/messaging/messaging-internal.js +484 -0
  58. package/lib/messaging/messaging.js +846 -0
  59. package/lib/project-management/android-app.js +176 -0
  60. package/lib/project-management/index.d.ts +363 -0
  61. package/lib/project-management/index.js +41 -0
  62. package/lib/project-management/ios-app.js +88 -0
  63. package/lib/project-management/project-management-api-request-internal.js +273 -0
  64. package/lib/project-management/project-management.js +254 -0
  65. package/lib/remote-config/index.d.ts +369 -0
  66. package/lib/remote-config/index.js +18 -0
  67. package/lib/remote-config/remote-config-api-client-internal.js +407 -0
  68. package/lib/remote-config/remote-config.js +304 -0
  69. package/lib/security-rules/index.d.ts +216 -0
  70. package/lib/security-rules/index.js +18 -0
  71. package/lib/security-rules/security-rules-api-client-internal.js +237 -0
  72. package/lib/security-rules/security-rules-internal.js +41 -0
  73. package/lib/security-rules/security-rules.js +310 -0
  74. package/lib/storage/index.d.ts +60 -0
  75. package/lib/storage/index.js +18 -0
  76. package/lib/storage/storage.js +123 -0
  77. package/lib/utils/api-request.js +845 -0
  78. package/lib/utils/crypto-signer.js +237 -0
  79. package/lib/utils/deep-copy.js +78 -0
  80. package/lib/utils/error.js +1063 -0
  81. package/lib/utils/index.js +217 -0
  82. package/lib/utils/jwt.js +355 -0
  83. package/lib/utils/validator.js +271 -0
  84. package/package.json +122 -0
@@ -0,0 +1,259 @@
1
+ /*! firebase-admin v9.12.0 */
2
+ "use strict";
3
+ /*!
4
+ * Copyright 2018 Google Inc.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.createSessionCookieVerifier = exports.createIdTokenVerifier = exports.FirebaseTokenVerifier = exports.SESSION_COOKIE_INFO = exports.ID_TOKEN_INFO = void 0;
20
+ var error_1 = require("../utils/error");
21
+ var util = require("../utils/index");
22
+ var validator = require("../utils/validator");
23
+ var jwt_1 = require("../utils/jwt");
24
+ // Audience to use for Firebase Auth Custom tokens
25
+ var FIREBASE_AUDIENCE = 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit';
26
+ // URL containing the public keys for the Google certs (whose private keys are used to sign Firebase
27
+ // Auth ID tokens)
28
+ var CLIENT_CERT_URL = 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com';
29
+ // URL containing the public keys for Firebase session cookies. This will be updated to a different URL soon.
30
+ var SESSION_COOKIE_CERT_URL = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/publicKeys';
31
+ var EMULATOR_VERIFIER = new jwt_1.EmulatorSignatureVerifier();
32
+ /** User facing token information related to the Firebase ID token. */
33
+ exports.ID_TOKEN_INFO = {
34
+ url: 'https://firebase.google.com/docs/auth/admin/verify-id-tokens',
35
+ verifyApiName: 'verifyIdToken()',
36
+ jwtName: 'Firebase ID token',
37
+ shortName: 'ID token',
38
+ expiredErrorCode: error_1.AuthClientErrorCode.ID_TOKEN_EXPIRED,
39
+ };
40
+ /** User facing token information related to the Firebase session cookie. */
41
+ exports.SESSION_COOKIE_INFO = {
42
+ url: 'https://firebase.google.com/docs/auth/admin/manage-cookies',
43
+ verifyApiName: 'verifySessionCookie()',
44
+ jwtName: 'Firebase session cookie',
45
+ shortName: 'session cookie',
46
+ expiredErrorCode: error_1.AuthClientErrorCode.SESSION_COOKIE_EXPIRED,
47
+ };
48
+ /**
49
+ * Class for verifying ID tokens and session cookies.
50
+ */
51
+ var FirebaseTokenVerifier = /** @class */ (function () {
52
+ function FirebaseTokenVerifier(clientCertUrl, issuer, tokenInfo, app) {
53
+ this.issuer = issuer;
54
+ this.tokenInfo = tokenInfo;
55
+ this.app = app;
56
+ if (!validator.isURL(clientCertUrl)) {
57
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'The provided public client certificate URL is an invalid URL.');
58
+ }
59
+ else if (!validator.isURL(issuer)) {
60
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'The provided JWT issuer is an invalid URL.');
61
+ }
62
+ else if (!validator.isNonNullObject(tokenInfo)) {
63
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'The provided JWT information is not an object or null.');
64
+ }
65
+ else if (!validator.isURL(tokenInfo.url)) {
66
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'The provided JWT verification documentation URL is invalid.');
67
+ }
68
+ else if (!validator.isNonEmptyString(tokenInfo.verifyApiName)) {
69
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'The JWT verify API name must be a non-empty string.');
70
+ }
71
+ else if (!validator.isNonEmptyString(tokenInfo.jwtName)) {
72
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'The JWT public full name must be a non-empty string.');
73
+ }
74
+ else if (!validator.isNonEmptyString(tokenInfo.shortName)) {
75
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'The JWT public short name must be a non-empty string.');
76
+ }
77
+ else if (!validator.isNonNullObject(tokenInfo.expiredErrorCode) || !('code' in tokenInfo.expiredErrorCode)) {
78
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'The JWT expiration error code must be a non-null ErrorInfo object.');
79
+ }
80
+ this.shortNameArticle = tokenInfo.shortName.charAt(0).match(/[aeiou]/i) ? 'an' : 'a';
81
+ this.signatureVerifier =
82
+ jwt_1.PublicKeySignatureVerifier.withCertificateUrl(clientCertUrl, app.options.httpAgent);
83
+ // For backward compatibility, the project ID is validated in the verification call.
84
+ }
85
+ /**
86
+ * Verifies the format and signature of a Firebase Auth JWT token.
87
+ *
88
+ * @param jwtToken The Firebase Auth JWT token to verify.
89
+ * @param isEmulator Whether to accept Auth Emulator tokens.
90
+ * @return A promise fulfilled with the decoded claims of the Firebase Auth ID token.
91
+ */
92
+ FirebaseTokenVerifier.prototype.verifyJWT = function (jwtToken, isEmulator) {
93
+ var _this = this;
94
+ if (isEmulator === void 0) { isEmulator = false; }
95
+ if (!validator.isString(jwtToken)) {
96
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "First argument to " + this.tokenInfo.verifyApiName + " must be a " + this.tokenInfo.jwtName + " string.");
97
+ }
98
+ return this.ensureProjectId()
99
+ .then(function (projectId) {
100
+ return _this.decodeAndVerify(jwtToken, projectId, isEmulator);
101
+ })
102
+ .then(function (decoded) {
103
+ var decodedIdToken = decoded.payload;
104
+ decodedIdToken.uid = decodedIdToken.sub;
105
+ return decodedIdToken;
106
+ });
107
+ };
108
+ FirebaseTokenVerifier.prototype.ensureProjectId = function () {
109
+ var _this = this;
110
+ return util.findProjectId(this.app)
111
+ .then(function (projectId) {
112
+ if (!validator.isNonEmptyString(projectId)) {
113
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_CREDENTIAL, 'Must initialize app with a cert credential or set your Firebase project ID as the ' +
114
+ ("GOOGLE_CLOUD_PROJECT environment variable to call " + _this.tokenInfo.verifyApiName + "."));
115
+ }
116
+ return Promise.resolve(projectId);
117
+ });
118
+ };
119
+ FirebaseTokenVerifier.prototype.decodeAndVerify = function (token, projectId, isEmulator) {
120
+ var _this = this;
121
+ return this.safeDecode(token)
122
+ .then(function (decodedToken) {
123
+ _this.verifyContent(decodedToken, projectId, isEmulator);
124
+ return _this.verifySignature(token, isEmulator)
125
+ .then(function () { return decodedToken; });
126
+ });
127
+ };
128
+ FirebaseTokenVerifier.prototype.safeDecode = function (jwtToken) {
129
+ var _this = this;
130
+ return jwt_1.decodeJwt(jwtToken)
131
+ .catch(function (err) {
132
+ if (err.code == jwt_1.JwtErrorCode.INVALID_ARGUMENT) {
133
+ var verifyJwtTokenDocsMessage = " See " + _this.tokenInfo.url + " " +
134
+ ("for details on how to retrieve " + _this.shortNameArticle + " " + _this.tokenInfo.shortName + ".");
135
+ var errorMessage = "Decoding " + _this.tokenInfo.jwtName + " failed. Make sure you passed " +
136
+ ("the entire string JWT which represents " + _this.shortNameArticle + " ") +
137
+ (_this.tokenInfo.shortName + ".") + verifyJwtTokenDocsMessage;
138
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, errorMessage);
139
+ }
140
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, err.message);
141
+ });
142
+ };
143
+ /**
144
+ * Verifies the content of a Firebase Auth JWT.
145
+ *
146
+ * @param fullDecodedToken The decoded JWT.
147
+ * @param projectId The Firebase Project Id.
148
+ * @param isEmulator Whether the token is an Emulator token.
149
+ */
150
+ FirebaseTokenVerifier.prototype.verifyContent = function (fullDecodedToken, projectId, isEmulator) {
151
+ var header = fullDecodedToken && fullDecodedToken.header;
152
+ var payload = fullDecodedToken && fullDecodedToken.payload;
153
+ var projectIdMatchMessage = " Make sure the " + this.tokenInfo.shortName + " comes from the same " +
154
+ 'Firebase project as the service account used to authenticate this SDK.';
155
+ var verifyJwtTokenDocsMessage = " See " + this.tokenInfo.url + " " +
156
+ ("for details on how to retrieve " + this.shortNameArticle + " " + this.tokenInfo.shortName + ".");
157
+ var errorMessage;
158
+ if (!isEmulator && typeof header.kid === 'undefined') {
159
+ var isCustomToken = (payload.aud === FIREBASE_AUDIENCE);
160
+ var isLegacyCustomToken = (header.alg === 'HS256' && payload.v === 0 && 'd' in payload && 'uid' in payload.d);
161
+ if (isCustomToken) {
162
+ errorMessage = this.tokenInfo.verifyApiName + " expects " + this.shortNameArticle + " " +
163
+ (this.tokenInfo.shortName + ", but was given a custom token.");
164
+ }
165
+ else if (isLegacyCustomToken) {
166
+ errorMessage = this.tokenInfo.verifyApiName + " expects " + this.shortNameArticle + " " +
167
+ (this.tokenInfo.shortName + ", but was given a legacy custom token.");
168
+ }
169
+ else {
170
+ errorMessage = 'Firebase ID token has no "kid" claim.';
171
+ }
172
+ errorMessage += verifyJwtTokenDocsMessage;
173
+ }
174
+ else if (!isEmulator && header.alg !== jwt_1.ALGORITHM_RS256) {
175
+ errorMessage = this.tokenInfo.jwtName + " has incorrect algorithm. Expected \"" + jwt_1.ALGORITHM_RS256 + '" but got ' +
176
+ '"' + header.alg + '".' + verifyJwtTokenDocsMessage;
177
+ }
178
+ else if (payload.aud !== projectId) {
179
+ errorMessage = this.tokenInfo.jwtName + " has incorrect \"aud\" (audience) claim. Expected \"" +
180
+ projectId + '" but got "' + payload.aud + '".' + projectIdMatchMessage +
181
+ verifyJwtTokenDocsMessage;
182
+ }
183
+ else if (payload.iss !== this.issuer + projectId) {
184
+ errorMessage = this.tokenInfo.jwtName + " has incorrect \"iss\" (issuer) claim. Expected " +
185
+ ("\"" + this.issuer) + projectId + '" but got "' +
186
+ payload.iss + '".' + projectIdMatchMessage + verifyJwtTokenDocsMessage;
187
+ }
188
+ else if (typeof payload.sub !== 'string') {
189
+ errorMessage = this.tokenInfo.jwtName + " has no \"sub\" (subject) claim." + verifyJwtTokenDocsMessage;
190
+ }
191
+ else if (payload.sub === '') {
192
+ errorMessage = this.tokenInfo.jwtName + " has an empty string \"sub\" (subject) claim." + verifyJwtTokenDocsMessage;
193
+ }
194
+ else if (payload.sub.length > 128) {
195
+ errorMessage = this.tokenInfo.jwtName + " has \"sub\" (subject) claim longer than 128 characters." +
196
+ verifyJwtTokenDocsMessage;
197
+ }
198
+ if (errorMessage) {
199
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, errorMessage);
200
+ }
201
+ };
202
+ FirebaseTokenVerifier.prototype.verifySignature = function (jwtToken, isEmulator) {
203
+ var _this = this;
204
+ var verifier = isEmulator ? EMULATOR_VERIFIER : this.signatureVerifier;
205
+ return verifier.verify(jwtToken)
206
+ .catch(function (error) {
207
+ throw _this.mapJwtErrorToAuthError(error);
208
+ });
209
+ };
210
+ /**
211
+ * Maps JwtError to FirebaseAuthError
212
+ *
213
+ * @param error JwtError to be mapped.
214
+ * @returns FirebaseAuthError or Error instance.
215
+ */
216
+ FirebaseTokenVerifier.prototype.mapJwtErrorToAuthError = function (error) {
217
+ var verifyJwtTokenDocsMessage = " See " + this.tokenInfo.url + " " +
218
+ ("for details on how to retrieve " + this.shortNameArticle + " " + this.tokenInfo.shortName + ".");
219
+ if (error.code === jwt_1.JwtErrorCode.TOKEN_EXPIRED) {
220
+ var errorMessage = this.tokenInfo.jwtName + " has expired. Get a fresh " + this.tokenInfo.shortName +
221
+ (" from your client app and try again (auth/" + this.tokenInfo.expiredErrorCode.code + ").") +
222
+ verifyJwtTokenDocsMessage;
223
+ return new error_1.FirebaseAuthError(this.tokenInfo.expiredErrorCode, errorMessage);
224
+ }
225
+ else if (error.code === jwt_1.JwtErrorCode.INVALID_SIGNATURE) {
226
+ var errorMessage = this.tokenInfo.jwtName + " has invalid signature." + verifyJwtTokenDocsMessage;
227
+ return new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, errorMessage);
228
+ }
229
+ else if (error.code === jwt_1.JwtErrorCode.NO_MATCHING_KID) {
230
+ var errorMessage = this.tokenInfo.jwtName + " has \"kid\" claim which does not " +
231
+ ("correspond to a known public key. Most likely the " + this.tokenInfo.shortName + " ") +
232
+ 'is expired, so get a fresh token from your client app and try again.';
233
+ return new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, errorMessage);
234
+ }
235
+ return new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, error.message);
236
+ };
237
+ return FirebaseTokenVerifier;
238
+ }());
239
+ exports.FirebaseTokenVerifier = FirebaseTokenVerifier;
240
+ /**
241
+ * Creates a new FirebaseTokenVerifier to verify Firebase ID tokens.
242
+ *
243
+ * @param app Firebase app instance.
244
+ * @return FirebaseTokenVerifier
245
+ */
246
+ function createIdTokenVerifier(app) {
247
+ return new FirebaseTokenVerifier(CLIENT_CERT_URL, 'https://securetoken.google.com/', exports.ID_TOKEN_INFO, app);
248
+ }
249
+ exports.createIdTokenVerifier = createIdTokenVerifier;
250
+ /**
251
+ * Creates a new FirebaseTokenVerifier to verify Firebase session cookies.
252
+ *
253
+ * @param app Firebase app instance.
254
+ * @return FirebaseTokenVerifier
255
+ */
256
+ function createSessionCookieVerifier(app) {
257
+ return new FirebaseTokenVerifier(SESSION_COOKIE_CERT_URL, 'https://session.firebase.google.com/', exports.SESSION_COOKIE_INFO, app);
258
+ }
259
+ exports.createSessionCookieVerifier = createSessionCookieVerifier;
@@ -0,0 +1,387 @@
1
+ /*! firebase-admin v9.12.0 */
2
+ "use strict";
3
+ /*!
4
+ * Copyright 2018 Google Inc.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.UserImportBuilder = exports.convertMultiFactorInfoToServerFormat = void 0;
20
+ var deep_copy_1 = require("../utils/deep-copy");
21
+ var utils = require("../utils");
22
+ var validator = require("../utils/validator");
23
+ var error_1 = require("../utils/error");
24
+ /**
25
+ * Converts a client format second factor object to server format.
26
+ * @param multiFactorInfo The client format second factor.
27
+ * @return The corresponding AuthFactorInfo server request format.
28
+ */
29
+ function convertMultiFactorInfoToServerFormat(multiFactorInfo) {
30
+ var enrolledAt;
31
+ if (typeof multiFactorInfo.enrollmentTime !== 'undefined') {
32
+ if (validator.isUTCDateString(multiFactorInfo.enrollmentTime)) {
33
+ // Convert from UTC date string (client side format) to ISO date string (server side format).
34
+ enrolledAt = new Date(multiFactorInfo.enrollmentTime).toISOString();
35
+ }
36
+ else {
37
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ENROLLMENT_TIME, "The second factor \"enrollmentTime\" for \"" + multiFactorInfo.uid + "\" must be a valid " +
38
+ 'UTC date string.');
39
+ }
40
+ }
41
+ // Currently only phone second factors are supported.
42
+ if (isPhoneFactor(multiFactorInfo)) {
43
+ // If any required field is missing or invalid, validation will still fail later.
44
+ var authFactorInfo = {
45
+ mfaEnrollmentId: multiFactorInfo.uid,
46
+ displayName: multiFactorInfo.displayName,
47
+ // Required for all phone second factors.
48
+ phoneInfo: multiFactorInfo.phoneNumber,
49
+ enrolledAt: enrolledAt,
50
+ };
51
+ for (var objKey in authFactorInfo) {
52
+ if (typeof authFactorInfo[objKey] === 'undefined') {
53
+ delete authFactorInfo[objKey];
54
+ }
55
+ }
56
+ return authFactorInfo;
57
+ }
58
+ else {
59
+ // Unsupported second factor.
60
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.UNSUPPORTED_SECOND_FACTOR, "Unsupported second factor \"" + JSON.stringify(multiFactorInfo) + "\" provided.");
61
+ }
62
+ }
63
+ exports.convertMultiFactorInfoToServerFormat = convertMultiFactorInfoToServerFormat;
64
+ function isPhoneFactor(multiFactorInfo) {
65
+ return multiFactorInfo.factorId === 'phone';
66
+ }
67
+ /**
68
+ * @param {any} obj The object to check for number field within.
69
+ * @param {string} key The entry key.
70
+ * @return {number} The corresponding number if available. Otherwise, NaN.
71
+ */
72
+ function getNumberField(obj, key) {
73
+ if (typeof obj[key] !== 'undefined' && obj[key] !== null) {
74
+ return parseInt(obj[key].toString(), 10);
75
+ }
76
+ return NaN;
77
+ }
78
+ /**
79
+ * Converts a UserImportRecord to a UploadAccountUser object. Throws an error when invalid
80
+ * fields are provided.
81
+ * @param {UserImportRecord} user The UserImportRecord to conver to UploadAccountUser.
82
+ * @param {ValidatorFunction=} userValidator The user validator function.
83
+ * @return {UploadAccountUser} The corresponding UploadAccountUser to return.
84
+ */
85
+ function populateUploadAccountUser(user, userValidator) {
86
+ var result = {
87
+ localId: user.uid,
88
+ email: user.email,
89
+ emailVerified: user.emailVerified,
90
+ displayName: user.displayName,
91
+ disabled: user.disabled,
92
+ photoUrl: user.photoURL,
93
+ phoneNumber: user.phoneNumber,
94
+ providerUserInfo: [],
95
+ mfaInfo: [],
96
+ tenantId: user.tenantId,
97
+ customAttributes: user.customClaims && JSON.stringify(user.customClaims),
98
+ };
99
+ if (typeof user.passwordHash !== 'undefined') {
100
+ if (!validator.isBuffer(user.passwordHash)) {
101
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PASSWORD_HASH);
102
+ }
103
+ result.passwordHash = utils.toWebSafeBase64(user.passwordHash);
104
+ }
105
+ if (typeof user.passwordSalt !== 'undefined') {
106
+ if (!validator.isBuffer(user.passwordSalt)) {
107
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PASSWORD_SALT);
108
+ }
109
+ result.salt = utils.toWebSafeBase64(user.passwordSalt);
110
+ }
111
+ if (validator.isNonNullObject(user.metadata)) {
112
+ if (validator.isNonEmptyString(user.metadata.creationTime)) {
113
+ result.createdAt = new Date(user.metadata.creationTime).getTime();
114
+ }
115
+ if (validator.isNonEmptyString(user.metadata.lastSignInTime)) {
116
+ result.lastLoginAt = new Date(user.metadata.lastSignInTime).getTime();
117
+ }
118
+ }
119
+ if (validator.isArray(user.providerData)) {
120
+ user.providerData.forEach(function (providerData) {
121
+ result.providerUserInfo.push({
122
+ providerId: providerData.providerId,
123
+ rawId: providerData.uid,
124
+ email: providerData.email,
125
+ displayName: providerData.displayName,
126
+ photoUrl: providerData.photoURL,
127
+ });
128
+ });
129
+ }
130
+ // Convert user.multiFactor.enrolledFactors to server format.
131
+ if (validator.isNonNullObject(user.multiFactor) &&
132
+ validator.isNonEmptyArray(user.multiFactor.enrolledFactors)) {
133
+ user.multiFactor.enrolledFactors.forEach(function (multiFactorInfo) {
134
+ result.mfaInfo.push(convertMultiFactorInfoToServerFormat(multiFactorInfo));
135
+ });
136
+ }
137
+ // Remove blank fields.
138
+ var key;
139
+ for (key in result) {
140
+ if (typeof result[key] === 'undefined') {
141
+ delete result[key];
142
+ }
143
+ }
144
+ if (result.providerUserInfo.length === 0) {
145
+ delete result.providerUserInfo;
146
+ }
147
+ if (result.mfaInfo.length === 0) {
148
+ delete result.mfaInfo;
149
+ }
150
+ // Validate the constructured user individual request. This will throw if an error
151
+ // is detected.
152
+ if (typeof userValidator === 'function') {
153
+ userValidator(result);
154
+ }
155
+ return result;
156
+ }
157
+ /**
158
+ * Class that provides a helper for building/validating uploadAccount requests and
159
+ * UserImportResult responses.
160
+ */
161
+ var UserImportBuilder = /** @class */ (function () {
162
+ /**
163
+ * @param {UserImportRecord[]} users The list of user records to import.
164
+ * @param {UserImportOptions=} options The import options which includes hashing
165
+ * algorithm details.
166
+ * @param {ValidatorFunction=} userRequestValidator The user request validator function.
167
+ * @constructor
168
+ */
169
+ function UserImportBuilder(users, options, userRequestValidator) {
170
+ this.requiresHashOptions = false;
171
+ this.validatedUsers = [];
172
+ this.userImportResultErrors = [];
173
+ this.indexMap = {};
174
+ this.validatedUsers = this.populateUsers(users, userRequestValidator);
175
+ this.validatedOptions = this.populateOptions(options, this.requiresHashOptions);
176
+ }
177
+ /**
178
+ * Returns the corresponding constructed uploadAccount request.
179
+ * @return {UploadAccountRequest} The constructed uploadAccount request.
180
+ */
181
+ UserImportBuilder.prototype.buildRequest = function () {
182
+ var users = this.validatedUsers.map(function (user) {
183
+ return deep_copy_1.deepCopy(user);
184
+ });
185
+ return deep_copy_1.deepExtend({ users: users }, deep_copy_1.deepCopy(this.validatedOptions));
186
+ };
187
+ /**
188
+ * Populates the UserImportResult using the client side detected errors and the server
189
+ * side returned errors.
190
+ * @return {UserImportResult} The user import result based on the returned failed
191
+ * uploadAccount response.
192
+ */
193
+ UserImportBuilder.prototype.buildResponse = function (failedUploads) {
194
+ var _this = this;
195
+ // Initialize user import result.
196
+ var importResult = {
197
+ successCount: this.validatedUsers.length,
198
+ failureCount: this.userImportResultErrors.length,
199
+ errors: deep_copy_1.deepCopy(this.userImportResultErrors),
200
+ };
201
+ importResult.failureCount += failedUploads.length;
202
+ importResult.successCount -= failedUploads.length;
203
+ failedUploads.forEach(function (failedUpload) {
204
+ importResult.errors.push({
205
+ // Map backend request index to original developer provided array index.
206
+ index: _this.indexMap[failedUpload.index],
207
+ error: new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_USER_IMPORT, failedUpload.message),
208
+ });
209
+ });
210
+ // Sort errors by index.
211
+ importResult.errors.sort(function (a, b) {
212
+ return a.index - b.index;
213
+ });
214
+ // Return sorted result.
215
+ return importResult;
216
+ };
217
+ /**
218
+ * Validates and returns the hashing options of the uploadAccount request.
219
+ * Throws an error whenever an invalid or missing options is detected.
220
+ * @param {UserImportOptions} options The UserImportOptions.
221
+ * @param {boolean} requiresHashOptions Whether to require hash options.
222
+ * @return {UploadAccountOptions} The populated UploadAccount options.
223
+ */
224
+ UserImportBuilder.prototype.populateOptions = function (options, requiresHashOptions) {
225
+ var populatedOptions;
226
+ if (!requiresHashOptions) {
227
+ return {};
228
+ }
229
+ if (!validator.isNonNullObject(options)) {
230
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '"UserImportOptions" are required when importing users with passwords.');
231
+ }
232
+ if (!validator.isNonNullObject(options.hash)) {
233
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.MISSING_HASH_ALGORITHM, '"hash.algorithm" is missing from the provided "UserImportOptions".');
234
+ }
235
+ if (typeof options.hash.algorithm === 'undefined' ||
236
+ !validator.isNonEmptyString(options.hash.algorithm)) {
237
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_ALGORITHM, '"hash.algorithm" must be a string matching the list of supported algorithms.');
238
+ }
239
+ var rounds;
240
+ switch (options.hash.algorithm) {
241
+ case 'HMAC_SHA512':
242
+ case 'HMAC_SHA256':
243
+ case 'HMAC_SHA1':
244
+ case 'HMAC_MD5':
245
+ if (!validator.isBuffer(options.hash.key)) {
246
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_KEY, 'A non-empty "hash.key" byte buffer must be provided for ' +
247
+ ("hash algorithm " + options.hash.algorithm + "."));
248
+ }
249
+ populatedOptions = {
250
+ hashAlgorithm: options.hash.algorithm,
251
+ signerKey: utils.toWebSafeBase64(options.hash.key),
252
+ };
253
+ break;
254
+ case 'MD5':
255
+ case 'SHA1':
256
+ case 'SHA256':
257
+ case 'SHA512': {
258
+ // MD5 is [0,8192] but SHA1, SHA256, and SHA512 are [1,8192]
259
+ rounds = getNumberField(options.hash, 'rounds');
260
+ var minRounds = options.hash.algorithm === 'MD5' ? 0 : 1;
261
+ if (isNaN(rounds) || rounds < minRounds || rounds > 8192) {
262
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_ROUNDS, "A valid \"hash.rounds\" number between " + minRounds + " and 8192 must be provided for " +
263
+ ("hash algorithm " + options.hash.algorithm + "."));
264
+ }
265
+ populatedOptions = {
266
+ hashAlgorithm: options.hash.algorithm,
267
+ rounds: rounds,
268
+ };
269
+ break;
270
+ }
271
+ case 'PBKDF_SHA1':
272
+ case 'PBKDF2_SHA256':
273
+ rounds = getNumberField(options.hash, 'rounds');
274
+ if (isNaN(rounds) || rounds < 0 || rounds > 120000) {
275
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_ROUNDS, 'A valid "hash.rounds" number between 0 and 120000 must be provided for ' +
276
+ ("hash algorithm " + options.hash.algorithm + "."));
277
+ }
278
+ populatedOptions = {
279
+ hashAlgorithm: options.hash.algorithm,
280
+ rounds: rounds,
281
+ };
282
+ break;
283
+ case 'SCRYPT': {
284
+ if (!validator.isBuffer(options.hash.key)) {
285
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_KEY, 'A "hash.key" byte buffer must be provided for ' +
286
+ ("hash algorithm " + options.hash.algorithm + "."));
287
+ }
288
+ rounds = getNumberField(options.hash, 'rounds');
289
+ if (isNaN(rounds) || rounds <= 0 || rounds > 8) {
290
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_ROUNDS, 'A valid "hash.rounds" number between 1 and 8 must be provided for ' +
291
+ ("hash algorithm " + options.hash.algorithm + "."));
292
+ }
293
+ var memoryCost = getNumberField(options.hash, 'memoryCost');
294
+ if (isNaN(memoryCost) || memoryCost <= 0 || memoryCost > 14) {
295
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_MEMORY_COST, 'A valid "hash.memoryCost" number between 1 and 14 must be provided for ' +
296
+ ("hash algorithm " + options.hash.algorithm + "."));
297
+ }
298
+ if (typeof options.hash.saltSeparator !== 'undefined' &&
299
+ !validator.isBuffer(options.hash.saltSeparator)) {
300
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_SALT_SEPARATOR, '"hash.saltSeparator" must be a byte buffer.');
301
+ }
302
+ populatedOptions = {
303
+ hashAlgorithm: options.hash.algorithm,
304
+ signerKey: utils.toWebSafeBase64(options.hash.key),
305
+ rounds: rounds,
306
+ memoryCost: memoryCost,
307
+ saltSeparator: utils.toWebSafeBase64(options.hash.saltSeparator || Buffer.from('')),
308
+ };
309
+ break;
310
+ }
311
+ case 'BCRYPT':
312
+ populatedOptions = {
313
+ hashAlgorithm: options.hash.algorithm,
314
+ };
315
+ break;
316
+ case 'STANDARD_SCRYPT': {
317
+ var cpuMemCost = getNumberField(options.hash, 'memoryCost');
318
+ if (isNaN(cpuMemCost)) {
319
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_MEMORY_COST, 'A valid "hash.memoryCost" number must be provided for ' +
320
+ ("hash algorithm " + options.hash.algorithm + "."));
321
+ }
322
+ var parallelization = getNumberField(options.hash, 'parallelization');
323
+ if (isNaN(parallelization)) {
324
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_PARALLELIZATION, 'A valid "hash.parallelization" number must be provided for ' +
325
+ ("hash algorithm " + options.hash.algorithm + "."));
326
+ }
327
+ var blockSize = getNumberField(options.hash, 'blockSize');
328
+ if (isNaN(blockSize)) {
329
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_BLOCK_SIZE, 'A valid "hash.blockSize" number must be provided for ' +
330
+ ("hash algorithm " + options.hash.algorithm + "."));
331
+ }
332
+ var dkLen = getNumberField(options.hash, 'derivedKeyLength');
333
+ if (isNaN(dkLen)) {
334
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_DERIVED_KEY_LENGTH, 'A valid "hash.derivedKeyLength" number must be provided for ' +
335
+ ("hash algorithm " + options.hash.algorithm + "."));
336
+ }
337
+ populatedOptions = {
338
+ hashAlgorithm: options.hash.algorithm,
339
+ cpuMemCost: cpuMemCost,
340
+ parallelization: parallelization,
341
+ blockSize: blockSize,
342
+ dkLen: dkLen,
343
+ };
344
+ break;
345
+ }
346
+ default:
347
+ throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_HASH_ALGORITHM, "Unsupported hash algorithm provider \"" + options.hash.algorithm + "\".");
348
+ }
349
+ return populatedOptions;
350
+ };
351
+ /**
352
+ * Validates and returns the users list of the uploadAccount request.
353
+ * Whenever a user with an error is detected, the error is cached and will later be
354
+ * merged into the user import result. This allows the processing of valid users without
355
+ * failing early on the first error detected.
356
+ * @param {UserImportRecord[]} users The UserImportRecords to convert to UnploadAccountUser
357
+ * objects.
358
+ * @param {ValidatorFunction=} userValidator The user validator function.
359
+ * @return {UploadAccountUser[]} The populated uploadAccount users.
360
+ */
361
+ UserImportBuilder.prototype.populateUsers = function (users, userValidator) {
362
+ var _this = this;
363
+ var populatedUsers = [];
364
+ users.forEach(function (user, index) {
365
+ try {
366
+ var result = populateUploadAccountUser(user, userValidator);
367
+ if (typeof result.passwordHash !== 'undefined') {
368
+ _this.requiresHashOptions = true;
369
+ }
370
+ // Only users that pass client screening will be passed to backend for processing.
371
+ populatedUsers.push(result);
372
+ // Map user's index (the one to be sent to backend) to original developer provided array.
373
+ _this.indexMap[populatedUsers.length - 1] = index;
374
+ }
375
+ catch (error) {
376
+ // Save the client side error with respect to the developer provided array.
377
+ _this.userImportResultErrors.push({
378
+ index: index,
379
+ error: error,
380
+ });
381
+ }
382
+ });
383
+ return populatedUsers;
384
+ };
385
+ return UserImportBuilder;
386
+ }());
387
+ exports.UserImportBuilder = UserImportBuilder;