{
    "componentChunkName": "component---src-pages-blog-markdown-remark-fields-slug-js",
    "path": "/blog/how-to-ban-users",
    "result": {"data":{"markdownRemark":{"html":"<p>User banning is one of those features that looks simple until it breaks. A status flag in a database is not a ban; it is a note that gets ignored the moment a valid session token bypasses the check. A real banning system closes active sessions, blocks reauthentication, and enforces restrictions at the server boundary, not the UI layer.</p>\n<p>This guide covers how to implement user banning correctly, where common approaches fall short, and how SuperTokens handles enforcement in real time across distributed environments.</p>\n<h2 id=\"why-you-need-a-real-banning-system\" style=\"position:relative;\"><a href=\"#why-you-need-a-real-banning-system\" aria-label=\"why you need a real banning system 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>Why You Need a Real Banning System</h2>\n<p>If you are building a modern product, understanding how to ban users in web apps is not just a feature requirement; it is a core part of your security and trust model.</p>\n<p>Banning a user is an act of policy enforcement. The scenarios that require it are varied: abusive behavior in a community product, a compromised account that needs immediate lockout, a terms of service violation, or a compliance restriction tied to geography or regulation.</p>\n<p>What all these scenarios share is urgency. When a ban is issued, it needs to take effect immediately, not at the next token refresh cycle or the next time the user logs in. The risk of weak banning logic is concrete: a user marked as banned in the database can continue making API calls, submitting forms, or running background jobs as long as a valid session token exists. The flag means nothing if the session verification layer never reads it.</p>\n<p>A production-grade banning system must do three things: mark the user as banned, revoke all active sessions, and prevent future authentication until the ban is lifted.</p>\n<h2 id=\"common-approaches-to-user-banning-and-their-pitfalls\" style=\"position:relative;\"><a href=\"#common-approaches-to-user-banning-and-their-pitfalls\" aria-label=\"common approaches to user banning and their pitfalls 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>Common Approaches to User Banning (and Their Pitfalls)</h2>\n<p>User banning is often treated as a simple toggle, but in practice, it’s a distributed systems problem involving sessions, tokens, and enforcement consistency. Without careful design, seemingly “banned” users can continue accessing systems through gaps in session handling and token lifecycle management.</p>\n<h3 id=\"database-flag-only\" style=\"position:relative;\"><a href=\"#database-flag-only\" aria-label=\"database flag only 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>Database Flag Only</strong></h3>\n<p>The simplest approach is adding a <code class=\"language-text\">banned</code> boolean to the user record and checking it on login. This works for blocking new logins but does nothing about sessions already in flight. A user banned at 2:00 PM with a session token valid until 4:00 PM can continue acting freely for two more hours. Background jobs or API clients using stored tokens face the same gap.</p>\n<h3 id=\"manual-session-revocation\" style=\"position:relative;\"><a href=\"#manual-session-revocation\" aria-label=\"manual session revocation 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>Manual Session Revocation</strong></h3>\n<p>A step up from the flag-only approach is explicitly deleting session tokens or calling logout endpoints when a ban is issued. The problem is coverage. A user signed in across three devices, a mobile app, and an API client — requiring five separate revocation calls. Miss one and the ban is partial. In microservice architectures where session state may be distributed, the surface area for incomplete revocation grows quickly.</p>\n<h3 id=\"library-based-solutions-betterauth-passportjs\" style=\"position:relative;\"><a href=\"#library-based-solutions-betterauth-passportjs\" aria-label=\"library based solutions betterauth passportjs 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>Library-Based Solutions (BetterAuth, Passport.js)</strong></h3>\n<p>Libraries like Passport.js and BetterAuth provide authentication primitives but leave ban enforcement to the developer. Middleware must be written and applied consistently across every protected route. Edge cases, concurrent sessions, refresh token reuse, and token leakage require explicit handling that is easy to skip during initial implementation and easy to forget during later refactors.</p>\n<h2 id=\"how-supertokens-handles-user-banning\" style=\"position:relative;\"><a href=\"#how-supertokens-handles-user-banning\" aria-label=\"how supertokens handles user banning 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>How SuperTokens Handles User Banning</h2>\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/9e2ba8940c8f1123465e76174989af40/d0c2f/Supertokens.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: 47.46835443037975%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsTAAALEwEAmpwYAAABqElEQVQoz02PbW/aMBSFMy0kvo6d2InjkDgJBEJJeX8tpGzVoIxO2mhFK6H9LH7yBJGqPTrf7nmke7RR5n8r5Pdxp+x3eqHXj8Q0U+MsnOTJXRK24jAQnu8L3xdSSiGE53lFUVwul81mow1iPlRsrthK8bVy18qdKa/sNleDYpGn4+Iuz3Pf9wHwJwCg6zoAaJ3AHtbtpWKl4quQz+p81lBP8+nDbLaZjlaLxXA8aWYZIQT+w7qhxS7pSrsv7YGgA+G0GZnk7fPp/eP942m7/3N8fTm87LbbKKzr+leEACFU+QghzaVW5JCUWS1mtV163G1/7/fPP3avb6fz+e/pdHo7Hn8+734dDo9liTE2b1BKGGOahcGxsCA4oDi0rbaK+t1u7763XC4f16vyYb6YjsbDXt5qSF+gG7VaTcowa91rAIABCCAHg4OBAhIuD4MgjaMsVa0kakQyFJzbxDSMSjYMw+NuohpXGd0GQBUAauiSs0YcN5NrsjRhGByzVtUQArOmUz+V2UD7XF9hXo/G9RFicZtyhzJqWcgE0zCrBoCpf6HJKBge/gGUPjjwL5U6kAAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"SuperTokens\"\n        title=\"SuperTokens\"\n        src=\"/static/9e2ba8940c8f1123465e76174989af40/f058b/Supertokens.png\"\n        srcset=\"/static/9e2ba8940c8f1123465e76174989af40/c26ae/Supertokens.png 158w,\n/static/9e2ba8940c8f1123465e76174989af40/6bdcf/Supertokens.png 315w,\n/static/9e2ba8940c8f1123465e76174989af40/f058b/Supertokens.png 630w,\n/static/9e2ba8940c8f1123465e76174989af40/40601/Supertokens.png 945w,\n/static/9e2ba8940c8f1123465e76174989af40/78612/Supertokens.png 1260w,\n/static/9e2ba8940c8f1123465e76174989af40/d0c2f/Supertokens.png 1362w\"\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<p><a href=\"https://supertokens.com/\" target=\"_blank\" rel=\"nofollow\">SuperTokens</a> provides built-in hooks that enforce ban checks during session verification, which happens on every authenticated request. Rather than relying on the application to remember to check a flag, the verification layer itself becomes the enforcement point. Sessions can be revoked in real time mid-request, mid-session, and the behavior applies uniformly whether requests arrive at a monolith, an API gateway, or an individual microservice.</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/746b55c5fca9b6e909bf80874a594b60/669eb/Workflow.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: 104.43037974683544%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAVCAYAAABG1c6oAAAACXBIWXMAAAsTAAALEwEAmpwYAAACMUlEQVQ4y6XUa3OiMBQG4CQkkIAEAaGgoFguUu1FZ1bb3f//u/LuaB3rtnXXbj88E8hJDichEyL6CcSygFiWRwXsuwLybgJvkcNvUoRVhKTQyHMPRaowTSRmsY3qzCyyUYYChLc5RDf+U5vDaTLI2wTjdoamb9G0t2jqCuNUIw0Esj3NkevXdi/1OYhVxLDKEazpUTkCL2LwSQg1idEteuy2O/z8+Qur5QpR4EK7FoZ76k2gLGjJQFg0wEHsHx3fQw9i6MENfegoxjCK4boObJvBEQyS01fitXWOCFU2qOu8Y+PQL20EyQjpuESSTxAlKbjgoISA0c8RwhkItz6yKAgXaBcr7HbP2G53eFz/gLAlCCF/c3ygZ05BCttRGPgRBn4IqTxQyq5M+BlKMAg14ixHfJNBj4ag1ncSMoL+8R7bl2dsX3ZYP28hpPONhITAcSVcX0MNNOTABWVfq9CctSdKevBc37zvfzf+YoWnoOAcVTXF09OTWW/W6LoGvu9dHH+xQi64UZ4yvvZNkqSmrmu0bWvSNDOBr42nlJHSuarCQ8B2hMmnpUnyzEg1eJtEhYmDyExvShPq4LolcyHMvJ5juXrAbVNDOOKtcothXs3Q93dYdB081/33kplloSgL03aNmc0rs//AKcaYGeeZqWaVmZaFkY5z1U+5gvj6OaSUfuhT0kFb14drrF8sEIXDrx/sc/s9e3y4x2azObhJ0+8l3LMYBT1cVfTTVfznHl72G3lTyxAqE2eOAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Workflow\"\n        title=\"Workflow\"\n        src=\"/static/746b55c5fca9b6e909bf80874a594b60/f058b/Workflow.png\"\n        srcset=\"/static/746b55c5fca9b6e909bf80874a594b60/c26ae/Workflow.png 158w,\n/static/746b55c5fca9b6e909bf80874a594b60/6bdcf/Workflow.png 315w,\n/static/746b55c5fca9b6e909bf80874a594b60/f058b/Workflow.png 630w,\n/static/746b55c5fca9b6e909bf80874a594b60/40601/Workflow.png 945w,\n/static/746b55c5fca9b6e909bf80874a594b60/669eb/Workflow.png 1244w\"\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<p>For more information, see <a href=\"https://supertokens.com/docs/post-authentication/user-management/user-banning\" target=\"_blank\" rel=\"nofollow\">User Banning Docs.</a></p>\n<h3 id=\"step-by-step-implementation\" style=\"position:relative;\"><a href=\"#step-by-step-implementation\" aria-label=\"step by step implementation 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>Step-By-Step Implementation</strong></h3>\n<p><strong>1. Add a banned flag to the user model</strong></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"2255473176601596700\"\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(`ALTER TABLE users ADD COLUMN is_banned BOOLEAN DEFAULT FALSE;`, `2255473176601596700`)\"\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=\"sql\"><pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">ALTER</span> <span class=\"token keyword\">TABLE</span> users <span class=\"token keyword\">ADD</span> <span class=\"token keyword\">COLUMN</span> is_banned <span class=\"token keyword\">BOOLEAN</span> <span class=\"token keyword\">DEFAULT</span> <span class=\"token boolean\">FALSE</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p><strong>2. Apply session verification middleware</strong></p>\n<p>SuperTokens’ <code class=\"language-text\">Session.verifySession()</code> runs on every request to a protected route. This is where the ban check is inserted, not in a login handler, not in a UI guard, but in the layer that every authenticated request passes through.</p>\n<p><strong>3. Check ban status and revoke if flagged</strong></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"97108979229580850000\"\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(`app.use(async (req, res, next) => {\n  let session = await Session.verifySession()(req, res);\n\n  const user = await getUserById(session.getUserId());\n\n  if (user.isBanned) {\n    await session.revokeSession();\n    return res.status(403).json({ message: &quot;User banned.&quot; });\n  }\n\n  next();\n});`, `97108979229580850000`)\"\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=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\">app<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">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  <span class=\"token keyword\">let</span> session <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> Session<span class=\"token punctuation\">.</span><span class=\"token function\">verifySession</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span>req<span class=\"token punctuation\">,</span> res<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">const</span> user <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">getUserById</span><span class=\"token punctuation\">(</span>session<span class=\"token punctuation\">.</span><span class=\"token function\">getUserId</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 keyword\">if</span> <span class=\"token punctuation\">(</span>user<span class=\"token punctuation\">.</span>isBanned<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">await</span> session<span class=\"token punctuation\">.</span><span class=\"token function\">revokeSession</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token function\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">403</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\">\"User banned.\"</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">next</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></code></pre></div>\n<p>When <code class=\"language-text\">isBanned</code> is true, the session is revoked server-side before the response is returned. The client receives a 403, and the token is invalidated. Any subsequent request with the same token fails at verification.</p>\n<p><strong>4. (Optional) Hook into ban events for logging</strong></p>\n<p>Emit a structured log event or trigger an admin notification when a ban is enforced. This creates an audit trail and surfaces unexpected ban activity, for example, a legitimate user being banned by a misconfigured automated rule.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"61672668193705935000\"\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(`if (user.isBanned) {\n  await session.revokeSession();\n\n  await logBanEvent({ userId: user.id, timestamp: Date.now() });\n\n  return res.status(403).json({ message: &quot;User banned.&quot; });\n}`, `61672668193705935000`)\"\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=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>user<span class=\"token punctuation\">.</span>isBanned<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">await</span> session<span class=\"token punctuation\">.</span><span class=\"token function\">revokeSession</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n  <span class=\"token keyword\">await</span> <span class=\"token function\">logBanEvent</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> <span class=\"token literal-property property\">userId</span><span class=\"token operator\">:</span> user<span class=\"token punctuation\">.</span>id<span class=\"token punctuation\">,</span> <span class=\"token literal-property property\">timestamp</span><span class=\"token operator\">:</span> Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</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 keyword\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token function\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">403</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\">\"User banned.\"</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h2 id=\"advanced-temporary-bans-and-tiered-restrictions\" style=\"position:relative;\"><a href=\"#advanced-temporary-bans-and-tiered-restrictions\" aria-label=\"advanced temporary bans and tiered restrictions 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>Advanced: Temporary Bans and Tiered Restrictions</h2>\n<p>Not every policy violation warrants a permanent ban. SuperTokens supports session claims, which allow dynamic enforcement rules to be embedded directly into the session payload.</p>\n<p>A <code class=\"language-text\">bannedUntil</code> timestamp claim enables time-limited suspensions. The verification middleware reads the claim, compares it against the current time, and either allows the request or returns a 403, no database lookup required on every request.</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"89871424595647230000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"Copied!\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`const bannedUntil =\n  session.getSessionDataFromDatabase()?.bannedUntil;\n\nif (bannedUntil && Date.now() < bannedUntil) {\n  return res.status(403).json({ message: &quot;Account suspended.&quot; });\n}`, `89871424595647230000`)\"\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=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> bannedUntil <span class=\"token operator\">=</span>\n  session<span class=\"token punctuation\">.</span><span class=\"token function\">getSessionDataFromDatabase</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token operator\">?.</span>bannedUntil<span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>bannedUntil <span class=\"token operator\">&amp;&amp;</span> Date<span class=\"token punctuation\">.</span><span class=\"token function\">now</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">&lt;</span> bannedUntil<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> res<span class=\"token punctuation\">.</span><span class=\"token function\">status</span><span class=\"token punctuation\">(</span><span class=\"token number\">403</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\">\"Account suspended.\"</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Feature-level restrictions are another pattern worth considering for community, gaming, or B2B applications. Instead of revoking full access, a claim like <code class=\"language-text\">canPost: false</code> or <code class=\"language-text\">canComment: false</code> restricts specific capabilities while leaving the account active. This is appropriate for graduated enforcement, such as a warning state before a full suspension.</p>\n<h2 id=\"security-considerations\" style=\"position:relative;\"><a href=\"#security-considerations\" aria-label=\"security considerations 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>Security Considerations</h2>\n<p>Several implementation details determine whether a banning system is genuinely enforceable or merely cosmetic.</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/b76f132ecdddd61e195d1fdf8c032cfa/c65fa/Security-Considerations.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: 77.21518987341771%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAAsTAAALEwEAmpwYAAACAklEQVQ4y42S6U5bMRBGvdu5+55LmhVKQwIpEKBVW3V5/zfoy3xflZS2CBqRH8eyRuMzHo+FsJoyclRFTD0saMY13axlWLSM5g3SecN0UjMbFazHJdtRzrZN2BSebWbZ7zHoU80+0RRCKwpnKCNPmUdUdUrd5QyjDtH4DevpGyxXp3h/vcJ4MkTW1Zitxtjcn2N5NUVdDZB4xdQrpF5RCCm4l+5u6i3FwFJnCX1ZQ+cp06rExbsz3Gwu0Q9bmHiAelJhvV1icj5CiCy0EjRawihBIcR+oZCSQikKLal9oBnE2BXRwcMFB+strHeQ1lB7A2kVtNMwTkMIgUcP/gmfoLShDxmMS1gmJR6WS/y4v8OsaWDsACeLDtsvG9x+3qCbVkcIlaHWdp8kpcDAe0QhwBmzP2ydwSAKe3b7/wq7fsT15Yar9RUns7fU2vNJ4jH8yf0dKKuW09mCk+mcXT+mUvaQ8LXYy2pSalobIIRmCAFdm6NtcxizG4Ci8w7VsEBzUiFE7mm7h9/Q+xTGRCzyHPd3S9xcn2EnktIxShNsHi7w8fsdsiY5Rmip9YvKr3F4yruWvc9gXcoyLnC9mOPrdovbs1M0cY44z3Hz6RIfvm1xup6+EB6uKhWcsWiLHH3TYFiW8MZCGY2qK9GNWqRF/Pzc0d/iWMTPY5CP/I3JR57l/gKLqUUB8ztwxQAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Security Considerations\"\n        title=\"Security Considerations\"\n        src=\"/static/b76f132ecdddd61e195d1fdf8c032cfa/f058b/Security-Considerations.png\"\n        srcset=\"/static/b76f132ecdddd61e195d1fdf8c032cfa/c26ae/Security-Considerations.png 158w,\n/static/b76f132ecdddd61e195d1fdf8c032cfa/6bdcf/Security-Considerations.png 315w,\n/static/b76f132ecdddd61e195d1fdf8c032cfa/f058b/Security-Considerations.png 630w,\n/static/b76f132ecdddd61e195d1fdf8c032cfa/40601/Security-Considerations.png 945w,\n/static/b76f132ecdddd61e195d1fdf8c032cfa/78612/Security-Considerations.png 1260w,\n/static/b76f132ecdddd61e195d1fdf8c032cfa/c65fa/Security-Considerations.png 1434w\"\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<p>Ban status must be checked server-side on every request. A client-side check hiding a button or disabling a form is a UX convenience, not an access control. Any client-side restriction can be bypassed with a direct API call.</p>\n<p>Refresh tokens require explicit revocation. Revoking the access token without revoking the refresh token allows a banned user to obtain a new access token silently. SuperTokens’ <code class=\"language-text\">revokeSession()</code> handles both, but custom implementations must address this explicitly.</p>\n<p>For abuse scenarios, banning by user ID alone is often insufficient. A determined bad actor can create a new account. Combining user ID banning with IP address or device fingerprint restrictions raises the cost of evasion. These layers belong in the ban enforcement logic, not a separate system.</p>\n<p>Every ban action should produce an audit record: who was banned, when, by whom, and under what policy. This is a compliance requirement in regulated environments and a debugging tool everywhere else.</p>\n<h2 id=\"combining-banning-with-rbac-and-session-security\" style=\"position:relative;\"><a href=\"#combining-banning-with-rbac-and-session-security\" aria-label=\"combining banning with rbac and session security 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>Combining Banning with RBAC and Session Security</h2>\n<p>Banning integrates naturally with role-based access control. Assigning a <code class=\"language-text\">restricted</code> role to a banned user means existing RBAC middleware can enforce reduced permissions without requiring separate ban-specific checks on every route. A banned user’s role change cascades through the permission layer automatically.</p>\n<p>Pair banning with session rotation and token theft detection for a zero-trust access model. A session that rotates tokens on each request limits the window of exposure if a token is leaked. Token theft detection flags anomalous reuse patterns as a signal that a session may be compromised and a candidate for preemptive revocation.</p>\n<p>For more information, see <a href=\"https://supertokens.com/docs/post-authentication/session-management/introduction\" target=\"_blank\" rel=\"nofollow\">Session Management Overview</a> and <a href=\"https://supertokens.com/docs/additional-verification/user-roles/role-management-actions\" target=\"_blank\" rel=\"nofollow\">User Roles and Permissions.</a></p>\n<h2 id=\"common-mistakes-to-avoid\" style=\"position:relative;\"><a href=\"#common-mistakes-to-avoid\" aria-label=\"common mistakes to avoid 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>Common Mistakes to Avoid</h2>\n<p>The most frequent implementation errors are not obscure edge cases; they are straightforward omissions that appear under time pressure:</p>\n<ul>\n<li><strong>Not invalidating sessions after setting the ban flag.</strong> The flag is inert until the session layer reads it. Revocation must happen at the time of the ban, not lazily on the next login.</li>\n<li><strong>Relying on front-end checks.</strong> Any restriction enforced only in the UI is not a security control.</li>\n<li><strong>Ignoring refresh tokens.</strong> Revoking an access token without revoking the associated refresh token leaves a reauthentication path open.</li>\n<li><strong>No audit logging.</strong> A ban with no record is unverifiable, undebuggable, and non-compliant in regulated contexts.</li>\n</ul>\n<h2 id=\"summary\" style=\"position:relative;\"><a href=\"#summary\" aria-label=\"summary 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>Summary</h2>\n<p>User banning is access control combined with session lifecycle enforcement. The database flag is necessary but not sufficient; it must be paired with immediate session revocation and a server-side check on every subsequent request.</p>\n<p>SuperTokens simplifies this by placing enforcement at the session verification layer, which every authenticated request already passes through. Temporary bans, tiered restrictions, and audit hooks are incremental additions to the same foundation.</p>\n<p>For the complete implementation documentation, see the official <a href=\"https://supertokens.com/docs/post-authentication/user-management/user-banning\" target=\"_blank\" rel=\"nofollow\">How to Ban Users in SuperTokens</a> guide. For more information about the patterns around token management and multi-device session handling, see <a href=\"https://supertokens.com/docs/migration/account-migration\" target=\"_blank\" rel=\"nofollow\">Migration Guide</a>.</p>","frontmatter":{"date":"March 22, 2026","title":"How to Ban Users in Your Web App By Using SuperTokens","cover":"how-to-ban-users.png","author":"Mostafa Ibrahim","description":"Learn how to ban or suspend users in your web app by using SuperTokens."},"fields":{"slug":"/how-to-ban-users/"}},"site":{"siteMetadata":{"title":"SuperTokens Blog"}}},"pageContext":{"id":"564cfd5c-3a19-5f21-adf3-b4dea6d3bfaa","fields__slug":"/how-to-ban-users/","__params":{"fields__slug":"how-to-ban-users"}}},
    "staticQueryHashes": []}