<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mudit Garg</title>
    <description>The latest articles on DEV Community by Mudit Garg (@himudit).</description>
    <link>https://dev.to/himudit</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3631195%2Fa9bf2df0-dc93-4cf3-9a76-b2d71c6ce676.jpeg</url>
      <title>DEV Community: Mudit Garg</title>
      <link>https://dev.to/himudit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/himudit"/>
    <language>en</language>
    <item>
      <title>System Design - Authentication</title>
      <dc:creator>Mudit Garg</dc:creator>
      <pubDate>Wed, 26 Nov 2025 18:13:11 +0000</pubDate>
      <link>https://dev.to/himudit/system-design-authentication-i1l</link>
      <guid>https://dev.to/himudit/system-design-authentication-i1l</guid>
      <description>&lt;h2&gt;
  
  
  JWT Authentication – Best Practices &amp;amp; Design Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Best Practices Followed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Rate Limiting&lt;/li&gt;
&lt;li&gt;Access &amp;amp; Refresh Tokens&lt;/li&gt;
&lt;li&gt;Exponential Backoff&lt;/li&gt;
&lt;li&gt;Token Blacklisting / Rotation Strategy&lt;/li&gt;
&lt;li&gt;Hashed Passwords (Argon2)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Layered Protection Strategy
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Layer 1: IP-based limiting&lt;/strong&gt;&lt;br&gt;
Prevents brute-force attacks from a single source&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Layer 2: Username / Email-based limiting&lt;/strong&gt;&lt;br&gt;
Protects specific user accounts from targeted attacks&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Layer 3: Progressive penalties (Exponential Backoff)&lt;/strong&gt;&lt;br&gt;
Slows down repeated failed login attempts&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Rate Limiting Strategy
&lt;/h2&gt;

&lt;h3&gt;
  
  
  IP-based Rate Limiting using Token Bucket
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Data Structure&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Key: IP Address  
Value: { tokens, last_refill_timestamp }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Max tokens: 10&lt;/li&gt;
&lt;li&gt;Refill rate: 1 token every 6 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Flow
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;New user (no existing entry):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IP → { tokens: 10, last_refill_timestamp: 1:00 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;User makes a request:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IP → { tokens: 9, last_refill_timestamp: 1:00 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;User exhausts all 10 tokens within 3 seconds&lt;/strong&gt;&lt;br&gt;
For the 11th request at &lt;code&gt;1:03&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;current_time - last_refill_timestamp = 3s&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;New tokens added = &lt;code&gt;3 / 6 = 0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Total tokens = &lt;code&gt;0&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Response: &lt;strong&gt;429 – Too Many Requests&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;At 7 seconds&lt;/strong&gt;, when the user makes another request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tokens are recalculated&lt;/li&gt;
&lt;li&gt;Bucket is refilled accordingly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;last_refill_timestamp&lt;/code&gt; is updated&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Access Token &amp;amp; Refresh Token Flow
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Access Token&lt;/strong&gt; → Short-lived&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refresh Token&lt;/strong&gt; → Long-lived&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Login
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The Auth Service generates both access and refresh tokens on successful login&lt;/li&gt;
&lt;li&gt;Tokens are signed using &lt;strong&gt;RS256 (asymmetric encryption)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;private key&lt;/strong&gt; is stored only in the Auth Service and is used to sign tokens&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;public key&lt;/strong&gt; is shared with other microservices for token verification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures that &lt;strong&gt;only the Auth Service can issue tokens&lt;/strong&gt;, while all other microservices can independently validate tokens without making network calls to the Auth Service.&lt;/p&gt;




&lt;h3&gt;
  
  
  API Requests
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Client sends the &lt;strong&gt;access token&lt;/strong&gt; with every request&lt;/li&gt;
&lt;li&gt;Backend validates the access token&lt;/li&gt;
&lt;li&gt;If access token is expired/invalid → return &lt;strong&gt;401 Unauthorized&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Token Refresh
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Client sends the &lt;strong&gt;refresh token&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If refresh token is valid → issue a new access token&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If refresh token is invalid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear local storage&lt;/li&gt;
&lt;li&gt;Cancel ongoing requests&lt;/li&gt;
&lt;li&gt;Redirect user to login&lt;/li&gt;
&lt;li&gt;Show “Login again” message&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Exponential Backoff (Login Only)
&lt;/h2&gt;

&lt;p&gt;Exponential backoff is applied &lt;strong&gt;only when login credentials are invalid&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Backoff Timing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;2 attempts → 1 second&lt;/li&gt;
&lt;li&gt;3 attempts → 2 seconds&lt;/li&gt;
&lt;li&gt;4 attempts → 4 seconds&lt;/li&gt;
&lt;li&gt;5 attempts → 8 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Request passes &lt;code&gt;rateLimiterMiddleware&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Auth Service checks if backoff is active for the user&lt;/li&gt;
&lt;li&gt;If active → return &lt;strong&gt;429 – Too Many Failed Attempts&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;If not active:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Validate credentials&lt;/li&gt;
&lt;li&gt;On success → reset backoff counter&lt;/li&gt;
&lt;li&gt;On failure → increment failure counter&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Algorithm
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;delay = baseDelay * 2^failCount
nextAllowedTime = currentTime + delay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Redis Storage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;login_backoff:&amp;lt;user&amp;gt; = {
    failCount: 2,
    nextAllowed: 1733842205
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Redis keys expire automatically using TTL&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Data is cleared either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After successful login, or&lt;/li&gt;
&lt;li&gt;When TTL expires&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;&lt;code&gt;TTL = nextAllowed - currentTime&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Token Versioning (Instead of Token Blacklisting)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Problem
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;User logs in → gets access + refresh token&lt;/li&gt;
&lt;li&gt;Attacker steals refresh token&lt;/li&gt;
&lt;li&gt;User logs out&lt;/li&gt;
&lt;li&gt;Attacker can still use stolen refresh token until it expires (e.g., 7 days)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Solution: Token Versioning
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Store &lt;code&gt;tokenVersion&lt;/code&gt; in user data&lt;/li&gt;
&lt;li&gt;Include &lt;code&gt;tokenVersion&lt;/code&gt; in access and refresh token payloads&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Increment &lt;code&gt;tokenVersion&lt;/code&gt; on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logout&lt;/li&gt;
&lt;li&gt;Password change&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  * Token refresh
&lt;/h2&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Refresh Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Validate the refresh token&lt;/li&gt;
&lt;li&gt;Check token version:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (user.tokenVersion !== payload.tokenVersion) {
    return "Token revoked"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If valid:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Increment &lt;code&gt;tokenVersion&lt;/code&gt; in the database&lt;/li&gt;
&lt;li&gt;Issue new access and refresh tokens with the updated &lt;code&gt;tokenVersion&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ensures stolen refresh tokens become unusable immediately.&lt;/p&gt;




</description>
      <category>architecture</category>
      <category>security</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
