{
    "componentChunkName": "component---src-pages-blog-markdown-remark-fields-slug-js",
    "path": "/blog/implementing-the-right-email-verification-flow",
    "result": {"data":{"markdownRemark":{"html":"<h2 id=\"introduction\" style=\"position:relative;\"><a href=\"#introduction\" aria-label=\"introduction permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Introduction</h2>\n<p>Email verification is contentious, it increases friction and adds yet another step to onboarding. However, when done right, email verification can lead to better communication with users, avoid fake accounts / negative externalities, and maintain high marketing reputation.</p>\n<p>In this post, we’ll break down why email verification is important and how to implement the right verification flow for your application.</p>\n<h2 id=\"why-we-need-email-verification\" style=\"position:relative;\"><a href=\"#why-we-need-email-verification\" aria-label=\"why we need email verification permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>Why we need email verification</strong></h2>\n<h3 id=\"communicating-with-users\" style=\"position:relative;\"><a href=\"#communicating-with-users\" aria-label=\"communicating with users permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Communicating with users</h3>\n<p>The main reason for adding email verification is to have a direct channel to users.</p>\n<p>Company emails aren’t the most exciting topic. I have a stack of AWS marketing emails collecting dust in my inbox. I’m simply not excited about AWS re:Invent or their latest product launch. However, many years ago, I was an avid League of Legends player and whenever new patch notes hit my inbox, I would spend hours reading through every change.</p>\n<p>Company content is in the same boat. Creating engaging content is one of the best ways to build a community of users that love your product. Plus, there are plenty of valid use cases for company communication, such as sending billing documents or password reset instructions.</p>\n<h3 id=\"avoiding-fake-accounts--negative-externalities\" style=\"position:relative;\"><a href=\"#avoiding-fake-accounts--negative-externalities\" aria-label=\"avoiding fake accounts  negative externalities permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Avoiding fake accounts / negative externalities</h3>\n<p>Email verification helps weed out fake accounts. Generally, fake accounts are meant to abuse a certain feature or product of the application. This can lead to rising server costs and skew data analytics. Adding in the additional friction in email verification can be a low-cost way of reducing the risk for product misuse.</p>\n<p>In particular, some fake accounts might have real email addresses attached. An attacker could sign up with the email <a href=\"mailto:john@fbi.gov\" target=\"_blank\" rel=\"nofollow\">john@fbi.gov</a> or <a href=\"mailto:susan@irs.gov\" target=\"_blank\" rel=\"nofollow\">susan@irs.gov</a>. The last thing you want to do is badger John and Susan with company content.</p>\n<h3 id=\"account-linking\" style=\"position:relative;\"><a href=\"#account-linking\" aria-label=\"account linking permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Account linking</h3>\n<p>Another reason to add email verification is to prevent attackers from hijacking accounts during automatic account linking.</p>\n<p>For example, if a user already has an account using Google Sign-In, an attacker can sign up with the same email on Email-password Sign-In. Most systems today will automatically link the two accounts since they share the same email address and give the attacker access to the original account.</p>\n<p>To solve this, the system must first verify the email-password account before linking it to the Google Sign-In account.</p>\n<h3 id=\"marketing-reputation\" style=\"position:relative;\"><a href=\"#marketing-reputation\" aria-label=\"marketing reputation permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Marketing reputation</h3>\n<p>If a product is shipped but users don’t discover it, was it really worth it?</p>\n<p>Companies can email users to celebrate feature updates, product launches, and case studies. But without proper email verification, companies can end up sending hundreds of emails that hard bounce - the email doesn’t exist.</p>\n<p>Email Service Providers (ESPs) don’t take kindly to accounts that see high levels of hard bounces. They automatically assume that the account is a spammer and blacklist the account. That blocks everything from company updates to more vital operations like sending password reset instructions.</p>\n<h2 id=\"a-how-to-guide-on-email-verification\" style=\"position:relative;\"><a href=\"#a-how-to-guide-on-email-verification\" aria-label=\"a how to guide on email verification permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>A how-to guide on email verification</h2>\n<p>Implementing a perfect email verification flow is a delicate balance between friction and user verification.</p>\n<h3 id=\"universal-verification\" style=\"position:relative;\"><a href=\"#universal-verification\" aria-label=\"universal verification permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Universal verification</h3>\n<p>The first question to ask is, “Does universal email verification make sense for my application?”</p>\n<p>In some cases, like building a financial application, having every user verify their email is critical. They’re likely already expecting detailed verification and have the patience to take the extra step of hunting through their inbox to complete verification.</p>\n<p>On the other hand, more trivial applications like image editing, might not have the same luxury. Most users are looking to access the product as quickly as possible, and throwing up an email verification barrier is bound to result in lower conversions or increased churn.</p>\n<h3 id=\"verification-triggers\" style=\"position:relative;\"><a href=\"#verification-triggers\" aria-label=\"verification triggers permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Verification triggers</h3>\n<p>Say we still want to set up email verification - we’ll need to figure out what actions trigger an email verification. We could require email verification as soon as a user signs up, or perhaps when the user comes back for the second session.</p>\n<p>Shifting the onboarding friction from email verification to a later time can make the process much more natural for users. For example, a social media platform can minimize friction during the sign up process so that a user can immediately start to consume content. Later, when the user wants to post content, the platform can verify emails to minimize spam.</p>\n<p>We can also segment verification by using conditionals.</p>\n<p>For example, if someone is signing up with a <a href=\"http://gmail.com\" target=\"_blank\" rel=\"nofollow\">gmail.com</a> email, we can be relatively sure that the email is legit. On the other hand, a <a href=\"http://tmmcv.net\" target=\"_blank\" rel=\"nofollow\">tmmcv.net</a> email has a high chance of being fake.</p>\n<p>Either way, it’s critical to define a trigger that will initiate the email verification flow. Once initiated, the flow should tell users that a verification email has been sent and lock features until the user has confirmed the email. If the user has no incentive to verify their email, they’ll simply ignore the verification email.</p>\n<h3 id=\"generating-verification-tokens\" style=\"position:relative;\"><a href=\"#generating-verification-tokens\" aria-label=\"generating verification tokens permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Generating verification tokens</h3>\n<p>Once the email verification process starts, the first step is to design a verification token. With SuperTokens, the verification token is a random 128 character string. You can generate any random 64-128 character token. The length prevents brute force attempts from attackers.</p>\n<p>Once the token is generated, it’s assigned an expiration time of 24 hours. Unlike password reset or email change functions, email verification is relatively harmless. As a result, some companies may even opt for longer verification times.</p>\n<p>Now that we’ve done all that, it’s time to send the user an email verification link.</p>\n<p>An example of this is:</p>\n<blockquote>\n<p><a href=\"https://example.com/auth/verify-email?token=OTJiN2E5NmZkMGQzMWQ3ZTRhZTkwOGU4MmVmZjZmYjE5ZTQyMmViO\" target=\"_blank\" rel=\"nofollow\">https://example.com/auth/verify-email?token=OTJiN2E5NmZkMGQzMWQ3ZTRhZTkwOGU4MmVmZjZmYjE5ZTQyMmViO</a></p>\n</blockquote>\n<p>Here is pseudocode for sending a verification email:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"80330662904864080000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`const crypto = require('crypto');\n\n/*\n\tFirst, we need to generate a verification token. \n\tWe're using the JSON Web Token library for our example.\n*/\nvar token = crypto.randomBytes(128).toString('hex');\n\n/*\n\tHere, we save the verification token in the db \nto be verified when the token is consumed\n*/\ndb_startTransaction() {\n\tdb_saveNewVerificationToken(userId, token);\n}\n\n/*\n\tNext, we use the token to generate a verification link. \n*/\nconst emailVerificationLink = 'https://example.com/auth/verify-email?token=' + token;\n\nconst mailConfigurations = {\n\tfrom: 'example@example.com',\n\tto: 'user@user.com',\n\t/*\n\t\tThe text and subject can be personalized for your specific use case. \n\t*/\n\tsubject: 'Verify your email to access Example',\n\ttext: emailVerificationLink\n};\n\n/*\n\tWith all that done, it's time to send an verification email to our user\n*/\nsendMail(mailConfigurations);`, `80330662904864080000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">const</span> crypto <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'crypto'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">/*\n\tFirst, we need to generate a verification token. \n\tWe're using the JSON Web Token library for our example.\n*/</span>\n<span class=\"token keyword\">var</span> token <span class=\"token operator\">=</span> crypto<span class=\"token punctuation\">.</span><span class=\"token function\">randomBytes</span><span class=\"token punctuation\">(</span><span class=\"token number\">128</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token string\">'hex'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">/*\n\tHere, we save the verification token in the db \nto be verified when the token is consumed\n*/</span>\n<span class=\"token function\">db_startTransaction</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token function\">db_saveNewVerificationToken</span><span class=\"token punctuation\">(</span>userId<span class=\"token punctuation\">,</span> token<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token comment\">/*\n\tNext, we use the token to generate a verification link. \n*/</span>\n<span class=\"token keyword\">const</span> emailVerificationLink <span class=\"token operator\">=</span> <span class=\"token string\">'https://example.com/auth/verify-email?token='</span> <span class=\"token operator\">+</span> token<span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> mailConfigurations <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token literal-property property\">from</span><span class=\"token operator\">:</span> <span class=\"token string\">'example@example.com'</span><span class=\"token punctuation\">,</span>\n\t<span class=\"token literal-property property\">to</span><span class=\"token operator\">:</span> <span class=\"token string\">'user@user.com'</span><span class=\"token punctuation\">,</span>\n\t<span class=\"token comment\">/*\n\t\tThe text and subject can be personalized for your specific use case. \n\t*/</span>\n\t<span class=\"token literal-property property\">subject</span><span class=\"token operator\">:</span> <span class=\"token string\">'Verify your email to access Example'</span><span class=\"token punctuation\">,</span>\n\t<span class=\"token literal-property property\">text</span><span class=\"token operator\">:</span> emailVerificationLink\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">/*\n\tWith all that done, it's time to send an verification email to our user\n*/</span>\n<span class=\"token function\">sendMail</span><span class=\"token punctuation\">(</span>mailConfigurations<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<h3 id=\"storing-verification-tokens-in-the-database\" style=\"position:relative;\"><a href=\"#storing-verification-tokens-in-the-database\" aria-label=\"storing verification tokens in the database permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Storing verification tokens in the database</h3>\n<p>Occasionally, a user might try to request multiple email verifications. Since we have a generous expiration time, we can reuse the previously generated token in the database and send the same link each time the user requests a new email. If the expiration time is up, we can generate a new token and send the new link to the user. Generally, this makes it easier for us to handle email verification logic without having to create and disable multiple verification tokens.</p>\n<p>On the other hand, if we wanted to tighten security, we could choose to generate a new verification token, replace the verification token in the database, and send a new email verification link each time. The drawback to this approach is mainly in dealing with previously generated tokens.</p>\n<p>In some cases, it makes sense to disable them. However, it could lead to frustrated users. Alternatively, we could keep all tokens enabled and disable all of them whenever one of the tokens is consumed.</p>\n<h3 id=\"passing-verification-to-the-backend\" style=\"position:relative;\"><a href=\"#passing-verification-to-the-backend\" aria-label=\"passing verification to the backend permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Passing verification to the backend</h3>\n<p>After a user clicks on the verification link, there should be a clear indication for the user that they’ve been email-verified. This can either take the form of a distinct landing page or it could be a pop-up on the home page of the application.</p>\n<p>One potential edge case with email verification links is that email clients might open the verification link for scanning purposes and consume the token. To prevent this, we can check if a session exists and if not, redirect the link to a page with a verify button. Email clients won’t click on the button and users that are redirected to this page can simply click on the button to finish their verification.</p>\n<p>The token in the URL is then passed onto the backend for verification. On the backend, once a verification token has been consumed, the verification status for the user should be changed.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"99132512341589640000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`function verifyEmail(token) {\n\t/* \n\t\tFirst, we need to verify that the token corresponds with an actual user.\n\t*/\n\n\trowFromDb = db.getRowThatContains(token)\n   if (rowFromDb == null) {\n       throw Error(“invalid password reset token”)\n   }\n   userId = rowFromDb.user_id\n\n\t/*\n    Now we know that the user exists, so it is valid. We start a \n\t\ttransaction to prevent race conditions.\n   */\n       \n   db_startTransaction() {\t\t\n\t\t\t/*\n\t\t\t  Here we check if the user is already verified.\n\t\t  */\n      if (userId.verified = 1) {\n        db_rollback();\n        throw Error(“User is already verified.”);\n\t    }\n\n      /*\n\t\t\t  Here we check if the current token has expired or not.\n\t\t  */\n      if (userId.token_expiry < time_now()) {\n        db_rollback();\n        throw Error(“Token has expired. Please try again.”);\n\t    }\n\n      /* \n        Here we'll change verifications status to verified. \n      */\n      db_saveNewVerified(userId, 1);\n      db_commitTransaction();\n  }`, `99132512341589640000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                <svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"jsx\"><pre class=\"language-jsx\"><code class=\"language-jsx\"><span class=\"token keyword\">function</span> <span class=\"token function\">verifyEmail</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">token</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n\t<span class=\"token comment\">/* \n\t\tFirst, we need to verify that the token corresponds with an actual user.\n\t*/</span>\n\n\trowFromDb <span class=\"token operator\">=</span> db<span class=\"token punctuation\">.</span><span class=\"token function\">getRowThatContains</span><span class=\"token punctuation\">(</span>token<span class=\"token punctuation\">)</span>\n   <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>rowFromDb <span class=\"token operator\">==</span> <span class=\"token keyword\">null</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n       <span class=\"token keyword\">throw</span> <span class=\"token function\">Error</span><span class=\"token punctuation\">(</span>“invalid password reset token”<span class=\"token punctuation\">)</span>\n   <span class=\"token punctuation\">}</span>\n   userId <span class=\"token operator\">=</span> rowFromDb<span class=\"token punctuation\">.</span>user_id\n\n\t<span class=\"token comment\">/*\n    Now we know that the user exists, so it is valid. We start a \n\t\ttransaction to prevent race conditions.\n   */</span>\n       \n   <span class=\"token function\">db_startTransaction</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\t\t\n\t\t\t<span class=\"token comment\">/*\n\t\t\t  Here we check if the user is already verified.\n\t\t  */</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>userId<span class=\"token punctuation\">.</span>verified <span class=\"token operator\">=</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">db_rollback</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">throw</span> <span class=\"token function\">Error</span><span class=\"token punctuation\">(</span>“User is already verified<span class=\"token punctuation\">.</span>”<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t    <span class=\"token punctuation\">}</span>\n\n      <span class=\"token comment\">/*\n\t\t\t  Here we check if the current token has expired or not.\n\t\t  */</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>userId<span class=\"token punctuation\">.</span>token_expiry <span class=\"token operator\">&lt;</span> <span class=\"token function\">time_now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token function\">db_rollback</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">throw</span> <span class=\"token function\">Error</span><span class=\"token punctuation\">(</span>“Token has expired<span class=\"token punctuation\">.</span> Please <span class=\"token keyword\">try</span> again<span class=\"token punctuation\">.</span>”<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\t    <span class=\"token punctuation\">}</span>\n\n      <span class=\"token comment\">/* \n        Here we'll change verifications status to verified. \n      */</span>\n      <span class=\"token function\">db_saveNewVerified</span><span class=\"token punctuation\">(</span>userId<span class=\"token punctuation\">,</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token function\">db_commitTransaction</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"conclusion\" style=\"position:relative;\"><a href=\"#conclusion\" aria-label=\"conclusion permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Conclusion</h2>\n<p>Email verification is only a small part of the overall onboarding process, but one with a surprising amount of nuance. Poor implementation can lead to frustrated users who will churn out. While you’ll get away with it in the beginning, ignoring this is a bad idea in the long term.</p>","frontmatter":{"date":"February 24, 2023","title":"Implementing the right Email Verification flow","cover":"email_verification_blog_banner.png","author":"Rishabh Poddar","description":"Email verification can increase friction and adds another stop to onboarding, but when done correctly can lead to better security and establish better communication with users."},"fields":{"slug":"/implementing-the-right-email-verification-flow/"}},"site":{"siteMetadata":{"title":"SuperTokens Blog"}}},"pageContext":{"id":"b2f151ae-5d7e-5652-9c2a-c6dca88100a8","fields__slug":"/implementing-the-right-email-verification-flow/","__params":{"fields__slug":"implementing-the-right-email-verification-flow"}}},
    "staticQueryHashes": []}