auth-api-request.js 89 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948
  1. /*! firebase-admin v12.1.1 */
  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. */
  19. Object.defineProperty(exports, "__esModule", { value: true });
  20. exports.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;
  21. const validator = require("../utils/validator");
  22. const deep_copy_1 = require("../utils/deep-copy");
  23. const error_1 = require("../utils/error");
  24. const api_request_1 = require("../utils/api-request");
  25. const utils = require("../utils/index");
  26. const user_import_builder_1 = require("./user-import-builder");
  27. const action_code_settings_builder_1 = require("./action-code-settings-builder");
  28. const tenant_1 = require("./tenant");
  29. const identifier_1 = require("./identifier");
  30. const auth_config_1 = require("./auth-config");
  31. const project_config_1 = require("./project-config");
  32. /** Firebase Auth request header. */
  33. const FIREBASE_AUTH_HEADER = {
  34. 'X-Client-Version': `Node/Admin/${utils.getSdkVersion()}`,
  35. };
  36. /** Firebase Auth request timeout duration in milliseconds. */
  37. const FIREBASE_AUTH_TIMEOUT = 25000;
  38. /** List of reserved claims which cannot be provided when creating a custom token. */
  39. exports.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. */
  44. exports.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. */
  48. const MAX_CLAIMS_PAYLOAD_SIZE = 1000;
  49. /** Maximum allowed number of users to batch download at one time. */
  50. const MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE = 1000;
  51. /** Maximum allowed number of users to batch upload at one time. */
  52. const MAX_UPLOAD_ACCOUNT_BATCH_SIZE = 1000;
  53. /** Maximum allowed number of users to batch get at one time. */
  54. const MAX_GET_ACCOUNTS_BATCH_SIZE = 100;
  55. /** Maximum allowed number of users to batch delete at one time. */
  56. const MAX_DELETE_ACCOUNTS_BATCH_SIZE = 1000;
  57. /** Minimum allowed session cookie duration in seconds (5 minutes). */
  58. const MIN_SESSION_COOKIE_DURATION_SECS = 5 * 60;
  59. /** Maximum allowed session cookie duration in seconds (2 weeks). */
  60. const MAX_SESSION_COOKIE_DURATION_SECS = 14 * 24 * 60 * 60;
  61. /** Maximum allowed number of provider configurations to batch download at one time. */
  62. const MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE = 100;
  63. /** The Firebase Auth backend base URL format. */
  64. const FIREBASE_AUTH_BASE_URL_FORMAT = 'https://identitytoolkit.googleapis.com/{version}/projects/{projectId}{api}';
  65. /** Firebase Auth base URlLformat when using the auth emultor. */
  66. const 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. */
  68. const 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. */
  70. const 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. */
  72. const MAX_LIST_TENANT_PAGE_SIZE = 1000;
  73. /**
  74. * Enum for the user write operation type.
  75. */
  76. var 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. */
  83. class 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. */
  141. class 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. */
  183. class 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. */
  198. function 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. */
  249. function 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. */
  295. function 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. */
  488. exports.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. */
  514. exports.FIREBASE_AUTH_UPLOAD_ACCOUNT = new api_request_1.ApiSettings('/accounts:batchCreate', 'POST');
  515. /**
  516. * Instantiates the downloadAccount endpoint settings.
  517. *
  518. * @internal
  519. */
  520. exports.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. */
  541. exports.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. */
  560. exports.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. */
  572. exports.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. */
  582. exports.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. */
  608. exports.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. */
  634. exports.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. });
  658. const 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. */
  683. const 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. */
  696. const 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. */
  702. const 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. */
  715. const 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. */
  728. const 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. */
  749. const 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. */
  762. const 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. */
  768. const 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. */
  781. const 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. */
  794. const 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. */
  815. class 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. }
  1652. exports.AbstractAuthRequestHandler = AbstractAuthRequestHandler;
  1653. /** Instantiates the getConfig endpoint settings. */
  1654. const 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. */
  1662. const 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. */
  1670. const 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. */
  1679. const DELETE_TENANT = new api_request_1.ApiSettings('/tenants/{tenantId}', 'DELETE');
  1680. /** Instantiates the updateTenant endpoint settings. */
  1681. const 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. */
  1691. const 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. */
  1708. const 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. */
  1722. class 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. }
  1879. exports.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. */
  1885. class 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. }
  1937. exports.TenantAwareAuthRequestHandler = TenantAwareAuthRequestHandler;
  1938. function 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. */
  1945. function useEmulator() {
  1946. return !!emulatorHost();
  1947. }
  1948. exports.useEmulator = useEmulator;