<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ezequiel E. Tejada</title>
    <description>The latest articles on DEV Community by Ezequiel E. Tejada (@ezequieltejada).</description>
    <link>https://dev.to/ezequieltejada</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F74264%2Fff4817c5-0a00-43d6-9740-3cdebfdde0d1.jpeg</url>
      <title>DEV Community: Ezequiel E. Tejada</title>
      <link>https://dev.to/ezequieltejada</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ezequieltejada"/>
    <language>en</language>
    <item>
      <title>Dipping my toe into VSCode's Remote Container feature.</title>
      <dc:creator>Ezequiel E. Tejada</dc:creator>
      <pubDate>Sat, 03 Jul 2021 10:33:39 +0000</pubDate>
      <link>https://dev.to/ezequieltejada/dipping-my-toe-into-vscode-s-remote-container-feature-829</link>
      <guid>https://dev.to/ezequieltejada/dipping-my-toe-into-vscode-s-remote-container-feature-829</guid>
      <description>&lt;p&gt;Hi! Long time no see. I hope to keep some regularity publishing articles along the way, but don't hold your breath.&lt;/p&gt;

&lt;p&gt;This time, I'd like to tackle a problem I'm having when I'm working on multiple projects that is keeping a consistent development environment.&lt;/p&gt;

&lt;p&gt;It's been a while I've heard that docker could be an appropriate solution for this problem, but I didn't want to learn it at the time because I was involved in other things.&lt;/p&gt;

&lt;p&gt;But the time has come and with the release of VSCode's Remote Container feature, now is the moment to deal with this and learn some docker, let's do this!&lt;/p&gt;

&lt;h2&gt;
  
  
  Docker installation
&lt;/h2&gt;

&lt;p&gt;First of all, we must install Docker Desktop. Note: I'm doing this on my Windows Computer, if you'd like a Linux flavored step by step, let me know in the comments.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://www.docker.com/products/docker-desktop"&gt;Docker Desktop download page&lt;/a&gt; and click on Download. Tip: Did you know you can install it using &lt;a href="https://chocolatey.org/"&gt;Chocolatey&lt;/a&gt; with the command "choco install docker-desktop"?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2lrDmRT_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1623828825596/yJhUu_FYD.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2lrDmRT_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1623828825596/yJhUu_FYD.png" alt="imagen.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  devcontainer.json
&lt;/h2&gt;

&lt;p&gt;This file tells VSCode how to handle the container. This is great if you want your team to use certain extensions.&lt;/p&gt;

&lt;p&gt;You can create the file in your project's root folder or on ".devcontainer/devcontainer.json"&lt;/p&gt;

&lt;p&gt;devcontainer.json example content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "image": "mcr.microsoft.com/vscode/devcontainers/typescript-node:0-12",
  "forwardPorts": [3000],
  "extensions": ["dbaeumer.vscode-eslint"]
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, you can select a devcontainer template file for your environment by selecting the Remote-Containers: Add Development Container Configuration Files... command from the Command Palette (F1) It will generate a folder called .devcontainer and inside we will find two files. .devcontainer.json Dockerfile&lt;/p&gt;

&lt;p&gt;These files are necessary to configure the image just like we wanted and setting everything up on image startup.&lt;/p&gt;

&lt;p&gt;For more information, there is a &lt;a href="https://code.visualstudio.com/docs/remote/devcontainerjson-reference"&gt;devcontainer reference page&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Kicking the tires
&lt;/h2&gt;

&lt;p&gt;Seems there we have the basic stuff to run our code in a container, so let's give it a go.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vVgR23XB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1624217440991/Id9VurHS6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vVgR23XB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1624217440991/Id9VurHS6.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the green button which is located at the bottom left corner of the VSCode screen and then select the "Reopen in container" option. This will use the .devcontainer.json file to use a docker image, load your files inside it, and getting it ready to serve.&lt;/p&gt;

&lt;p&gt;It kinda worked, however, it seems that some things were missing. Since it's a new image, all the global commands weren't there. Also, all the sessions and environment variables were gone too.&lt;/p&gt;

&lt;p&gt;To fix that, we should work on the Dockerfile adding this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RUN su node -c "npm install -g @angular/cli"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will run every time we rebuild the image, to make the "ng" command available as soon as possible.&lt;/p&gt;

&lt;p&gt;With firebase cli, we can make use of the firebase login:ci command to have a token and avoid logging in into Firebase every time the image rebuilds. The token can be saved into devcontainer.json with the "containerEnv" property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    "containerEnv": {
        "MY_CONTAINER_VAR": "some-value-here",
        "MY_CONTAINER_VAR2": "${localEnv:SOME_LOCAL_VAR}"
    },

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or creating a devcontainer.env file in the .devcontainer folder. Beware to gitignore this file to avoid committing env variables that shouldn't be public.&lt;/p&gt;

&lt;p&gt;Once we have Angular and Firebase setup, we can continue to work on our project. &lt;em&gt;Is it done? Can we improve it?&lt;/em&gt; "Sure to both questions" but that's for another post. I hope you've liked it.&lt;/p&gt;

&lt;p&gt;As always, I'd like to know what do you think about this. Have you tried it? Have you encountered any problems? Please let me know in the comments.&lt;/p&gt;

&lt;p&gt;See you next time!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>My take on... the recruiting process</title>
      <dc:creator>Ezequiel E. Tejada</dc:creator>
      <pubDate>Mon, 31 May 2021 07:43:44 +0000</pubDate>
      <link>https://dev.to/ezequieltejada/my-take-on-the-recruiting-process-58i8</link>
      <guid>https://dev.to/ezequieltejada/my-take-on-the-recruiting-process-58i8</guid>
      <description>&lt;p&gt;Howdy 👋🏻&lt;/p&gt;

&lt;p&gt;This time, I'd like to talk about the recruiting process. You know, the first step before getting your next job.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.giphy.com/media/l2Sq6TpbRV4oD7zbi/giphy.gif"&gt;https://media.giphy.com/media/l2Sq6TpbRV4oD7zbi/giphy.gif&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It can be quite frustrating for everyone involved, and I guess it may be because each party doesn't know what to expect from the other side. You (talking from the developer side) will feel dazzled when the recruiters ask you personal questions that are not related to the position or feel they are doing market research with you. Not cool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't be a call center agent.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.giphy.com/media/1oEttHTW7Dh6WFabZd/giphy.gif"&gt;https://media.giphy.com/media/1oEttHTW7Dh6WFabZd/giphy.gif&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My first job was in a call center, there is nothing wrong with it, however, being a recruiter and a call center agent are two quite different jobs.&lt;/p&gt;

&lt;p&gt;The recipe for getting low-quality candidates is measuring success by the number of phone calls the recruiter has. It means they will try to get as many phone calls as possible and not pay attention to the candidate's profile that fits the position.&lt;/p&gt;

&lt;p&gt;It results in a frustrated recruiter that has a very large list of candidates and keeps shifting from one candidate to the next one. "Does not fit", "they are not interested", "don't even know the tech stack" may be the most common conclusions on those calls.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.giphy.com/media/LO8ipFG7sfgAPRyJW7/giphy.gif"&gt;https://media.giphy.com/media/LO8ipFG7sfgAPRyJW7/giphy.gif&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thus, doing some proper research beforehand will save everyone's time. Recruiters could go through the candidate's profile and pay close attention to the technologies, the experience, what they are currently doing. Maybe they have the knowledge and experience, but checking their last positions, the recruiter can see if the candidate is currently working on a different level than the job opportunity offers.&lt;/p&gt;

&lt;p&gt;In addition, having phone calls are exhausting and inefficient. Recruiters can spend days if not weeks calling a couple of dozens of people. Although, having a compelling message, with all the details (especially salary details) will help them to reach many more professionals and have a better chance to find someone that fits.&lt;/p&gt;

&lt;h2&gt;
  
  
  How much?
&lt;/h2&gt;

&lt;p&gt;Oh boy! This topic has so many different and conflicting visions that I understand why so few people talk about this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.giphy.com/media/l2Je7t18abJCLdDVe/giphy.gif"&gt;https://media.giphy.com/media/l2Je7t18abJCLdDVe/giphy.gif&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This reminds me of a call I had with a recruiter for a development company related to SAP. We've been through the conversation regarding the position without any problem until we've talked about salary.&lt;/p&gt;

&lt;p&gt;She tried to convince me with an example of a car dealership. She said: "If you want to buy a car, you should ask how much it is worth" (In this time, where there is this movement to avoid "objectivizing" people, suddenly, I'm a car 🤷🏻‍♂️) and I answered, "Well, in this case, is the salesperson who came to my house to offer a car and doesn't want to tell me how much is it"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.giphy.com/media/26Ff0IG86VpW6S9vG/giphy.gif"&gt;https://media.giphy.com/media/26Ff0IG86VpW6S9vG/giphy.gif&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rule should be simple, whoever contacts the other party, should be the first one to disclose the number. If a recruiter contacts you, they should say: "I have X position for X money, are you interested?". The other way around is, if you are looking for a change, you should disclose how much you need to make the change (However, it's a bonus if the recruiter does it before you in this situation)&lt;/p&gt;

&lt;p&gt;This, combined with the first point it's a red flag for me. If a recruiter contacts me and insists on having a call to then avoid disclosing all the details about the position, especially, the salary, it's a "no-go" for me. That kind of unprofessionalism is not what I want to have in my job environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical interview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://youtu.be/ipsPgNEmAXI"&gt;https://youtu.be/ipsPgNEmAXI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I was going to have a gif, but the whole scene deserves it 😂&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here, I don't have any complaints, to be honest. Although, there are a couple of things that made me feel more comfortable than others.&lt;/p&gt;

&lt;p&gt;It is crucial to make the candidate comfortable and to achieve that, there are a couple of strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask the candidate to talk about any personal project they have with the technologies related to the position. This helps the candidate shine in a familiar environment.&lt;/li&gt;
&lt;li&gt;Make it "One on One", having a conversation one on one is more comfortable than having the candidate talk to two or more people.&lt;/li&gt;
&lt;li&gt;"We're on the same level, on the same team" I highly recommend this, make it very clear that both of you seek the same goal, working together. It is not a competition, so don't rush for answers. And that takes me to the next point.&lt;/li&gt;
&lt;li&gt;Don't look for answers, &lt;strong&gt;look for ways of thinking/solving problems&lt;/strong&gt;. Nobody knows all the answers, particularly in our job, where we lean on documentation a lot (at least I do most of the time). Because developers are not hired to give answers, they are hired to solve problems and many people confuse them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now, this is my take on the recruiting process. Probably I haven't covered much, but I'd gladly take any feedback and edit, add more details on what interests you the most. Furthermore, as we get more experience our ideas change, so this is what I think at the time of writing. It may change in the future, and it should, because it means we are evolving.&lt;/p&gt;

&lt;p&gt;As always, I hope you can take anything useful from this and I'd like to know your thoughts.&lt;/p&gt;

&lt;p&gt;Feel free to reach me on &lt;a href="https://www.linkedin.com/in/ezequieltejada/"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/ezeteja"&gt;Twitter&lt;/a&gt; . 👋🏻&lt;/p&gt;

</description>
      <category>interview</category>
      <category>devlife</category>
    </item>
    <item>
      <title>Quick take on Angular Imports</title>
      <dc:creator>Ezequiel E. Tejada</dc:creator>
      <pubDate>Wed, 26 May 2021 11:28:19 +0000</pubDate>
      <link>https://dev.to/ezequieltejada/quick-take-on-angular-imports-1f4o</link>
      <guid>https://dev.to/ezequieltejada/quick-take-on-angular-imports-1f4o</guid>
      <description>&lt;p&gt;Good morning ☕!&lt;/p&gt;

&lt;p&gt;A quick take that I'd like to share before I forget it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What?
&lt;/h2&gt;

&lt;p&gt;Working in Angular with many packages, sometimes VSCode doesn't "see" a new package and we've just installed and forces us to import it manually to get the IntelliSense for that module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why?
&lt;/h2&gt;

&lt;p&gt;That's because Angular CLI adds a property in the "tsconfig.app.json" under "compilerOptions" called "types". &lt;a href="https://www.typescriptlang.org/tsconfig#types"&gt;Typescript documentation&lt;/a&gt; says: "If types is specified, only packages listed will be included in the global scope."&lt;/p&gt;

&lt;p&gt;It means that since it's specified in our tsconfig.app.json it will overwrite all other typings that typescript may find in our node_modules folder.&lt;/p&gt;

&lt;h2&gt;
  
  
  How?
&lt;/h2&gt;

&lt;p&gt;Just remove it, and Typescript will get every typing in our node_modules folder.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://github.com/maximedasilva"&gt;maximedasilva&lt;/a&gt; for finding this out!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Solving Eric's storage problems</title>
      <dc:creator>Ezequiel E. Tejada</dc:creator>
      <pubDate>Fri, 14 May 2021 11:39:46 +0000</pubDate>
      <link>https://dev.to/ezequieltejada/solving-eric-s-storage-problems-3jmg</link>
      <guid>https://dev.to/ezequieltejada/solving-eric-s-storage-problems-3jmg</guid>
      <description>&lt;p&gt;Welcome back! 👋🏻 This time is not a development post, but it's something quite interesting if you need more space o a good backup strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is Eric? 🤜🏻🤛🏻
&lt;/h2&gt;

&lt;p&gt;Eric is a good friend of mine, we've worked together in Globant around 2013 and we've worked on a freelance project for an E-Commerce site. He is a gifted graphic designer, photographer, and musician.&lt;/p&gt;

&lt;p&gt;You should check his &lt;a href="https://www.linkedin.com/in/ericboldt-dg/"&gt;LinkedIn&lt;/a&gt; and &lt;a href="https://www.behance.net/ericboldt-dg"&gt;portfolio&lt;/a&gt; If you need to work with him, send an &lt;a href="//mailto:ericboldtdg@gmail.com"&gt;email&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What problem does Eric have?
&lt;/h2&gt;

&lt;p&gt;He had some trouble with his computer and replaced it with a new one. Having the old hard drives working fine, he wanted to use them as external hard drives for backup.&lt;/p&gt;

&lt;p&gt;That's not bad, not at all, one backup is great if you had none, but we all know that a single backup is no backup at all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.giphy.com/media/y9gcCOXpNX8UfZrp0X/giphy.gif"&gt;https://media.giphy.com/media/y9gcCOXpNX8UfZrp0X/giphy.gif&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What do other people do? 👀
&lt;/h2&gt;

&lt;p&gt;When you work with clients' assets you shouldn't rely only on an external hard drive. This is way too risky, and it wouldn't be nice to ask for the assets again because your hard drive failed.&lt;/p&gt;

&lt;p&gt;Some people have two+ physical hard drives or save the assets on their Gmail account but they are not very convenient or they can become very expensive, very soon.&lt;/p&gt;

&lt;p&gt;Well, you can have multiple accounts and sparse the assets between all the accounts, but it means that you should know where you've saved what, and keep logging in and out can be quite time-consuming.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.giphy.com/media/3o7TKswXkG2qVFIop2/giphy.gif"&gt;https://media.giphy.com/media/3o7TKswXkG2qVFIop2/giphy.gif&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Take 🐱‍👤
&lt;/h2&gt;

&lt;p&gt;Having the data on the cloud is definitively the best place to have the backup of his assets, but it can be hard to manage if he doesn't organize them.&lt;/p&gt;

&lt;p&gt;Also, there is a problem with having multiple desktop clients for each cloud provider. They consume a lot of resources that he is going to need to work on his computer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tool 🔧
&lt;/h2&gt;

&lt;p&gt;Here comes &lt;a href="https://rclone.org/"&gt;RClone&lt;/a&gt; They call it "The Swiss army knife of cloud storage" and I agree.&lt;/p&gt;

&lt;p&gt;It allows him to back up, restore and sync data from his computer to almost any cloud provider. Mount cloud drives as network-attached / local disks. Check files for integrity and also union file systems to present multiple disks as one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.giphy.com/media/PvDM6QHuLPCxi/giphy.gif"&gt;https://media.giphy.com/media/PvDM6QHuLPCxi/giphy.gif&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we know the tool, let's kick the tires and see how it works. 💨&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating an account in Mega
&lt;/h3&gt;

&lt;p&gt;Let's suppose he wants to enjoy having 100GB for free courtesy of &lt;a href="https://mega.io/"&gt;MEGA.io&lt;/a&gt; so he has to create two accounts. I won't get into the details, it's just filling a couple of forms and confirming emails.&lt;/p&gt;

&lt;p&gt;Once he has both accounts, we can set up RClone.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up RClone
&lt;/h3&gt;

&lt;p&gt;RClone is a binary made in go. It means that it only runs when we execute it but we wanted to have it running all the time, like a service.&lt;/p&gt;

&lt;h4&gt;
  
  
  Install and configure required applications
&lt;/h4&gt;

&lt;p&gt;To run RClone as a service we can use an application called &lt;a href="https://nssm.cc/download"&gt;NSSM (Non-Sucking Service Manager)&lt;/a&gt; I suggest downloading the "latest release" version. It says on the NSSM webpage: "No "installation" of nssm is needed. Just place it somewhere on the system (preferably somewhere in your PATH) and run it."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0gKKlGtN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620858452874/W9HEC3zsz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0gKKlGtN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620858452874/W9HEC3zsz.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, on Windows, we need to install &lt;a href="http://www.secfs.net/winfsp/"&gt;winfsp&lt;/a&gt; to make possible mounting remote folders.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configure each remote
&lt;/h4&gt;

&lt;p&gt;The last step to run RClone is to configure the remote (each mega account)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VhB4SiRv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620859252535/hppDsRPj3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VhB4SiRv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620859252535/hppDsRPj3.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To do that, we open a command prompt and run "rclone config" and go through these steps for each remote:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;"New remote" - Type name of the remote (this is not the provider's name, but a unique name to identify this remote drive)&lt;/li&gt;
&lt;li&gt;Select the provider (here we can use Box, Dropbox, OneDrive, GDrive, but in this case, we are going to use Mega)&lt;/li&gt;
&lt;li&gt;Type the username (mega's account username)&lt;/li&gt;
&lt;li&gt;Type the password (and confirm)&lt;/li&gt;
&lt;li&gt;"Edit advance config?" (no, at this time)&lt;/li&gt;
&lt;li&gt;Confirm that everything is OK.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Test run mounting your cloud drive
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4OXH3F11--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620978758262/PDfVVvuXB.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4OXH3F11--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620978758262/PDfVVvuXB.png" alt="imagen.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's verify that we can access our Cloud Drive. Open a command prompt and type "rclone mount --vfs-cache-mode full {remote name1)}:/ {drive letter}: {remote name2)}:/ {drive letter}:"&lt;/p&gt;

&lt;p&gt;Where "remote name" is what you typed in step 2 of the last section and "drive letter" it's the letter that you want to use to access it in My Computer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5RUmmkt---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620978699004/NMOY9Lvy0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5RUmmkt---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620978699004/NMOY9Lvy0.png" alt="imagen.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A couple of issues we may face in this step. ⚠&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I get this error: "&lt;em&gt;2021/05/14 09:48:32 Fatal error: failed to mount FUSE fs: mount stopped before calling Init: mount failed: cgofuse: cannot find winfsp&lt;/em&gt;" - This means we haven't installed WinFsp, which is needed for mounting in Windows.&lt;/li&gt;
&lt;li&gt;I run the command, it says: "&lt;em&gt;The service rclone has been started.&lt;/em&gt;" but I don't see the drive in My Computer. - This happens when the command is executed on a command prompt with elevated privileges (as Administrator). The command must be executed by the user who wants to have the drive mounted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Well, we've installed the programs, configured the remote, and tested that it mounts as a drive on our computer. Right now it will be available while the RClone command is running and as soon as we close it, the drive will disappear from our computer. To make these changes permanent, we must run RClone as a service, and here is where NSSM comes in.&lt;/p&gt;

&lt;h4&gt;
  
  
  Creating a service for RClone
&lt;/h4&gt;

&lt;p&gt;To create a service for RClone we ran the command "nssm install {service name}"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G3b07qyg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620858782032/vyqeQw-jv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G3b07qyg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620858782032/vyqeQw-jv.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where "service name" is how we want to identify the service that will be running RClone for us.&lt;/p&gt;

&lt;p&gt;In the application tab, we must fill in the path of our RClone binary location. Here is a tip, if you don't know where it is, you can find it by typing RClone in your start menu and then look for "Open file location", and then the arguments which are everything we typed after the "rclone" command on our testing. "mount --vfs-cache-mode full {remote name)}:/ {drive letter}:" (Refer to "Test run mounting your cloud drive" to know what "remote name" and "drive letter" mean)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H_zXWOZJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620982030563/qCPiv9bFT.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H_zXWOZJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620982030563/qCPiv9bFT.png" alt="imagen.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we go to the "Log on" tab, select "This account..." and fill in your Windows user credentials. Another tip, if you don't know your FULL windows username you can open a command prompt and execute "whoami" and it will get you the information you need in NSSM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6vfhn5zy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620982101757/jsGCFpxfA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6vfhn5zy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620982101757/jsGCFpxfA.png" alt="imagen.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we must go to the "Shutdown" tab and change the timeout for "Generate Control-C" to 10 seconds, it is needed because if RClone is syncing data, it won't exit immediately and if we force it, it may corrupt data. And then click "Install service"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KQgxzE8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620982142886/i35Vg_RzK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KQgxzE8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620982142886/i35Vg_RzK.png" alt="imagen.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mexoac_a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620859195856/SQSVJ85aK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mexoac_a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620859195856/SQSVJ85aK.png" alt="image.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have two options, the first one is to reboot the computer to have the RClone service started or we can initialize the service manually on Task Manager. To do that, we must open Task manager and look for the "Service" tab, there we should find our fresh service waiting to be started. Right-click it, and then click "Start".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OTEYTBsv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620983174004/LvdrFEuzN.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OTEYTBsv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620983174004/LvdrFEuzN.png" alt="imagen.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And now we have our Cloud Drive ready to go. 🚀&lt;/p&gt;

&lt;h3&gt;
  
  
  "CLI sucks!" 💩
&lt;/h3&gt;

&lt;p&gt;Well, first of all... you suck! But I understand, sometimes having a graphical interface saves quite some time searching for the correct argument.&lt;/p&gt;

&lt;p&gt;That's why RClone has a great Web GUI where you can tweak everything related to any remote. Just as an example, let's set a bandwidth limit because, as it is configured, it will use every single drop of the bandwidth you have (not cool)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mw2lNt7n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620984318823/4WVvGiNai.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mw2lNt7n--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620984318823/4WVvGiNai.png" alt="imagen.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To open the Web Gui, we have to open a command prompt and execute this command: "rclone rcd --rc-web-gui" It will open a browser window with a dashboard where we can tweak RClone.&lt;/p&gt;

&lt;p&gt;On your right, you will find "Bandwidth". Click on "Modify". As an example, I set 300Kb for uploads and 1Mb for downloads.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MPDgB5oi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620984561575/UkEbuC_0F.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MPDgB5oi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620984561575/UkEbuC_0F.png" alt="imagen.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But there are many options you can change, add remotes, setting mounts. I highly recommend going through the &lt;a href="https://rclone.org/docs/"&gt;RClone documentation&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Careful syncing data ⚠
&lt;/h3&gt;

&lt;p&gt;Syncing data takes time, much more time than copying files to a hard drive or USB Stick. When you copy a file to a Cloud Drive, it will look like the copy is finished, but actually, RClone is syncing in the background.&lt;/p&gt;

&lt;p&gt;This is meant to be used as a backup drive. If you need to upload a file immediately, you can use RClone web GUI or each service website.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's all folks 👋🏻
&lt;/h2&gt;

&lt;p&gt;If you've got here, Thanks!!! It's quite a load of information to process, so I appreciate you've read it. If you've found this information useful or interesting, please let me know in the comments, thoughts, and if this topic gets enough attention I'll write about how you can backup USB drives automagically or how to "kind of" fusion your cloud drives for "unlimited" storage. 🤯&lt;/p&gt;

&lt;p&gt;Feel free to reach me at &lt;a href="https://www.linkedin.com/in/ezequieltejada/"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/ezeteja"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>storage</category>
      <category>cloud</category>
      <category>story</category>
      <category>operatingsystem</category>
    </item>
    <item>
      <title>Custom 2FA With Firebase Cloud Functions</title>
      <dc:creator>Ezequiel E. Tejada</dc:creator>
      <pubDate>Wed, 12 May 2021 11:39:14 +0000</pubDate>
      <link>https://dev.to/ezequieltejada/custom-2fa-with-firebase-cloud-functions-4dj6</link>
      <guid>https://dev.to/ezequieltejada/custom-2fa-with-firebase-cloud-functions-4dj6</guid>
      <description>&lt;p&gt;This is my first (of many, I hope) post here, and ever. And I'll try to show all the iterations and learnings from each of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The requirement
&lt;/h2&gt;

&lt;p&gt;Our client required an email and password login and then depending on the role, it requires the user to type the missing numbers of a static Pincode. Then if the user/password/Pincode matched, the application takes the user to the dashboard.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The blank spaces are random, so it should request a different set of 3 places each time&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  First implementation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p5bRm6Sp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620811562892/VbjuuP-vd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p5bRm6Sp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn.hashnode.com/res/hashnode/image/upload/v1620811562892/VbjuuP-vd.png" alt="imagen.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the beginning, it was a 2 step &lt;a href="https://material.angular.io/components/stepper/overview"&gt;mat-stepper&lt;/a&gt; where in the first step it asks for the email and password and the second step (required for the roles that have pincodes) where it asks for the Pincode.&lt;/p&gt;

&lt;p&gt;Once we get the email and password from the form, we would then log the user in and if the user requires a Pincode we would send them to the Pincode screen. The user types its Pincode and we would compare them with what we have in Firestore. If it matches we would let the user go to the authorized part of the app, if not, the user will be logged out.&lt;/p&gt;

&lt;h3&gt;
  
  
  The problem with this approach
&lt;/h3&gt;

&lt;p&gt;To get the Pincode from Firestore the user must be logged in, which defeats the purpose of the Pincode.&lt;/p&gt;

&lt;p&gt;A contractor made a pentest on the app, and one of the vulnerabilities they've found was that the user was able to change the URL to the authorized section and bypass the Pincode part of the process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.giphy.com/media/11fot0YzpQMA0g/giphy.gif"&gt;https://media.giphy.com/media/11fot0YzpQMA0g/giphy.gif&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Yeap, that happened&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Second implementation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.giphy.com/media/1URYTNvDM2LJoMIdxE/giphy.gif"&gt;https://media.giphy.com/media/1URYTNvDM2LJoMIdxE/giphy.gif&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This needed fixing, so we decided to lean on Firebase Cloud Functions to be our gatekeeper.&lt;/p&gt;

&lt;p&gt;We take the email and password from the login form, and then send them to the cloud function. There it checks for the credentials, if the user requires a Pincode, it returns the positions needed for the second step along with a token that identifies that attempt (The same user can have multiple attempts with differents positions requested by the cloud function), if not, it returns a custom token that will be used to authenticate the user in the frontend.&lt;/p&gt;

&lt;p&gt;When the user requires a Pincode and has the positions requested by the cloud function, it types the numbers and sends the values (not the positions), the token, email, and password to be all checked on the cloud function. In the Cloud Function, we check the credentials, get the positions based on the token provided, and validate the user's values with the stored Pincode. If everything matches, a custom token is returned to authenticate the user on the frontend, else, the cloud function sends a generic error message to let the user know that should try again.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can be improved
&lt;/h2&gt;

&lt;p&gt;As in every project, we do what we can with the time we have. It means that some improvements that could be made, weren't.&lt;/p&gt;

&lt;h3&gt;
  
  
  Static Pincode
&lt;/h3&gt;

&lt;p&gt;At the time of writing this post, the Pincode is static, which means that it is stored in the database as plain text. It should be dynamic, a code that changes over time, to avoid someone that finds out a Pincode to use it anytime.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hash Pincode and Custom Claims
&lt;/h3&gt;

&lt;p&gt;The Pincode should be stored as a hash instead of plain text since it's very sensitive data. If we decide to use a one-way hash, we could also store the hashed Pincode as a &lt;a href="https://firebase.google.com/docs/auth/admin/custom-claims"&gt;Custom Claim&lt;/a&gt; property in the user's account. That would avoid a call to the server and speed up the Pincode checking process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Simplifing the login process
&lt;/h3&gt;

&lt;p&gt;Regarding logging in in two steps, I think they could be merged on a single step that requests email and password, but when the user finishes typing their email (blur the input) triggers a call to a cloud function that checks if that email belongs to a user that requires a Pincode if it does the Pincode input could be shown below the password input.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get the authentication process from the Pros
&lt;/h3&gt;

&lt;p&gt;Firebase offers a quite robust solution regarding Authentication, however, when there are special needs, it's a better idea to rely on a third-party solution than build it on our own.&lt;/p&gt;

&lt;p&gt;Firebase offers the possibility to integrate a third-party solution quite seamlessly ( &lt;a href="https://firebase.google.com/docs/auth/web/custom-auth"&gt;docs&lt;/a&gt; ) Actually, we use this to integrate our Pincode solution but it would be better if we've used it to integrate another solution like the one provided by &lt;a href="https://auth0.com/authentication"&gt;Auth0&lt;/a&gt;, &lt;a href="https://www.twilio.com/solutions/account-security"&gt;Twilio (Authy)&lt;/a&gt; , or &lt;a href="https://www.okta.com/products/authentication/"&gt;Okta&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Well, this is it. My take on this problem and how we've solved it. I hope you can take anything useful from our experience and I'd like to know your thoughts about this.&lt;/p&gt;

&lt;p&gt;Feel free to reach me at &lt;a href="https://www.linkedin.com/in/ezequieltejada/"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://twitter.com/ezeteja"&gt;Twitter&lt;/a&gt; .&lt;/p&gt;

</description>
      <category>firebase</category>
      <category>angular</category>
      <category>node</category>
      <category>googlecloud</category>
    </item>
  </channel>
</rss>
