<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[GHKTech Blog]]></title><description><![CDATA[Tech blog for all things technology]]></description><link>https://gkhakim.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>GHKTech Blog</title><link>https://gkhakim.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 23 Sep 2026 17:45:16 GMT</lastBuildDate><atom:link href="https://gkhakim.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Hybrid Auth Strategy: HttpOnly Cookies for Web, Tokens for Mobile]]></title><description><![CDATA[When building a backend with NestJS to serve both a React web app and a React Native mobile app, a "one size fits all" authentication approach often leads to security gaps or integration headaches. Th]]></description><link>https://gkhakim.hashnode.dev/the-hybrid-auth-strategy-httponly-cookies-for-web-tokens-for-mobile</link><guid isPermaLink="true">https://gkhakim.hashnode.dev/the-hybrid-auth-strategy-httponly-cookies-for-web-tokens-for-mobile</guid><dc:creator><![CDATA[Hakim Katende]]></dc:creator><pubDate>Thu, 10 Apr 2025 06:00:00 GMT</pubDate><content:encoded><![CDATA[<p>When building a backend with NestJS to serve both a <strong>React web app</strong> and a <strong>React Native mobile app</strong>, a "one size fits all" authentication approach often leads to security gaps or integration headaches. The best solution? A <strong>Hybrid Auth Flow.</strong></p>
<p><strong>The Problem: Browsers vs. Native Apps</strong></p>
<ul>
<li><p><strong>On the Web:</strong> Storing tokens in <code>localStorage</code> is risky because any XSS (Cross-Site Scripting) vulnerability can leak your user's session. <strong>HttpOnly Cookies</strong> are the gold standard here because JavaScript can't touch them.</p>
</li>
<li><p><strong>In Mobile:</strong> React Native doesn't handle cookies as gracefully as browsers do. Instead, mobile OSs provide "Secure Enclaves" (like Keychain or Keystore) which are incredibly secure places to store raw strings.</p>
</li>
</ul>
<p><strong>The Solution: The Dual-Response Pattern</strong></p>
<p>Your NestJS backend should detect the client type and respond accordingly:</p>
<ol>
<li><p><strong>For Web:</strong> Set a <code>Set-Cookie</code> header with <code>httpOnly: true</code>. This keeps the token out of the browser's reach.</p>
</li>
<li><p><strong>For Mobile:</strong> Return the <code>accessToken</code> in the JSON response body. The mobile app can then save this to <strong>React Native Keychain</strong>.</p>
</li>
</ol>
<p><strong>NestJS Implementation (Quick Look)</strong></p>
<p>In your Auth Controller, use a custom header (like <code>x-platform</code>) to decide your strategy:</p>
<p><strong>typescript</strong></p>
<pre><code class="language-plaintext">@Post('login')
async login(@Req() req: Request, @Res({ passthrough: true }) res: Response) {
  const { accessToken, user } = await this.authService.validateUser(req.body);
  const isMobile = req.headers['x-platform'] === 'mobile';

  if (isMobile) {
    // Return token directly for mobile storage
    return { accessToken, user };
  }

  // Secure cookie for web users
  res.cookie('access_token', accessToken, {
    httpOnly: true,
    secure: true, // Use false for local dev
    sameSite: 'lax',
  });
  return { user };
}
</code></pre>
<p>Use code with caution.</p>
<p><strong>Why This Wins</strong></p>
<ul>
<li><p><strong>Max Security:</strong> Web users are protected from XSS via cookies; mobile users are protected via native device encryption.</p>
</li>
<li><p><strong>Developer Experience:</strong> Your mobile team doesn't have to fight cookie-persistence libraries, and your web team doesn't have to worry about manual header management.</p>
</li>
<li><p><strong>One Backend:</strong> Your Guards can simply check <em>both</em> the <code>cookies</code> object and the <code>Authorization</code> header to validate the user, keeping your logic DRY.</p>
</li>
</ul>
]]></content:encoded></item></channel></rss>