<?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: Alexander</title>
    <description>The latest articles on DEV Community by Alexander (@alex_tokyo).</description>
    <link>https://dev.to/alex_tokyo</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%2F37470%2Fc566c8f8-a3a0-4fec-8a29-0d6519b5dc3a.JPG</url>
      <title>DEV Community: Alexander</title>
      <link>https://dev.to/alex_tokyo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alex_tokyo"/>
    <language>en</language>
    <item>
      <title>Generating 2FA One-Time Passwords in JS Using Web Crypto API</title>
      <dc:creator>Alexander</dc:creator>
      <pubDate>Wed, 07 Aug 2019 15:35:40 +0000</pubDate>
      <link>https://dev.to/alex_tokyo/generating-2fa-one-time-passwords-in-js-using-web-crypto-api-1hfo</link>
      <guid>https://dev.to/alex_tokyo/generating-2fa-one-time-passwords-in-js-using-web-crypto-api-1hfo</guid>
      <description>&lt;h6&gt;
  
  
  Photo by &lt;a href="https://unsplash.com/@bergerteam" rel="noopener noreferrer"&gt;Florian Berger&lt;/a&gt;
&lt;/h6&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Today 2FA is everywhere. It has made stealing accounts a bit harder than just obtaining a correct password. And while by no means it makes your online assets hackproof, it requires more sophisticated and multi-leveled attacks. As with anything in this world, the more complex something is — the more likely it is to fail.&lt;/p&gt;

&lt;p&gt;I am pretty sure that everyone who is reading this has used OTP-based 2FA in their lifetime. Today, I am inviting you to look under the hood of this simple yet such widespread technique that is guarding countless accounts today.&lt;/p&gt;

&lt;p&gt;But before we dive in — here's the &lt;a href="https://khovansky.me/demos/web-otp" rel="noopener noreferrer"&gt;demo&lt;/a&gt; of what we will be building today.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basics
&lt;/h2&gt;

&lt;p&gt;When talking about OTPs first thing that should be mentioned is that there are two types of them. &lt;strong&gt;HOTP&lt;/strong&gt; and &lt;strong&gt;TOTP&lt;/strong&gt;. Namely, &lt;strong&gt;HMAC-based One Time Password&lt;/strong&gt; and &lt;strong&gt;Time-based OTP&lt;/strong&gt;. TOTP is not something completely different but an enhancement over HOTP, so let's first talk about the basic form.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HOTP&lt;/strong&gt; algorithm is described by &lt;a href="https://tools.ietf.org/html/rfc4226" rel="noopener noreferrer"&gt;RFC4226&lt;/a&gt;. It's a small, 35 pages long spec, that contains everything from formal description to implementation example and test cases. Let's look at some of it's core concepts.&lt;/p&gt;

&lt;p&gt;First of all, what does &lt;strong&gt;HMAC&lt;/strong&gt;-based mean? HMAC stands for &lt;strong&gt;Hash-based Message Authentication Code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MAC&lt;/strong&gt; is a way of proving that a message came from the expected sender and not someone else. &lt;strong&gt;MAC algorithm&lt;/strong&gt; produces a &lt;strong&gt;MAC tag&lt;/strong&gt; using a secret key that is only known to the sender and the receiver. So when you receive a message, you can recalculate MAC tag yourself and if it matches to the one that was sent along — then you can be sure the message came from the expected sender and not one of those balaclava-wearing hackers, duh. As a bonus this also verifies data integrity, as in whether the data was damaged along the way. You cannot really tell one event from another but it's safe to consider the data corrupted in both cases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fje8t165ars4io9awph20.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fje8t165ars4io9awph20.PNG" alt="Mac tag"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have included some graphics similar to this one in this article. It might be silly but hopefully will help illustrate some things and make this wall of text less dull. Maybe they are &lt;em&gt;too&lt;/em&gt; silly though...&lt;/p&gt;

&lt;p&gt;Now, what is a hash? A hash is a product of running a message through a &lt;strong&gt;Hash Function&lt;/strong&gt;. Hash functions take your data and make other fixed-length data out of it. For example, there is a well-known &lt;strong&gt;MD5&lt;/strong&gt; hash function. It was widely used to verify that the data you downloaded is not damaged. Basically, you feed it some data and get a string that looks like &lt;code&gt;CCB4DB2088210…&lt;/code&gt; at the output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MAC&lt;/strong&gt; itself is not a specific algorithm, but rather a term that refers to one. &lt;strong&gt;HMAC&lt;/strong&gt;, in turn, IS a specific implementation. Or, to be more precise — HMAC-&lt;em&gt;X&lt;/em&gt;, where &lt;em&gt;X&lt;/em&gt; is one of the crypthographic hash functions. Now, HMAC takes two parameters — a secret key and your message, mixes them together in a special way, applies a hash function of your choice twice and produces a MAC tag.&lt;/p&gt;

&lt;p&gt;This article is not about cryptography though and you are probably wondering — how the hell is all of this related to one-time passwords? Don't worry — we are almost there.&lt;/p&gt;

&lt;p&gt;According to the specification, HOTP is calculated based on 2 values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;K&lt;/strong&gt; — a &lt;strong&gt;secret key&lt;/strong&gt; shared between client and server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt; — a &lt;strong&gt;counter&lt;/strong&gt; or a moving factor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Secret key is a value that must be over 128 bits long, preferrably 160. It is generated when you initially setup your 2FA.&lt;/p&gt;

&lt;p&gt;Counter is an 8-byte value that is synchronized between the server and client. It will be constantly updated as you generate passwords. In HOTP, client counter is incremented each time a new password is generated and server counter — each time a password is validated. Since we can generate passwords without actually using them, server allows counter values to be a bit ahead of what the current one is but only within a certain window. If you played with your OTP token too much and it was an &lt;code&gt;HOTP&lt;/code&gt; algorithm — you'll have to resync your token with server.&lt;/p&gt;

&lt;p&gt;Alright. As you have probably noticed, there are two input arguments here, just like in the HMAC itself. RFC4226 defines HOTP like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

HOTP(K,C) = Truncate(HMAC-SHA-1(K,C))


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

&lt;/div&gt;

&lt;p&gt;So, K is predictably used as our secred key and &lt;strong&gt;C&lt;/strong&gt;ounter is used as the message. After HMAC generates the MAC — a mysterious &lt;code&gt;Truncate&lt;/code&gt; function is used to extract a familiar numeric one-time password you see in your authenticator app.&lt;/p&gt;

&lt;p&gt;Let's start generating and learn the rest along the way as we code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation plan
&lt;/h2&gt;

&lt;p&gt;We will need the following steps to get our hands on those OTPs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F5q7gaqkfytoevuwugwbw.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F5q7gaqkfytoevuwugwbw.PNG" alt="Implementation plan"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate HMAC-SHA1 value from our &lt;strong&gt;K&lt;/strong&gt; and &lt;strong&gt;C&lt;/strong&gt; parameters. This will be a 20-byte string&lt;/li&gt;
&lt;li&gt;Extract 4 bytes from that string in a specific way&lt;/li&gt;
&lt;li&gt;Convert those bytes into a number, divide that number by 10^n, where n = number of digits in the OTP and take the remainder. Usually n=6.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Doesn't seem too complicated, right? We'll start with generating the HMAC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating HMAC-SHA1
&lt;/h2&gt;

&lt;p&gt;This is probably the most straightforward part of our plan. We are not going to roll our own crypto, of course. Never roll your own crypto. We are going to use &lt;a href="https://www.w3.org/TR/WebCryptoAPI/" rel="noopener noreferrer"&gt;&lt;strong&gt;Web Crypto API&lt;/strong&gt;&lt;/a&gt;. Now, one thing to mention here is that by specification it is only exposed in a Secure Context. What this means is that you will be unable to tinker with it unless your scripts are running on an HTTPS website. And I doubt your localhost dev server is configured this way. Mine certainly isn't! You can read more history on why it became this way (as well as countless disappointed devs voices) &lt;a href="https://github.com/w3c/webcrypto/issues/28" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Luckily, in Firefox you CAN use Webcrypto in any context and don't have to reinvent the wheel or import any third-party libraries to do that. So, for the purpose of this article we are going to use FF.&lt;/p&gt;

&lt;p&gt;Crypto API itself resides under &lt;code&gt;window.crypto.subtle&lt;/code&gt;. If you are wondering what is so subtle about it — let me cite the spec here:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is named &lt;code&gt;SubtleCrypto&lt;/code&gt; to reflect the fact that many of these algorithms have subtle usage requirements in order to provide the required algorithmic security guarantees&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's quickly run through the Crypto API methods we will be using and set everything up. &lt;strong&gt;NB&lt;/strong&gt;: all the methods mentioned here are async and return promises.&lt;/p&gt;

&lt;p&gt;First of all, we would need the &lt;code&gt;importKey&lt;/code&gt; method, since we are bringing our own key instead of generating one in browser. It takes 5 arguments:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nf"&gt;importKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;format&lt;/span&gt;
    &lt;span class="nx"&gt;keyData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;algorithm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;extractable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;usages&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In our case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;format&lt;/code&gt; will be &lt;code&gt;'raw'&lt;/code&gt;, meaning that we will supply the key as raw bytes in an &lt;code&gt;ArrayBuffer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;keyData&lt;/code&gt; is the ArrayBuffer mentioned above. We'll talk about generating it in a bit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;algorithm&lt;/code&gt; will be &lt;code&gt;HMAC-SHA1&lt;/code&gt; as per OTP spec. This has to be an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HmacImportParams" rel="noopener noreferrer"&gt;HmacImportParams&lt;/a&gt; object&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;extractable&lt;/code&gt; can be false, since we don't plan to export the key&lt;/li&gt;
&lt;li&gt;And finally, of all possible &lt;code&gt;usages&lt;/code&gt; we will only need &lt;code&gt;'sign'&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Our secret key will be a long random string. In reality it could be a sequence of bytes that are not necessarily printable but for the sake of convenience in this article let's just go with a string. To convert it to an &lt;code&gt;ArrayBuffer&lt;/code&gt; we will use &lt;code&gt;TextEncoder&lt;/code&gt;. With it this process takes just two lines of code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secretBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Now, let's compile everything together:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Crypto&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subtle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TextEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secretBytes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;importKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;raw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;secretBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HMAC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SHA-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sign&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Great! We have our crypto instance on standby. Now let's deal with the counter and finally sign the message.&lt;/p&gt;

&lt;p&gt;Our counter, according to the spec, should be 8 bytes and will also come in an &lt;code&gt;ArrayBuffer&lt;/code&gt; form. To convert it into this form we will first use a trick that is usually used to pad numbers with leading zeroes in JS and then put each individual byte into the &lt;code&gt;ArrayBuffer&lt;/code&gt; using a &lt;code&gt;DataView&lt;/code&gt;. Please note that according to spec all binary data is treated as &lt;strong&gt;big endian&lt;/strong&gt; (most significant bit first).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;padCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DataView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;byteString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 8 bytes&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;byteString&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;byteValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bCounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;bView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUint8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;byte&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;byteValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F5s74etjvd3wzjmcv2180.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F5s74etjvd3wzjmcv2180.PNG" alt="Pad counter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that in place — we are ready to sign! To do that we will just need to use &lt;code&gt;sign&lt;/code&gt; function of &lt;code&gt;SubtleCrypto&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counterArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;padCounter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HMAC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;counterArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Bam! First stage complete. We have our &lt;code&gt;HS&lt;/code&gt; value calculated. While this is a cryptic variable name, this is how this value is called in spec, so I decided to leave it be. It will be easier to map steps from spec to our code this way. What's next?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Step 2: Generate a 4-byte string (Dynamic Truncation)&lt;br&gt;
Let Sbits = DT(HS)   //  DT, defined below,&lt;br&gt;
                     //  returns a 31-bit string&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;DT stands for Dynamic Truncation. Here's how it works:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// First we take the last byte of our generated HS and extract last 4 bits out of it.&lt;/span&gt;
  &lt;span class="c1"&gt;// This will be our _offset_, a number between 0 and 15.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;HS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mb"&gt;0b1111&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Next we take 4 bytes out of the HS, starting at the offset&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;P&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;HS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x7f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;HS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;HS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="c1"&gt;// Finally, convert it into a binary string representation&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;P&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;pString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fqflqwf0t0udwp9nie0tf.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fqflqwf0t0udwp9nie0tf.PNG" alt="Truncation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note how we apply bitwise AND to the first byte of HS. &lt;code&gt;0x7f&lt;/code&gt; in binary is &lt;code&gt;0b01111111&lt;/code&gt; , so we are just dropping the first bit here. In JS it just implements truncation to the spec-defined 31-bit, but in other platforms it would also ensure that the first bit, which is also the sign bit, is masked off to avoid confusion between signed/unsigned numbers.&lt;/p&gt;

&lt;p&gt;Alright, we are almost there! Now we only need to convert what we got from DT to an integer and off we go to stage 3.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Sbits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Snum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Sbits&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Snum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Stage 3 is really small. All we need to do now is to divide our resulting number by &lt;code&gt;10 ** (number of digits in OTP)&lt;/code&gt; and take the remainder of that division. This way we basically cut last N digits from the resulting number. The spec mentions that you must extract at least 6 digits and possibly 7 or 8. Theoretically since it's a 31-bit integer you can extract up to 9 digits, but in reality I've never seen anything over 6. Have you?&lt;/p&gt;

&lt;p&gt;Code for the final function that encompasses all functions we created above will then look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;generateHOTP&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;generateKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Snum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Make sure we keep leading zeroes&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;padded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;000000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Snum&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;padded&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Hooray! Now, how do we verify that what we just coded is, in fact, correct?&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;To test our implementation we will use examples provided in the RFC. Appendix D provides reference values for the secret string&lt;code&gt;"12345678901234567890"&lt;/code&gt; and counter values from 0 to 9. It also provides us with calculated HMACs and intermediate truncated values. Very useful for debugging all the steps of this algorithm. Here's the sample of that table with only counter and HOTP values:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

   Count    HOTP
   0        755224
   1        287082
   2        359152
   3        969429
   ...


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

&lt;/div&gt;

&lt;p&gt;If you have not yet checked the &lt;a href="https://khovansky.me/demos/web-otp" rel="noopener noreferrer"&gt;demo&lt;/a&gt; page, now is the time. Go ahead and try some of the RFC values over there. Make sure to come back though as we are about to move on to TOTPs!&lt;/p&gt;

&lt;h2&gt;
  
  
  TOTP
&lt;/h2&gt;

&lt;p&gt;Finally, we've made it to the more modern form of 2FA — TOTP. When you open your favorite authenticator app and see a small clock ticking backwards, counting seconds until your code expires — that's TOTP. So what's the difference?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time-based&lt;/strong&gt; means that instead of a static counter, current time is used as a moving factor. Or, to be precise, current &lt;em&gt;time step&lt;/em&gt;. To calculate this &lt;em&gt;time step&lt;/em&gt; we take current unix epoch time (number of milliseconds since 00:00:00 UTC on 1 January 1970) and divide it by a &lt;em&gt;time window&lt;/em&gt; (usually 30 seconds). Server usually allows for a bit of time drift to account for imperfections in time sync — about 1 step forwards and backwards depending on the configuration.&lt;/p&gt;

&lt;p&gt;As you can see, this is clearly more secure than plain &lt;code&gt;HOTP&lt;/code&gt;. In time-based case every 30 seconds a valid OTP changes even if it was not used. In the original algorithm valid password is defined by whatever counter value is currently stored on the server + whatever window there is for ahead of counter passwords. If you don't authenticate, that OTP stays valid for indefinite amount of time. More on TOTPs can be found in &lt;a href="https://tools.ietf.org/html/rfc6238" rel="noopener noreferrer"&gt;RFC6238&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Due to time-based scheme being an extension over original algorithm, no changes to the original implementation are required. We will use &lt;code&gt;requestAnimationFrame&lt;/code&gt; and check on every tick if we are still inside the time window. If we are not — we will calculate a new time step (counter) and regenerate HOTP with it. Omitting all the administrative code it will look roughly like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stepWindow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 30 seconds in ms&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastTimeStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateTOTPCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeSinceStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;lastTimeStep&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;stepWindow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;timeLeft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stepWindow&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;timeSinceStep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeLeft&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateTOTPCounter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;timeStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTOTPCounter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;lastTimeStep&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timeStep&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;regenerate&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateTOTPCounter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Finishing touch — QR support
&lt;/h2&gt;

&lt;p&gt;Usually when we setup 2FA we do so by scanning a setup QR code that contains all the required data: secret, selected OTP algorithm, account name, issuer name, number of digits.&lt;/p&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/al_khovansky/intro-to-screen-capture-api-scanning-qr-codes-bgi"&gt;previous article&lt;/a&gt; I talked about how we can scan QR codes right from the screen using &lt;code&gt;getDisplayMedia&lt;/code&gt; API. I ended up creating a small npm library that we can now use to easily add QR code reading support into our demo. The library in question is called &lt;a href="https://github.com/khovansky-al/stream-display" rel="noopener noreferrer"&gt;stream-display&lt;/a&gt; and it will be accompanied by an amazing &lt;a href="https://github.com/cozmo/jsQR" rel="noopener noreferrer"&gt;jsQR&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;URL encoded in the QR code for 2FA should be in the following format:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

otpauth://TYPE/LABEL?PARAMETERS


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

&lt;/div&gt;

&lt;p&gt;So, for example:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

otpauth://totp/label?secret=oyu55d4q5kllrwhy4euqh3ouw7hebnhm5qsflfcqggczoafxu75lsagt&amp;amp;algorithm=SHA1&amp;amp;digits=6&amp;amp;period=30


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

&lt;/div&gt;

&lt;p&gt;I will omit the setup code for the stream/recognition itself since it can be easily found in both libs' documentation. Instead, here's how we can parse this URL:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setupFromQR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// drop the "//" and get TYPE and LABEL&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;secret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hotp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;stepWindow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;period&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getTOTPCounter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;In a real world scenario the secret will be a base-&lt;strong&gt;32&lt;/strong&gt; (!) encoded string, because some shared secret bytes can be non-printable. But here we once again omit that for demo purposes. Unfortunately, I cannot find any information on why exactly it was decided to be base-32, or this specific format. There seems to be no actual RFC for the &lt;code&gt;otpauth&lt;/code&gt; and the format itself seems to be invented by Google. You can read a bit more about it &lt;a href="https://github.com/google/google-authenticator/wiki/Key-Uri-Format" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to generate your own 2FA QR codes for testing purposes you can use an amazing &lt;a href="https://freeotp.github.io/qrcode.html" rel="noopener noreferrer"&gt;FreeOTP&lt;/a&gt; tool. I sure used it a lot while making this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And with this — we shall be done! Once again, you can check out the &lt;a href="https://khovansky.me/demos/web-otp" rel="noopener noreferrer"&gt;demo&lt;/a&gt; to see it in action or to see the full code driving the entire process.&lt;/p&gt;

&lt;p&gt;I think we covered some important tech we use on a daily basis and hope that you have learned something new today. I spent much more time writing this article that I imagined it would take. But it is also very enjoyable to turn a paper spec into something working and something so familiar. We have more interesting things to talk about in the future, so stay tuned.&lt;/p&gt;

&lt;p&gt;Until next time!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Intro to Screen Capture API - Scanning QR codes in the browser</title>
      <dc:creator>Alexander</dc:creator>
      <pubDate>Sat, 20 Jul 2019 16:48:11 +0000</pubDate>
      <link>https://dev.to/alex_tokyo/intro-to-screen-capture-api-scanning-qr-codes-bgi</link>
      <guid>https://dev.to/alex_tokyo/intro-to-screen-capture-api-scanning-qr-codes-bgi</guid>
      <description>&lt;h6&gt;
  
  
  Cover image by &lt;a href="https://unsplash.com/@lianhao" rel="noopener noreferrer"&gt;Lianhao Qu&lt;/a&gt;
&lt;/h6&gt;

&lt;h2&gt;
  
  
  Preface
&lt;/h2&gt;

&lt;p&gt;In this small article we will talk about, you guessed it, Screen Capture API. It's hard to call it a "new" API since it's spec dates as far back as &lt;strong&gt;2014&lt;/strong&gt;. But even with browser support still lacking, it looks like a fun thing to experiment with or use in personal projects where supporting a variety of browsers is not a requirement.&lt;/p&gt;

&lt;p&gt;Here are some tldr links to get us started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/TR/screen-capture/" rel="noopener noreferrer"&gt;Full spec&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Screen_Capture_API/Using_Screen_Capture" rel="noopener noreferrer"&gt;MDN Usage Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://khovansky.me/demos/live-qr/" rel="noopener noreferrer"&gt;Demo of what we are about to implement&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And here is how the end product is supposed to work in case links stop working:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwj2hws4n0t7yau5v8fer.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwj2hws4n0t7yau5v8fer.gif" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's start building.&lt;/p&gt;

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

&lt;p&gt;Recently I’ve had an idea of a particular web app that involves using QR codes. While they are good for transmitting complex data in physical world where you can point a mobile device on them, they are not so easy to use when you have them on screen of your desktop device AND you need info encoded into them on that device. You have to save the image or make a screenshot, find a recognition service, upload your screenshot. Meh.&lt;/p&gt;

&lt;p&gt;Some vendors, like, for instance, &lt;strong&gt;1Password&lt;/strong&gt; have found a way to make use of QR codes on desktop fun, easy and kinda magical. If you are not familiar with it — they have a transparent modal window appear on screen. You drag it over your QR code and boom! You have added an account! Or something else. Here's what it looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbj95ks5q5jt47md8set.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffbj95ks5q5jt47md8set.png" alt="1password"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pretty neat. But we cannot have a browser window capturing whatever is underneath it. Or can we?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter getDisplayMedia
&lt;/h2&gt;

&lt;p&gt;Well, sort of. Here's where the Screen Capture API with it's sole member &lt;code&gt;getDisplayMedia&lt;/code&gt; comes into play. It is kind of like &lt;code&gt;getUserMedia&lt;/code&gt; but for the user's screen instead of a camera. Unfortunately, browser support for this API is much less widespread, but, according to MDN, Screen Capture API is supported by Firefox, Chrome, Edge (with non-standard location of the method) + Edge Mobile and… Opera for Android.&lt;/p&gt;

&lt;p&gt;A peculiar set of mobile user agents in this company of usual big actors indeed.&lt;/p&gt;

&lt;p&gt;Now, the API itself is dead simple. It works in the same fashion as &lt;code&gt;getUserMedia&lt;/code&gt;, but allows you to capture video feed from screen, or to be more specific — from one of the defined &lt;strong&gt;display surfaces&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;monitor&lt;/strong&gt; (entire screen)&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;window&lt;/strong&gt; or all windows of a specific application&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;browser&lt;/strong&gt; in a form of a document. In Chrome it looks like this means every individual open tab. In FF this option seems to be lacking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means that we can grab video feed from any of those and parse it however we want. Do live text recognition and modification similar to what Google Translate Camera is doing or many other cool things. I'll leave inventions part to the reader. And best part of it — unlike with many other browser APIs we are not completely caged inside of the browser (not that I am advocating giving browsers such powers, no).&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring it up
&lt;/h2&gt;

&lt;p&gt;So, we got power of realtime screen capture in our hands. How do we harness it?&lt;/p&gt;

&lt;p&gt;We will use &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; with some JS glue. On a high level the process looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feed stream into &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;With a set refresh rate draw frame from &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; into a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Grab &lt;code&gt;ImageData&lt;/code&gt; from &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; using &lt;code&gt;getImageData&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It might sound a bit weird but to my knowledge it's a quite popular method that is also commonly used for grabbing feed from camera with our other friend &lt;code&gt;getUserMedia&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Omitting all the setup code for starting the stream and grabbing a frame — the meaningful part looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;video&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displayMediaOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;video&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;never&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;audio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaDevices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDisplayMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;displayMediaOptions&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoTrack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getVideoTracks&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;videoTrack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSettings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getImageData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;As described before — here we create our &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; and obtain a &lt;code&gt;CanvasRenderingContext2D&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Then, we define &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints" rel="noopener noreferrer"&gt;constraints&lt;/a&gt; for our capture requests. Not a lot of them. We don't want a cursor and we don't need audio. Although at the moment of writing this article nobody supports audio capture in Screen Capture.&lt;/p&gt;

&lt;p&gt;After that, we hook the resulting &lt;code&gt;MediaStream&lt;/code&gt; to our &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;. Be aware that &lt;code&gt;getDisplayMedia&lt;/code&gt; returns a Promise, hence await in the sample code.&lt;/p&gt;

&lt;p&gt;Finally, we get actual video feed dimensions from the video track draw the frame to canvas and extract it back as ImageData.&lt;/p&gt;

&lt;p&gt;Now, in a real world scenario you would probably want to process frames in a loop rather than once, waiting for specific data to appear in the frame or continuously operate on some data. And this has a few caveats.&lt;/p&gt;

&lt;p&gt;When somebody mentions "processing something in a continuous loop in background" first thing that comes to mind is likely the &lt;code&gt;requestAnimationFrame&lt;/code&gt;. And in this case it is, unfortunately, not the right choice. See, browsers tend to pause your rAF loop as soon as the tab enters background, and this is where all the work will be happening.&lt;/p&gt;

&lt;p&gt;So, instead of the rAF we will be using the good old &lt;code&gt;setInterval&lt;/code&gt;. Although still there is a gotcha. A &lt;code&gt;setInterval&lt;/code&gt; loop in a background can not run more often than &lt;em&gt;once per 1000ms&lt;/em&gt;. But, I guess that is good enough for most purposes.&lt;/p&gt;

&lt;p&gt;As you have now probably guessed — at this point the frames can be sent to any processing pipeline. In our case —  to &lt;a href="https://github.com/cozmo/jsQR" rel="noopener noreferrer"&gt;jsQR&lt;/a&gt;. It is super simple to use: you just provide the &lt;code&gt;ImageData&lt;/code&gt;, width and height and if there is a QR code in the image — you get back a JS object with recognition data. So you can augment previous example with a simple&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;jsQR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;imageData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;streamWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;streamHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;and it's done!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap it up
&lt;/h2&gt;

&lt;p&gt;I thought it might be neat to wrap it into an npm module to save the hassle of setting everything up yourself. Right now it is quite simple — it sends data to a callback provided by you in a loop and takes only one additional option — interval between captures. I'll see if there is a point in expanding the functionality.&lt;/p&gt;

&lt;p&gt;The package is called &lt;code&gt;stream-display&lt;/code&gt;: &lt;a href="https://www.npmjs.com/package/stream-display" rel="noopener noreferrer"&gt;NPM&lt;/a&gt; | &lt;a href="https://github.com/khovansky-al/stream-display" rel="noopener noreferrer"&gt;Github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The core module does not have any parsers included, so bring your own. Using this library all the code you have to write to get it up and running comes down to this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;imageData&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt; &lt;span class="c1"&gt;// do whatever with those images&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;capture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StreamDisplay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// specify where the ImageData will go&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startCapture&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// when ready&lt;/span&gt;
&lt;span class="nx"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopCapture&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// when done&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;To showcase the the idea behind this article I created &lt;a href="https://khovansky.me/demos/live-qr/" rel="noopener noreferrer"&gt;this little demo&lt;/a&gt;. Also available in a &lt;a href="https://codepen.io/alx-khovansky/pen/OeYmRG" rel="noopener noreferrer"&gt;CodePen&lt;/a&gt; format for quick experiments. It uses the aforementioned module.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on testing
&lt;/h2&gt;

&lt;p&gt;Making a library out of this code forced me to think about how one would approach testing code that relies on this API.&lt;/p&gt;

&lt;p&gt;I wanted to avoid having to download 50 MB of headless Chrome just to run a few small tests and ended up using &lt;code&gt;tape&lt;/code&gt; and mocking everything manually. It might seem tedious at first but in the end you really only need to mock the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;code&gt;document&lt;/code&gt; and DOM elements. I used &lt;a href="https://github.com/jsdom/jsdom" rel="noopener noreferrer"&gt;jsdom&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Some methods that are not implemented in jsdom — &lt;code&gt;HTMLMediaElement#play&lt;/code&gt;, &lt;code&gt;HTMLCanvasElement#getContext&lt;/code&gt; and &lt;code&gt;navigator.mediaDevices#getDisplayMedia&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Time-space continuum. I used &lt;a href="https://github.com/sinonjs/sinon" rel="noopener noreferrer"&gt;sinon&lt;/a&gt;'s &lt;code&gt;useFakeTimers&lt;/code&gt; which calls &lt;code&gt;lolex&lt;/code&gt; under the hood. It comes with replacements for &lt;code&gt;setInterval&lt;/code&gt;, &lt;code&gt;requestAnimationFrame&lt;/code&gt; and all the other time-based things that can be precisely controlled with a magical time remote. Skip milliseconds, skip to next timer, skip to next tick, you name it. One word of warning though: if you enable custom timers before jsdom — the universe will freeze due to jsdom trying to initialize some things based on time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also used sinon for all the fake methods that needed tracking. Other methods used plain JS functions. Of course, you can use whatever tools you are already most comfortable with. The end result can be seen in the library's git repo. It might be not pretty but it seems to be working and should give you an idea.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It's not as elegant as a desktop solution pictured in the beginning of this article, but I am sure the web will get there, eventually. Let's just hope that when time comes and browsers can literally see through their windows — it will be properly secured and you will be fully in control of such functionality. But for now keep in mind that whenever you are sharing your screen through Screen Share API someone can be parsing whatever it is on it, so don't share more than you are comfortable with and keep your password managers away.&lt;/p&gt;

&lt;p&gt;Anyway, I hope you learned a new trick today. If you have any ideas how else this can be applied — please share. Until next time!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webrtc</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
