function validateCreateEditRequest()

in src/auth/auth-api-request.ts [373:565]


function validateCreateEditRequest(request: any, writeOperationType: WriteOperationType): void {
  const uploadAccountRequest = writeOperationType === WriteOperationType.Upload;
  // Hash set of whitelisted parameters.
  const 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 linkProviderUserInfo only for updates (i.e. not for uploads.)
    linkProviderUserInfo: !uploadAccountRequest,
    // Pass tenantId only for uploadAccount requests.
    tenantId: uploadAccountRequest,
    passwordHash: uploadAccountRequest,
    salt: uploadAccountRequest,
    createdAt: uploadAccountRequest,
    lastLoginAt: uploadAccountRequest,
    providerUserInfo: uploadAccountRequest,
    mfaInfo: uploadAccountRequest,
    // Only for non-uploadAccount requests.
    mfa: !uploadAccountRequest,
  };
  // Remove invalid keys from original request.
  for (const key in request) {
    if (!(key in validKeys)) {
      delete request[key];
    }
  }
  if (typeof request.tenantId !== 'undefined' &&
      !validator.isNonEmptyString(request.tenantId)) {
    throw new FirebaseAuthError(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 FirebaseAuthError(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 FirebaseAuthError(AuthClientErrorCode.INVALID_UID);
  }
  // email should be a string and a valid email.
  if (typeof request.email !== 'undefined' && !validator.isEmail(request.email)) {
    throw new FirebaseAuthError(AuthClientErrorCode.INVALID_EMAIL);
  }
  // phoneNumber should be a string and a valid phone number.
  if (typeof request.phoneNumber !== 'undefined' &&
      !validator.isPhoneNumber(request.phoneNumber)) {
    throw new FirebaseAuthError(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 FirebaseAuthError(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 FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD);
  }
  // emailVerified should be a boolean.
  if (typeof request.emailVerified !== 'undefined' &&
      typeof request.emailVerified !== 'boolean') {
    throw new FirebaseAuthError(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 FirebaseAuthError(AuthClientErrorCode.INVALID_PHOTO_URL);
  }
  // disabled should be a boolean.
  if (typeof request.disabled !== 'undefined' &&
      typeof request.disabled !== 'boolean') {
    throw new FirebaseAuthError(AuthClientErrorCode.INVALID_DISABLED_FIELD);
  }
  // validSince should be a number.
  if (typeof request.validSince !== 'undefined' &&
      !validator.isNumber(request.validSince)) {
    throw new FirebaseAuthError(AuthClientErrorCode.INVALID_TOKENS_VALID_AFTER_TIME);
  }
  // createdAt should be a number.
  if (typeof request.createdAt !== 'undefined' &&
      !validator.isNumber(request.createdAt)) {
    throw new FirebaseAuthError(AuthClientErrorCode.INVALID_CREATION_TIME);
  }
  // lastSignInAt should be a number.
  if (typeof request.lastLoginAt !== 'undefined' &&
      !validator.isNumber(request.lastLoginAt)) {
    throw new FirebaseAuthError(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 FirebaseAuthError(AuthClientErrorCode.INVALID_DISABLED_FIELD);
  }
  // customAttributes should be stringified JSON with no blacklisted claims.
  // The payload should not exceed 1KB.
  if (typeof request.customAttributes !== 'undefined') {
    let developerClaims: object;
    try {
      developerClaims = 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 FirebaseAuthError(AuthClientErrorCode.INVALID_CLAIMS, error.message);
    }
    const invalidClaims: string[] = [];
    // Check for any invalid claims.
    RESERVED_CLAIMS.forEach((blacklistedClaim) => {
      if (Object.prototype.hasOwnProperty.call(developerClaims, blacklistedClaim)) {
        invalidClaims.push(blacklistedClaim);
      }
    });
    // Throw an error if an invalid claim is detected.
    if (invalidClaims.length > 0) {
      throw new FirebaseAuthError(
        AuthClientErrorCode.FORBIDDEN_CLAIM,
        invalidClaims.length > 1 ?
          `Developer claims "${invalidClaims.join('", "')}" are reserved and cannot be specified.` :
          `Developer claim "${invalidClaims[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 FirebaseAuthError(
        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 FirebaseAuthError(AuthClientErrorCode.INVALID_PASSWORD_HASH);
  }
  // salt has to be a base64 encoded string.
  if (typeof request.salt !== 'undefined' &&
      !validator.isString(request.salt)) {
    throw new FirebaseAuthError(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 FirebaseAuthError(AuthClientErrorCode.INVALID_PROVIDER_DATA);
  } else if (validator.isArray(request.providerUserInfo)) {
    request.providerUserInfo.forEach((providerUserInfoEntry: any) => {
      validateProviderUserInfo(providerUserInfoEntry);
    });
  }

  // linkProviderUserInfo must be a (single) UserProvider value.
  if (typeof request.linkProviderUserInfo !== 'undefined') {
    validateProviderUserInfo(request.linkProviderUserInfo);
  }

  // mfaInfo is used for importUsers.
  // mfa.enrollments is used for setAccountInfo.
  // enrollments has to be an array of valid AuthFactorInfo requests.
  let enrollments: AuthFactorInfo[] | null = null;
  if (request.mfaInfo) {
    enrollments = request.mfaInfo;
  } else if (request.mfa && request.mfa.enrollments) {
    enrollments = request.mfa.enrollments;
  }
  if (enrollments) {
    if (!validator.isArray(enrollments)) {
      throw new FirebaseAuthError(AuthClientErrorCode.INVALID_ENROLLED_FACTORS);
    }
    enrollments.forEach((authFactorInfoEntry: AuthFactorInfo) => {
      validateAuthFactorInfo(authFactorInfoEntry);
    });
  }
}