UNPKG

90.6 kBJavaScriptView Raw
1/*! firebase-admin v12.0.0 */
2"use strict";
3/*!
4 * @license
5 * Copyright 2017 Google Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19Object.defineProperty(exports, "__esModule", { value: true });
20exports.useEmulator = exports.TenantAwareAuthRequestHandler = exports.AuthRequestHandler = exports.AbstractAuthRequestHandler = exports.FIREBASE_AUTH_SIGN_UP_NEW_USER = exports.FIREBASE_AUTH_SET_ACCOUNT_INFO = exports.FIREBASE_AUTH_BATCH_DELETE_ACCOUNTS = exports.FIREBASE_AUTH_DELETE_ACCOUNT = exports.FIREBASE_AUTH_GET_ACCOUNTS_INFO = exports.FIREBASE_AUTH_GET_ACCOUNT_INFO = exports.FIREBASE_AUTH_DOWNLOAD_ACCOUNT = exports.FIREBASE_AUTH_UPLOAD_ACCOUNT = exports.FIREBASE_AUTH_CREATE_SESSION_COOKIE = exports.EMAIL_ACTION_REQUEST_TYPES = exports.RESERVED_CLAIMS = void 0;
21const validator = require("../utils/validator");
22const deep_copy_1 = require("../utils/deep-copy");
23const error_1 = require("../utils/error");
24const api_request_1 = require("../utils/api-request");
25const utils = require("../utils/index");
26const user_import_builder_1 = require("./user-import-builder");
27const action_code_settings_builder_1 = require("./action-code-settings-builder");
28const tenant_1 = require("./tenant");
29const identifier_1 = require("./identifier");
30const auth_config_1 = require("./auth-config");
31const project_config_1 = require("./project-config");
32/** Firebase Auth request header. */
33const FIREBASE_AUTH_HEADER = {
34 'X-Client-Version': `Node/Admin/${utils.getSdkVersion()}`,
35};
36/** Firebase Auth request timeout duration in milliseconds. */
37const FIREBASE_AUTH_TIMEOUT = 25000;
38/** List of reserved claims which cannot be provided when creating a custom token. */
39exports.RESERVED_CLAIMS = [
40 'acr', 'amr', 'at_hash', 'aud', 'auth_time', 'azp', 'cnf', 'c_hash', 'exp', 'iat',
41 'iss', 'jti', 'nbf', 'nonce', 'sub', 'firebase',
42];
43/** List of supported email action request types. */
44exports.EMAIL_ACTION_REQUEST_TYPES = [
45 'PASSWORD_RESET', 'VERIFY_EMAIL', 'EMAIL_SIGNIN', 'VERIFY_AND_CHANGE_EMAIL',
46];
47/** Maximum allowed number of characters in the custom claims payload. */
48const MAX_CLAIMS_PAYLOAD_SIZE = 1000;
49/** Maximum allowed number of users to batch download at one time. */
50const MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE = 1000;
51/** Maximum allowed number of users to batch upload at one time. */
52const MAX_UPLOAD_ACCOUNT_BATCH_SIZE = 1000;
53/** Maximum allowed number of users to batch get at one time. */
54const MAX_GET_ACCOUNTS_BATCH_SIZE = 100;
55/** Maximum allowed number of users to batch delete at one time. */
56const MAX_DELETE_ACCOUNTS_BATCH_SIZE = 1000;
57/** Minimum allowed session cookie duration in seconds (5 minutes). */
58const MIN_SESSION_COOKIE_DURATION_SECS = 5 * 60;
59/** Maximum allowed session cookie duration in seconds (2 weeks). */
60const MAX_SESSION_COOKIE_DURATION_SECS = 14 * 24 * 60 * 60;
61/** Maximum allowed number of provider configurations to batch download at one time. */
62const MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE = 100;
63/** The Firebase Auth backend base URL format. */
64const FIREBASE_AUTH_BASE_URL_FORMAT = 'https://identitytoolkit.googleapis.com/{version}/projects/{projectId}{api}';
65/** Firebase Auth base URlLformat when using the auth emultor. */
66const FIREBASE_AUTH_EMULATOR_BASE_URL_FORMAT = 'http://{host}/identitytoolkit.googleapis.com/{version}/projects/{projectId}{api}';
67/** The Firebase Auth backend multi-tenancy base URL format. */
68const FIREBASE_AUTH_TENANT_URL_FORMAT = FIREBASE_AUTH_BASE_URL_FORMAT.replace('projects/{projectId}', 'projects/{projectId}/tenants/{tenantId}');
69/** Firebase Auth base URL format when using the auth emultor with multi-tenancy. */
70const FIREBASE_AUTH_EMULATOR_TENANT_URL_FORMAT = FIREBASE_AUTH_EMULATOR_BASE_URL_FORMAT.replace('projects/{projectId}', 'projects/{projectId}/tenants/{tenantId}');
71/** Maximum allowed number of tenants to download at one time. */
72const MAX_LIST_TENANT_PAGE_SIZE = 1000;
73/**
74 * Enum for the user write operation type.
75 */
76var WriteOperationType;
77(function (WriteOperationType) {
78 WriteOperationType["Create"] = "create";
79 WriteOperationType["Update"] = "update";
80 WriteOperationType["Upload"] = "upload";
81})(WriteOperationType || (WriteOperationType = {}));
82/** Defines a base utility to help with resource URL construction. */
83class AuthResourceUrlBuilder {
84 /**
85 * The resource URL builder constructor.
86 *
87 * @param projectId - The resource project ID.
88 * @param version - The endpoint API version.
89 * @constructor
90 */
91 constructor(app, version = 'v1') {
92 this.app = app;
93 this.version = version;
94 if (useEmulator()) {
95 this.urlFormat = utils.formatString(FIREBASE_AUTH_EMULATOR_BASE_URL_FORMAT, {
96 host: emulatorHost()
97 });
98 }
99 else {
100 this.urlFormat = FIREBASE_AUTH_BASE_URL_FORMAT;
101 }
102 }
103 /**
104 * Returns the resource URL corresponding to the provided parameters.
105 *
106 * @param api - The backend API name.
107 * @param params - The optional additional parameters to substitute in the
108 * URL path.
109 * @returns The corresponding resource URL.
110 */
111 getUrl(api, params) {
112 return this.getProjectId()
113 .then((projectId) => {
114 const baseParams = {
115 version: this.version,
116 projectId,
117 api: api || '',
118 };
119 const baseUrl = utils.formatString(this.urlFormat, baseParams);
120 // Substitute additional api related parameters.
121 return utils.formatString(baseUrl, params || {});
122 });
123 }
124 getProjectId() {
125 if (this.projectId) {
126 return Promise.resolve(this.projectId);
127 }
128 return utils.findProjectId(this.app)
129 .then((projectId) => {
130 if (!validator.isNonEmptyString(projectId)) {
131 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_CREDENTIAL, 'Failed to determine project ID for Auth. Initialize the '
132 + 'SDK with service account credentials or set project ID as an app option. '
133 + 'Alternatively set the GOOGLE_CLOUD_PROJECT environment variable.');
134 }
135 this.projectId = projectId;
136 return projectId;
137 });
138 }
139}
140/** Tenant aware resource builder utility. */
141class TenantAwareAuthResourceUrlBuilder extends AuthResourceUrlBuilder {
142 /**
143 * The tenant aware resource URL builder constructor.
144 *
145 * @param projectId - The resource project ID.
146 * @param version - The endpoint API version.
147 * @param tenantId - The tenant ID.
148 * @constructor
149 */
150 constructor(app, version, tenantId) {
151 super(app, version);
152 this.app = app;
153 this.version = version;
154 this.tenantId = tenantId;
155 if (useEmulator()) {
156 this.urlFormat = utils.formatString(FIREBASE_AUTH_EMULATOR_TENANT_URL_FORMAT, {
157 host: emulatorHost()
158 });
159 }
160 else {
161 this.urlFormat = FIREBASE_AUTH_TENANT_URL_FORMAT;
162 }
163 }
164 /**
165 * Returns the resource URL corresponding to the provided parameters.
166 *
167 * @param api - The backend API name.
168 * @param params - The optional additional parameters to substitute in the
169 * URL path.
170 * @returns The corresponding resource URL.
171 */
172 getUrl(api, params) {
173 return super.getUrl(api, params)
174 .then((url) => {
175 return utils.formatString(url, { tenantId: this.tenantId });
176 });
177 }
178}
179/**
180 * Auth-specific HTTP client which uses the special "owner" token
181 * when communicating with the Auth Emulator.
182 */
183class AuthHttpClient extends api_request_1.AuthorizedHttpClient {
184 getToken() {
185 if (useEmulator()) {
186 return Promise.resolve('owner');
187 }
188 return super.getToken();
189 }
190}
191/**
192 * Validates an AuthFactorInfo object. All unsupported parameters
193 * are removed from the original request. If an invalid field is passed
194 * an error is thrown.
195 *
196 * @param request - The AuthFactorInfo request object.
197 */
198function validateAuthFactorInfo(request) {
199 const validKeys = {
200 mfaEnrollmentId: true,
201 displayName: true,
202 phoneInfo: true,
203 enrolledAt: true,
204 };
205 // Remove unsupported keys from the original request.
206 for (const key in request) {
207 if (!(key in validKeys)) {
208 delete request[key];
209 }
210 }
211 // No enrollment ID is available for signupNewUser. Use another identifier.
212 const authFactorInfoIdentifier = request.mfaEnrollmentId || request.phoneInfo || JSON.stringify(request);
213 // Enrollment uid may or may not be specified for update operations.
214 if (typeof request.mfaEnrollmentId !== 'undefined' &&
215 !validator.isNonEmptyString(request.mfaEnrollmentId)) {
216 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID, 'The second factor "uid" must be a valid non-empty string.');
217 }
218 if (typeof request.displayName !== 'undefined' &&
219 !validator.isString(request.displayName)) {
220 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_DISPLAY_NAME, `The second factor "displayName" for "${authFactorInfoIdentifier}" must be a valid string.`);
221 }
222 // enrolledAt must be a valid UTC date string.
223 if (typeof request.enrolledAt !== 'undefined' &&
224 !validator.isISODateString(request.enrolledAt)) {
225 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ENROLLMENT_TIME, `The second factor "enrollmentTime" for "${authFactorInfoIdentifier}" must be a valid ` +
226 'UTC date string.');
227 }
228 // Validate required fields depending on second factor type.
229 if (typeof request.phoneInfo !== 'undefined') {
230 // phoneNumber should be a string and a valid phone number.
231 if (!validator.isPhoneNumber(request.phoneInfo)) {
232 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PHONE_NUMBER, `The second factor "phoneNumber" for "${authFactorInfoIdentifier}" must be a non-empty ` +
233 'E.164 standard compliant identifier string.');
234 }
235 }
236 else {
237 // Invalid second factor. For example, a phone second factor may have been provided without
238 // a phone number. A TOTP based second factor may require a secret key, etc.
239 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ENROLLED_FACTORS, 'MFAInfo object provided is invalid.');
240 }
241}
242/**
243 * Validates a providerUserInfo object. All unsupported parameters
244 * are removed from the original request. If an invalid field is passed
245 * an error is thrown.
246 *
247 * @param request - The providerUserInfo request object.
248 */
249function validateProviderUserInfo(request) {
250 const validKeys = {
251 rawId: true,
252 providerId: true,
253 email: true,
254 displayName: true,
255 photoUrl: true,
256 };
257 // Remove invalid keys from original request.
258 for (const key in request) {
259 if (!(key in validKeys)) {
260 delete request[key];
261 }
262 }
263 if (!validator.isNonEmptyString(request.providerId)) {
264 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID);
265 }
266 if (typeof request.displayName !== 'undefined' &&
267 typeof request.displayName !== 'string') {
268 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_DISPLAY_NAME, `The provider "displayName" for "${request.providerId}" must be a valid string.`);
269 }
270 if (!validator.isNonEmptyString(request.rawId)) {
271 // This is called localId on the backend but the developer specifies this as
272 // uid externally. So the error message should use the client facing name.
273 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID, `The provider "uid" for "${request.providerId}" must be a valid non-empty string.`);
274 }
275 // email should be a string and a valid email.
276 if (typeof request.email !== 'undefined' && !validator.isEmail(request.email)) {
277 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_EMAIL, `The provider "email" for "${request.providerId}" must be a valid email string.`);
278 }
279 // photoUrl should be a URL.
280 if (typeof request.photoUrl !== 'undefined' &&
281 !validator.isURL(request.photoUrl)) {
282 // This is called photoUrl on the backend but the developer specifies this as
283 // photoURL externally. So the error message should use the client facing name.
284 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PHOTO_URL, `The provider "photoURL" for "${request.providerId}" must be a valid URL string.`);
285 }
286}
287/**
288 * Validates a create/edit request object. All unsupported parameters
289 * are removed from the original request. If an invalid field is passed
290 * an error is thrown.
291 *
292 * @param request - The create/edit request object.
293 * @param writeOperationType - The write operation type.
294 */
295function validateCreateEditRequest(request, writeOperationType) {
296 const uploadAccountRequest = writeOperationType === WriteOperationType.Upload;
297 // Hash set of whitelisted parameters.
298 const validKeys = {
299 displayName: true,
300 localId: true,
301 email: true,
302 password: true,
303 rawPassword: true,
304 emailVerified: true,
305 photoUrl: true,
306 disabled: true,
307 disableUser: true,
308 deleteAttribute: true,
309 deleteProvider: true,
310 sanityCheck: true,
311 phoneNumber: true,
312 customAttributes: true,
313 validSince: true,
314 // Pass linkProviderUserInfo only for updates (i.e. not for uploads.)
315 linkProviderUserInfo: !uploadAccountRequest,
316 // Pass tenantId only for uploadAccount requests.
317 tenantId: uploadAccountRequest,
318 passwordHash: uploadAccountRequest,
319 salt: uploadAccountRequest,
320 createdAt: uploadAccountRequest,
321 lastLoginAt: uploadAccountRequest,
322 providerUserInfo: uploadAccountRequest,
323 mfaInfo: uploadAccountRequest,
324 // Only for non-uploadAccount requests.
325 mfa: !uploadAccountRequest,
326 };
327 // Remove invalid keys from original request.
328 for (const key in request) {
329 if (!(key in validKeys)) {
330 delete request[key];
331 }
332 }
333 if (typeof request.tenantId !== 'undefined' &&
334 !validator.isNonEmptyString(request.tenantId)) {
335 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_TENANT_ID);
336 }
337 // For any invalid parameter, use the external key name in the error description.
338 // displayName should be a string.
339 if (typeof request.displayName !== 'undefined' &&
340 !validator.isString(request.displayName)) {
341 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_DISPLAY_NAME);
342 }
343 if ((typeof request.localId !== 'undefined' || uploadAccountRequest) &&
344 !validator.isUid(request.localId)) {
345 // This is called localId on the backend but the developer specifies this as
346 // uid externally. So the error message should use the client facing name.
347 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID);
348 }
349 // email should be a string and a valid email.
350 if (typeof request.email !== 'undefined' && !validator.isEmail(request.email)) {
351 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_EMAIL);
352 }
353 // phoneNumber should be a string and a valid phone number.
354 if (typeof request.phoneNumber !== 'undefined' &&
355 !validator.isPhoneNumber(request.phoneNumber)) {
356 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PHONE_NUMBER);
357 }
358 // password should be a string and a minimum of 6 chars.
359 if (typeof request.password !== 'undefined' &&
360 !validator.isPassword(request.password)) {
361 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PASSWORD);
362 }
363 // rawPassword should be a string and a minimum of 6 chars.
364 if (typeof request.rawPassword !== 'undefined' &&
365 !validator.isPassword(request.rawPassword)) {
366 // This is called rawPassword on the backend but the developer specifies this as
367 // password externally. So the error message should use the client facing name.
368 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PASSWORD);
369 }
370 // emailVerified should be a boolean.
371 if (typeof request.emailVerified !== 'undefined' &&
372 typeof request.emailVerified !== 'boolean') {
373 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_EMAIL_VERIFIED);
374 }
375 // photoUrl should be a URL.
376 if (typeof request.photoUrl !== 'undefined' &&
377 !validator.isURL(request.photoUrl)) {
378 // This is called photoUrl on the backend but the developer specifies this as
379 // photoURL externally. So the error message should use the client facing name.
380 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PHOTO_URL);
381 }
382 // disabled should be a boolean.
383 if (typeof request.disabled !== 'undefined' &&
384 typeof request.disabled !== 'boolean') {
385 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_DISABLED_FIELD);
386 }
387 // validSince should be a number.
388 if (typeof request.validSince !== 'undefined' &&
389 !validator.isNumber(request.validSince)) {
390 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_TOKENS_VALID_AFTER_TIME);
391 }
392 // createdAt should be a number.
393 if (typeof request.createdAt !== 'undefined' &&
394 !validator.isNumber(request.createdAt)) {
395 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_CREATION_TIME);
396 }
397 // lastSignInAt should be a number.
398 if (typeof request.lastLoginAt !== 'undefined' &&
399 !validator.isNumber(request.lastLoginAt)) {
400 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_LAST_SIGN_IN_TIME);
401 }
402 // disableUser should be a boolean.
403 if (typeof request.disableUser !== 'undefined' &&
404 typeof request.disableUser !== 'boolean') {
405 // This is called disableUser on the backend but the developer specifies this as
406 // disabled externally. So the error message should use the client facing name.
407 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_DISABLED_FIELD);
408 }
409 // customAttributes should be stringified JSON with no blacklisted claims.
410 // The payload should not exceed 1KB.
411 if (typeof request.customAttributes !== 'undefined') {
412 let developerClaims;
413 try {
414 developerClaims = JSON.parse(request.customAttributes);
415 }
416 catch (error) {
417 // JSON parsing error. This should never happen as we stringify the claims internally.
418 // However, we still need to check since setAccountInfo via edit requests could pass
419 // this field.
420 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_CLAIMS, error.message);
421 }
422 const invalidClaims = [];
423 // Check for any invalid claims.
424 exports.RESERVED_CLAIMS.forEach((blacklistedClaim) => {
425 if (Object.prototype.hasOwnProperty.call(developerClaims, blacklistedClaim)) {
426 invalidClaims.push(blacklistedClaim);
427 }
428 });
429 // Throw an error if an invalid claim is detected.
430 if (invalidClaims.length > 0) {
431 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.FORBIDDEN_CLAIM, invalidClaims.length > 1 ?
432 `Developer claims "${invalidClaims.join('", "')}" are reserved and cannot be specified.` :
433 `Developer claim "${invalidClaims[0]}" is reserved and cannot be specified.`);
434 }
435 // Check claims payload does not exceed maxmimum size.
436 if (request.customAttributes.length > MAX_CLAIMS_PAYLOAD_SIZE) {
437 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.CLAIMS_TOO_LARGE, `Developer claims payload should not exceed ${MAX_CLAIMS_PAYLOAD_SIZE} characters.`);
438 }
439 }
440 // passwordHash has to be a base64 encoded string.
441 if (typeof request.passwordHash !== 'undefined' &&
442 !validator.isString(request.passwordHash)) {
443 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PASSWORD_HASH);
444 }
445 // salt has to be a base64 encoded string.
446 if (typeof request.salt !== 'undefined' &&
447 !validator.isString(request.salt)) {
448 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PASSWORD_SALT);
449 }
450 // providerUserInfo has to be an array of valid UserInfo requests.
451 if (typeof request.providerUserInfo !== 'undefined' &&
452 !validator.isArray(request.providerUserInfo)) {
453 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_DATA);
454 }
455 else if (validator.isArray(request.providerUserInfo)) {
456 request.providerUserInfo.forEach((providerUserInfoEntry) => {
457 validateProviderUserInfo(providerUserInfoEntry);
458 });
459 }
460 // linkProviderUserInfo must be a (single) UserProvider value.
461 if (typeof request.linkProviderUserInfo !== 'undefined') {
462 validateProviderUserInfo(request.linkProviderUserInfo);
463 }
464 // mfaInfo is used for importUsers.
465 // mfa.enrollments is used for setAccountInfo.
466 // enrollments has to be an array of valid AuthFactorInfo requests.
467 let enrollments = null;
468 if (request.mfaInfo) {
469 enrollments = request.mfaInfo;
470 }
471 else if (request.mfa && request.mfa.enrollments) {
472 enrollments = request.mfa.enrollments;
473 }
474 if (enrollments) {
475 if (!validator.isArray(enrollments)) {
476 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ENROLLED_FACTORS);
477 }
478 enrollments.forEach((authFactorInfoEntry) => {
479 validateAuthFactorInfo(authFactorInfoEntry);
480 });
481 }
482}
483/**
484 * Instantiates the createSessionCookie endpoint settings.
485 *
486 * @internal
487 */
488exports.FIREBASE_AUTH_CREATE_SESSION_COOKIE = new api_request_1.ApiSettings(':createSessionCookie', 'POST')
489 // Set request validator.
490 .setRequestValidator((request) => {
491 // Validate the ID token is a non-empty string.
492 if (!validator.isNonEmptyString(request.idToken)) {
493 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ID_TOKEN);
494 }
495 // Validate the custom session cookie duration.
496 if (!validator.isNumber(request.validDuration) ||
497 request.validDuration < MIN_SESSION_COOKIE_DURATION_SECS ||
498 request.validDuration > MAX_SESSION_COOKIE_DURATION_SECS) {
499 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION);
500 }
501})
502 // Set response validator.
503 .setResponseValidator((response) => {
504 // Response should always contain the session cookie.
505 if (!validator.isNonEmptyString(response.sessionCookie)) {
506 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR);
507 }
508});
509/**
510 * Instantiates the uploadAccount endpoint settings.
511 *
512 * @internal
513 */
514exports.FIREBASE_AUTH_UPLOAD_ACCOUNT = new api_request_1.ApiSettings('/accounts:batchCreate', 'POST');
515/**
516 * Instantiates the downloadAccount endpoint settings.
517 *
518 * @internal
519 */
520exports.FIREBASE_AUTH_DOWNLOAD_ACCOUNT = new api_request_1.ApiSettings('/accounts:batchGet', 'GET')
521 // Set request validator.
522 .setRequestValidator((request) => {
523 // Validate next page token.
524 if (typeof request.nextPageToken !== 'undefined' &&
525 !validator.isNonEmptyString(request.nextPageToken)) {
526 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PAGE_TOKEN);
527 }
528 // Validate max results.
529 if (!validator.isNumber(request.maxResults) ||
530 request.maxResults <= 0 ||
531 request.maxResults > MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE) {
532 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive integer that does not exceed ' +
533 `${MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE}.`);
534 }
535});
536/**
537 * Instantiates the getAccountInfo endpoint settings.
538 *
539 * @internal
540 */
541exports.FIREBASE_AUTH_GET_ACCOUNT_INFO = new api_request_1.ApiSettings('/accounts:lookup', 'POST')
542 // Set request validator.
543 .setRequestValidator((request) => {
544 if (!request.localId && !request.email && !request.phoneNumber && !request.federatedUserId) {
545 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier');
546 }
547})
548 // Set response validator.
549 .setResponseValidator((response) => {
550 if (!response.users || !response.users.length) {
551 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.USER_NOT_FOUND);
552 }
553});
554/**
555 * Instantiates the getAccountInfo endpoint settings for use when fetching info
556 * for multiple accounts.
557 *
558 * @internal
559 */
560exports.FIREBASE_AUTH_GET_ACCOUNTS_INFO = new api_request_1.ApiSettings('/accounts:lookup', 'POST')
561 // Set request validator.
562 .setRequestValidator((request) => {
563 if (!request.localId && !request.email && !request.phoneNumber && !request.federatedUserId) {
564 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier');
565 }
566});
567/**
568 * Instantiates the deleteAccount endpoint settings.
569 *
570 * @internal
571 */
572exports.FIREBASE_AUTH_DELETE_ACCOUNT = new api_request_1.ApiSettings('/accounts:delete', 'POST')
573 // Set request validator.
574 .setRequestValidator((request) => {
575 if (!request.localId) {
576 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier');
577 }
578});
579/**
580 * @internal
581 */
582exports.FIREBASE_AUTH_BATCH_DELETE_ACCOUNTS = new api_request_1.ApiSettings('/accounts:batchDelete', 'POST')
583 .setRequestValidator((request) => {
584 if (!request.localIds) {
585 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifiers');
586 }
587 if (typeof request.force === 'undefined' || request.force !== true) {
588 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing force=true field');
589 }
590})
591 .setResponseValidator((response) => {
592 const errors = response.errors || [];
593 errors.forEach((batchDeleteErrorInfo) => {
594 if (typeof batchDeleteErrorInfo.index === 'undefined') {
595 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server BatchDeleteAccountResponse is missing an errors.index field');
596 }
597 if (!batchDeleteErrorInfo.localId) {
598 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server BatchDeleteAccountResponse is missing an errors.localId field');
599 }
600 // Allow the (error) message to be missing/undef.
601 });
602});
603/**
604 * Instantiates the setAccountInfo endpoint settings for updating existing accounts.
605 *
606 * @internal
607 */
608exports.FIREBASE_AUTH_SET_ACCOUNT_INFO = new api_request_1.ApiSettings('/accounts:update', 'POST')
609 // Set request validator.
610 .setRequestValidator((request) => {
611 // localId is a required parameter.
612 if (typeof request.localId === 'undefined') {
613 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier');
614 }
615 // Throw error when tenantId is passed in POST body.
616 if (typeof request.tenantId !== 'undefined') {
617 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '"tenantId" is an invalid "UpdateRequest" property.');
618 }
619 validateCreateEditRequest(request, WriteOperationType.Update);
620})
621 // Set response validator.
622 .setResponseValidator((response) => {
623 // If the localId is not returned, then the request failed.
624 if (!response.localId) {
625 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.USER_NOT_FOUND);
626 }
627});
628/**
629 * Instantiates the signupNewUser endpoint settings for creating a new user with or without
630 * uid being specified. The backend will create a new one if not provided and return it.
631 *
632 * @internal
633 */
634exports.FIREBASE_AUTH_SIGN_UP_NEW_USER = new api_request_1.ApiSettings('/accounts', 'POST')
635 // Set request validator.
636 .setRequestValidator((request) => {
637 // signupNewUser does not support customAttributes.
638 if (typeof request.customAttributes !== 'undefined') {
639 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '"customAttributes" cannot be set when creating a new user.');
640 }
641 // signupNewUser does not support validSince.
642 if (typeof request.validSince !== 'undefined') {
643 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '"validSince" cannot be set when creating a new user.');
644 }
645 // Throw error when tenantId is passed in POST body.
646 if (typeof request.tenantId !== 'undefined') {
647 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '"tenantId" is an invalid "CreateRequest" property.');
648 }
649 validateCreateEditRequest(request, WriteOperationType.Create);
650})
651 // Set response validator.
652 .setResponseValidator((response) => {
653 // If the localId is not returned, then the request failed.
654 if (!response.localId) {
655 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new user');
656 }
657});
658const FIREBASE_AUTH_GET_OOB_CODE = new api_request_1.ApiSettings('/accounts:sendOobCode', 'POST')
659 // Set request validator.
660 .setRequestValidator((request) => {
661 if (!validator.isEmail(request.email)) {
662 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_EMAIL);
663 }
664 if (typeof request.newEmail !== 'undefined' && !validator.isEmail(request.newEmail)) {
665 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_NEW_EMAIL);
666 }
667 if (exports.EMAIL_ACTION_REQUEST_TYPES.indexOf(request.requestType) === -1) {
668 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, `"${request.requestType}" is not a supported email action request type.`);
669 }
670})
671 // Set response validator.
672 .setResponseValidator((response) => {
673 // If the oobLink is not returned, then the request failed.
674 if (!response.oobLink) {
675 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create the email action link');
676 }
677});
678/**
679 * Instantiates the retrieve OIDC configuration endpoint settings.
680 *
681 * @internal
682 */
683const GET_OAUTH_IDP_CONFIG = new api_request_1.ApiSettings('/oauthIdpConfigs/{providerId}', 'GET')
684 // Set response validator.
685 .setResponseValidator((response) => {
686 // Response should always contain the OIDC provider resource name.
687 if (!validator.isNonEmptyString(response.name)) {
688 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to get OIDC configuration');
689 }
690});
691/**
692 * Instantiates the delete OIDC configuration endpoint settings.
693 *
694 * @internal
695 */
696const DELETE_OAUTH_IDP_CONFIG = new api_request_1.ApiSettings('/oauthIdpConfigs/{providerId}', 'DELETE');
697/**
698 * Instantiates the create OIDC configuration endpoint settings.
699 *
700 * @internal
701 */
702const CREATE_OAUTH_IDP_CONFIG = new api_request_1.ApiSettings('/oauthIdpConfigs?oauthIdpConfigId={providerId}', 'POST')
703 // Set response validator.
704 .setResponseValidator((response) => {
705 // Response should always contain the OIDC provider resource name.
706 if (!validator.isNonEmptyString(response.name)) {
707 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new OIDC configuration');
708 }
709});
710/**
711 * Instantiates the update OIDC configuration endpoint settings.
712 *
713 * @internal
714 */
715const UPDATE_OAUTH_IDP_CONFIG = new api_request_1.ApiSettings('/oauthIdpConfigs/{providerId}?updateMask={updateMask}', 'PATCH')
716 // Set response validator.
717 .setResponseValidator((response) => {
718 // Response should always contain the configuration resource name.
719 if (!validator.isNonEmptyString(response.name)) {
720 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update OIDC configuration');
721 }
722});
723/**
724 * Instantiates the list OIDC configuration endpoint settings.
725 *
726 * @internal
727 */
728const LIST_OAUTH_IDP_CONFIGS = new api_request_1.ApiSettings('/oauthIdpConfigs', 'GET')
729 // Set request validator.
730 .setRequestValidator((request) => {
731 // Validate next page token.
732 if (typeof request.pageToken !== 'undefined' &&
733 !validator.isNonEmptyString(request.pageToken)) {
734 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PAGE_TOKEN);
735 }
736 // Validate max results.
737 if (!validator.isNumber(request.pageSize) ||
738 request.pageSize <= 0 ||
739 request.pageSize > MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE) {
740 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive integer that does not exceed ' +
741 `${MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE}.`);
742 }
743});
744/**
745 * Instantiates the retrieve SAML configuration endpoint settings.
746 *
747 * @internal
748 */
749const GET_INBOUND_SAML_CONFIG = new api_request_1.ApiSettings('/inboundSamlConfigs/{providerId}', 'GET')
750 // Set response validator.
751 .setResponseValidator((response) => {
752 // Response should always contain the SAML provider resource name.
753 if (!validator.isNonEmptyString(response.name)) {
754 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to get SAML configuration');
755 }
756});
757/**
758 * Instantiates the delete SAML configuration endpoint settings.
759 *
760 * @internal
761 */
762const DELETE_INBOUND_SAML_CONFIG = new api_request_1.ApiSettings('/inboundSamlConfigs/{providerId}', 'DELETE');
763/**
764 * Instantiates the create SAML configuration endpoint settings.
765 *
766 * @internal
767 */
768const CREATE_INBOUND_SAML_CONFIG = new api_request_1.ApiSettings('/inboundSamlConfigs?inboundSamlConfigId={providerId}', 'POST')
769 // Set response validator.
770 .setResponseValidator((response) => {
771 // Response should always contain the SAML provider resource name.
772 if (!validator.isNonEmptyString(response.name)) {
773 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new SAML configuration');
774 }
775});
776/**
777 * Instantiates the update SAML configuration endpoint settings.
778 *
779 * @internal
780 */
781const UPDATE_INBOUND_SAML_CONFIG = new api_request_1.ApiSettings('/inboundSamlConfigs/{providerId}?updateMask={updateMask}', 'PATCH')
782 // Set response validator.
783 .setResponseValidator((response) => {
784 // Response should always contain the configuration resource name.
785 if (!validator.isNonEmptyString(response.name)) {
786 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update SAML configuration');
787 }
788});
789/**
790 * Instantiates the list SAML configuration endpoint settings.
791 *
792 * @internal
793 */
794const LIST_INBOUND_SAML_CONFIGS = new api_request_1.ApiSettings('/inboundSamlConfigs', 'GET')
795 // Set request validator.
796 .setRequestValidator((request) => {
797 // Validate next page token.
798 if (typeof request.pageToken !== 'undefined' &&
799 !validator.isNonEmptyString(request.pageToken)) {
800 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PAGE_TOKEN);
801 }
802 // Validate max results.
803 if (!validator.isNumber(request.pageSize) ||
804 request.pageSize <= 0 ||
805 request.pageSize > MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE) {
806 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive integer that does not exceed ' +
807 `${MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE}.`);
808 }
809});
810/**
811 * Class that provides the mechanism to send requests to the Firebase Auth backend endpoints.
812 *
813 * @internal
814 */
815class AbstractAuthRequestHandler {
816 /**
817 * @param response - The response to check for errors.
818 * @returns The error code if present; null otherwise.
819 */
820 static getErrorCode(response) {
821 return (validator.isNonNullObject(response) && response.error && response.error.message) || null;
822 }
823 static addUidToRequest(id, request) {
824 if (!validator.isUid(id.uid)) {
825 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID);
826 }
827 request.localId ? request.localId.push(id.uid) : request.localId = [id.uid];
828 return request;
829 }
830 static addEmailToRequest(id, request) {
831 if (!validator.isEmail(id.email)) {
832 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_EMAIL);
833 }
834 request.email ? request.email.push(id.email) : request.email = [id.email];
835 return request;
836 }
837 static addPhoneToRequest(id, request) {
838 if (!validator.isPhoneNumber(id.phoneNumber)) {
839 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PHONE_NUMBER);
840 }
841 request.phoneNumber ? request.phoneNumber.push(id.phoneNumber) : request.phoneNumber = [id.phoneNumber];
842 return request;
843 }
844 static addProviderToRequest(id, request) {
845 if (!validator.isNonEmptyString(id.providerId)) {
846 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID);
847 }
848 if (!validator.isNonEmptyString(id.providerUid)) {
849 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_UID);
850 }
851 const federatedUserId = {
852 providerId: id.providerId,
853 rawId: id.providerUid,
854 };
855 request.federatedUserId
856 ? request.federatedUserId.push(federatedUserId)
857 : request.federatedUserId = [federatedUserId];
858 return request;
859 }
860 /**
861 * @param app - The app used to fetch access tokens to sign API requests.
862 * @constructor
863 */
864 constructor(app) {
865 this.app = app;
866 if (typeof app !== 'object' || app === null || !('options' in app)) {
867 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'First argument passed to admin.auth() must be a valid Firebase app instance.');
868 }
869 this.httpClient = new AuthHttpClient(app);
870 }
871 /**
872 * Creates a new Firebase session cookie with the specified duration that can be used for
873 * session management (set as a server side session cookie with custom cookie policy).
874 * The session cookie JWT will have the same payload claims as the provided ID token.
875 *
876 * @param idToken - The Firebase ID token to exchange for a session cookie.
877 * @param expiresIn - The session cookie duration in milliseconds.
878 *
879 * @returns A promise that resolves on success with the created session cookie.
880 */
881 createSessionCookie(idToken, expiresIn) {
882 const request = {
883 idToken,
884 // Convert to seconds.
885 validDuration: expiresIn / 1000,
886 };
887 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_CREATE_SESSION_COOKIE, request)
888 .then((response) => response.sessionCookie);
889 }
890 /**
891 * Looks up a user by uid.
892 *
893 * @param uid - The uid of the user to lookup.
894 * @returns A promise that resolves with the user information.
895 */
896 getAccountInfoByUid(uid) {
897 if (!validator.isUid(uid)) {
898 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID));
899 }
900 const request = {
901 localId: [uid],
902 };
903 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_GET_ACCOUNT_INFO, request);
904 }
905 /**
906 * Looks up a user by email.
907 *
908 * @param email - The email of the user to lookup.
909 * @returns A promise that resolves with the user information.
910 */
911 getAccountInfoByEmail(email) {
912 if (!validator.isEmail(email)) {
913 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_EMAIL));
914 }
915 const request = {
916 email: [email],
917 };
918 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_GET_ACCOUNT_INFO, request);
919 }
920 /**
921 * Looks up a user by phone number.
922 *
923 * @param phoneNumber - The phone number of the user to lookup.
924 * @returns A promise that resolves with the user information.
925 */
926 getAccountInfoByPhoneNumber(phoneNumber) {
927 if (!validator.isPhoneNumber(phoneNumber)) {
928 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PHONE_NUMBER));
929 }
930 const request = {
931 phoneNumber: [phoneNumber],
932 };
933 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_GET_ACCOUNT_INFO, request);
934 }
935 getAccountInfoByFederatedUid(providerId, rawId) {
936 if (!validator.isNonEmptyString(providerId) || !validator.isNonEmptyString(rawId)) {
937 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID);
938 }
939 const request = {
940 federatedUserId: [{
941 providerId,
942 rawId,
943 }],
944 };
945 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_GET_ACCOUNT_INFO, request);
946 }
947 /**
948 * Looks up multiple users by their identifiers (uid, email, etc).
949 *
950 * @param identifiers - The identifiers indicating the users
951 * to be looked up. Must have <= 100 entries.
952 * @param A - promise that resolves with the set of successfully
953 * looked up users. Possibly empty if no users were looked up.
954 */
955 getAccountInfoByIdentifiers(identifiers) {
956 if (identifiers.length === 0) {
957 return Promise.resolve({ users: [] });
958 }
959 else if (identifiers.length > MAX_GET_ACCOUNTS_BATCH_SIZE) {
960 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, '`identifiers` parameter must have <= ' + MAX_GET_ACCOUNTS_BATCH_SIZE + ' entries.');
961 }
962 let request = {};
963 for (const id of identifiers) {
964 if ((0, identifier_1.isUidIdentifier)(id)) {
965 request = AbstractAuthRequestHandler.addUidToRequest(id, request);
966 }
967 else if ((0, identifier_1.isEmailIdentifier)(id)) {
968 request = AbstractAuthRequestHandler.addEmailToRequest(id, request);
969 }
970 else if ((0, identifier_1.isPhoneIdentifier)(id)) {
971 request = AbstractAuthRequestHandler.addPhoneToRequest(id, request);
972 }
973 else if ((0, identifier_1.isProviderIdentifier)(id)) {
974 request = AbstractAuthRequestHandler.addProviderToRequest(id, request);
975 }
976 else {
977 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'Unrecognized identifier: ' + id);
978 }
979 }
980 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_GET_ACCOUNTS_INFO, request);
981 }
982 /**
983 * Exports the users (single batch only) with a size of maxResults and starting from
984 * the offset as specified by pageToken.
985 *
986 * @param maxResults - The page size, 1000 if undefined. This is also the maximum
987 * allowed limit.
988 * @param pageToken - The next page token. If not specified, returns users starting
989 * without any offset. Users are returned in the order they were created from oldest to
990 * newest, relative to the page token offset.
991 * @returns A promise that resolves with the current batch of downloaded
992 * users and the next page token if available. For the last page, an empty list of users
993 * and no page token are returned.
994 */
995 downloadAccount(maxResults = MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE, pageToken) {
996 // Construct request.
997 const request = {
998 maxResults,
999 nextPageToken: pageToken,
1000 };
1001 // Remove next page token if not provided.
1002 if (typeof request.nextPageToken === 'undefined') {
1003 delete request.nextPageToken;
1004 }
1005 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_DOWNLOAD_ACCOUNT, request)
1006 .then((response) => {
1007 // No more users available.
1008 if (!response.users) {
1009 response.users = [];
1010 }
1011 return response;
1012 });
1013 }
1014 /**
1015 * Imports the list of users provided to Firebase Auth. This is useful when
1016 * migrating from an external authentication system without having to use the Firebase CLI SDK.
1017 * At most, 1000 users are allowed to be imported one at a time.
1018 * When importing a list of password users, UserImportOptions are required to be specified.
1019 *
1020 * @param users - The list of user records to import to Firebase Auth.
1021 * @param options - The user import options, required when the users provided
1022 * include password credentials.
1023 * @returns A promise that resolves when the operation completes
1024 * with the result of the import. This includes the number of successful imports, the number
1025 * of failed uploads and their corresponding errors.
1026 */
1027 uploadAccount(users, options) {
1028 // This will throw if any error is detected in the hash options.
1029 // For errors in the list of users, this will not throw and will report the errors and the
1030 // corresponding user index in the user import generated response below.
1031 // No need to validate raw request or raw response as this is done in UserImportBuilder.
1032 const userImportBuilder = new user_import_builder_1.UserImportBuilder(users, options, (userRequest) => {
1033 // Pass true to validate the uploadAccount specific fields.
1034 validateCreateEditRequest(userRequest, WriteOperationType.Upload);
1035 });
1036 const request = userImportBuilder.buildRequest();
1037 // Fail quickly if more users than allowed are to be imported.
1038 if (validator.isArray(users) && users.length > MAX_UPLOAD_ACCOUNT_BATCH_SIZE) {
1039 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, `A maximum of ${MAX_UPLOAD_ACCOUNT_BATCH_SIZE} users can be imported at once.`);
1040 }
1041 // If no remaining user in request after client side processing, there is no need
1042 // to send the request to the server.
1043 if (!request.users || request.users.length === 0) {
1044 return Promise.resolve(userImportBuilder.buildResponse([]));
1045 }
1046 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_UPLOAD_ACCOUNT, request)
1047 .then((response) => {
1048 // No error object is returned if no error encountered.
1049 const failedUploads = (response.error || []);
1050 // Rewrite response as UserImportResult and re-insert client previously detected errors.
1051 return userImportBuilder.buildResponse(failedUploads);
1052 });
1053 }
1054 /**
1055 * Deletes an account identified by a uid.
1056 *
1057 * @param uid - The uid of the user to delete.
1058 * @returns A promise that resolves when the user is deleted.
1059 */
1060 deleteAccount(uid) {
1061 if (!validator.isUid(uid)) {
1062 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID));
1063 }
1064 const request = {
1065 localId: uid,
1066 };
1067 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_DELETE_ACCOUNT, request);
1068 }
1069 deleteAccounts(uids, force) {
1070 if (uids.length === 0) {
1071 return Promise.resolve({});
1072 }
1073 else if (uids.length > MAX_DELETE_ACCOUNTS_BATCH_SIZE) {
1074 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.MAXIMUM_USER_COUNT_EXCEEDED, '`uids` parameter must have <= ' + MAX_DELETE_ACCOUNTS_BATCH_SIZE + ' entries.');
1075 }
1076 const request = {
1077 localIds: [],
1078 force,
1079 };
1080 uids.forEach((uid) => {
1081 if (!validator.isUid(uid)) {
1082 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID);
1083 }
1084 request.localIds.push(uid);
1085 });
1086 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_BATCH_DELETE_ACCOUNTS, request);
1087 }
1088 /**
1089 * Sets additional developer claims on an existing user identified by provided UID.
1090 *
1091 * @param uid - The user to edit.
1092 * @param customUserClaims - The developer claims to set.
1093 * @returns A promise that resolves when the operation completes
1094 * with the user id that was edited.
1095 */
1096 setCustomUserClaims(uid, customUserClaims) {
1097 // Validate user UID.
1098 if (!validator.isUid(uid)) {
1099 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID));
1100 }
1101 else if (!validator.isObject(customUserClaims)) {
1102 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'CustomUserClaims argument must be an object or null.'));
1103 }
1104 // Delete operation. Replace null with an empty object.
1105 if (customUserClaims === null) {
1106 customUserClaims = {};
1107 }
1108 // Construct custom user attribute editting request.
1109 const request = {
1110 localId: uid,
1111 customAttributes: JSON.stringify(customUserClaims),
1112 };
1113 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_SET_ACCOUNT_INFO, request)
1114 .then((response) => {
1115 return response.localId;
1116 });
1117 }
1118 /**
1119 * Edits an existing user.
1120 *
1121 * @param uid - The user to edit.
1122 * @param properties - The properties to set on the user.
1123 * @returns A promise that resolves when the operation completes
1124 * with the user id that was edited.
1125 */
1126 updateExistingAccount(uid, properties) {
1127 if (!validator.isUid(uid)) {
1128 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID));
1129 }
1130 else if (!validator.isNonNullObject(properties)) {
1131 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'Properties argument must be a non-null object.'));
1132 }
1133 else if (validator.isNonNullObject(properties.providerToLink)) {
1134 // TODO(rsgowman): These checks overlap somewhat with
1135 // validateProviderUserInfo. It may be possible to refactor a bit.
1136 if (!validator.isNonEmptyString(properties.providerToLink.providerId)) {
1137 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'providerToLink.providerId of properties argument must be a non-empty string.');
1138 }
1139 if (!validator.isNonEmptyString(properties.providerToLink.uid)) {
1140 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'providerToLink.uid of properties argument must be a non-empty string.');
1141 }
1142 }
1143 else if (typeof properties.providersToUnlink !== 'undefined') {
1144 if (!validator.isArray(properties.providersToUnlink)) {
1145 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'providersToUnlink of properties argument must be an array of strings.');
1146 }
1147 properties.providersToUnlink.forEach((providerId) => {
1148 if (!validator.isNonEmptyString(providerId)) {
1149 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'providersToUnlink of properties argument must be an array of strings.');
1150 }
1151 });
1152 }
1153 // Build the setAccountInfo request.
1154 const request = (0, deep_copy_1.deepCopy)(properties);
1155 request.localId = uid;
1156 // For deleting displayName or photoURL, these values must be passed as null.
1157 // They will be removed from the backend request and an additional parameter
1158 // deleteAttribute: ['PHOTO_URL', 'DISPLAY_NAME']
1159 // with an array of the parameter names to delete will be passed.
1160 // Parameters that are deletable and their deleteAttribute names.
1161 // Use client facing names, photoURL instead of photoUrl.
1162 const deletableParams = {
1163 displayName: 'DISPLAY_NAME',
1164 photoURL: 'PHOTO_URL',
1165 };
1166 // Properties to delete if available.
1167 request.deleteAttribute = [];
1168 for (const key in deletableParams) {
1169 if (request[key] === null) {
1170 // Add property identifier to list of attributes to delete.
1171 request.deleteAttribute.push(deletableParams[key]);
1172 // Remove property from request.
1173 delete request[key];
1174 }
1175 }
1176 if (request.deleteAttribute.length === 0) {
1177 delete request.deleteAttribute;
1178 }
1179 // For deleting phoneNumber, this value must be passed as null.
1180 // It will be removed from the backend request and an additional parameter
1181 // deleteProvider: ['phone'] with an array of providerIds (phone in this case),
1182 // will be passed.
1183 if (request.phoneNumber === null) {
1184 request.deleteProvider ? request.deleteProvider.push('phone') : request.deleteProvider = ['phone'];
1185 delete request.phoneNumber;
1186 }
1187 if (typeof (request.providerToLink) !== 'undefined') {
1188 request.linkProviderUserInfo = (0, deep_copy_1.deepCopy)(request.providerToLink);
1189 delete request.providerToLink;
1190 request.linkProviderUserInfo.rawId = request.linkProviderUserInfo.uid;
1191 delete request.linkProviderUserInfo.uid;
1192 }
1193 if (typeof (request.providersToUnlink) !== 'undefined') {
1194 if (!validator.isArray(request.deleteProvider)) {
1195 request.deleteProvider = [];
1196 }
1197 request.deleteProvider = request.deleteProvider.concat(request.providersToUnlink);
1198 delete request.providersToUnlink;
1199 }
1200 // Rewrite photoURL to photoUrl.
1201 if (typeof request.photoURL !== 'undefined') {
1202 request.photoUrl = request.photoURL;
1203 delete request.photoURL;
1204 }
1205 // Rewrite disabled to disableUser.
1206 if (typeof request.disabled !== 'undefined') {
1207 request.disableUser = request.disabled;
1208 delete request.disabled;
1209 }
1210 // Construct mfa related user data.
1211 if (validator.isNonNullObject(request.multiFactor)) {
1212 if (request.multiFactor.enrolledFactors === null) {
1213 // Remove all second factors.
1214 request.mfa = {};
1215 }
1216 else if (validator.isArray(request.multiFactor.enrolledFactors)) {
1217 request.mfa = {
1218 enrollments: [],
1219 };
1220 try {
1221 request.multiFactor.enrolledFactors.forEach((multiFactorInfo) => {
1222 request.mfa.enrollments.push((0, user_import_builder_1.convertMultiFactorInfoToServerFormat)(multiFactorInfo));
1223 });
1224 }
1225 catch (e) {
1226 return Promise.reject(e);
1227 }
1228 if (request.mfa.enrollments.length === 0) {
1229 delete request.mfa.enrollments;
1230 }
1231 }
1232 delete request.multiFactor;
1233 }
1234 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_SET_ACCOUNT_INFO, request)
1235 .then((response) => {
1236 return response.localId;
1237 });
1238 }
1239 /**
1240 * Revokes all refresh tokens for the specified user identified by the uid provided.
1241 * In addition to revoking all refresh tokens for a user, all ID tokens issued
1242 * before revocation will also be revoked on the Auth backend. Any request with an
1243 * ID token generated before revocation will be rejected with a token expired error.
1244 * Note that due to the fact that the timestamp is stored in seconds, any tokens minted in
1245 * the same second as the revocation will still be valid. If there is a chance that a token
1246 * was minted in the last second, delay for 1 second before revoking.
1247 *
1248 * @param uid - The user whose tokens are to be revoked.
1249 * @returns A promise that resolves when the operation completes
1250 * successfully with the user id of the corresponding user.
1251 */
1252 revokeRefreshTokens(uid) {
1253 // Validate user UID.
1254 if (!validator.isUid(uid)) {
1255 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID));
1256 }
1257 const request = {
1258 localId: uid,
1259 // validSince is in UTC seconds.
1260 validSince: Math.floor(new Date().getTime() / 1000),
1261 };
1262 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_SET_ACCOUNT_INFO, request)
1263 .then((response) => {
1264 return response.localId;
1265 });
1266 }
1267 /**
1268 * Create a new user with the properties supplied.
1269 *
1270 * @param properties - The properties to set on the user.
1271 * @returns A promise that resolves when the operation completes
1272 * with the user id that was created.
1273 */
1274 createNewAccount(properties) {
1275 if (!validator.isNonNullObject(properties)) {
1276 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'Properties argument must be a non-null object.'));
1277 }
1278 const request = (0, deep_copy_1.deepCopy)(properties);
1279 // Rewrite photoURL to photoUrl.
1280 if (typeof request.photoURL !== 'undefined') {
1281 request.photoUrl = request.photoURL;
1282 delete request.photoURL;
1283 }
1284 // Rewrite uid to localId if it exists.
1285 if (typeof request.uid !== 'undefined') {
1286 request.localId = request.uid;
1287 delete request.uid;
1288 }
1289 // Construct mfa related user data.
1290 if (validator.isNonNullObject(request.multiFactor)) {
1291 if (validator.isNonEmptyArray(request.multiFactor.enrolledFactors)) {
1292 const mfaInfo = [];
1293 try {
1294 request.multiFactor.enrolledFactors.forEach((multiFactorInfo) => {
1295 // Enrollment time and uid are not allowed for signupNewUser endpoint.
1296 // They will automatically be provisioned server side.
1297 if ('enrollmentTime' in multiFactorInfo) {
1298 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '"enrollmentTime" is not supported when adding second factors via "createUser()"');
1299 }
1300 else if ('uid' in multiFactorInfo) {
1301 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '"uid" is not supported when adding second factors via "createUser()"');
1302 }
1303 mfaInfo.push((0, user_import_builder_1.convertMultiFactorInfoToServerFormat)(multiFactorInfo));
1304 });
1305 }
1306 catch (e) {
1307 return Promise.reject(e);
1308 }
1309 request.mfaInfo = mfaInfo;
1310 }
1311 delete request.multiFactor;
1312 }
1313 return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_SIGN_UP_NEW_USER, request)
1314 .then((response) => {
1315 // Return the user id.
1316 return response.localId;
1317 });
1318 }
1319 /**
1320 * Generates the out of band email action link for the email specified using the action code settings provided.
1321 * Returns a promise that resolves with the generated link.
1322 *
1323 * @param requestType - The request type. This could be either used for password reset,
1324 * email verification, email link sign-in.
1325 * @param email - The email of the user the link is being sent to.
1326 * @param actionCodeSettings - The optional action code setings which defines whether
1327 * the link is to be handled by a mobile app and the additional state information to be passed in the
1328 * deep link, etc. Required when requestType === 'EMAIL_SIGNIN'
1329 * @param newEmail - The email address the account is being updated to.
1330 * Required only for VERIFY_AND_CHANGE_EMAIL requests.
1331 * @returns A promise that resolves with the email action link.
1332 */
1333 getEmailActionLink(requestType, email, actionCodeSettings, newEmail) {
1334 let request = {
1335 requestType,
1336 email,
1337 returnOobLink: true,
1338 ...(typeof newEmail !== 'undefined') && { newEmail },
1339 };
1340 // ActionCodeSettings required for email link sign-in to determine the url where the sign-in will
1341 // be completed.
1342 if (typeof actionCodeSettings === 'undefined' && requestType === 'EMAIL_SIGNIN') {
1343 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "`actionCodeSettings` is required when `requestType` === 'EMAIL_SIGNIN'"));
1344 }
1345 if (typeof actionCodeSettings !== 'undefined' || requestType === 'EMAIL_SIGNIN') {
1346 try {
1347 const builder = new action_code_settings_builder_1.ActionCodeSettingsBuilder(actionCodeSettings);
1348 request = (0, deep_copy_1.deepExtend)(request, builder.buildRequest());
1349 }
1350 catch (e) {
1351 return Promise.reject(e);
1352 }
1353 }
1354 if (requestType === 'VERIFY_AND_CHANGE_EMAIL' && typeof newEmail === 'undefined') {
1355 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "`newEmail` is required when `requestType` === 'VERIFY_AND_CHANGE_EMAIL'"));
1356 }
1357 return this.invokeRequestHandler(this.getAuthUrlBuilder(), FIREBASE_AUTH_GET_OOB_CODE, request)
1358 .then((response) => {
1359 // Return the link.
1360 return response.oobLink;
1361 });
1362 }
1363 /**
1364 * Looks up an OIDC provider configuration by provider ID.
1365 *
1366 * @param providerId - The provider identifier of the configuration to lookup.
1367 * @returns A promise that resolves with the provider configuration information.
1368 */
1369 getOAuthIdpConfig(providerId) {
1370 if (!auth_config_1.OIDCConfig.isProviderId(providerId)) {
1371 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID));
1372 }
1373 return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), GET_OAUTH_IDP_CONFIG, {}, { providerId });
1374 }
1375 /**
1376 * Lists the OIDC configurations (single batch only) with a size of maxResults and starting from
1377 * the offset as specified by pageToken.
1378 *
1379 * @param maxResults - The page size, 100 if undefined. This is also the maximum
1380 * allowed limit.
1381 * @param pageToken - The next page token. If not specified, returns OIDC configurations
1382 * without any offset. Configurations are returned in the order they were created from oldest to
1383 * newest, relative to the page token offset.
1384 * @returns A promise that resolves with the current batch of downloaded
1385 * OIDC configurations and the next page token if available. For the last page, an empty list of provider
1386 * configuration and no page token are returned.
1387 */
1388 listOAuthIdpConfigs(maxResults = MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE, pageToken) {
1389 const request = {
1390 pageSize: maxResults,
1391 };
1392 // Add next page token if provided.
1393 if (typeof pageToken !== 'undefined') {
1394 request.pageToken = pageToken;
1395 }
1396 return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), LIST_OAUTH_IDP_CONFIGS, request)
1397 .then((response) => {
1398 if (!response.oauthIdpConfigs) {
1399 response.oauthIdpConfigs = [];
1400 delete response.nextPageToken;
1401 }
1402 return response;
1403 });
1404 }
1405 /**
1406 * Deletes an OIDC configuration identified by a providerId.
1407 *
1408 * @param providerId - The identifier of the OIDC configuration to delete.
1409 * @returns A promise that resolves when the OIDC provider is deleted.
1410 */
1411 deleteOAuthIdpConfig(providerId) {
1412 if (!auth_config_1.OIDCConfig.isProviderId(providerId)) {
1413 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID));
1414 }
1415 return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), DELETE_OAUTH_IDP_CONFIG, {}, { providerId })
1416 .then(() => {
1417 // Return nothing.
1418 });
1419 }
1420 /**
1421 * Creates a new OIDC provider configuration with the properties provided.
1422 *
1423 * @param options - The properties to set on the new OIDC provider configuration to be created.
1424 * @returns A promise that resolves with the newly created OIDC
1425 * configuration.
1426 */
1427 createOAuthIdpConfig(options) {
1428 // Construct backend request.
1429 let request;
1430 try {
1431 request = auth_config_1.OIDCConfig.buildServerRequest(options) || {};
1432 }
1433 catch (e) {
1434 return Promise.reject(e);
1435 }
1436 const providerId = options.providerId;
1437 return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), CREATE_OAUTH_IDP_CONFIG, request, { providerId })
1438 .then((response) => {
1439 if (!auth_config_1.OIDCConfig.getProviderIdFromResourceName(response.name)) {
1440 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new OIDC provider configuration');
1441 }
1442 return response;
1443 });
1444 }
1445 /**
1446 * Updates an existing OIDC provider configuration with the properties provided.
1447 *
1448 * @param providerId - The provider identifier of the OIDC configuration to update.
1449 * @param options - The properties to update on the existing configuration.
1450 * @returns A promise that resolves with the modified provider
1451 * configuration.
1452 */
1453 updateOAuthIdpConfig(providerId, options) {
1454 if (!auth_config_1.OIDCConfig.isProviderId(providerId)) {
1455 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID));
1456 }
1457 // Construct backend request.
1458 let request;
1459 try {
1460 request = auth_config_1.OIDCConfig.buildServerRequest(options, true) || {};
1461 }
1462 catch (e) {
1463 return Promise.reject(e);
1464 }
1465 const updateMask = utils.generateUpdateMask(request);
1466 return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), UPDATE_OAUTH_IDP_CONFIG, request, { providerId, updateMask: updateMask.join(',') })
1467 .then((response) => {
1468 if (!auth_config_1.OIDCConfig.getProviderIdFromResourceName(response.name)) {
1469 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update OIDC provider configuration');
1470 }
1471 return response;
1472 });
1473 }
1474 /**
1475 * Looks up an SAML provider configuration by provider ID.
1476 *
1477 * @param providerId - The provider identifier of the configuration to lookup.
1478 * @returns A promise that resolves with the provider configuration information.
1479 */
1480 getInboundSamlConfig(providerId) {
1481 if (!auth_config_1.SAMLConfig.isProviderId(providerId)) {
1482 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID));
1483 }
1484 return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), GET_INBOUND_SAML_CONFIG, {}, { providerId });
1485 }
1486 /**
1487 * Lists the SAML configurations (single batch only) with a size of maxResults and starting from
1488 * the offset as specified by pageToken.
1489 *
1490 * @param maxResults - The page size, 100 if undefined. This is also the maximum
1491 * allowed limit.
1492 * @param pageToken - The next page token. If not specified, returns SAML configurations starting
1493 * without any offset. Configurations are returned in the order they were created from oldest to
1494 * newest, relative to the page token offset.
1495 * @returns A promise that resolves with the current batch of downloaded
1496 * SAML configurations and the next page token if available. For the last page, an empty list of provider
1497 * configuration and no page token are returned.
1498 */
1499 listInboundSamlConfigs(maxResults = MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE, pageToken) {
1500 const request = {
1501 pageSize: maxResults,
1502 };
1503 // Add next page token if provided.
1504 if (typeof pageToken !== 'undefined') {
1505 request.pageToken = pageToken;
1506 }
1507 return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), LIST_INBOUND_SAML_CONFIGS, request)
1508 .then((response) => {
1509 if (!response.inboundSamlConfigs) {
1510 response.inboundSamlConfigs = [];
1511 delete response.nextPageToken;
1512 }
1513 return response;
1514 });
1515 }
1516 /**
1517 * Deletes a SAML configuration identified by a providerId.
1518 *
1519 * @param providerId - The identifier of the SAML configuration to delete.
1520 * @returns A promise that resolves when the SAML provider is deleted.
1521 */
1522 deleteInboundSamlConfig(providerId) {
1523 if (!auth_config_1.SAMLConfig.isProviderId(providerId)) {
1524 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID));
1525 }
1526 return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), DELETE_INBOUND_SAML_CONFIG, {}, { providerId })
1527 .then(() => {
1528 // Return nothing.
1529 });
1530 }
1531 /**
1532 * Creates a new SAML provider configuration with the properties provided.
1533 *
1534 * @param options - The properties to set on the new SAML provider configuration to be created.
1535 * @returns A promise that resolves with the newly created SAML
1536 * configuration.
1537 */
1538 createInboundSamlConfig(options) {
1539 // Construct backend request.
1540 let request;
1541 try {
1542 request = auth_config_1.SAMLConfig.buildServerRequest(options) || {};
1543 }
1544 catch (e) {
1545 return Promise.reject(e);
1546 }
1547 const providerId = options.providerId;
1548 return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), CREATE_INBOUND_SAML_CONFIG, request, { providerId })
1549 .then((response) => {
1550 if (!auth_config_1.SAMLConfig.getProviderIdFromResourceName(response.name)) {
1551 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new SAML provider configuration');
1552 }
1553 return response;
1554 });
1555 }
1556 /**
1557 * Updates an existing SAML provider configuration with the properties provided.
1558 *
1559 * @param providerId - The provider identifier of the SAML configuration to update.
1560 * @param options - The properties to update on the existing configuration.
1561 * @returns A promise that resolves with the modified provider
1562 * configuration.
1563 */
1564 updateInboundSamlConfig(providerId, options) {
1565 if (!auth_config_1.SAMLConfig.isProviderId(providerId)) {
1566 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID));
1567 }
1568 // Construct backend request.
1569 let request;
1570 try {
1571 request = auth_config_1.SAMLConfig.buildServerRequest(options, true) || {};
1572 }
1573 catch (e) {
1574 return Promise.reject(e);
1575 }
1576 const updateMask = utils.generateUpdateMask(request);
1577 return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), UPDATE_INBOUND_SAML_CONFIG, request, { providerId, updateMask: updateMask.join(',') })
1578 .then((response) => {
1579 if (!auth_config_1.SAMLConfig.getProviderIdFromResourceName(response.name)) {
1580 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update SAML provider configuration');
1581 }
1582 return response;
1583 });
1584 }
1585 /**
1586 * Invokes the request handler based on the API settings object passed.
1587 *
1588 * @param urlBuilder - The URL builder for Auth endpoints.
1589 * @param apiSettings - The API endpoint settings to apply to request and response.
1590 * @param requestData - The request data.
1591 * @param additionalResourceParams - Additional resource related params if needed.
1592 * @returns A promise that resolves with the response.
1593 */
1594 invokeRequestHandler(urlBuilder, apiSettings, requestData, additionalResourceParams) {
1595 return urlBuilder.getUrl(apiSettings.getEndpoint(), additionalResourceParams)
1596 .then((url) => {
1597 // Validate request.
1598 if (requestData) {
1599 const requestValidator = apiSettings.getRequestValidator();
1600 requestValidator(requestData);
1601 }
1602 // Process request.
1603 const req = {
1604 method: apiSettings.getHttpMethod(),
1605 url,
1606 headers: FIREBASE_AUTH_HEADER,
1607 data: requestData,
1608 timeout: FIREBASE_AUTH_TIMEOUT,
1609 };
1610 return this.httpClient.send(req);
1611 })
1612 .then((response) => {
1613 // Validate response.
1614 const responseValidator = apiSettings.getResponseValidator();
1615 responseValidator(response.data);
1616 // Return entire response.
1617 return response.data;
1618 })
1619 .catch((err) => {
1620 if (err instanceof api_request_1.HttpError) {
1621 const error = err.response.data;
1622 const errorCode = AbstractAuthRequestHandler.getErrorCode(error);
1623 if (!errorCode) {
1624 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'Error returned from server: ' + error + '. Additionally, an ' +
1625 'internal error occurred while attempting to extract the ' +
1626 'errorcode from the error.');
1627 }
1628 throw error_1.FirebaseAuthError.fromServerError(errorCode, /* message */ undefined, error);
1629 }
1630 throw err;
1631 });
1632 }
1633 /**
1634 * @returns The current Auth user management resource URL builder.
1635 */
1636 getAuthUrlBuilder() {
1637 if (!this.authUrlBuilder) {
1638 this.authUrlBuilder = this.newAuthUrlBuilder();
1639 }
1640 return this.authUrlBuilder;
1641 }
1642 /**
1643 * @returns The current project config resource URL builder.
1644 */
1645 getProjectConfigUrlBuilder() {
1646 if (!this.projectConfigUrlBuilder) {
1647 this.projectConfigUrlBuilder = this.newProjectConfigUrlBuilder();
1648 }
1649 return this.projectConfigUrlBuilder;
1650 }
1651}
1652exports.AbstractAuthRequestHandler = AbstractAuthRequestHandler;
1653/** Instantiates the getConfig endpoint settings. */
1654const GET_PROJECT_CONFIG = new api_request_1.ApiSettings('/config', 'GET')
1655 .setResponseValidator((response) => {
1656 // Response should always contain at least the config name.
1657 if (!validator.isNonEmptyString(response.name)) {
1658 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to get project config');
1659 }
1660});
1661/** Instantiates the updateConfig endpoint settings. */
1662const UPDATE_PROJECT_CONFIG = new api_request_1.ApiSettings('/config?updateMask={updateMask}', 'PATCH')
1663 .setResponseValidator((response) => {
1664 // Response should always contain at least the config name.
1665 if (!validator.isNonEmptyString(response.name)) {
1666 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update project config');
1667 }
1668});
1669/** Instantiates the getTenant endpoint settings. */
1670const GET_TENANT = new api_request_1.ApiSettings('/tenants/{tenantId}', 'GET')
1671 // Set response validator.
1672 .setResponseValidator((response) => {
1673 // Response should always contain at least the tenant name.
1674 if (!validator.isNonEmptyString(response.name)) {
1675 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to get tenant');
1676 }
1677});
1678/** Instantiates the deleteTenant endpoint settings. */
1679const DELETE_TENANT = new api_request_1.ApiSettings('/tenants/{tenantId}', 'DELETE');
1680/** Instantiates the updateTenant endpoint settings. */
1681const UPDATE_TENANT = new api_request_1.ApiSettings('/tenants/{tenantId}?updateMask={updateMask}', 'PATCH')
1682 // Set response validator.
1683 .setResponseValidator((response) => {
1684 // Response should always contain at least the tenant name.
1685 if (!validator.isNonEmptyString(response.name) ||
1686 !tenant_1.Tenant.getTenantIdFromResourceName(response.name)) {
1687 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update tenant');
1688 }
1689});
1690/** Instantiates the listTenants endpoint settings. */
1691const LIST_TENANTS = new api_request_1.ApiSettings('/tenants', 'GET')
1692 // Set request validator.
1693 .setRequestValidator((request) => {
1694 // Validate next page token.
1695 if (typeof request.pageToken !== 'undefined' &&
1696 !validator.isNonEmptyString(request.pageToken)) {
1697 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PAGE_TOKEN);
1698 }
1699 // Validate max results.
1700 if (!validator.isNumber(request.pageSize) ||
1701 request.pageSize <= 0 ||
1702 request.pageSize > MAX_LIST_TENANT_PAGE_SIZE) {
1703 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'Required "maxResults" must be a positive non-zero number that does not exceed ' +
1704 `the allowed ${MAX_LIST_TENANT_PAGE_SIZE}.`);
1705 }
1706});
1707/** Instantiates the createTenant endpoint settings. */
1708const CREATE_TENANT = new api_request_1.ApiSettings('/tenants', 'POST')
1709 // Set response validator.
1710 .setResponseValidator((response) => {
1711 // Response should always contain at least the tenant name.
1712 if (!validator.isNonEmptyString(response.name) ||
1713 !tenant_1.Tenant.getTenantIdFromResourceName(response.name)) {
1714 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new tenant');
1715 }
1716});
1717/**
1718 * Utility for sending requests to Auth server that are Auth instance related. This includes user, tenant,
1719 * and project config management related APIs. This extends the BaseFirebaseAuthRequestHandler class and defines
1720 * additional tenant management related APIs.
1721 */
1722class AuthRequestHandler extends AbstractAuthRequestHandler {
1723 /**
1724 * The FirebaseAuthRequestHandler constructor used to initialize an instance using a FirebaseApp.
1725 *
1726 * @param app - The app used to fetch access tokens to sign API requests.
1727 * @constructor
1728 */
1729 constructor(app) {
1730 super(app);
1731 this.authResourceUrlBuilder = new AuthResourceUrlBuilder(app, 'v2');
1732 }
1733 /**
1734 * @returns A new Auth user management resource URL builder instance.
1735 */
1736 newAuthUrlBuilder() {
1737 return new AuthResourceUrlBuilder(this.app, 'v1');
1738 }
1739 /**
1740 * @returns A new project config resource URL builder instance.
1741 */
1742 newProjectConfigUrlBuilder() {
1743 return new AuthResourceUrlBuilder(this.app, 'v2');
1744 }
1745 /**
1746 * Get the current project's config
1747 * @returns A promise that resolves with the project config information.
1748 */
1749 getProjectConfig() {
1750 return this.invokeRequestHandler(this.authResourceUrlBuilder, GET_PROJECT_CONFIG, {}, {})
1751 .then((response) => {
1752 return response;
1753 });
1754 }
1755 /**
1756 * Update the current project's config.
1757 * @returns A promise that resolves with the project config information.
1758 */
1759 updateProjectConfig(options) {
1760 try {
1761 const request = project_config_1.ProjectConfig.buildServerRequest(options);
1762 const updateMask = utils.generateUpdateMask(request);
1763 return this.invokeRequestHandler(this.authResourceUrlBuilder, UPDATE_PROJECT_CONFIG, request, { updateMask: updateMask.join(',') })
1764 .then((response) => {
1765 return response;
1766 });
1767 }
1768 catch (e) {
1769 return Promise.reject(e);
1770 }
1771 }
1772 /**
1773 * Looks up a tenant by tenant ID.
1774 *
1775 * @param tenantId - The tenant identifier of the tenant to lookup.
1776 * @returns A promise that resolves with the tenant information.
1777 */
1778 getTenant(tenantId) {
1779 if (!validator.isNonEmptyString(tenantId)) {
1780 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_TENANT_ID));
1781 }
1782 return this.invokeRequestHandler(this.authResourceUrlBuilder, GET_TENANT, {}, { tenantId })
1783 .then((response) => {
1784 return response;
1785 });
1786 }
1787 /**
1788 * Exports the tenants (single batch only) with a size of maxResults and starting from
1789 * the offset as specified by pageToken.
1790 *
1791 * @param maxResults - The page size, 1000 if undefined. This is also the maximum
1792 * allowed limit.
1793 * @param pageToken - The next page token. If not specified, returns tenants starting
1794 * without any offset. Tenants are returned in the order they were created from oldest to
1795 * newest, relative to the page token offset.
1796 * @returns A promise that resolves with the current batch of downloaded
1797 * tenants and the next page token if available. For the last page, an empty list of tenants
1798 * and no page token are returned.
1799 */
1800 listTenants(maxResults = MAX_LIST_TENANT_PAGE_SIZE, pageToken) {
1801 const request = {
1802 pageSize: maxResults,
1803 pageToken,
1804 };
1805 // Remove next page token if not provided.
1806 if (typeof request.pageToken === 'undefined') {
1807 delete request.pageToken;
1808 }
1809 return this.invokeRequestHandler(this.authResourceUrlBuilder, LIST_TENANTS, request)
1810 .then((response) => {
1811 if (!response.tenants) {
1812 response.tenants = [];
1813 delete response.nextPageToken;
1814 }
1815 return response;
1816 });
1817 }
1818 /**
1819 * Deletes a tenant identified by a tenantId.
1820 *
1821 * @param tenantId - The identifier of the tenant to delete.
1822 * @returns A promise that resolves when the tenant is deleted.
1823 */
1824 deleteTenant(tenantId) {
1825 if (!validator.isNonEmptyString(tenantId)) {
1826 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_TENANT_ID));
1827 }
1828 return this.invokeRequestHandler(this.authResourceUrlBuilder, DELETE_TENANT, undefined, { tenantId })
1829 .then(() => {
1830 // Return nothing.
1831 });
1832 }
1833 /**
1834 * Creates a new tenant with the properties provided.
1835 *
1836 * @param tenantOptions - The properties to set on the new tenant to be created.
1837 * @returns A promise that resolves with the newly created tenant object.
1838 */
1839 createTenant(tenantOptions) {
1840 try {
1841 // Construct backend request.
1842 const request = tenant_1.Tenant.buildServerRequest(tenantOptions, true);
1843 return this.invokeRequestHandler(this.authResourceUrlBuilder, CREATE_TENANT, request)
1844 .then((response) => {
1845 return response;
1846 });
1847 }
1848 catch (e) {
1849 return Promise.reject(e);
1850 }
1851 }
1852 /**
1853 * Updates an existing tenant with the properties provided.
1854 *
1855 * @param tenantId - The tenant identifier of the tenant to update.
1856 * @param tenantOptions - The properties to update on the existing tenant.
1857 * @returns A promise that resolves with the modified tenant object.
1858 */
1859 updateTenant(tenantId, tenantOptions) {
1860 if (!validator.isNonEmptyString(tenantId)) {
1861 return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_TENANT_ID));
1862 }
1863 try {
1864 // Construct backend request.
1865 const request = tenant_1.Tenant.buildServerRequest(tenantOptions, false);
1866 // Do not traverse deep into testPhoneNumbers. The entire content should be replaced
1867 // and not just specific phone numbers.
1868 const updateMask = utils.generateUpdateMask(request, ['testPhoneNumbers']);
1869 return this.invokeRequestHandler(this.authResourceUrlBuilder, UPDATE_TENANT, request, { tenantId, updateMask: updateMask.join(',') })
1870 .then((response) => {
1871 return response;
1872 });
1873 }
1874 catch (e) {
1875 return Promise.reject(e);
1876 }
1877 }
1878}
1879exports.AuthRequestHandler = AuthRequestHandler;
1880/**
1881 * Utility for sending requests to Auth server that are tenant Auth instance related. This includes user
1882 * management related APIs for specified tenants.
1883 * This extends the BaseFirebaseAuthRequestHandler class.
1884 */
1885class TenantAwareAuthRequestHandler extends AbstractAuthRequestHandler {
1886 /**
1887 * The FirebaseTenantRequestHandler constructor used to initialize an instance using a
1888 * FirebaseApp and a tenant ID.
1889 *
1890 * @param app - The app used to fetch access tokens to sign API requests.
1891 * @param tenantId - The request handler's tenant ID.
1892 * @constructor
1893 */
1894 constructor(app, tenantId) {
1895 super(app);
1896 this.tenantId = tenantId;
1897 }
1898 /**
1899 * @returns A new Auth user management resource URL builder instance.
1900 */
1901 newAuthUrlBuilder() {
1902 return new TenantAwareAuthResourceUrlBuilder(this.app, 'v1', this.tenantId);
1903 }
1904 /**
1905 * @returns A new project config resource URL builder instance.
1906 */
1907 newProjectConfigUrlBuilder() {
1908 return new TenantAwareAuthResourceUrlBuilder(this.app, 'v2', this.tenantId);
1909 }
1910 /**
1911 * Imports the list of users provided to Firebase Auth. This is useful when
1912 * migrating from an external authentication system without having to use the Firebase CLI SDK.
1913 * At most, 1000 users are allowed to be imported one at a time.
1914 * When importing a list of password users, UserImportOptions are required to be specified.
1915 *
1916 * Overrides the superclass methods by adding an additional check to match tenant IDs of
1917 * imported user records if present.
1918 *
1919 * @param users - The list of user records to import to Firebase Auth.
1920 * @param options - The user import options, required when the users provided
1921 * include password credentials.
1922 * @returns A promise that resolves when the operation completes
1923 * with the result of the import. This includes the number of successful imports, the number
1924 * of failed uploads and their corresponding errors.
1925 */
1926 uploadAccount(users, options) {
1927 // Add additional check to match tenant ID of imported user records.
1928 users.forEach((user, index) => {
1929 if (validator.isNonEmptyString(user.tenantId) &&
1930 user.tenantId !== this.tenantId) {
1931 throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.MISMATCHING_TENANT_ID, `UserRecord of index "${index}" has mismatching tenant ID "${user.tenantId}"`);
1932 }
1933 });
1934 return super.uploadAccount(users, options);
1935 }
1936}
1937exports.TenantAwareAuthRequestHandler = TenantAwareAuthRequestHandler;
1938function emulatorHost() {
1939 return process.env.FIREBASE_AUTH_EMULATOR_HOST;
1940}
1941/**
1942 * When true the SDK should communicate with the Auth Emulator for all API
1943 * calls and also produce unsigned tokens.
1944 */
1945function useEmulator() {
1946 return !!emulatorHost();
1947}
1948exports.useEmulator = useEmulator;