{
    "componentChunkName": "component---src-pages-blog-markdown-remark-fields-slug-js",
    "path": "/blog/captcha-supertokens",
    "result": {"data":{"markdownRemark":{"html":"<p>Bot traffic targeting authentication endpoints represents a persistent security challenge. Automated account creation, credential stuffing attacks, and fake registrations consume resources, pollute user databases, and expose applications to fraud. CAPTCHA (Completely\nAutomated Public Turing test to tell Computers and Humans Apart) provides a standard defense mechanism by requiring human interaction\nbefore processing authentication requests.</p>\n<p>Traditional CAPTCHA integration requires coordinating frontend widget libraries, backend token validation, error handling, and provider-specific API calls across signup, login, and password reset flows. This scattered implementation increases maintenance burden and creates coupling between authentication logic and bot prevention mechanisms.</p>\n<p><a href=\"https://supertokens.com/\" target=\"_blank\" rel=\"nofollow\">SuperTokens</a> addresses this complexity through a plugin architecture that isolates CAPTCHA logic from core authentication. The plugin model enables developers to add CAPTCHA protection to specific endpoints and signup forms, login pages,\npassword reset flows with minimal wiring. Configuration changes control which APIs enforce CAPTCHA, which provider handles verification, and under what conditions challenges appear, all without modifying authentication business logic.</p>\n<p>This article demonstrates practical CAPTCHA integration patterns, explains server-side verification requirements, and shows how SuperTokens’ plugin system simplifies deployment across multiple authentication flows.</p>\n<h2 id=\"i-want-to-protect-my-signup-form-from-bots-how-do-i-integrate-captcha\" style=\"position:relative;\"><a href=\"#i-want-to-protect-my-signup-form-from-bots-how-do-i-integrate-captcha\" aria-label=\"i want to protect my signup form from bots how do i integrate captcha 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>I Want to Protect My Signup Form From Bots. How Do I Integrate CAPTCHA?</strong></h2>\n<p>Bot-driven fake account creation inflates user counts, triggers verification emails that land in spam folders, and creates opportunities\nfor downstream abuse. Signup forms represent the highest-value target for CAPTCHA protection.</p>\n<h3 id=\"frontend-integration\" style=\"position:relative;\"><a href=\"#frontend-integration\" aria-label=\"frontend integration 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>Frontend Integration</strong></h3>\n<p>CAPTCHA integration begins with embedding the provider’s widget on the signup form. The widget renders a challenge (image selection, checkbox interaction, or invisible background verification) and generates a token upon successful completion.</p>\n<p><strong>Basic Frontend Flow:</strong></p>\n<ol>\n<li>User completes signup form fields (email, password)</li>\n<li>CAPTCHA widget loads and displays challenge</li>\n<li>User solves challenge; widget generates verification token</li>\n<li>Frontend sends signup request with CAPTCHA token included</li>\n<li>Backend validates token before creating account</li>\n</ol>\n<p>The CAPTCHA widget should appear only where required. Showing challenges on every page degrades user experience without benefiting security. Selective placement—signup form only, not login page—reduces friction while protecting the highest-risk endpoint.</p>\n<p><strong>SuperTokens Frontend Integration:</strong></p>\n<p>SuperTokens’ frontend SDK accepts CAPTCHA plugins that inject widgets automatically at specified forms. First, install the plugin:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"97411298799529150000\"\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(`npm install supertokens-auth-react @supertokens-plugins/captcha-react`, `97411298799529150000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\">npm install supertokens<span class=\"token operator\">-</span>auth<span class=\"token operator\">-</span>react @supertokens<span class=\"token operator\">-</span>plugins<span class=\"token operator\">/</span>captcha<span class=\"token operator\">-</span>react</code></pre></div>\n<p>Then configure the plugin in your SuperTokens initialization:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"68412232182160260000\"\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(`// frontend/src/supertokensConfig.js\nimport SuperTokens from &quot;supertokens-auth-react&quot;;\nimport EmailPassword from &quot;supertokens-auth-react/recipe/emailpassword&quot;;\nimport Session from &quot;supertokens-auth-react/recipe/session&quot;;\nimport CaptchaPlugin from &quot;@supertokens-plugins/captcha-react&quot;;\nSuperTokens.init({\n  appInfo: {\n    appName: &quot;SuperTokens Demo&quot;,\n    apiDomain: &quot;http://localhost:3001&quot;,\n    websiteDomain: &quot;http://localhost:3000&quot;,\n    apiBasePath: &quot;/auth&quot;,\n    websiteBasePath: &quot;/auth&quot;,\n  },\n  useShadowDom: false,\n  recipeList: [\n    EmailPassword.init(),\n    Session.init(),\n  ],\n  experimental: {\n    plugins: [\n      CaptchaPlugin.init({\n        type: &quot;reCAPTCHAv3&quot;,\n        captcha: {\n          sitekey: &quot;YOUR_RECAPTCHA_SITE_KEY&quot;,\n        },\n      }),\n    ],\n  },\n});`, `68412232182160260000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// frontend/src/supertokensConfig.js</span>\n<span class=\"token keyword\">import</span> SuperTokens <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-auth-react\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> EmailPassword <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-auth-react/recipe/emailpassword\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> Session <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-auth-react/recipe/session\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> CaptchaPlugin <span class=\"token keyword\">from</span> <span class=\"token string\">\"@supertokens-plugins/captcha-react\"</span><span class=\"token punctuation\">;</span>\nSuperTokens<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">appInfo</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">appName</span><span class=\"token operator\">:</span> <span class=\"token string\">\"SuperTokens Demo\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">apiDomain</span><span class=\"token operator\">:</span> <span class=\"token string\">\"http://localhost:3001\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">websiteDomain</span><span class=\"token operator\">:</span> <span class=\"token string\">\"http://localhost:3000\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">apiBasePath</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/auth\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">websiteBasePath</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/auth\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">useShadowDom</span><span class=\"token operator\">:</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">recipeList</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    EmailPassword<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    Session<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">experimental</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">plugins</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      CaptchaPlugin<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">\"reCAPTCHAv3\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">captcha</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">sitekey</span><span class=\"token operator\">:</span> <span class=\"token string\">\"YOUR_RECAPTCHA_SITE_KEY\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Important: Set <code class=\"language-text\">useShadowDom: false</code> when using CAPTCHA plugins to ensure proper widget rendering.</p>\n<p>Initialize your React application with SuperTokens:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"26760281016401244000\"\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(`// frontend/src/index.js\nimport React from &quot;react&quot;;\nimport { createRoot } from &quot;react-dom/client&quot;;\nimport { BrowserRouter, Routes } from &quot;react-router-dom&quot;;\nimport SuperTokens, { SuperTokensWrapper } from &quot;supertokens-auth-react&quot;;\nimport { getSuperTokensRoutesForReactRouterDom } from &quot;supertokens-auth-react/ui&quot;;\nimport { EmailPasswordPreBuiltUI } from &quot;supertokens-auth-react/recipe/emailpassword/prebuiltui&quot;;\nimport * as reactRouterDom from &quot;react-router-dom&quot;;\nimport &quot;./supertokensConfig&quot;;\nfunction App() {\n  return (\n    <SuperTokensWrapper>\n      <BrowserRouter>\n        <Routes>\n          {getSuperTokensRoutesForReactRouterDom(reactRouterDom, [\n            EmailPasswordPreBuiltUI,\n          ])}\n          {/* your app routes */}\n        </Routes>\n      </BrowserRouter>\n    </SuperTokensWrapper>\n  );\n}\nconst root = createRoot(document.getElementById(&quot;root&quot;));\nroot.render(<App />);`, `26760281016401244000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// frontend/src/index.js</span>\n<span class=\"token keyword\">import</span> React <span class=\"token keyword\">from</span> <span class=\"token string\">\"react\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> createRoot <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"react-dom/client\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> BrowserRouter<span class=\"token punctuation\">,</span> Routes <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"react-router-dom\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> SuperTokens<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> SuperTokensWrapper <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-auth-react\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> getSuperTokensRoutesForReactRouterDom <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-auth-react/ui\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> EmailPasswordPreBuiltUI <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-auth-react/recipe/emailpassword/prebuiltui\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token operator\">*</span> <span class=\"token keyword\">as</span> reactRouterDom <span class=\"token keyword\">from</span> <span class=\"token string\">\"react-router-dom\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token string\">\"./supertokensConfig\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">App</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token operator\">&lt;</span>SuperTokensWrapper<span class=\"token operator\">></span>\n      <span class=\"token operator\">&lt;</span>BrowserRouter<span class=\"token operator\">></span>\n        <span class=\"token operator\">&lt;</span>Routes<span class=\"token operator\">></span>\n          <span class=\"token punctuation\">{</span><span class=\"token function\">getSuperTokensRoutesForReactRouterDom</span><span class=\"token punctuation\">(</span>reactRouterDom<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>\n            EmailPasswordPreBuiltUI<span class=\"token punctuation\">,</span>\n          <span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">}</span>\n          <span class=\"token punctuation\">{</span><span class=\"token comment\">/* your app routes */</span><span class=\"token punctuation\">}</span>\n        <span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>Routes<span class=\"token operator\">></span>\n      <span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>BrowserRouter<span class=\"token operator\">></span>\n    <span class=\"token operator\">&lt;</span><span class=\"token operator\">/</span>SuperTokensWrapper<span class=\"token operator\">></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token keyword\">const</span> root <span class=\"token operator\">=</span> <span class=\"token function\">createRoot</span><span class=\"token punctuation\">(</span>document<span class=\"token punctuation\">.</span><span class=\"token function\">getElementById</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"root\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\nroot<span class=\"token punctuation\">.</span><span class=\"token function\">render</span><span class=\"token punctuation\">(</span><span class=\"token operator\">&lt;</span>App <span class=\"token operator\">/</span><span class=\"token operator\">></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>This configuration automatically handles CAPTCHA widget rendering, token capture, and inclusion in API requests. No manual DOM manipulation or event handler wiring required.</p>\n<h3 id=\"backend-token-validation\" style=\"position:relative;\"><a href=\"#backend-token-validation\" aria-label=\"backend token validation 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>Backend Token Validation</strong></h3>\n<p>Frontend CAPTCHA widgets generate tokens that must be verified server-side before processing requests. Relying on client-side validation alone provides no security—attackers can trivially bypass frontend checks or submit requests directly to the API.</p>\n<p><strong>Backend Validation Requirements:</strong></p>\n<ul>\n<li>Extract CAPTCHA token from request payload</li>\n<li>Submit token to provider’s verification API with secret key</li>\n<li>Verify response indicates successful human verification</li>\n<li>Reject request if verification fails or token is invalid</li>\n</ul>\n<p><strong>SuperTokens Backend Integration:</strong></p>\n<p>Install the backend plugin:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"97526038533011800000\"\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(`npm install supertokens-node @supertokens-plugins/captcha-nodejs`, `97526038533011800000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\">npm install supertokens<span class=\"token operator\">-</span>node @supertokens<span class=\"token operator\">-</span>plugins<span class=\"token operator\">/</span>captcha<span class=\"token operator\">-</span>nodejs</code></pre></div>\n<p>Configure your Express server with SuperTokens and CAPTCHA validation:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"45790633935066595000\"\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(`// backend/server/index.js\nimport express from &quot;express&quot;;\nimport cors from &quot;cors&quot;;\n\n// Core SuperTokens imports\nimport SuperTokens from &quot;supertokens-node&quot;;\nimport { middleware,errorHandler } from &quot;supertokens-node/framework/express&quot;;\n\n// SuperTokens recipes\nimport Session from &quot;supertokens-node/recipe/session&quot;;\nimport EmailPassword from &quot;supertokens-node/recipe/emailpassword&quot;;\n\n// CAPTCHA plugin (Node.js)\nimport CaptchaPlugin from &quot;@supertokens-plugins/captcha-nodejs&quot;;\n\n// -------------------------------------------------------------------\n// Express app setup\n// -------------------------------------------------------------------\n\nconst app=express();\n\n// Parse incoming JSON requests\napp.use(express.json());\n\n// -------------------------------------------------------------------\n// Application configuration\n// -------------------------------------------------------------------\n\nconst API_DOMAIN=&quot;http://localhost:3001&quot;;\nconst WEBSITE_DOMAIN=&quot;http://localhost:3000&quot;;\n\n// -------------------------------------------------------------------\n// SuperTokens initialization\n// -------------------------------------------------------------------\n\nSuperTokens.init({\n  framework:&quot;express&quot;,\n\n  // Connection to SuperTokens Core\n  supertokens:{\n    connectionURI:&quot;https://try.supertokens.io&quot;\n  },\n\n  // App metadata shared between frontend and backend\n  appInfo:{\n    appName:&quot;SuperTokens Demo&quot;,\n    apiDomain:API_DOMAIN,\n    websiteDomain:WEBSITE_DOMAIN,\n    apiBasePath:&quot;/auth&quot;,\n    websiteBasePath:&quot;/auth&quot;\n  },\n\n  // Authentication recipes\n  recipeList:[\n    EmailPassword.init(), // Email + password authentication\n    Session.init(),       // Session management\n  ],\n\n  // Experimental features (plugins)\n  experimental:{\n    plugins:[\n      CaptchaPlugin.init({\n\n        // CAPTCHA provider type\n        type:&quot;reCAPTCHAv3&quot;,\n\n        // Provider configuration\n        captcha:{\n          secretKey:process.env.RECAPTCHA_SECRET_KEY\n        },\n\n        // Decide when CAPTCHA should be enforced\n        shouldValidate:(api)=>{          \n          // Enforce CAPTCHA only for signup requests\n          return api === &quot;signUpPOST&quot;;\n        },\n      }),\n    ],\n  },\n});\n\n// -------------------------------------------------------------------\n// CORS configuration\n// IMPORTANT: Must be registered BEFORE SuperTokens middleware\n// -------------------------------------------------------------------\n\napp.use(\n  cors({\n    origin:WEBSITE_DOMAIN,\n    credentials:true,\n    allowedHeaders:[\n      &quot;content-type&quot;,\n      ...SuperTokens.getAllCORSHeaders()\n    ],\n  })\n);\n\n// -------------------------------------------------------------------\n// SuperTokens middleware\n// Handles authentication APIs and session verification\n// -------------------------------------------------------------------\n\napp.use(middleware());\n\n// -------------------------------------------------------------------\n// Application routes\n// -------------------------------------------------------------------\n\napp.get(&quot;/ping&quot;, (req, res) => {\n  res.json({ ok: true });\n});\n\n// -------------------------------------------------------------------\n// SuperTokens error handler\n// IMPORTANT: Must come AFTER all routes\n// -------------------------------------------------------------------\n\napp.use(errorHandler());\n\n// -------------------------------------------------------------------\n// Custom application error handler\n// -------------------------------------------------------------------\napp.use((err, req, res, next) => {\n  console.error(err);\n  res.status(500).json({ message: &quot;Internal server error&quot; });\n});\n\n// -------------------------------------------------------------------\n// Start the server\n// -------------------------------------------------------------------\napp.listen(3001, () => {\n  console.log(&quot;🚀 Backend running on http://localhost:3001&quot;);\n});`, `45790633935066595000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// backend/server/index.js</span>\n<span class=\"token keyword\">import</span> express <span class=\"token keyword\">from</span> <span class=\"token string\">\"express\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> cors <span class=\"token keyword\">from</span> <span class=\"token string\">\"cors\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Core SuperTokens imports</span>\n<span class=\"token keyword\">import</span> SuperTokens <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-node\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> middleware<span class=\"token punctuation\">,</span>errorHandler <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-node/framework/express\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// SuperTokens recipes</span>\n<span class=\"token keyword\">import</span> Session <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-node/recipe/session\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> EmailPassword <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-node/recipe/emailpassword\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// CAPTCHA plugin (Node.js)</span>\n<span class=\"token keyword\">import</span> CaptchaPlugin <span class=\"token keyword\">from</span> <span class=\"token string\">\"@supertokens-plugins/captcha-nodejs\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n<span class=\"token comment\">// Express app setup</span>\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n\n<span class=\"token keyword\">const</span> app<span class=\"token operator\">=</span><span class=\"token function\">express</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Parse incoming JSON requests</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span>express<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n<span class=\"token comment\">// Application configuration</span>\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token constant\">API_DOMAIN</span><span class=\"token operator\">=</span><span class=\"token string\">\"http://localhost:3001\"</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> <span class=\"token constant\">WEBSITE_DOMAIN</span><span class=\"token operator\">=</span><span class=\"token string\">\"http://localhost:3000\"</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n<span class=\"token comment\">// SuperTokens initialization</span>\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n\nSuperTokens<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">framework</span><span class=\"token operator\">:</span><span class=\"token string\">\"express\"</span><span class=\"token punctuation\">,</span>\n\n  <span class=\"token comment\">// Connection to SuperTokens Core</span>\n  <span class=\"token literal-property property\">supertokens</span><span class=\"token operator\">:</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">connectionURI</span><span class=\"token operator\">:</span><span class=\"token string\">\"https://try.supertokens.io\"</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n\n  <span class=\"token comment\">// App metadata shared between frontend and backend</span>\n  <span class=\"token literal-property property\">appInfo</span><span class=\"token operator\">:</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">appName</span><span class=\"token operator\">:</span><span class=\"token string\">\"SuperTokens Demo\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">apiDomain</span><span class=\"token operator\">:</span><span class=\"token constant\">API_DOMAIN</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">websiteDomain</span><span class=\"token operator\">:</span><span class=\"token constant\">WEBSITE_DOMAIN</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">apiBasePath</span><span class=\"token operator\">:</span><span class=\"token string\">\"/auth\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">websiteBasePath</span><span class=\"token operator\">:</span><span class=\"token string\">\"/auth\"</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n\n  <span class=\"token comment\">// Authentication recipes</span>\n  <span class=\"token literal-property property\">recipeList</span><span class=\"token operator\">:</span><span class=\"token punctuation\">[</span>\n    EmailPassword<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Email + password authentication</span>\n    Session<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>       <span class=\"token comment\">// Session management</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n\n  <span class=\"token comment\">// Experimental features (plugins)</span>\n  <span class=\"token literal-property property\">experimental</span><span class=\"token operator\">:</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">plugins</span><span class=\"token operator\">:</span><span class=\"token punctuation\">[</span>\n      CaptchaPlugin<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n\n        <span class=\"token comment\">// CAPTCHA provider type</span>\n        <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span><span class=\"token string\">\"reCAPTCHAv3\"</span><span class=\"token punctuation\">,</span>\n\n        <span class=\"token comment\">// Provider configuration</span>\n        <span class=\"token literal-property property\">captcha</span><span class=\"token operator\">:</span><span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">secretKey</span><span class=\"token operator\">:</span>process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">RECAPTCHA_SECRET_KEY</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n\n        <span class=\"token comment\">// Decide when CAPTCHA should be enforced</span>\n        <span class=\"token function-variable function\">shouldValidate</span><span class=\"token operator\">:</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">api</span><span class=\"token punctuation\">)</span><span class=\"token operator\">=></span><span class=\"token punctuation\">{</span>          \n          <span class=\"token comment\">// Enforce CAPTCHA only for signup requests</span>\n          <span class=\"token keyword\">return</span> api <span class=\"token operator\">===</span> <span class=\"token string\">\"signUpPOST\"</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n<span class=\"token comment\">// CORS configuration</span>\n<span class=\"token comment\">// IMPORTANT: Must be registered BEFORE SuperTokens middleware</span>\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n\napp<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span>\n  <span class=\"token function\">cors</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">origin</span><span class=\"token operator\">:</span><span class=\"token constant\">WEBSITE_DOMAIN</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">credentials</span><span class=\"token operator\">:</span><span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">allowedHeaders</span><span class=\"token operator\">:</span><span class=\"token punctuation\">[</span>\n      <span class=\"token string\">\"content-type\"</span><span class=\"token punctuation\">,</span>\n      <span class=\"token operator\">...</span>SuperTokens<span class=\"token punctuation\">.</span><span class=\"token function\">getAllCORSHeaders</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n<span class=\"token comment\">// SuperTokens middleware</span>\n<span class=\"token comment\">// Handles authentication APIs and session verification</span>\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n\napp<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span><span class=\"token function\">middleware</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n<span class=\"token comment\">// Application routes</span>\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n\napp<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"/ping\"</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  res<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">ok</span><span class=\"token operator\">:</span> <span class=\"token boolean\">true</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n<span class=\"token comment\">// SuperTokens error handler</span>\n<span class=\"token comment\">// IMPORTANT: Must come AFTER all routes</span>\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n\napp<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span><span class=\"token function\">errorHandler</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n<span class=\"token comment\">// Custom application error handler</span>\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">err<span class=\"token punctuation\">,</span> req<span class=\"token punctuation\">,</span> res<span class=\"token punctuation\">,</span> next</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span>err<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  res<span class=\"token punctuation\">.</span><span class=\"token function\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">message</span><span class=\"token operator\">:</span> <span class=\"token string\">\"Internal server error\"</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\n<span class=\"token comment\">// Start the server</span>\n<span class=\"token comment\">// -------------------------------------------------------------------</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">listen</span><span class=\"token punctuation\">(</span><span class=\"token number\">3001</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"🚀 Backend running on http://localhost:3001\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>The <code class=\"language-text\">shouldValidate</code> function controls which endpoints require CAPTCHA verification. Returning <code class=\"language-text\">true</code> for <code class=\"language-text\">signUpPOST</code> enables protection on account creation while leaving login endpoints unaffected.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 630px; \"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/224b19f8fbcbc534b2649a93ace99487/29007/Signup.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 40.50632911392405%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAoUlEQVQY05WQWwrCMBBFu0tdlP+6CVGRgiAIIioVsSqoVDeQj9KAYJImmblSCiLYgjmfF848boQfmBmAc04IURoDgIjQRNQmK6WklC9tuUpCZGNKa60n75wnonrcv7LW+vvUABlAdn9stkl6PCe7/eV6C/gZwCE9DcfT2XwxmsTL1Tq4sLaEGVUHzGBu3kxE3ldV1Xw0AIV89uK80y+6g/wNJUHS09o/laIAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Signup\"\n        title=\"Signup\"\n        src=\"/static/224b19f8fbcbc534b2649a93ace99487/f058b/Signup.png\"\n        srcset=\"/static/224b19f8fbcbc534b2649a93ace99487/c26ae/Signup.png 158w,\n/static/224b19f8fbcbc534b2649a93ace99487/6bdcf/Signup.png 315w,\n/static/224b19f8fbcbc534b2649a93ace99487/f058b/Signup.png 630w,\n/static/224b19f8fbcbc534b2649a93ace99487/40601/Signup.png 945w,\n/static/224b19f8fbcbc534b2649a93ace99487/78612/Signup.png 1260w,\n/static/224b19f8fbcbc534b2649a93ace99487/29007/Signup.png 1600w\"\n        sizes=\"(max-width: 630px) 100vw, 630px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n        decoding=\"async\"\n      />\n  </a>\n    </span></p>\n<h2 id=\"how-do-i-verify-captcha-tokens-server-side-in-my-authentication-backend\" style=\"position:relative;\"><a href=\"#how-do-i-verify-captcha-tokens-server-side-in-my-authentication-backend\" aria-label=\"how do i verify captcha tokens server side in my authentication 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><strong>How Do I Verify CAPTCHA Tokens Server-Side in My Authentication Backend?</strong></h2>\n<p>Client-side CAPTCHA widgets prevent casual automated submissions but offer no protection against deliberate attacks. Adversaries can extract API endpoints, bypass frontend code entirely, and submit requests with fabricated or missing CAPTCHA tokens. Server-side verification provides the actual security boundary.</p>\n<h3 id=\"why-client-side-validation-is-insufficient\" style=\"position:relative;\"><a href=\"#why-client-side-validation-is-insufficient\" aria-label=\"why client side validation is insufficient 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 Client-Side Validation Is Insufficient</strong></h3>\n<p>Frontend code executes in an untrusted environment controlled by the user. Browser developer tools enable attackers to:</p>\n<ul>\n<li>Disable JavaScript that enforces CAPTCHA display</li>\n<li>Modify the DOM to remove widget requirements</li>\n<li>Intercept and replay valid tokens from legitimate sessions</li>\n<li>Submit API requests directly using curl or automated scripts</li>\n</ul>\n<p>Server-side verification closes these attack vectors by requiring cryptographic proof that each token came from the CAPTCHA provider and\nrepresents a successful human challenge.</p>\n<h3 id=\"provider-token-verification-flow\" style=\"position:relative;\"><a href=\"#provider-token-verification-flow\" aria-label=\"provider token verification flow 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>Provider Token Verification Flow</strong></h3>\n<p>All major CAPTCHA providers follow a similar server-side verification pattern:</p>\n<ol>\n<li>Frontend widget generates a token after the user completes the challenge</li>\n<li>Client sends token to application backend in signup/login request</li>\n<li>Backend extracts the token and forwards it to the provider verification API</li>\n<li>Provider validates token authenticity, checks expiration, and returns success/failure</li>\n<li>Backend proceeds with the authentication request only if verification succeeds</li>\n</ol>\n<p><strong>Generic Verification Example:</strong></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"51339303175761650000\"\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(`async function verifyCaptchaToken(token, secretKey) {\n    const verificationURL = &quot;https://www.google.com/recaptcha/api/siteverify&quot;;\n   \n    const response = await fetch(verificationURL, {\n        method: &quot;POST&quot;,\n        headers: { &quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot; },\n        body: \\`secret=\\${secretKey}&response=\\${token}\\`\n    });\n   \n    const result = await response.json();\n   \n    if (!result.success) {\n        throw new Error(&quot;CAPTCHA verification failed&quot;);\n    }\n   \n    return result;\n}`, `51339303175761650000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">verifyCaptchaToken</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">token<span class=\"token punctuation\">,</span> secretKey</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> verificationURL <span class=\"token operator\">=</span> <span class=\"token string\">\"https://www.google.com/recaptcha/api/siteverify\"</span><span class=\"token punctuation\">;</span>\n   \n    <span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>verificationURL<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">method</span><span class=\"token operator\">:</span> <span class=\"token string\">\"POST\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">headers</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span> <span class=\"token string-property property\">\"Content-Type\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"application/x-www-form-urlencoded\"</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">body</span><span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">secret=</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>secretKey<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">&amp;response=</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>token<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n   \n    <span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n   \n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>result<span class=\"token punctuation\">.</span>success<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">throw</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Error</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"CAPTCHA verification failed\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n   \n    <span class=\"token keyword\">return</span> result<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>This verification request authenticates with the secret key (never exposed to clients) and confirms the token’s validity. The provider\nresponse includes additional metadata such as timestamp, hostname, and risk scores.</p>\n<h3 id=\"supertokens-verification-logic\" style=\"position:relative;\"><a href=\"#supertokens-verification-logic\" aria-label=\"supertokens verification logic 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>SuperTokens Verification Logic</strong></h3>\n<p>SuperTokens abstracts provider-specific verification APIs behind a unified plugin interface. The <code class=\"language-text\">shouldValidate</code> function receives API\ncontext and request input, enabling conditional enforcement:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"20968666748031283000\"\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(`EmailPassword.init({\n    captcha: {\n        secretKey: process.env.RECAPTCHA_SECRET_KEY,\n        shouldValidate: async (api, input) => {\n            // Always require CAPTCHA on signup\n            if (api === &quot;signUpPOST&quot;) {\n                return true;\n            }\n           \n            // Require CAPTCHA on signin after failed attempts\n            if (api === &quot;signInPOST&quot;) {\n                const failedAttempts = await getFailedLoginCount(input.email);\n                return failedAttempts >= 3;\n            }\n           \n            // Require CAPTCHA on password reset\n            if (api === &quot;generatePasswordResetTokenPOST&quot;) {\n                return true;\n            }\n           \n            return false;\n        }\n    }\n});`, `20968666748031283000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\">EmailPassword<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">captcha</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">secretKey</span><span class=\"token operator\">:</span> process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">RECAPTCHA_SECRET_KEY</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function-variable function\">shouldValidate</span><span class=\"token operator\">:</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">api<span class=\"token punctuation\">,</span> input</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// Always require CAPTCHA on signup</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>api <span class=\"token operator\">===</span> <span class=\"token string\">\"signUpPOST\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n           \n            <span class=\"token comment\">// Require CAPTCHA on signin after failed attempts</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>api <span class=\"token operator\">===</span> <span class=\"token string\">\"signInPOST\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">const</span> failedAttempts <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">getFailedLoginCount</span><span class=\"token punctuation\">(</span>input<span class=\"token punctuation\">.</span>email<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token keyword\">return</span> failedAttempts <span class=\"token operator\">>=</span> <span class=\"token number\">3</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n           \n            <span class=\"token comment\">// Require CAPTCHA on password reset</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>api <span class=\"token operator\">===</span> <span class=\"token string\">\"generatePasswordResetTokenPOST\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n           \n            <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>This logic applies different policies per endpoint: mandatory CAPTCHA for signup and password reset, conditional CAPTCHA for login based on failure history, and no CAPTCHA for other operations. The verification request happens automatically when <code class=\"language-text\">shouldValidate</code> returns <code class=\"language-text\">true</code>.</p>\n<h2 id=\"can-i-show-captcha-only-on-suspicious-login-attempts\" style=\"position:relative;\"><a href=\"#can-i-show-captcha-only-on-suspicious-login-attempts\" aria-label=\"can i show captcha only on suspicious login attempts 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>Can I Show CAPTCHA Only on Suspicious Login Attempts?</strong></h2>\n<p>Always-on CAPTCHA enforcement protects against bots but frustrates legitimate users. Image selection challenges interrupt workflow, invisible CAPTCHAs introduce loading delays, and checkbox interactions add friction to every authentication. Applying CAPTCHA universally\nreduces user experience without proportional security benefit—most login attempts are legitimate.</p>\n<h3 id=\"risk-based-captcha-enforcement\" style=\"position:relative;\"><a href=\"#risk-based-captcha-enforcement\" aria-label=\"risk based captcha enforcement 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>Risk-Based CAPTCHA Enforcement</strong></h3>\n<p>Conditional CAPTCHA strategies apply bot prevention selectively based on risk signals:</p>\n<p><strong>Trigger Conditions:</strong></p>\n<ul>\n<li>Multiple failed login attempts from same email address</li>\n<li>Requests originating from suspicious user agents</li>\n<li>Velocity anomalies (many login requests in short timeframe)</li>\n<li>Missing or malformed request headers indicating automated tools</li>\n<li>Geographic mismatches between registration and login location</li>\n</ul>\n<p>Risk-based enforcement maintains security while preserving user experience. Legitimate users with clean history encounter no CAPTCHA\nchallenges. Only when the system detects suspicious patterns does the additional verification step appear.</p>\n<h3 id=\"backend-conditional-validation\" style=\"position:relative;\"><a href=\"#backend-conditional-validation\" aria-label=\"backend conditional validation 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>Backend Conditional Validation</strong></h3>\n<p>The <code class=\"language-text\">shouldValidate</code> function examines request characteristics and escalates protection conditionally. This function receives both the API\nidentifier and input context:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"6997497713386314000\"\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(`import SuperTokens from 'supertokens-node';\nimport CaptchaPlugin, { ShouldValidate } from '@supertokens-plugins/captcha-nodejs';\n\n\n// Track failed attempts in memory (use Redis in production)\nconst failedLoginCache = new Map();\n\n\nfunction getFailedLoginCount(email) {\n  return failedLoginCache.get(email) || 0;\n}\n\n\nfunction incrementFailedLogin(email) {\n  const current = failedLoginCache.get(email) || 0;\n  failedLoginCache.set(email, current + 1);\n \n  // Expire after 1 hour\n  setTimeout(() => failedLoginCache.delete(email), 3600000);\n}\n\n\nconst shouldValidate: ShouldValidate = (api, input) => {\n  // Only require CAPTCHA for sign up\n  if (api === 'signUpPOST') {\n    return true;\n  }\n\n\n  // Check request headers for suspicious activity\n  if (api === 'signInPOST') {\n    const userAgent = input.options.req.getHeaderValue('user-agent');\n    if (!userAgent || userAgent.includes('bot')) {\n      return true;\n    }\n\n\n    // Optional: Check failed login history\n    const emailField = input.formFields?.find(f => f.id === 'email');\n    if (emailField) {\n      const failedCount = getFailedLoginCount(emailField.value);\n      if (failedCount >= 3) {\n        return true;\n      }\n    }\n\n\n    return false;\n  }\n\n\n  return false;\n};\n\n\nSuperTokens.init({\n  supertokens: {\n    connectionURI: '...',\n  },\n  appInfo: {\n    // your app info\n  },\n  recipeList: [\n    // your recipes\n  ],\n  experimental: {\n    plugins: [\n      CaptchaPlugin.init({\n        type: 'reCAPTCHAv3', // or &quot;reCAPTCHAv2&quot; or &quot;turnstile&quot;\n        captcha: {\n          secretKey: process.env.RECAPTCHA_SECRET_KEY,\n        },\n        shouldValidate,\n      }),\n    ],\n  }\n});`, `6997497713386314000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> SuperTokens <span class=\"token keyword\">from</span> <span class=\"token string\">'supertokens-node'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> CaptchaPlugin<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span> ShouldValidate <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'@supertokens-plugins/captcha-nodejs'</span><span class=\"token punctuation\">;</span>\n\n\n<span class=\"token comment\">// Track failed attempts in memory (use Redis in production)</span>\n<span class=\"token keyword\">const</span> failedLoginCache <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Map</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">getFailedLoginCount</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">email</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> failedLoginCache<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span>email<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">incrementFailedLogin</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">email</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> current <span class=\"token operator\">=</span> failedLoginCache<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span>email<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token number\">0</span><span class=\"token punctuation\">;</span>\n  failedLoginCache<span class=\"token punctuation\">.</span><span class=\"token function\">set</span><span class=\"token punctuation\">(</span>email<span class=\"token punctuation\">,</span> current <span class=\"token operator\">+</span> <span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n \n  <span class=\"token comment\">// Expire after 1 hour</span>\n  <span class=\"token function\">setTimeout</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> failedLoginCache<span class=\"token punctuation\">.</span><span class=\"token function\">delete</span><span class=\"token punctuation\">(</span>email<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3600000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n\n<span class=\"token keyword\">const</span> <span class=\"token literal-property property\">shouldValidate</span><span class=\"token operator\">:</span> <span class=\"token function-variable function\">ShouldValidate</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">api<span class=\"token punctuation\">,</span> input</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">// Only require CAPTCHA for sign up</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>api <span class=\"token operator\">===</span> <span class=\"token string\">'signUpPOST'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n\n  <span class=\"token comment\">// Check request headers for suspicious activity</span>\n  <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>api <span class=\"token operator\">===</span> <span class=\"token string\">'signInPOST'</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> userAgent <span class=\"token operator\">=</span> input<span class=\"token punctuation\">.</span>options<span class=\"token punctuation\">.</span>req<span class=\"token punctuation\">.</span><span class=\"token function\">getHeaderValue</span><span class=\"token punctuation\">(</span><span class=\"token string\">'user-agent'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>userAgent <span class=\"token operator\">||</span> userAgent<span class=\"token punctuation\">.</span><span class=\"token function\">includes</span><span class=\"token punctuation\">(</span><span class=\"token string\">'bot'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n\n    <span class=\"token comment\">// Optional: Check failed login history</span>\n    <span class=\"token keyword\">const</span> emailField <span class=\"token operator\">=</span> input<span class=\"token punctuation\">.</span>formFields<span class=\"token operator\">?.</span><span class=\"token function\">find</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">f</span> <span class=\"token operator\">=></span> f<span class=\"token punctuation\">.</span>id <span class=\"token operator\">===</span> <span class=\"token string\">'email'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>emailField<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> failedCount <span class=\"token operator\">=</span> <span class=\"token function\">getFailedLoginCount</span><span class=\"token punctuation\">(</span>emailField<span class=\"token punctuation\">.</span>value<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>failedCount <span class=\"token operator\">>=</span> <span class=\"token number\">3</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n\n\n    <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n\n  <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n\nSuperTokens<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">supertokens</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">connectionURI</span><span class=\"token operator\">:</span> <span class=\"token string\">'...'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">appInfo</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// your app info</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">recipeList</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token comment\">// your recipes</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">experimental</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">plugins</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      CaptchaPlugin<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'reCAPTCHAv3'</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// or \"reCAPTCHAv2\" or \"turnstile\"</span>\n        <span class=\"token literal-property property\">captcha</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">secretKey</span><span class=\"token operator\">:</span> process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">RECAPTCHA_SECRET_KEY</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        shouldValidate<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>This logic applies layered risk assessment:</p>\n<ul>\n<li><strong>Always protect signup</strong> - Unconditional CAPTCHA for <code class=\"language-text\">signUpPOST</code></li>\n<li><strong>User agent inspection</strong> - Trigger CAPTCHA for missing or bot-like user agents</li>\n<li><strong>Failed attempt history</strong> - Escalate to CAPTCHA after 3 failed login attempts</li>\n<li><strong>API-specific policies</strong> - Different rules for different authentication endpoints</li>\n</ul>\n<p>The validation logic remains isolated in the plugin configuration rather than scattered across authentication code. The <code class=\"language-text\">input</code> parameter provides access to request headers, form fields, and other context needed for risk assessment.</p>\n<p><strong>Production Note:</strong> Replace the in-memory <code class=\"language-text\">Map</code> with Redis or another persistent cache to maintain state across server restarts and multiple instances.</p>\n<h3 id=\"frontend-conditional-rendering\" style=\"position:relative;\"><a href=\"#frontend-conditional-rendering\" aria-label=\"frontend conditional rendering 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>Frontend Conditional Rendering</strong></h3>\n<p>The frontend must coordinate with backend enforcement to display CAPTCHA widgets only when required. SuperTokens enables conditional rendering through custom input containers.</p>\n<p>For reCAPTCHA v2 with visible widgets or when you want precise control over which forms display CAPTCHA:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"14227175720607410000\"\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(`import { forwardRef, useEffect } from 'react';\nimport SuperTokens from 'supertokens-auth-react';\nimport CaptchaPlugin, {\n  CaptchInputContainerProps,\n  useCaptcha,\n} from '@supertokens-plugins/captcha-react';\n\n\nconst CaptchaInputContainer = forwardRef\n  HTMLDivElement,\n  CaptchInputContainerProps\n>((props, ref) => {\n  const { form, ...rest } = props;\n  const { loadAndRender, containerId } = useCaptcha();\n\n\n  useEffect(() => {\n    // CAPTCHA applies/renders only for the EmailPasswordSignUpForm\n    // and the EmailPasswordResetPasswordEmail\n    if (\n      form === 'EmailPasswordSignUpForm' ||\n      form === 'EmailPasswordResetPasswordEmail'\n    ) {\n      loadAndRender();\n    }\n  }, [form]);\n\n\n  return (\n    <div ref={ref} id={containerId} className=&quot;CAPTCHA-container&quot; {...rest} />\n  );\n});\n\n\nSuperTokens.init({\n  appInfo: {\n    // your app info\n  },\n  recipeList: [\n    // your recipes\n  ],\n  experimental: {\n    plugins: [\n      CaptchaPlugin.init({\n        type: 'reCAPTCHAv3', // or &quot;reCAPTCHAv2&quot; or &quot;turnstile&quot;\n        captcha: {\n          sitekey: 'your-site-key',\n        },\n        InputContainer: CaptchaInputContainer,\n      }),\n    ],\n  }\n});`, `14227175720607410000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> forwardRef<span class=\"token punctuation\">,</span> useEffect <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'react'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> SuperTokens <span class=\"token keyword\">from</span> <span class=\"token string\">'supertokens-auth-react'</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">import</span> CaptchaPlugin<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n  CaptchInputContainerProps<span class=\"token punctuation\">,</span>\n  useCaptcha<span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'@supertokens-plugins/captcha-react'</span><span class=\"token punctuation\">;</span>\n\n\n<span class=\"token keyword\">const</span> CaptchaInputContainer <span class=\"token operator\">=</span> forwardRef\n  HTMLDivElement<span class=\"token punctuation\">,</span>\n  CaptchInputContainerProps\n<span class=\"token operator\">></span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">props<span class=\"token punctuation\">,</span> ref</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> form<span class=\"token punctuation\">,</span> <span class=\"token operator\">...</span>rest <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> props<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> loadAndRender<span class=\"token punctuation\">,</span> containerId <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">useCaptcha</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\n  <span class=\"token function\">useEffect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// CAPTCHA applies/renders only for the EmailPasswordSignUpForm</span>\n    <span class=\"token comment\">// and the EmailPasswordResetPasswordEmail</span>\n    <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>\n      form <span class=\"token operator\">===</span> <span class=\"token string\">'EmailPasswordSignUpForm'</span> <span class=\"token operator\">||</span>\n      form <span class=\"token operator\">===</span> <span class=\"token string\">'EmailPasswordResetPasswordEmail'</span>\n    <span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token function\">loadAndRender</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">[</span>form<span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\n  <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>\n    <span class=\"token operator\">&lt;</span>div ref<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span>ref<span class=\"token punctuation\">}</span> id<span class=\"token operator\">=</span><span class=\"token punctuation\">{</span>containerId<span class=\"token punctuation\">}</span> className<span class=\"token operator\">=</span><span class=\"token string\">\"CAPTCHA-container\"</span> <span class=\"token punctuation\">{</span><span class=\"token operator\">...</span>rest<span class=\"token punctuation\">}</span> <span class=\"token operator\">/</span><span class=\"token operator\">></span>\n  <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\nSuperTokens<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">appInfo</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// your app info</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">recipeList</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token comment\">// your recipes</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token literal-property property\">experimental</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">plugins</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      CaptchaPlugin<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">'reCAPTCHAv3'</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// or \"reCAPTCHAv2\" or \"turnstile\"</span>\n        <span class=\"token literal-property property\">captcha</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">sitekey</span><span class=\"token operator\">:</span> <span class=\"token string\">'your-site-key'</span><span class=\"token punctuation\">,</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">InputContainer</span><span class=\"token operator\">:</span> CaptchaInputContainer<span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>This configuration renders CAPTCHA widgets only on the signup form and password reset email form, matching common backend enforcement policies. The form prop identifies which form is currently being rendered, allowing precise control over CAPTCHA placement.</p>\n<p><strong>Note:</strong> For reCAPTCHA v3 or Turnstile (both invisible), conditional rendering via <code class=\"language-text\">InputContainer</code> is optional since the widgets handle\nverification transparently in the background.</p>\n<h2 id=\"which-captcha-provider-should-i-use-recaptcha-hcaptcha-or-turnstile\" style=\"position:relative;\"><a href=\"#which-captcha-provider-should-i-use-recaptcha-hcaptcha-or-turnstile\" aria-label=\"which captcha provider should i use recaptcha hcaptcha or turnstile 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>Which CAPTCHA Provider Should I Use: reCAPTCHA, hCaptcha, or Turnstile?</strong></h2>\n<p>CAPTCHA provider selection involves balancing security effectiveness, user experience friction, privacy implications, and ecosystem\ncompatibility. Three major providers dominate the market, each with distinct characteristics.</p>\n<h3 id=\"recaptcha-google\" style=\"position:relative;\"><a href=\"#recaptcha-google\" aria-label=\"recaptcha google 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>reCAPTCHA (Google)</strong></h3>\n<p><strong>Strengths:</strong></p>\n<ul>\n<li>The largest deployment base provides extensive training data for bot detection</li>\n<li>Invisible reCAPTCHA v3 scores requests without user interaction</li>\n<li>Deep integration with Google’s security infrastructure</li>\n<li>Comprehensive documentation and community support</li>\n</ul>\n<p><strong>Considerations:</strong></p>\n<ul>\n<li>Data collection raises privacy concerns in some jurisdictions</li>\n<li>Google dependency may conflict with privacy policies or vendor diversification goals</li>\n<li>V2 checkbox challenges can frustrate users with accessibility needs</li>\n</ul>\n<p><strong>Use Cases:</strong>\nApplications prioritizing maximum bot detection accuracy and compatibility with existing Google infrastructure. Best suited for high-traffic services where Google’s data advantages deliver measurable value.</p>\n<h3 id=\"hcaptcha\" style=\"position:relative;\"><a href=\"#hcaptcha\" aria-label=\"hcaptcha 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>hCaptcha</strong></h3>\n<p><strong>Strengths:</strong></p>\n<ul>\n<li>Privacy-focused alternative to reCAPTCHA with a GDPR compliance focus</li>\n<li>Pays website operators for solved CAPTCHA (machine learning data labeling business model)</li>\n<li>Accessible design with screen reader support</li>\n<li>No Google dependency for privacy-conscious deployments</li>\n</ul>\n<p><strong>Considerations:</strong></p>\n<ul>\n<li>A smaller training dataset compared to reCAPTCHA may affect detection accuracy.</li>\n<li>Less widespread adoption results in fewer integration examples</li>\n<li>Challenge difficulty can vary based on training data requirements</li>\n</ul>\n<p><strong>Use Cases:</strong>\nApplications with strict privacy requirements, European deployments requiring GDPR compliance, or organizations seeking vendor diversification away from Google services.</p>\n<h3 id=\"turnstile-cloudflare\" style=\"position:relative;\"><a href=\"#turnstile-cloudflare\" aria-label=\"turnstile cloudflare 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>Turnstile (Cloudflare)</strong></h3>\n<p><strong>Strengths:</strong></p>\n<ul>\n<li>Minimal user friction with invisible background verification</li>\n<li>Privacy-first design with no personal data collection</li>\n<li>Free tier with generous limits</li>\n<li>Integration with Cloudflare’s broader security ecosystem</li>\n</ul>\n<p><strong>Considerations:</strong></p>\n<ul>\n<li>Newer product with a smaller deployment history</li>\n<li>Effectiveness against sophisticated bots is still being validated at scale</li>\n<li>Requires a Cloudflare account even if not using other Cloudflare services</li>\n</ul>\n<p><strong>Use Cases:</strong>\nModern applications prioritize user experience and privacy. Ideal for Cloudflare customers who can leverage integrated security features. Best for deployments accepting newer technology in exchange for reduced friction.</p>\n<h3 id=\"provider-comparison\" style=\"position:relative;\"><a href=\"#provider-comparison\" aria-label=\"provider comparison 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>Provider Comparison</strong></h3>\n<table>\n<thead>\n<tr>\n<th>Feature</th>\n<th>reCAPTCHA</th>\n<th>hCaptcha</th>\n<th>Turnstile</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>User Friction</td>\n<td>Low (v3) / Medium (v2)</td>\n<td>Medium</td>\n<td>Very Low</td>\n</tr>\n<tr>\n<td>Privacy</td>\n<td>Lower</td>\n<td>Higher</td>\n<td>Highest</td>\n</tr>\n<tr>\n<td>Accuracy</td>\n<td>Highest</td>\n<td>High</td>\n<td>Developing</td>\n</tr>\n<tr>\n<td>Adoption</td>\n<td>Highest</td>\n<td>Growing</td>\n<td>Early</td>\n</tr>\n<tr>\n<td>Cost</td>\n<td>Free (limits)</td>\n<td>Free (limits)</td>\n<td>Free (generous)</td>\n</tr>\n</tbody>\n</table>\n<h3 id=\"supertokens-provider-flexibility\" style=\"position:relative;\"><a href=\"#supertokens-provider-flexibility\" aria-label=\"supertokens provider flexibility 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>SuperTokens Provider Flexibility</strong></h3>\n<p>SuperTokens maintains provider-agnostic CAPTCHA integration. The plugin interface abstracts provider-specific details, enabling swaps without refactoring authentication logic:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"70403808435685630000\"\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(`// Switch from reCAPTCHA to Turnstile\nimport { Turnstile } from &quot;supertokens-auth-react/recipe/emailpassword/captcha&quot;;\n\n\nEmailPassword.init({\n    captcha: Turnstile({\n        siteKey: &quot;YOUR_TURNSTILE_SITE_KEY&quot;\n    })\n});`, `70403808435685630000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// Switch from reCAPTCHA to Turnstile</span>\n<span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> Turnstile <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-auth-react/recipe/emailpassword/captcha\"</span><span class=\"token punctuation\">;</span>\n\n\nEmailPassword<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">captcha</span><span class=\"token operator\">:</span> <span class=\"token function\">Turnstile</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">siteKey</span><span class=\"token operator\">:</span> <span class=\"token string\">\"YOUR_TURNSTILE_SITE_KEY\"</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Backend configuration similarly accepts different providers through a consistent interface:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"9031015974848367000\"\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(`EmailPassword.init({\n    captcha: {\n        provider: &quot;turnstile&quot;,\n        secretKey: process.env.TURNSTILE_SECRET_KEY,\n        shouldValidate: async (api) => api === &quot;signUpPOST&quot;\n    }\n});`, `9031015974848367000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\">EmailPassword<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">captcha</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">provider</span><span class=\"token operator\">:</span> <span class=\"token string\">\"turnstile\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">secretKey</span><span class=\"token operator\">:</span> process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">TURNSTILE_SECRET_KEY</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function-variable function\">shouldValidate</span><span class=\"token operator\">:</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">api</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> api <span class=\"token operator\">===</span> <span class=\"token string\">\"signUpPOST\"</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>This abstraction protects authentication flows from provider churn. Switching CAPTCHA vendors requires updating the configuration, not\nrewriting the integration code.</p>\n<h2 id=\"can-i-integrate-captcha-with-supertokens-passwordless-or-reset-flows\" style=\"position:relative;\"><a href=\"#can-i-integrate-captcha-with-supertokens-passwordless-or-reset-flows\" aria-label=\"can i integrate captcha with supertokens passwordless or reset flows 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>Can I Integrate CAPTCHA With SuperTokens Passwordless or Reset Flows?</strong></h2>\n<p>Bot protection extends beyond traditional username/password authentication. Passwordless flows, password reset mechanisms, and magic\nlink generation all present abuse vectors that benefit from CAPTCHA enforcement.</p>\n<h3 id=\"passwordless-code-generation\" style=\"position:relative;\"><a href=\"#passwordless-code-generation\" aria-label=\"passwordless code generation 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>Passwordless Code Generation</strong></h3>\n<p>Passwordless authentication sends one-time codes via email or SMS. Without protection, attackers can:</p>\n<ul>\n<li>Flood email/SMS services with code generation requests</li>\n<li>Exhaust rate limits and trigger service degradation</li>\n<li>Execute denial-of-service attacks targeting specific email addresses</li>\n<li>Generate costs through SMS provider billing</li>\n</ul>\n<p>CAPTCHA on code generation endpoints prevents automated abuse while maintaining passwordless convenience.</p>\n<h3 id=\"password-reset-protection\" style=\"position:relative;\"><a href=\"#password-reset-protection\" aria-label=\"password reset protection 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>Password Reset Protection</strong></h3>\n<p>Password reset flows enable account takeover if insufficiently protected:</p>\n<ul>\n<li>Attackers enumerate valid email addresses by triggering reset emails</li>\n<li>Mass password reset requests overload email services</li>\n<li>Targeted reset requests harass specific users with spam</li>\n</ul>\n<p>Requiring CAPTCHA before sending reset emails mitigates these vectors without weakening account recovery for legitimate users.</p>\n<h3 id=\"supertokens-passwordless-captcha\" style=\"position:relative;\"><a href=\"#supertokens-passwordless-captcha\" aria-label=\"supertokens passwordless captcha 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>SuperTokens Passwordless CAPTCHA</strong></h3>\n<p>SuperTokens applies CAPTCHA to passwordless flows through the same plugin mechanism:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"42259988889161090000\"\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(`import Passwordless from &quot;supertokens-node/recipe/passwordless&quot;;\n\n\nPasswordless.init({\n    contactMethod: &quot;EMAIL&quot;,\n    captcha: {\n        secretKey: process.env.RECAPTCHA_SECRET_KEY,\n        shouldValidate: async (api) => {\n            // Protect code creation\n            if (api === &quot;createCodePOST&quot;) {\n                return true;\n            }\n            // Optionally protect code consumption\n            if (api === &quot;consumeCodePOST&quot;) {\n                const failedAttempts = await getCodeFailureCount();\n                return failedAttempts >= 3;\n            }\n            return false;\n        }\n    }\n});`, `42259988889161090000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> Passwordless <span class=\"token keyword\">from</span> <span class=\"token string\">\"supertokens-node/recipe/passwordless\"</span><span class=\"token punctuation\">;</span>\n\n\nPasswordless<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">contactMethod</span><span class=\"token operator\">:</span> <span class=\"token string\">\"EMAIL\"</span><span class=\"token punctuation\">,</span>\n    <span class=\"token literal-property property\">captcha</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">secretKey</span><span class=\"token operator\">:</span> process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">RECAPTCHA_SECRET_KEY</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function-variable function\">shouldValidate</span><span class=\"token operator\">:</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">api</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// Protect code creation</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>api <span class=\"token operator\">===</span> <span class=\"token string\">\"createCodePOST\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token comment\">// Optionally protect code consumption</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>api <span class=\"token operator\">===</span> <span class=\"token string\">\"consumeCodePOST\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">const</span> failedAttempts <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">getCodeFailureCount</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n                <span class=\"token keyword\">return</span> failedAttempts <span class=\"token operator\">>=</span> <span class=\"token number\">3</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>This configuration protects code generation unconditionally while applying conditional protection to code consumption based on failure\nhistory.</p>\n<h3 id=\"password-reset-captcha\" style=\"position:relative;\"><a href=\"#password-reset-captcha\" aria-label=\"password reset captcha 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>Password Reset CAPTCHA</strong></h3>\n<p>Standard EmailPassword recipe supports CAPTCHA on reset flows:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"56228148413448030000\"\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(`EmailPassword.init({\n    captcha: {\n        secretKey: process.env.RECAPTCHA_SECRET_KEY,\n        shouldValidate: async (api) => {\n            // Protect password reset token generation\n            if (api === &quot;generatePasswordResetTokenPOST&quot;) {\n                return true;\n            }\n            // Optionally protect reset submission\n            if (api === &quot;passwordResetPOST&quot;) {\n                return true;\n            }\n            return false;\n        }\n    }\n});`, `56228148413448030000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\">EmailPassword<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">captcha</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">secretKey</span><span class=\"token operator\">:</span> process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">RECAPTCHA_SECRET_KEY</span><span class=\"token punctuation\">,</span>\n        <span class=\"token function-variable function\">shouldValidate</span><span class=\"token operator\">:</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">api</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n            <span class=\"token comment\">// Protect password reset token generation</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>api <span class=\"token operator\">===</span> <span class=\"token string\">\"generatePasswordResetTokenPOST\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token comment\">// Optionally protect reset submission</span>\n            <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>api <span class=\"token operator\">===</span> <span class=\"token string\">\"passwordResetPOST\"</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n                <span class=\"token keyword\">return</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">;</span>\n            <span class=\"token punctuation\">}</span>\n            <span class=\"token keyword\">return</span> <span class=\"token boolean\">false</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Protecting both token generation and reset submission provides defense in depth: CAPTCHA prevents automated token requests and validates human interaction when users submit new passwords.</p>\n<h2 id=\"how-do-i-configure-site-keys-and-secret-keys-for-captcha\" style=\"position:relative;\"><a href=\"#how-do-i-configure-site-keys-and-secret-keys-for-captcha\" aria-label=\"how do i configure site keys and secret keys for captcha 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>How Do I Configure Site Keys and Secret Keys for CAPTCHA?</strong></h2>\n<p>CAPTCHA providers issue two credentials that serve distinct roles in the verification flow:</p>\n<p><strong>Site Key (Public)</strong></p>\n<p>The site key identifies your application to the CAPTCHA provider and enables frontend widget loading. This key appears in client-side code, browser network traffic, and page source. Site keys are not sensitive; they merely identify which provider account handles verification requests.</p>\n<p><strong>Secret Key (Private)</strong></p>\n<p>The secret key authenticates backend verification requests to the provider’s API. This credential proves your backend’s authority to\nvalidate tokens. Secret keys must remain confidential; exposure enables attackers to bypass CAPTCHA by validating their own tokens.</p>\n<h3 id=\"obtaining-provider-keys\" style=\"position:relative;\"><a href=\"#obtaining-provider-keys\" aria-label=\"obtaining provider keys 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>Obtaining Provider Keys</strong></h3>\n<p><strong>reCAPTCHA:</strong>\nRegister at <a href=\"https://www.google.com/recaptcha/admin\" target=\"_blank\" rel=\"nofollow\">https://www.google.com/recaptcha/admin</a>, create a site registration, and receive both the site key and the secret key.</p>\n<p><strong>hCaptcha:</strong>\nCreate an account at <a href=\"https://dashboard.hcaptcha.com\" target=\"_blank\" rel=\"nofollow\">https://dashboard.hcaptcha.com</a>, add a new site, and obtain the sitekey and secret from settings.</p>\n<p><strong>Turnstile:</strong>\nAccess Cloudflare dashboard at <a href=\"https://dash.cloudflare.com\" target=\"_blank\" rel=\"nofollow\">https://dash.cloudflare.com</a>, navigate to Turnstile, create a site, and retrieve keys.</p>\n<h3 id=\"secure-storage-best-practices\" style=\"position:relative;\"><a href=\"#secure-storage-best-practices\" aria-label=\"secure storage best practices 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>Secure Storage Best Practices</strong></h3>\n<p>Secret keys require protection equivalent to database passwords or API tokens:</p>\n<p><strong>Environment Variables:</strong>\nStore secret keys in environment variables, never hardcode in source files:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"96544743560989980000\"\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(`# .env file (never committed to version control)\nRECAPTCHA_SECRET_KEY=YOUR_KEY\nTURNSTILE_SECRET_KEY=YOUR_KEY`, `96544743560989980000`)\"\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=\"dotenv\"><pre class=\"language-dotenv\"><code class=\"language-dotenv\"># .env file (never committed to version control)\nRECAPTCHA_SECRET_KEY=YOUR_KEY\nTURNSTILE_SECRET_KEY=YOUR_KEY</code></pre></div>\n<p>Load environment variables in your backend:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"82206126053025300000\"\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(`import dotenv from &quot;dotenv&quot;;\ndotenv.config();\nconst secretKey = process.env.RECAPTCHA_SECRET_KEY;`, `82206126053025300000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> dotenv <span class=\"token keyword\">from</span> <span class=\"token string\">\"dotenv\"</span><span class=\"token punctuation\">;</span>\ndotenv<span class=\"token punctuation\">.</span><span class=\"token function\">config</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> secretKey <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">RECAPTCHA_SECRET_KEY</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>For production, set environment variables through your hosting platform (Vercel, Netlify, Heroku, etc.).</p>\n<h3 id=\"supertokens-configuration\" style=\"position:relative;\"><a href=\"#supertokens-configuration\" aria-label=\"supertokens configuration 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>SuperTokens Configuration</strong></h3>\n<p>SuperTokens separates frontend and backend key configuration:</p>\n<p><strong>Frontend (Site Key):</strong></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"61194070893768475000\"\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(`import CaptchaPlugin from &quot;@supertokens-plugins/captcha-react&quot;;\n\n\nSuperTokens.init({\n  experimental: {\n    plugins: [\n      CaptchaPlugin.init({\n        type: &quot;reCAPTCHAv3&quot;,\n        captcha: {\n          sitekey: &quot;YOUR_SITE_KEY&quot;, // Public site key\n        },\n      }),\n    ],\n  }\n});`, `61194070893768475000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> CaptchaPlugin <span class=\"token keyword\">from</span> <span class=\"token string\">\"@supertokens-plugins/captcha-react\"</span><span class=\"token punctuation\">;</span>\n\n\nSuperTokens<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">experimental</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">plugins</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      CaptchaPlugin<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">\"reCAPTCHAv3\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">captcha</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">sitekey</span><span class=\"token operator\">:</span> <span class=\"token string\">\"YOUR_SITE_KEY\"</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Public site key</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p><strong>Backend (Secret Key):</strong></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"11084941901613222000\"\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(`import CaptchaPlugin from &quot;@supertokens-plugins/captcha-nodejs&quot;;\n\n\nSuperTokens.init({\n  experimental: {\n    plugins: [\n      CaptchaPlugin.init({\n        type: &quot;reCAPTCHAv3&quot;,\n        captcha: {\n          secretKey: process.env.RECAPTCHA_SECRET_KEY, // From environment\n        },\n      }),\n    ],\n  }\n});`, `11084941901613222000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> CaptchaPlugin <span class=\"token keyword\">from</span> <span class=\"token string\">\"@supertokens-plugins/captcha-nodejs\"</span><span class=\"token punctuation\">;</span>\n\n\nSuperTokens<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  <span class=\"token literal-property property\">experimental</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token literal-property property\">plugins</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      CaptchaPlugin<span class=\"token punctuation\">.</span><span class=\"token function\">init</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n        <span class=\"token literal-property property\">type</span><span class=\"token operator\">:</span> <span class=\"token string\">\"reCAPTCHAv3\"</span><span class=\"token punctuation\">,</span>\n        <span class=\"token literal-property property\">captcha</span><span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n          <span class=\"token literal-property property\">secretKey</span><span class=\"token operator\">:</span> process<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span><span class=\"token constant\">RECAPTCHA_SECRET_KEY</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// From environment</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n      <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>This separation ensures secret keys remain server-side while frontend code receives only public credentials required for widget initialization.</p>\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><strong>Conclusion</strong></h2>\n<p>Effective CAPTCHA use requires balancing security and user experience. Blanket enforcement blocks bots but often frustrates legitimate users, while risk-based enforcement maintains security with minimal friction.</p>\n<p>Server-side verification is mandatory. Client-side checks provide no real security, whereas backend validation with the provider’s secret key ensures the challenge was legitimately solved.</p>\n<p>SuperTokens’ plugin architecture keeps CAPTCHA logic separate from authentication, enabling selective enforcement, conditional triggers,\nand easy provider swaps through configuration rather than code changes. Teams should track false positives, false negatives, and user completion rates to continuously tune policies, minimize user impact, and avoid vendor lock-in.</p>","frontmatter":{"date":"December 26, 2025","title":"How Do I Add CAPTCHA to My Login Page?","cover":"captcha-supertokens.png","author":"Mostafa Ibrahim","description":" Learn how to add CAPTCHA to login and signup pages, protect your app from bots, and see how SuperTokens makes it simple."},"fields":{"slug":"/captcha-supertokens/"}},"site":{"siteMetadata":{"title":"SuperTokens Blog"}}},"pageContext":{"id":"5b44398d-fb89-5247-ab25-aa9ca8032444","fields__slug":"/captcha-supertokens/","__params":{"fields__slug":"captcha-supertokens"}}},
    "staticQueryHashes": []}