/*! firebase-admin v8.9.2 */ "use strict"; /*! * Copyright 2017 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); var validator = require("../utils/validator"); var deep_copy_1 = require("../utils/deep-copy"); var error_1 = require("../utils/error"); var api_request_1 = require("../utils/api-request"); var user_import_builder_1 = require("./user-import-builder"); var utils = require("../utils/index"); var action_code_settings_builder_1 = require("./action-code-settings-builder"); var auth_config_1 = require("./auth-config"); var tenant_1 = require("./tenant"); /** Firebase Auth request header. */ var FIREBASE_AUTH_HEADER = { 'X-Client-Version': 'Node/Admin/8.9.2', }; /** Firebase Auth request timeout duration in milliseconds. */ var FIREBASE_AUTH_TIMEOUT = 25000; /** List of reserved claims which cannot be provided when creating a custom token. */ exports.RESERVED_CLAIMS = [ 'acr', 'amr', 'at_hash', 'aud', 'auth_time', 'azp', 'cnf', 'c_hash', 'exp', 'iat', 'iss', 'jti', 'nbf', 'nonce', 'sub', 'firebase', ]; /** List of supported email action request types. */ exports.EMAIL_ACTION_REQUEST_TYPES = [ 'PASSWORD_RESET', 'VERIFY_EMAIL', 'EMAIL_SIGNIN', ]; /** Maximum allowed number of characters in the custom claims payload. */ var MAX_CLAIMS_PAYLOAD_SIZE = 1000; /** Maximum allowed number of users to batch download at one time. */ var MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE = 1000; /** Maximum allowed number of users to batch upload at one time. */ var MAX_UPLOAD_ACCOUNT_BATCH_SIZE = 1000; /** Minimum allowed session cookie duration in seconds (5 minutes). */ var MIN_SESSION_COOKIE_DURATION_SECS = 5 * 60; /** Maximum allowed session cookie duration in seconds (2 weeks). */ var MAX_SESSION_COOKIE_DURATION_SECS = 14 * 24 * 60 * 60; /** Maximum allowed number of provider configurations to batch download at one time. */ var MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE = 100; /** The Firebase Auth backend base URL format. */ var FIREBASE_AUTH_BASE_URL_FORMAT = 'https://identitytoolkit.googleapis.com/{version}/projects/{projectId}{api}'; /** The Firebase Auth backend multi-tenancy base URL format. */ var FIREBASE_AUTH_TENANT_URL_FORMAT = FIREBASE_AUTH_BASE_URL_FORMAT.replace('projects/{projectId}', 'projects/{projectId}/tenants/{tenantId}'); /** Maximum allowed number of tenants to download at one time. */ var MAX_LIST_TENANT_PAGE_SIZE = 1000; /** Defines a base utility to help with resource URL construction. */ var AuthResourceUrlBuilder = /** @class */ (function () { /** * The resource URL builder constructor. * * @param {string} projectId The resource project ID. * @param {string} version The endpoint API version. * @constructor */ function AuthResourceUrlBuilder(app, version) { if (version === void 0) { version = 'v1'; } this.app = app; this.version = version; this.urlFormat = FIREBASE_AUTH_BASE_URL_FORMAT; } /** * Returns the resource URL corresponding to the provided parameters. * * @param {string=} api The backend API name. * @param {object=} params The optional additional parameters to substitute in the * URL path. * @return {Promise} The corresponding resource URL. */ AuthResourceUrlBuilder.prototype.getUrl = function (api, params) { var _this = this; return this.getProjectId() .then(function (projectId) { var baseParams = { version: _this.version, projectId: projectId, api: api || '', }; var baseUrl = utils.formatString(_this.urlFormat, baseParams); // Substitute additional api related parameters. return utils.formatString(baseUrl, params || {}); }); }; AuthResourceUrlBuilder.prototype.getProjectId = function () { var _this = this; if (this.projectId) { return Promise.resolve(this.projectId); } return utils.findProjectId(this.app) .then(function (projectId) { if (!validator.isNonEmptyString(projectId)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_CREDENTIAL, 'Failed to determine project ID for Auth. Initialize the ' + 'SDK with service account credentials or set project ID as an app option. ' + 'Alternatively set the GOOGLE_CLOUD_PROJECT environment variable.'); } _this.projectId = projectId; return projectId; }); }; return AuthResourceUrlBuilder; }()); /** Tenant aware resource builder utility. */ var TenantAwareAuthResourceUrlBuilder = /** @class */ (function (_super) { __extends(TenantAwareAuthResourceUrlBuilder, _super); /** * The tenant aware resource URL builder constructor. * * @param {string} projectId The resource project ID. * @param {string} version The endpoint API version. * @param {string} tenantId The tenant ID. * @constructor */ function TenantAwareAuthResourceUrlBuilder(app, version, tenantId) { var _this = _super.call(this, app, version) || this; _this.app = app; _this.version = version; _this.tenantId = tenantId; _this.urlFormat = FIREBASE_AUTH_TENANT_URL_FORMAT; return _this; } /** * Returns the resource URL corresponding to the provided parameters. * * @param {string=} api The backend API name. * @param {object=} params The optional additional parameters to substitute in the * URL path. * @return {Promise} The corresponding resource URL. */ TenantAwareAuthResourceUrlBuilder.prototype.getUrl = function (api, params) { var _this = this; return _super.prototype.getUrl.call(this, api, params) .then(function (url) { return utils.formatString(url, { tenantId: _this.tenantId }); }); }; return TenantAwareAuthResourceUrlBuilder; }(AuthResourceUrlBuilder)); /** * Validates a providerUserInfo object. All unsupported parameters * are removed from the original request. If an invalid field is passed * an error is thrown. * * @param {any} request The providerUserInfo request object. */ function validateProviderUserInfo(request) { var validKeys = { rawId: true, providerId: true, email: true, displayName: true, photoUrl: true, }; // Remove invalid keys from original request. for (var key in request) { if (!(key in validKeys)) { delete request[key]; } } if (!validator.isNonEmptyString(request.providerId)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID); } if (typeof request.displayName !== 'undefined' && typeof request.displayName !== 'string') { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_DISPLAY_NAME, "The provider \"displayName\" for \"" + request.providerId + "\" must be a valid string."); } if (!validator.isNonEmptyString(request.rawId)) { // This is called localId on the backend but the developer specifies this as // uid externally. So the error message should use the client facing name. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID, "The provider \"uid\" for \"" + request.providerId + "\" must be a valid non-empty string."); } // email should be a string and a valid email. if (typeof request.email !== 'undefined' && !validator.isEmail(request.email)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_EMAIL, "The provider \"email\" for \"" + request.providerId + "\" must be a valid email string."); } // photoUrl should be a URL. if (typeof request.photoUrl !== 'undefined' && !validator.isURL(request.photoUrl)) { // This is called photoUrl on the backend but the developer specifies this as // photoURL externally. So the error message should use the client facing name. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PHOTO_URL, "The provider \"photoURL\" for \"" + request.providerId + "\" must be a valid URL string."); } } /** * Validates a create/edit request object. All unsupported parameters * are removed from the original request. If an invalid field is passed * an error is thrown. * * @param {any} request The create/edit request object. * @param {boolean=} uploadAccountRequest Whether to validate as an uploadAccount request. */ function validateCreateEditRequest(request, uploadAccountRequest) { if (uploadAccountRequest === void 0) { uploadAccountRequest = false; } // Hash set of whitelisted parameters. var validKeys = { displayName: true, localId: true, email: true, password: true, rawPassword: true, emailVerified: true, photoUrl: true, disabled: true, disableUser: true, deleteAttribute: true, deleteProvider: true, sanityCheck: true, phoneNumber: true, customAttributes: true, validSince: true, // Pass tenantId only for uploadAccount requests. tenantId: uploadAccountRequest, passwordHash: uploadAccountRequest, salt: uploadAccountRequest, createdAt: uploadAccountRequest, lastLoginAt: uploadAccountRequest, providerUserInfo: uploadAccountRequest, }; // Remove invalid keys from original request. for (var key in request) { if (!(key in validKeys)) { delete request[key]; } } if (typeof request.tenantId !== 'undefined' && !validator.isNonEmptyString(request.tenantId)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_TENANT_ID); } // For any invalid parameter, use the external key name in the error description. // displayName should be a string. if (typeof request.displayName !== 'undefined' && !validator.isString(request.displayName)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_DISPLAY_NAME); } if ((typeof request.localId !== 'undefined' || uploadAccountRequest) && !validator.isUid(request.localId)) { // This is called localId on the backend but the developer specifies this as // uid externally. So the error message should use the client facing name. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID); } // email should be a string and a valid email. if (typeof request.email !== 'undefined' && !validator.isEmail(request.email)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_EMAIL); } // phoneNumber should be a string and a valid phone number. if (typeof request.phoneNumber !== 'undefined' && !validator.isPhoneNumber(request.phoneNumber)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PHONE_NUMBER); } // password should be a string and a minimum of 6 chars. if (typeof request.password !== 'undefined' && !validator.isPassword(request.password)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PASSWORD); } // rawPassword should be a string and a minimum of 6 chars. if (typeof request.rawPassword !== 'undefined' && !validator.isPassword(request.rawPassword)) { // This is called rawPassword on the backend but the developer specifies this as // password externally. So the error message should use the client facing name. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PASSWORD); } // emailVerified should be a boolean. if (typeof request.emailVerified !== 'undefined' && typeof request.emailVerified !== 'boolean') { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_EMAIL_VERIFIED); } // photoUrl should be a URL. if (typeof request.photoUrl !== 'undefined' && !validator.isURL(request.photoUrl)) { // This is called photoUrl on the backend but the developer specifies this as // photoURL externally. So the error message should use the client facing name. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PHOTO_URL); } // disabled should be a boolean. if (typeof request.disabled !== 'undefined' && typeof request.disabled !== 'boolean') { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_DISABLED_FIELD); } // validSince should be a number. if (typeof request.validSince !== 'undefined' && !validator.isNumber(request.validSince)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_TOKENS_VALID_AFTER_TIME); } // createdAt should be a number. if (typeof request.createdAt !== 'undefined' && !validator.isNumber(request.createdAt)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_CREATION_TIME); } // lastSignInAt should be a number. if (typeof request.lastLoginAt !== 'undefined' && !validator.isNumber(request.lastLoginAt)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_LAST_SIGN_IN_TIME); } // disableUser should be a boolean. if (typeof request.disableUser !== 'undefined' && typeof request.disableUser !== 'boolean') { // This is called disableUser on the backend but the developer specifies this as // disabled externally. So the error message should use the client facing name. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_DISABLED_FIELD); } // customAttributes should be stringified JSON with no blacklisted claims. // The payload should not exceed 1KB. if (typeof request.customAttributes !== 'undefined') { var developerClaims_1; try { developerClaims_1 = JSON.parse(request.customAttributes); } catch (error) { // JSON parsing error. This should never happen as we stringify the claims internally. // However, we still need to check since setAccountInfo via edit requests could pass // this field. throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_CLAIMS, error.message); } var invalidClaims_1 = []; // Check for any invalid claims. exports.RESERVED_CLAIMS.forEach(function (blacklistedClaim) { if (developerClaims_1.hasOwnProperty(blacklistedClaim)) { invalidClaims_1.push(blacklistedClaim); } }); // Throw an error if an invalid claim is detected. if (invalidClaims_1.length > 0) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.FORBIDDEN_CLAIM, invalidClaims_1.length > 1 ? "Developer claims \"" + invalidClaims_1.join('", "') + "\" are reserved and cannot be specified." : "Developer claim \"" + invalidClaims_1[0] + "\" is reserved and cannot be specified."); } // Check claims payload does not exceed maxmimum size. if (request.customAttributes.length > MAX_CLAIMS_PAYLOAD_SIZE) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.CLAIMS_TOO_LARGE, "Developer claims payload should not exceed " + MAX_CLAIMS_PAYLOAD_SIZE + " characters."); } } // passwordHash has to be a base64 encoded string. if (typeof request.passwordHash !== 'undefined' && !validator.isString(request.passwordHash)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PASSWORD_HASH); } // salt has to be a base64 encoded string. if (typeof request.salt !== 'undefined' && !validator.isString(request.salt)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PASSWORD_SALT); } // providerUserInfo has to be an array of valid UserInfo requests. if (typeof request.providerUserInfo !== 'undefined' && !validator.isArray(request.providerUserInfo)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_DATA); } else if (validator.isArray(request.providerUserInfo)) { request.providerUserInfo.forEach(function (providerUserInfoEntry) { validateProviderUserInfo(providerUserInfoEntry); }); } } /** Instantiates the createSessionCookie endpoint settings. */ exports.FIREBASE_AUTH_CREATE_SESSION_COOKIE = new api_request_1.ApiSettings(':createSessionCookie', 'POST') // Set request validator. .setRequestValidator(function (request) { // Validate the ID token is a non-empty string. if (!validator.isNonEmptyString(request.idToken)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ID_TOKEN); } // Validate the custom session cookie duration. if (!validator.isNumber(request.validDuration) || request.validDuration < MIN_SESSION_COOKIE_DURATION_SECS || request.validDuration > MAX_SESSION_COOKIE_DURATION_SECS) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_SESSION_COOKIE_DURATION); } }) // Set response validator. .setResponseValidator(function (response) { // Response should always contain the session cookie. if (!validator.isNonEmptyString(response.sessionCookie)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR); } }); /** Instantiates the uploadAccount endpoint settings. */ exports.FIREBASE_AUTH_UPLOAD_ACCOUNT = new api_request_1.ApiSettings('/accounts:batchCreate', 'POST'); /** Instantiates the downloadAccount endpoint settings. */ exports.FIREBASE_AUTH_DOWNLOAD_ACCOUNT = new api_request_1.ApiSettings('/accounts:batchGet', 'GET') // Set request validator. .setRequestValidator(function (request) { // Validate next page token. if (typeof request.nextPageToken !== 'undefined' && !validator.isNonEmptyString(request.nextPageToken)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PAGE_TOKEN); } // Validate max results. if (!validator.isNumber(request.maxResults) || request.maxResults <= 0 || request.maxResults > MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "Required \"maxResults\" must be a positive integer that does not exceed " + (MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE + ".")); } }); /** Instantiates the getAccountInfo endpoint settings. */ exports.FIREBASE_AUTH_GET_ACCOUNT_INFO = new api_request_1.ApiSettings('/accounts:lookup', 'POST') // Set request validator. .setRequestValidator(function (request) { if (!request.localId && !request.email && !request.phoneNumber) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier'); } }) // Set response validator. .setResponseValidator(function (response) { if (!response.users) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.USER_NOT_FOUND); } }); /** Instantiates the deleteAccount endpoint settings. */ exports.FIREBASE_AUTH_DELETE_ACCOUNT = new api_request_1.ApiSettings('/accounts:delete', 'POST') // Set request validator. .setRequestValidator(function (request) { if (!request.localId) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier'); } }); /** Instantiates the setAccountInfo endpoint settings for updating existing accounts. */ exports.FIREBASE_AUTH_SET_ACCOUNT_INFO = new api_request_1.ApiSettings('/accounts:update', 'POST') // Set request validator. .setRequestValidator(function (request) { // localId is a required parameter. if (typeof request.localId === 'undefined') { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Server request is missing user identifier'); } // Throw error when tenantId is passed in POST body. if (typeof request.tenantId !== 'undefined') { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '"tenantId" is an invalid "UpdateRequest" property.'); } validateCreateEditRequest(request); }) // Set response validator. .setResponseValidator(function (response) { // If the localId is not returned, then the request failed. if (!response.localId) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.USER_NOT_FOUND); } }); /** * Instantiates the signupNewUser endpoint settings for creating a new user with or without * uid being specified. The backend will create a new one if not provided and return it. */ exports.FIREBASE_AUTH_SIGN_UP_NEW_USER = new api_request_1.ApiSettings('/accounts', 'POST') // Set request validator. .setRequestValidator(function (request) { // signupNewUser does not support customAttributes. if (typeof request.customAttributes !== 'undefined') { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "\"customAttributes\" cannot be set when creating a new user."); } // signupNewUser does not support validSince. if (typeof request.validSince !== 'undefined') { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "\"validSince\" cannot be set when creating a new user."); } // Throw error when tenantId is passed in POST body. if (typeof request.tenantId !== 'undefined') { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, '"tenantId" is an invalid "CreateRequest" property.'); } validateCreateEditRequest(request); }) // Set response validator. .setResponseValidator(function (response) { // If the localId is not returned, then the request failed. if (!response.localId) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new user'); } }); var FIREBASE_AUTH_GET_OOB_CODE = new api_request_1.ApiSettings('/accounts:sendOobCode', 'POST') // Set request validator. .setRequestValidator(function (request) { if (!validator.isEmail(request.email)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_EMAIL); } if (exports.EMAIL_ACTION_REQUEST_TYPES.indexOf(request.requestType) === -1) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "\"" + request.requestType + "\" is not a supported email action request type."); } }) // Set response validator. .setResponseValidator(function (response) { // If the oobLink is not returned, then the request failed. if (!response.oobLink) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create the email action link'); } }); /** Instantiates the retrieve OIDC configuration endpoint settings. */ var GET_OAUTH_IDP_CONFIG = new api_request_1.ApiSettings('/oauthIdpConfigs/{providerId}', 'GET') // Set response validator. .setResponseValidator(function (response) { // Response should always contain the OIDC provider resource name. if (!validator.isNonEmptyString(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to get OIDC configuration'); } }); /** Instantiates the delete OIDC configuration endpoint settings. */ var DELETE_OAUTH_IDP_CONFIG = new api_request_1.ApiSettings('/oauthIdpConfigs/{providerId}', 'DELETE'); /** Instantiates the create OIDC configuration endpoint settings. */ var CREATE_OAUTH_IDP_CONFIG = new api_request_1.ApiSettings('/oauthIdpConfigs?oauthIdpConfigId={providerId}', 'POST') // Set response validator. .setResponseValidator(function (response) { // Response should always contain the OIDC provider resource name. if (!validator.isNonEmptyString(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new OIDC configuration'); } }); /** Instantiates the update OIDC configuration endpoint settings. */ var UPDATE_OAUTH_IDP_CONFIG = new api_request_1.ApiSettings('/oauthIdpConfigs/{providerId}?updateMask={updateMask}', 'PATCH') // Set response validator. .setResponseValidator(function (response) { // Response should always contain the configuration resource name. if (!validator.isNonEmptyString(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update OIDC configuration'); } }); /** Instantiates the list OIDC configuration endpoint settings. */ var LIST_OAUTH_IDP_CONFIGS = new api_request_1.ApiSettings('/oauthIdpConfigs', 'GET') // Set request validator. .setRequestValidator(function (request) { // Validate next page token. if (typeof request.pageToken !== 'undefined' && !validator.isNonEmptyString(request.pageToken)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PAGE_TOKEN); } // Validate max results. if (!validator.isNumber(request.pageSize) || request.pageSize <= 0 || request.pageSize > MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "Required \"maxResults\" must be a positive integer that does not exceed " + (MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE + ".")); } }); /** Instantiates the retrieve SAML configuration endpoint settings. */ var GET_INBOUND_SAML_CONFIG = new api_request_1.ApiSettings('/inboundSamlConfigs/{providerId}', 'GET') // Set response validator. .setResponseValidator(function (response) { // Response should always contain the SAML provider resource name. if (!validator.isNonEmptyString(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to get SAML configuration'); } }); /** Instantiates the delete SAML configuration endpoint settings. */ var DELETE_INBOUND_SAML_CONFIG = new api_request_1.ApiSettings('/inboundSamlConfigs/{providerId}', 'DELETE'); /** Instantiates the create SAML configuration endpoint settings. */ var CREATE_INBOUND_SAML_CONFIG = new api_request_1.ApiSettings('/inboundSamlConfigs?inboundSamlConfigId={providerId}', 'POST') // Set response validator. .setResponseValidator(function (response) { // Response should always contain the SAML provider resource name. if (!validator.isNonEmptyString(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new SAML configuration'); } }); /** Instantiates the update SAML configuration endpoint settings. */ var UPDATE_INBOUND_SAML_CONFIG = new api_request_1.ApiSettings('/inboundSamlConfigs/{providerId}?updateMask={updateMask}', 'PATCH') // Set response validator. .setResponseValidator(function (response) { // Response should always contain the configuration resource name. if (!validator.isNonEmptyString(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update SAML configuration'); } }); /** Instantiates the list SAML configuration endpoint settings. */ var LIST_INBOUND_SAML_CONFIGS = new api_request_1.ApiSettings('/inboundSamlConfigs', 'GET') // Set request validator. .setRequestValidator(function (request) { // Validate next page token. if (typeof request.pageToken !== 'undefined' && !validator.isNonEmptyString(request.pageToken)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PAGE_TOKEN); } // Validate max results. if (!validator.isNumber(request.pageSize) || request.pageSize <= 0 || request.pageSize > MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "Required \"maxResults\" must be a positive integer that does not exceed " + (MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE + ".")); } }); /** * Class that provides the mechanism to send requests to the Firebase Auth backend endpoints. */ var AbstractAuthRequestHandler = /** @class */ (function () { /** * @param {FirebaseApp} app The app used to fetch access tokens to sign API requests. * @constructor */ function AbstractAuthRequestHandler(app) { this.app = app; if (typeof app !== 'object' || app === null || !('options' in app)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'First argument passed to admin.auth() must be a valid Firebase app instance.'); } this.httpClient = new api_request_1.AuthorizedHttpClient(app); } /** * @param {any} response The response to check for errors. * @return {string|null} The error code if present; null otherwise. */ AbstractAuthRequestHandler.getErrorCode = function (response) { return (validator.isNonNullObject(response) && response.error && response.error.message) || null; }; /** * Creates a new Firebase session cookie with the specified duration that can be used for * session management (set as a server side session cookie with custom cookie policy). * The session cookie JWT will have the same payload claims as the provided ID token. * * @param {string} idToken The Firebase ID token to exchange for a session cookie. * @param {number} expiresIn The session cookie duration in milliseconds. * * @return {Promise} A promise that resolves on success with the created session cookie. */ AbstractAuthRequestHandler.prototype.createSessionCookie = function (idToken, expiresIn) { var request = { idToken: idToken, // Convert to seconds. validDuration: expiresIn / 1000, }; return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_CREATE_SESSION_COOKIE, request) .then(function (response) { return response.sessionCookie; }); }; /** * Looks up a user by uid. * * @param {string} uid The uid of the user to lookup. * @return {Promise} A promise that resolves with the user information. */ AbstractAuthRequestHandler.prototype.getAccountInfoByUid = function (uid) { if (!validator.isUid(uid)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID)); } var request = { localId: [uid], }; return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_GET_ACCOUNT_INFO, request); }; /** * Looks up a user by email. * * @param {string} email The email of the user to lookup. * @return {Promise} A promise that resolves with the user information. */ AbstractAuthRequestHandler.prototype.getAccountInfoByEmail = function (email) { if (!validator.isEmail(email)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_EMAIL)); } var request = { email: [email], }; return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_GET_ACCOUNT_INFO, request); }; /** * Looks up a user by phone number. * * @param {string} phoneNumber The phone number of the user to lookup. * @return {Promise} A promise that resolves with the user information. */ AbstractAuthRequestHandler.prototype.getAccountInfoByPhoneNumber = function (phoneNumber) { if (!validator.isPhoneNumber(phoneNumber)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PHONE_NUMBER)); } var request = { phoneNumber: [phoneNumber], }; return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_GET_ACCOUNT_INFO, request); }; /** * Exports the users (single batch only) with a size of maxResults and starting from * the offset as specified by pageToken. * * @param {number=} maxResults The page size, 1000 if undefined. This is also the maximum * allowed limit. * @param {string=} pageToken The next page token. If not specified, returns users starting * without any offset. Users are returned in the order they were created from oldest to * newest, relative to the page token offset. * @return {Promise} A promise that resolves with the current batch of downloaded * users and the next page token if available. For the last page, an empty list of users * and no page token are returned. */ AbstractAuthRequestHandler.prototype.downloadAccount = function (maxResults, pageToken) { if (maxResults === void 0) { maxResults = MAX_DOWNLOAD_ACCOUNT_PAGE_SIZE; } // Construct request. var request = { maxResults: maxResults, nextPageToken: pageToken, }; // Remove next page token if not provided. if (typeof request.nextPageToken === 'undefined') { delete request.nextPageToken; } return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_DOWNLOAD_ACCOUNT, request) .then(function (response) { // No more users available. if (!response.users) { response.users = []; } return response; }); }; /** * Imports the list of users provided to Firebase Auth. This is useful when * migrating from an external authentication system without having to use the Firebase CLI SDK. * At most, 1000 users are allowed to be imported one at a time. * When importing a list of password users, UserImportOptions are required to be specified. * * @param {UserImportRecord[]} users The list of user records to import to Firebase Auth. * @param {UserImportOptions=} options The user import options, required when the users provided * include password credentials. * @return {Promise} A promise that resolves when the operation completes * with the result of the import. This includes the number of successful imports, the number * of failed uploads and their corresponding errors. */ AbstractAuthRequestHandler.prototype.uploadAccount = function (users, options) { // This will throw if any error is detected in the hash options. // For errors in the list of users, this will not throw and will report the errors and the // corresponding user index in the user import generated response below. // No need to validate raw request or raw response as this is done in UserImportBuilder. var userImportBuilder = new user_import_builder_1.UserImportBuilder(users, options, function (userRequest) { // Pass true to validate the uploadAccount specific fields. validateCreateEditRequest(userRequest, true); }); var request = userImportBuilder.buildRequest(); // Fail quickly if more users than allowed are to be imported. if (validator.isArray(users) && users.length > MAX_UPLOAD_ACCOUNT_BATCH_SIZE) { 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."); } // If no remaining user in request after client side processing, there is no need // to send the request to the server. if (!request.users || request.users.length === 0) { return Promise.resolve(userImportBuilder.buildResponse([])); } return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_UPLOAD_ACCOUNT, request) .then(function (response) { // No error object is returned if no error encountered. var failedUploads = (response.error || []); // Rewrite response as UserImportResult and re-insert client previously detected errors. return userImportBuilder.buildResponse(failedUploads); }); }; /** * Deletes an account identified by a uid. * * @param {string} uid The uid of the user to delete. * @return {Promise} A promise that resolves when the user is deleted. */ AbstractAuthRequestHandler.prototype.deleteAccount = function (uid) { if (!validator.isUid(uid)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID)); } var request = { localId: uid, }; return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_DELETE_ACCOUNT, request); }; /** * Sets additional developer claims on an existing user identified by provided UID. * * @param {string} uid The user to edit. * @param {object} customUserClaims The developer claims to set. * @return {Promise} A promise that resolves when the operation completes * with the user id that was edited. */ AbstractAuthRequestHandler.prototype.setCustomUserClaims = function (uid, customUserClaims) { // Validate user UID. if (!validator.isUid(uid)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID)); } else if (!validator.isObject(customUserClaims)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'CustomUserClaims argument must be an object or null.')); } // Delete operation. Replace null with an empty object. if (customUserClaims === null) { customUserClaims = {}; } // Construct custom user attribute editting request. var request = { localId: uid, customAttributes: JSON.stringify(customUserClaims), }; return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_SET_ACCOUNT_INFO, request) .then(function (response) { return response.localId; }); }; /** * Edits an existing user. * * @param {string} uid The user to edit. * @param {object} properties The properties to set on the user. * @return {Promise} A promise that resolves when the operation completes * with the user id that was edited. */ AbstractAuthRequestHandler.prototype.updateExistingAccount = function (uid, properties) { if (!validator.isUid(uid)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID)); } else if (!validator.isNonNullObject(properties)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'Properties argument must be a non-null object.')); } // Build the setAccountInfo request. var request = deep_copy_1.deepCopy(properties); request.localId = uid; // For deleting displayName or photoURL, these values must be passed as null. // They will be removed from the backend request and an additional parameter // deleteAttribute: ['PHOTO_URL', 'DISPLAY_NAME'] // with an array of the parameter names to delete will be passed. // Parameters that are deletable and their deleteAttribute names. // Use client facing names, photoURL instead of photoUrl. var deletableParams = { displayName: 'DISPLAY_NAME', photoURL: 'PHOTO_URL', }; // Properties to delete if available. request.deleteAttribute = []; for (var key in deletableParams) { if (request[key] === null) { // Add property identifier to list of attributes to delete. request.deleteAttribute.push(deletableParams[key]); // Remove property from request. delete request[key]; } } if (request.deleteAttribute.length === 0) { delete request.deleteAttribute; } // For deleting phoneNumber, this value must be passed as null. // It will be removed from the backend request and an additional parameter // deleteProvider: ['phone'] with an array of providerIds (phone in this case), // will be passed. // Currently this applies to phone provider only. if (request.phoneNumber === null) { request.deleteProvider = ['phone']; delete request.phoneNumber; } else { // Doesn't apply to other providers in admin SDK. delete request.deleteProvider; } // Rewrite photoURL to photoUrl. if (typeof request.photoURL !== 'undefined') { request.photoUrl = request.photoURL; delete request.photoURL; } // Rewrite disabled to disableUser. if (typeof request.disabled !== 'undefined') { request.disableUser = request.disabled; delete request.disabled; } return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_SET_ACCOUNT_INFO, request) .then(function (response) { return response.localId; }); }; /** * Revokes all refresh tokens for the specified user identified by the uid provided. * In addition to revoking all refresh tokens for a user, all ID tokens issued * before revocation will also be revoked on the Auth backend. Any request with an * ID token generated before revocation will be rejected with a token expired error. * Note that due to the fact that the timestamp is stored in seconds, any tokens minted in * the same second as the revocation will still be valid. If there is a chance that a token * was minted in the last second, delay for 1 second before revoking. * * @param {string} uid The user whose tokens are to be revoked. * @return {Promise} A promise that resolves when the operation completes * successfully with the user id of the corresponding user. */ AbstractAuthRequestHandler.prototype.revokeRefreshTokens = function (uid) { // Validate user UID. if (!validator.isUid(uid)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_UID)); } var request = { localId: uid, // validSince is in UTC seconds. validSince: Math.ceil(new Date().getTime() / 1000), }; return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_SET_ACCOUNT_INFO, request) .then(function (response) { return response.localId; }); }; /** * Create a new user with the properties supplied. * * @param {object} properties The properties to set on the user. * @return {Promise} A promise that resolves when the operation completes * with the user id that was created. */ AbstractAuthRequestHandler.prototype.createNewAccount = function (properties) { if (!validator.isNonNullObject(properties)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, 'Properties argument must be a non-null object.')); } // Build the signupNewUser request. var request = deep_copy_1.deepCopy(properties); // Rewrite photoURL to photoUrl. if (typeof request.photoURL !== 'undefined') { request.photoUrl = request.photoURL; delete request.photoURL; } // Rewrite uid to localId if it exists. if (typeof request.uid !== 'undefined') { request.localId = request.uid; delete request.uid; } return this.invokeRequestHandler(this.getAuthUrlBuilder(), exports.FIREBASE_AUTH_SIGN_UP_NEW_USER, request) .then(function (response) { // Return the user id. return response.localId; }); }; /** * Generates the out of band email action link for the email specified using the action code settings provided. * Returns a promise that resolves with the generated link. * * @param {string} requestType The request type. This could be either used for password reset, * email verification, email link sign-in. * @param {string} email The email of the user the link is being sent to. * @param {ActionCodeSettings=} actionCodeSettings The optional action code setings which defines whether * the link is to be handled by a mobile app and the additional state information to be passed in the * deep link, etc. Required when requestType == 'EMAIL_SIGNIN' * @return {Promise} A promise that resolves with the email action link. */ AbstractAuthRequestHandler.prototype.getEmailActionLink = function (requestType, email, actionCodeSettings) { var request = { requestType: requestType, email: email, returnOobLink: true }; // ActionCodeSettings required for email link sign-in to determine the url where the sign-in will // be completed. if (typeof actionCodeSettings === 'undefined' && requestType === 'EMAIL_SIGNIN') { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "`actionCodeSettings` is required when `requestType` === 'EMAIL_SIGNIN'")); } if (typeof actionCodeSettings !== 'undefined' || requestType === 'EMAIL_SIGNIN') { try { var builder = new action_code_settings_builder_1.ActionCodeSettingsBuilder(actionCodeSettings); request = deep_copy_1.deepExtend(request, builder.buildRequest()); } catch (e) { return Promise.reject(e); } } return this.invokeRequestHandler(this.getAuthUrlBuilder(), FIREBASE_AUTH_GET_OOB_CODE, request) .then(function (response) { // Return the link. return response.oobLink; }); }; /** * Looks up an OIDC provider configuration by provider ID. * * @param {string} providerId The provider identifier of the configuration to lookup. * @return {Promise} A promise that resolves with the provider configuration information. */ AbstractAuthRequestHandler.prototype.getOAuthIdpConfig = function (providerId) { if (!auth_config_1.OIDCConfig.isProviderId(providerId)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID)); } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), GET_OAUTH_IDP_CONFIG, {}, { providerId: providerId }); }; /** * Lists the OIDC configurations (single batch only) with a size of maxResults and starting from * the offset as specified by pageToken. * * @param {number=} maxResults The page size, 100 if undefined. This is also the maximum * allowed limit. * @param {string=} pageToken The next page token. If not specified, returns OIDC configurations * without any offset. Configurations are returned in the order they were created from oldest to * newest, relative to the page token offset. * @return {Promise} A promise that resolves with the current batch of downloaded * OIDC configurations and the next page token if available. For the last page, an empty list of provider * configuration and no page token are returned. */ AbstractAuthRequestHandler.prototype.listOAuthIdpConfigs = function (maxResults, pageToken) { if (maxResults === void 0) { maxResults = MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE; } var request = { pageSize: maxResults, }; // Add next page token if provided. if (typeof pageToken !== 'undefined') { request.pageToken = pageToken; } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), LIST_OAUTH_IDP_CONFIGS, request) .then(function (response) { if (!response.oauthIdpConfigs) { response.oauthIdpConfigs = []; delete response.nextPageToken; } return response; }); }; /** * Deletes an OIDC configuration identified by a providerId. * * @param {string} providerId The identifier of the OIDC configuration to delete. * @return {Promise} A promise that resolves when the OIDC provider is deleted. */ AbstractAuthRequestHandler.prototype.deleteOAuthIdpConfig = function (providerId) { if (!auth_config_1.OIDCConfig.isProviderId(providerId)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID)); } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), DELETE_OAUTH_IDP_CONFIG, {}, { providerId: providerId }) .then(function (response) { // Return nothing. }); }; /** * Creates a new OIDC provider configuration with the properties provided. * * @param {AuthProviderConfig} options The properties to set on the new OIDC provider configuration to be created. * @return {Promise} A promise that resolves with the newly created OIDC * configuration. */ AbstractAuthRequestHandler.prototype.createOAuthIdpConfig = function (options) { // Construct backend request. var request; try { request = auth_config_1.OIDCConfig.buildServerRequest(options) || {}; } catch (e) { return Promise.reject(e); } var providerId = options.providerId; return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), CREATE_OAUTH_IDP_CONFIG, request, { providerId: providerId }) .then(function (response) { if (!auth_config_1.OIDCConfig.getProviderIdFromResourceName(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new OIDC provider configuration'); } return response; }); }; /** * Updates an existing OIDC provider configuration with the properties provided. * * @param {string} providerId The provider identifier of the OIDC configuration to update. * @param {OIDCUpdateAuthProviderRequest} options The properties to update on the existing configuration. * @return {Promise} A promise that resolves with the modified provider * configuration. */ AbstractAuthRequestHandler.prototype.updateOAuthIdpConfig = function (providerId, options) { if (!auth_config_1.OIDCConfig.isProviderId(providerId)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID)); } // Construct backend request. var request; try { request = auth_config_1.OIDCConfig.buildServerRequest(options, true) || {}; } catch (e) { return Promise.reject(e); } var updateMask = utils.generateUpdateMask(request); return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), UPDATE_OAUTH_IDP_CONFIG, request, { providerId: providerId, updateMask: updateMask.join(',') }) .then(function (response) { if (!auth_config_1.OIDCConfig.getProviderIdFromResourceName(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update OIDC provider configuration'); } return response; }); }; /** * Looks up an SAML provider configuration by provider ID. * * @param {string} providerId The provider identifier of the configuration to lookup. * @return {Promise} A promise that resolves with the provider configuration information. */ AbstractAuthRequestHandler.prototype.getInboundSamlConfig = function (providerId) { if (!auth_config_1.SAMLConfig.isProviderId(providerId)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID)); } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), GET_INBOUND_SAML_CONFIG, {}, { providerId: providerId }); }; /** * Lists the SAML configurations (single batch only) with a size of maxResults and starting from * the offset as specified by pageToken. * * @param {number=} maxResults The page size, 100 if undefined. This is also the maximum * allowed limit. * @param {string=} pageToken The next page token. If not specified, returns SAML configurations starting * without any offset. Configurations are returned in the order they were created from oldest to * newest, relative to the page token offset. * @return {Promise} A promise that resolves with the current batch of downloaded * SAML configurations and the next page token if available. For the last page, an empty list of provider * configuration and no page token are returned. */ AbstractAuthRequestHandler.prototype.listInboundSamlConfigs = function (maxResults, pageToken) { if (maxResults === void 0) { maxResults = MAX_LIST_PROVIDER_CONFIGURATION_PAGE_SIZE; } var request = { pageSize: maxResults, }; // Add next page token if provided. if (typeof pageToken !== 'undefined') { request.pageToken = pageToken; } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), LIST_INBOUND_SAML_CONFIGS, request) .then(function (response) { if (!response.inboundSamlConfigs) { response.inboundSamlConfigs = []; delete response.nextPageToken; } return response; }); }; /** * Deletes a SAML configuration identified by a providerId. * * @param {string} providerId The identifier of the SAML configuration to delete. * @return {Promise} A promise that resolves when the SAML provider is deleted. */ AbstractAuthRequestHandler.prototype.deleteInboundSamlConfig = function (providerId) { if (!auth_config_1.SAMLConfig.isProviderId(providerId)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID)); } return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), DELETE_INBOUND_SAML_CONFIG, {}, { providerId: providerId }) .then(function (response) { // Return nothing. }); }; /** * Creates a new SAML provider configuration with the properties provided. * * @param {AuthProviderConfig} options The properties to set on the new SAML provider configuration to be created. * @return {Promise} A promise that resolves with the newly created SAML * configuration. */ AbstractAuthRequestHandler.prototype.createInboundSamlConfig = function (options) { // Construct backend request. var request; try { request = auth_config_1.SAMLConfig.buildServerRequest(options) || {}; } catch (e) { return Promise.reject(e); } var providerId = options.providerId; return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), CREATE_INBOUND_SAML_CONFIG, request, { providerId: providerId }) .then(function (response) { if (!auth_config_1.SAMLConfig.getProviderIdFromResourceName(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new SAML provider configuration'); } return response; }); }; /** * Updates an existing SAML provider configuration with the properties provided. * * @param {string} providerId The provider identifier of the SAML configuration to update. * @param {SAMLUpdateAuthProviderRequest} options The properties to update on the existing configuration. * @return {Promise} A promise that resolves with the modified provider * configuration. */ AbstractAuthRequestHandler.prototype.updateInboundSamlConfig = function (providerId, options) { if (!auth_config_1.SAMLConfig.isProviderId(providerId)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PROVIDER_ID)); } // Construct backend request. var request; try { request = auth_config_1.SAMLConfig.buildServerRequest(options, true) || {}; } catch (e) { return Promise.reject(e); } var updateMask = utils.generateUpdateMask(request); return this.invokeRequestHandler(this.getProjectConfigUrlBuilder(), UPDATE_INBOUND_SAML_CONFIG, request, { providerId: providerId, updateMask: updateMask.join(',') }) .then(function (response) { if (!auth_config_1.SAMLConfig.getProviderIdFromResourceName(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update SAML provider configuration'); } return response; }); }; /** * Invokes the request handler based on the API settings object passed. * * @param {AuthResourceUrlBuilder} urlBuilder The URL builder for Auth endpoints. * @param {ApiSettings} apiSettings The API endpoint settings to apply to request and response. * @param {object} requestData The request data. * @param {object=} additionalResourceParams Additional resource related params if needed. * @return {Promise} A promise that resolves with the response. */ AbstractAuthRequestHandler.prototype.invokeRequestHandler = function (urlBuilder, apiSettings, requestData, additionalResourceParams) { var _this = this; return urlBuilder.getUrl(apiSettings.getEndpoint(), additionalResourceParams) .then(function (url) { // Validate request. var requestValidator = apiSettings.getRequestValidator(); requestValidator(requestData); // Process request. var req = { method: apiSettings.getHttpMethod(), url: url, headers: FIREBASE_AUTH_HEADER, data: requestData, timeout: FIREBASE_AUTH_TIMEOUT, }; return _this.httpClient.send(req); }) .then(function (response) { // Validate response. var responseValidator = apiSettings.getResponseValidator(); responseValidator(response.data); // Return entire response. return response.data; }) .catch(function (err) { if (err instanceof api_request_1.HttpError) { var error = err.response.data; var errorCode = AbstractAuthRequestHandler.getErrorCode(error); if (!errorCode) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'Error returned from server: ' + error + '. Additionally, an ' + 'internal error occurred while attempting to extract the ' + 'errorcode from the error.'); } throw error_1.FirebaseAuthError.fromServerError(errorCode, /* message */ undefined, error); } throw err; }); }; /** * @return {AuthResourceUrlBuilder} The current Auth user management resource URL builder. */ AbstractAuthRequestHandler.prototype.getAuthUrlBuilder = function () { if (!this.authUrlBuilder) { this.authUrlBuilder = this.newAuthUrlBuilder(); } return this.authUrlBuilder; }; /** * @return {AuthResourceUrlBuilder} The current project config resource URL builder. */ AbstractAuthRequestHandler.prototype.getProjectConfigUrlBuilder = function () { if (!this.projectConfigUrlBuilder) { this.projectConfigUrlBuilder = this.newProjectConfigUrlBuilder(); } return this.projectConfigUrlBuilder; }; return AbstractAuthRequestHandler; }()); exports.AbstractAuthRequestHandler = AbstractAuthRequestHandler; /** Instantiates the getTenant endpoint settings. */ var GET_TENANT = new api_request_1.ApiSettings('/tenants/{tenantId}', 'GET') // Set response validator. .setResponseValidator(function (response) { // Response should always contain at least the tenant name. if (!validator.isNonEmptyString(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to get tenant'); } }); /** Instantiates the deleteTenant endpoint settings. */ var DELETE_TENANT = new api_request_1.ApiSettings('/tenants/{tenantId}', 'DELETE'); /** Instantiates the updateTenant endpoint settings. */ var UPDATE_TENANT = new api_request_1.ApiSettings('/tenants/{tenantId}?updateMask={updateMask}', 'PATCH') // Set response validator. .setResponseValidator(function (response) { // Response should always contain at least the tenant name. if (!validator.isNonEmptyString(response.name) || !tenant_1.Tenant.getTenantIdFromResourceName(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to update tenant'); } }); /** Instantiates the listTenants endpoint settings. */ var LIST_TENANTS = new api_request_1.ApiSettings('/tenants', 'GET') // Set request validator. .setRequestValidator(function (request) { // Validate next page token. if (typeof request.pageToken !== 'undefined' && !validator.isNonEmptyString(request.pageToken)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_PAGE_TOKEN); } // Validate max results. if (!validator.isNumber(request.pageSize) || request.pageSize <= 0 || request.pageSize > MAX_LIST_TENANT_PAGE_SIZE) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_ARGUMENT, "Required \"maxResults\" must be a positive non-zero number that does not exceed " + ("the allowed " + MAX_LIST_TENANT_PAGE_SIZE + ".")); } }); /** Instantiates the createTenant endpoint settings. */ var CREATE_TENANT = new api_request_1.ApiSettings('/tenants', 'POST') // Set response validator. .setResponseValidator(function (response) { // Response should always contain at least the tenant name. if (!validator.isNonEmptyString(response.name) || !tenant_1.Tenant.getTenantIdFromResourceName(response.name)) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INTERNAL_ERROR, 'INTERNAL ASSERT FAILED: Unable to create new tenant'); } }); /** * Utility for sending requests to Auth server that are Auth instance related. This includes user and * tenant management related APIs. This extends the BaseFirebaseAuthRequestHandler class and defines * additional tenant management related APIs. */ var AuthRequestHandler = /** @class */ (function (_super) { __extends(AuthRequestHandler, _super); /** * The FirebaseAuthRequestHandler constructor used to initialize an instance using a FirebaseApp. * * @param {FirebaseApp} app The app used to fetch access tokens to sign API requests. * @constructor. */ function AuthRequestHandler(app) { var _this = _super.call(this, app) || this; _this.tenantMgmtResourceBuilder = new AuthResourceUrlBuilder(app, 'v2'); return _this; } /** * @return {AuthResourceUrlBuilder} A new Auth user management resource URL builder instance. */ AuthRequestHandler.prototype.newAuthUrlBuilder = function () { return new AuthResourceUrlBuilder(this.app, 'v1'); }; /** * @return {AuthResourceUrlBuilder} A new project config resource URL builder instance. */ AuthRequestHandler.prototype.newProjectConfigUrlBuilder = function () { return new AuthResourceUrlBuilder(this.app, 'v2'); }; /** * Looks up a tenant by tenant ID. * * @param {string} tenantId The tenant identifier of the tenant to lookup. * @return {Promise} A promise that resolves with the tenant information. */ AuthRequestHandler.prototype.getTenant = function (tenantId) { if (!validator.isNonEmptyString(tenantId)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_TENANT_ID)); } return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, GET_TENANT, {}, { tenantId: tenantId }) .then(function (response) { return response; }); }; /** * Exports the tenants (single batch only) with a size of maxResults and starting from * the offset as specified by pageToken. * * @param {number=} maxResults The page size, 1000 if undefined. This is also the maximum * allowed limit. * @param {string=} pageToken The next page token. If not specified, returns tenants starting * without any offset. Tenants are returned in the order they were created from oldest to * newest, relative to the page token offset. * @return {Promise} A promise that resolves with the current batch of downloaded * tenants and the next page token if available. For the last page, an empty list of tenants * and no page token are returned. */ AuthRequestHandler.prototype.listTenants = function (maxResults, pageToken) { if (maxResults === void 0) { maxResults = MAX_LIST_TENANT_PAGE_SIZE; } var request = { pageSize: maxResults, pageToken: pageToken, }; // Remove next page token if not provided. if (typeof request.pageToken === 'undefined') { delete request.pageToken; } return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, LIST_TENANTS, request) .then(function (response) { if (!response.tenants) { response.tenants = []; delete response.nextPageToken; } return response; }); }; /** * Deletes a tenant identified by a tenantId. * * @param {string} tenantId The identifier of the tenant to delete. * @return {Promise} A promise that resolves when the tenant is deleted. */ AuthRequestHandler.prototype.deleteTenant = function (tenantId) { if (!validator.isNonEmptyString(tenantId)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_TENANT_ID)); } return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, DELETE_TENANT, {}, { tenantId: tenantId }) .then(function (response) { // Return nothing. }); }; /** * Creates a new tenant with the properties provided. * * @param {TenantOptions} tenantOptions The properties to set on the new tenant to be created. * @return {Promise} A promise that resolves with the newly created tenant object. */ AuthRequestHandler.prototype.createTenant = function (tenantOptions) { try { // Construct backend request. var request = tenant_1.Tenant.buildServerRequest(tenantOptions, true); return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, CREATE_TENANT, request) .then(function (response) { return response; }); } catch (e) { return Promise.reject(e); } }; /** * Updates an existing tenant with the properties provided. * * @param {string} tenantId The tenant identifier of the tenant to update. * @param {TenantOptions} tenantOptions The properties to update on the existing tenant. * @return {Promise} A promise that resolves with the modified tenant object. */ AuthRequestHandler.prototype.updateTenant = function (tenantId, tenantOptions) { if (!validator.isNonEmptyString(tenantId)) { return Promise.reject(new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.INVALID_TENANT_ID)); } try { // Construct backend request. var request = tenant_1.Tenant.buildServerRequest(tenantOptions, false); var updateMask = utils.generateUpdateMask(request); return this.invokeRequestHandler(this.tenantMgmtResourceBuilder, UPDATE_TENANT, request, { tenantId: tenantId, updateMask: updateMask.join(',') }) .then(function (response) { return response; }); } catch (e) { return Promise.reject(e); } }; return AuthRequestHandler; }(AbstractAuthRequestHandler)); exports.AuthRequestHandler = AuthRequestHandler; /** * Utility for sending requests to Auth server that are tenant Auth instance related. This includes user * management related APIs for specified tenants. * This extends the BaseFirebaseAuthRequestHandler class. */ var TenantAwareAuthRequestHandler = /** @class */ (function (_super) { __extends(TenantAwareAuthRequestHandler, _super); /** * The FirebaseTenantRequestHandler constructor used to initialize an instance using a * FirebaseApp and a tenant ID. * * @param {FirebaseApp} app The app used to fetch access tokens to sign API requests. * @param {string} tenantId The request handler's tenant ID. * @constructor */ function TenantAwareAuthRequestHandler(app, tenantId) { var _this = _super.call(this, app) || this; _this.tenantId = tenantId; return _this; } /** * @return {AuthResourceUrlBuilder} A new Auth user management resource URL builder instance. */ TenantAwareAuthRequestHandler.prototype.newAuthUrlBuilder = function () { return new TenantAwareAuthResourceUrlBuilder(this.app, 'v1', this.tenantId); }; /** * @return {AuthResourceUrlBuilder} A new project config resource URL builder instance. */ TenantAwareAuthRequestHandler.prototype.newProjectConfigUrlBuilder = function () { return new TenantAwareAuthResourceUrlBuilder(this.app, 'v2', this.tenantId); }; /** * Imports the list of users provided to Firebase Auth. This is useful when * migrating from an external authentication system without having to use the Firebase CLI SDK. * At most, 1000 users are allowed to be imported one at a time. * When importing a list of password users, UserImportOptions are required to be specified. * * Overrides the superclass methods by adding an additional check to match tenant IDs of * imported user records if present. * * @param {UserImportRecord[]} users The list of user records to import to Firebase Auth. * @param {UserImportOptions=} options The user import options, required when the users provided * include password credentials. * @return {Promise} A promise that resolves when the operation completes * with the result of the import. This includes the number of successful imports, the number * of failed uploads and their corresponding errors. */ TenantAwareAuthRequestHandler.prototype.uploadAccount = function (users, options) { var _this = this; // Add additional check to match tenant ID of imported user records. users.forEach(function (user, index) { if (validator.isNonEmptyString(user.tenantId) && user.tenantId !== _this.tenantId) { throw new error_1.FirebaseAuthError(error_1.AuthClientErrorCode.MISMATCHING_TENANT_ID, "UserRecord of index \"" + index + "\" has mismatching tenant ID \"" + user.tenantId + "\""); } }); return _super.prototype.uploadAccount.call(this, users, options); }; return TenantAwareAuthRequestHandler; }(AbstractAuthRequestHandler)); exports.TenantAwareAuthRequestHandler = TenantAwareAuthRequestHandler;