<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Totp on IAMDevBox</title><link>https://www.iamdevbox.com/tags/totp/</link><description>Recent content in Totp on IAMDevBox</description><image><title>IAMDevBox</title><url>https://www.iamdevbox.com/IAMDevBox.com.jpg</url><link>https://www.iamdevbox.com/IAMDevBox.com.jpg</link></image><generator>Hugo -- 0.146.0</generator><language>en-us</language><lastBuildDate>Mon, 13 Jul 2026 16:38:05 +0000</lastBuildDate><atom:link href="https://www.iamdevbox.com/tags/totp/index.xml" rel="self" type="application/rss+xml"/><item><title>Building Multi-Factor Authentication with TOTP and WebAuthn</title><link>https://www.iamdevbox.com/posts/building-multi-factor-authentication-with-totp-and-webauthn/</link><pubDate>Mon, 13 Jul 2026 16:38:00 +0000</pubDate><guid>https://www.iamdevbox.com/posts/building-multi-factor-authentication-with-totp-and-webauthn/</guid><description>Learn how to build robust Multi-Factor Authentication using TOTP and WebAuthn. This guide includes code examples and security best practices.</description><content:encoded><![CDATA[<p>Multi-Factor Authentication (MFA) is a method of verifying a user&rsquo;s identity by requiring more than one form of evidence, such as something they know, something they have, and something they are. In this guide, we&rsquo;ll dive into implementing two popular MFA methods: Time-Based One-Time Passwords (TOTP) and Web Authentication (WebAuthn).</p>
<h2 id="what-is-time-based-one-time-password-totp">What is Time-Based One-Time Password (TOTP)?</h2>
<p>Time-Based One-Time Password (TOTP) is a type of one-time password algorithm that generates a unique passcode every 30 seconds based on a shared secret key between the authentication server and the user&rsquo;s device. TOTP is widely used in applications like Google Authenticator, Authy, and many others.</p>
<h2 id="what-is-web-authentication-webauthn">What is Web Authentication (WebAuthn)?</h2>
<p>Web Authentication (WebAuthn) is a W3C standard that enables strong, phishing-resistant authentication using public key cryptography. Unlike TOTP, which relies on a shared secret, WebAuthn uses asymmetric keys generated by the authenticator (such as a hardware security key or built-in authenticator in devices like smartphones and laptops).</p>
<h2 id="why-choose-totp-and-webauthn-for-mfa">Why choose TOTP and WebAuthn for MFA?</h2>
<p>TOTP and WebAuthn offer different strengths:</p>
<ul>
<li><strong>TOTP</strong>: Easy to implement, widely supported, and doesn&rsquo;t require special hardware.</li>
<li><strong>WebAuthn</strong>: More secure, resistant to phishing, and supports biometric authentication methods.</li>
</ul>
<h2 id="setting-up-totp">Setting up TOTP</h2>
<p>Let&rsquo;s start by setting up TOTP for MFA.</p>
<h3 id="step-by-step-guide">Step-by-step Guide</h3>
<div class="step-guide">
<div class="step-item"><div class="step-content">
<h4>Install a TOTP library</h4>
Choose a library that suits your programming language. For Node.js, `speakeasy` is a good choice.
</div></div>
<div class="step-item"><div class="step-content">
<h4>Generate a secret key</h4>
Create a secret key that will be shared between the server and the user's device.
</div></div>
<div class="step-item"><div class="step-content">
<h4>Display the QR code</h4>
Encode the secret key into a QR code that the user can scan with their TOTP app.
</div></div>
<div class="step-item"><div class="step-content">
<h4>Verify the TOTP code</h4>
Check the TOTP code provided by the user against the expected value generated by the server.
</div></div>
</div>
<h3 id="code-example">Code Example</h3>
<p>Here’s how you can set up TOTP using the <code>speakeasy</code> library in Node.js.</p>
<h4 id="install-the-library">Install the library</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install speakeasy qrcode
</span></span></code></pre></div><h4 id="generate-a-secret-key-and-qr-code">Generate a secret key and QR code</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">speakeasy</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;speakeasy&#39;</span>);
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">qr</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;qrcode&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Generate a secret key
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">secret</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">speakeasy</span>.<span style="color:#a6e22e">generateSecret</span>({ <span style="color:#a6e22e">length</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">20</span> });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Display the QR code URL
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#a6e22e">qr</span>.<span style="color:#a6e22e">toDataURL</span>(<span style="color:#a6e22e">secret</span>.<span style="color:#a6e22e">otpauth_url</span>, <span style="color:#66d9ef">function</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">image_data</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">image_data</span>); <span style="color:#75715e">// This is the QR code URL
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>});
</span></span></code></pre></div><h4 id="verify-the-totp-code">Verify the TOTP code</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#75715e">// Assume `token` is the code entered by the user
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">token</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;123456&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Verify the token
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">verified</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">speakeasy</span>.<span style="color:#a6e22e">totp</span>.<span style="color:#a6e22e">verify</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">secret</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">secret</span>.<span style="color:#a6e22e">base32</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">encoding</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;base32&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">token</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">token</span>
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">verified</span> <span style="color:#f92672">?</span> <span style="color:#e6db74">&#39;Token is valid&#39;</span> <span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Invalid token&#39;</span>);
</span></span></code></pre></div><h3 id="security-considerations">Security Considerations</h3>
<div class="notice warning">⚠️ <strong>Warning:</strong> Never store the secret key in plain text. Use a secure method to store it, such as environment variables or a secure vault.</div>
<h2 id="setting-up-webauthn">Setting up WebAuthn</h2>
<p>Next, let&rsquo;s integrate WebAuthn into your application.</p>
<h3 id="step-by-step-guide-1">Step-by-step Guide</h3>
<div class="step-guide">
<div class="step-item"><div class="step-content">
<h4>Register the user</h4>
Create a registration ceremony where the user's authenticator generates a public/private key pair.
</div></div>
<div class="step-item"><div class="step-content">
<h4>Store the public key</h4>
Save the public key generated during registration for later verification.
</div></div>
<div class="step-item"><div class="step-content">
<h4>Authenticate the user</h4>
Initiate an authentication ceremony where the user proves possession of the private key.
</div></div>
<div class="step-item"><div class="step-content">
<h4>Verify the signature</h4>
Check the signature provided by the user's authenticator to ensure it's valid.
</div></div>
</div>
<h3 id="code-example-1">Code Example</h3>
<p>Here’s a basic example using the <code>simple-webauthn-server</code> and <code>simple-webauthn-browser</code> libraries.</p>
<h4 id="install-the-libraries">Install the libraries</h4>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>npm install @simplewebauthn/server @simplewebauthn/browser
</span></span></code></pre></div><h4 id="register-the-user">Register the user</h4>
<p><strong>Server-side</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">generateRegistrationOptions</span>, <span style="color:#a6e22e">verifyRegistrationResponse</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;@simplewebauthn/server&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Generate registration options
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">registrationOptions</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">generateRegistrationOptions</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">rpName</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;Example Corp.&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">rpID</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;example.com&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">userID</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;unique-user-id&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">userName</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;johndoe@example.com&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">userDisplayName</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;John Doe&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">attestationType</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;none&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">supportedAlgorithmIDs</span><span style="color:#f92672">:</span> [<span style="color:#f92672">-</span><span style="color:#ae81ff">7</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">257</span>],
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Send `registrationOptions` to the client
</span></span></span></code></pre></div><p><strong>Client-side</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">startRegistration</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;@simplewebauthn/browser&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Assume `registrationOptions` is received from the server
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">credential</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">startRegistration</span>(<span style="color:#a6e22e">registrationOptions</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Send `credential` back to the server
</span></span></span></code></pre></div><p><strong>Server-side (verify)</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">expectedChallenge</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;expected-challenge&#39;</span>; <span style="color:#75715e">// Store this securely during registration
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">verification</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">verifyRegistrationResponse</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">credential</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">credential</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">expectedChallenge</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">expectedChallenge</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">expectedOrigin</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;https://example.com&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">expectedRPID</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;example.com&#39;</span>,
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">verified</span>, <span style="color:#a6e22e">registrationInfo</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">verification</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">verified</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">credentialPublicKey</span>, <span style="color:#a6e22e">credentialID</span>, <span style="color:#a6e22e">counter</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">registrationInfo</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Save `credentialPublicKey`, `credentialID`, and `counter` for future authentication
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div><h4 id="authenticate-the-user">Authenticate the user</h4>
<p><strong>Server-side</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">generateAuthenticationOptions</span>, <span style="color:#a6e22e">verifyAuthenticationResponse</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">require</span>(<span style="color:#e6db74">&#39;@simplewebauthn/server&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Generate authentication options
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">authenticationOptions</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">generateAuthenticationOptions</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">allowCredentials</span><span style="color:#f92672">:</span> [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">id</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">Buffer</span>.<span style="color:#a6e22e">from</span>(<span style="color:#e6db74">&#39;credentialID&#39;</span>, <span style="color:#e6db74">&#39;base64url&#39;</span>),
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">type</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;public-key&#39;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">transports</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;usb&#39;</span>, <span style="color:#e6db74">&#39;nfc&#39;</span>, <span style="color:#e6db74">&#39;ble&#39;</span>, <span style="color:#e6db74">&#39;internal&#39;</span>],
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">userVerification</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;preferred&#39;</span>,
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Send `authenticationOptions` to the client
</span></span></span></code></pre></div><p><strong>Client-side</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">startAuthentication</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;@simplewebauthn/browser&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Assume `authenticationOptions` is received from the server
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">assertion</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">startAuthentication</span>(<span style="color:#a6e22e">authenticationOptions</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Send `assertion` back to the server
</span></span></span></code></pre></div><p><strong>Server-side (verify)</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">expectedChallenge</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">&#39;expected-challenge&#39;</span>; <span style="color:#75715e">// Store this securely during authentication
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">verification</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">await</span> <span style="color:#a6e22e">verifyAuthenticationResponse</span>({
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">credential</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">assertion</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">expectedChallenge</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">expectedChallenge</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">expectedOrigin</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;https://example.com&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">expectedRPID</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;example.com&#39;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">authenticator</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">credentialPublicKey</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">Buffer</span>.<span style="color:#a6e22e">from</span>(<span style="color:#e6db74">&#39;credentialPublicKey&#39;</span>, <span style="color:#e6db74">&#39;base64url&#39;</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">credentialID</span><span style="color:#f92672">:</span> <span style="color:#a6e22e">Buffer</span>.<span style="color:#a6e22e">from</span>(<span style="color:#e6db74">&#39;credentialID&#39;</span>, <span style="color:#e6db74">&#39;base64url&#39;</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">counter</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">1</span>, <span style="color:#75715e">// The counter value from the previous authentication
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>    },
</span></span><span style="display:flex;"><span>});
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">verified</span>, <span style="color:#a6e22e">authenticationInfo</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">verification</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">verified</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> { <span style="color:#a6e22e">newCounter</span> } <span style="color:#f92672">=</span> <span style="color:#a6e22e">authenticationInfo</span>;
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Update the counter value in your database
</span></span></span><span style="display:flex;"><span><span style="color:#75715e"></span>}
</span></span></code></pre></div><h3 id="security-considerations-1">Security Considerations</h3>
<div class="notice danger">🚨 <strong>Security Alert:</strong> Ensure that challenges are unique and unpredictable to prevent replay attacks.</div>
<h2 id="comparison-totp-vs-webauthn">Comparison: TOTP vs WebAuthn</h2>
<table class="comparison-table">
<thead><tr><th>Approach</th><th>Pros</th><th>Cons</th><th>Use When</th></tr></thead>
<tbody>
<tr><td>TOTP</td><td>Easy to implement, widely supported</td><td>Less secure, vulnerable to phishing</td><td>Basic MFA requirement, no special hardware</td></tr>
<tr><td>WebAuthn</td><td>More secure, phishing-resistant, supports biometrics</td><td>Requires user consent, some devices may not support it</td><td>Strong security, high assurance required</td></tr>
</tbody>
</table>
<h2 id="quick-reference">Quick Reference</h2>
<div class="quick-ref">
<h4>📋 Quick Reference</h4>
<ul>
<li><code>speakeasy.generateSecret()</code> - Generates a secret key for TOTP</li>
<li><code>speakeasy.totp.verify()</code> - Verifies a TOTP code</li>
<li><code>generateRegistrationOptions()</code> - Generates options for WebAuthn registration</li>
<li><code>verifyRegistrationResponse()</code> - Verifies WebAuthn registration response</li>
<li><code>generateAuthenticationOptions()</code> - Generates options for WebAuthn authentication</li>
<li><code>verifyAuthenticationResponse()</code> - Verifies WebAuthn authentication response</li>
</ul>
</div>
<h2 id="troubleshooting-common-issues">Troubleshooting Common Issues</h2>
<h3 id="error-invalid-totp-code">Error: Invalid TOTP code</h3>
<p>Ensure that:</p>
<ul>
<li>The secret key is correctly shared between the server and the user&rsquo;s device.</li>
<li>The server&rsquo;s clock is synchronized with NTP.</li>
</ul>
<h3 id="error-registration-failed">Error: Registration failed</h3>
<p>Check that:</p>
<ul>
<li>The user&rsquo;s authenticator supports the required algorithms.</li>
<li>The challenge is unique and unpredictable.</li>
</ul>
<h3 id="error-authentication-failed">Error: Authentication failed</h3>
<p>Verify that:</p>
<ul>
<li>The public key and counter are correctly stored and retrieved.</li>
<li>The challenge matches the expected value.</li>
</ul>
<h2 id="key-takeaways">Key Takeaways</h2>
<div class="key-takeaway">
<h4>🎯 Key Takeaways</h4>
<ul>
<li>TOTP is easy to implement but less secure compared to WebAuthn.</li>
<li>WebAuthn offers stronger security and supports biometric authentication.</li>
<li>Both methods require careful handling of secrets and challenges to prevent security vulnerabilities.</li>
</ul>
</div>
<p>Implementing TOTP and WebAuthn in your application can significantly enhance security by adding an additional layer of authentication. Choose the method that best fits your security requirements and user base. Happy coding!</p>
]]></content:encoded></item></channel></rss>