<?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>Forem: Shahadat Siddikee Shawon</title>
    <description>The latest articles on Forem by Shahadat Siddikee Shawon (@shawon0c).</description>
    <link>https://forem.com/shawon0c</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%2F3581504%2Fb020f2f5-2641-411d-9b9b-08aee8714316.jpeg</url>
      <title>Forem: Shahadat Siddikee Shawon</title>
      <link>https://forem.com/shawon0c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shawon0c"/>
    <language>en</language>
    <item>
      <title>Users could act as both Buyers and Sellers — and they could switch roles instantly from the navbar</title>
      <dc:creator>Shahadat Siddikee Shawon</dc:creator>
      <pubDate>Fri, 24 Oct 2025 09:47:28 +0000</pubDate>
      <link>https://forem.com/shawon0c/users-could-act-as-both-buyers-and-sellers-and-they-could-switch-roles-instantly-from-the-navbar-4olh</link>
      <guid>https://forem.com/shawon0c/users-could-act-as-both-buyers-and-sellers-and-they-could-switch-roles-instantly-from-the-navbar-4olh</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0ekq5w9iuvvvrx1qgak.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0ekq5w9iuvvvrx1qgak.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;strong&gt;🧩 When Users Switch Roles (Buyer ↔ Seller): Handling Dynamic Authentication the Smart Way&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;🧠 A real-world lesson from building a marketplace app with Next.js, Redux Toolkit, and Strapi.&lt;/p&gt;

&lt;p&gt;While building a multi-role marketplace, I faced a tricky challenge:&lt;br&gt;
Users could act as both Buyers and Sellers — and they could switch roles instantly from the navbar.&lt;/p&gt;

&lt;p&gt;At first, this sounded easy:&lt;br&gt;
“Just toggle a flag in Redux and change the UI.”&lt;br&gt;
But once we got into implementation, things got messy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ What Went Wrong&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The token stored in cookies or localStorage was tied to one role only (buyer/seller).When switching, the UI changed — but the backend still saw the old role.Cached data and Redux persisted state caused stale user context.Even logout didn’t fully clear cookies (due to async cookie sync issue).&lt;/p&gt;

&lt;p&gt;This caused:&lt;/p&gt;

&lt;p&gt;Unauthorized errors on protected routes&lt;br&gt;
Wrong API data (seller dashboard showing buyer stats)&lt;br&gt;
“Ghost login” — user still authenticated as previous role&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;🔍 Understanding the Core Issue *&lt;/em&gt;&lt;br&gt;
The problem wasn’t in the UI — it was in the authentication design.&lt;br&gt;
Each role had a different access token and permission set, so switching roles meant more than toggling state.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We needed to:&lt;/em&gt;&lt;br&gt;
Invalidate the old token&lt;br&gt;
Clear cached Redux + localStorage&lt;br&gt;
Request a new token&lt;br&gt;
Sync with server-side session (Strapi or JWT logic)&lt;br&gt;
Rebuild UI with correct permissions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🛠️ The Solution — Step by Step&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;1. Separate Auth Context per Role&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Instead of one authSlice, I split logic like this:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;// authSlice.ts&lt;br&gt;
const initialState = {&lt;br&gt;
  user: null,&lt;br&gt;
  token: null,&lt;br&gt;
  role: null, // 'buyer' | 'seller'&lt;br&gt;
  loading: false,&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;const authSlice = createSlice({&lt;br&gt;
  name: "auth",&lt;br&gt;
  initialState,&lt;br&gt;
  reducers: {&lt;br&gt;
    setCredentials: (state, action) =&amp;gt; {&lt;br&gt;
      const { user, token, role } = action.payload;&lt;br&gt;
      state.user = user;&lt;br&gt;
      state.token = token;&lt;br&gt;
      state.role = role;&lt;br&gt;
    },&lt;br&gt;
    logout: (state) =&amp;gt; {&lt;br&gt;
      state.user = null;&lt;br&gt;
      state.token = null;&lt;br&gt;
      state.role = null;&lt;br&gt;
    },&lt;br&gt;
  },&lt;br&gt;
});&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. On Role Switch → Clean Up &amp;amp; Re-authenticate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;const handleRoleSwitch = async (newRole: "buyer" | "seller") =&amp;gt; {&lt;br&gt;
  dispatch(logout()); // clear Redux + cookies&lt;br&gt;
  Cookies.remove("authToken");&lt;/p&gt;

&lt;p&gt;const res = await fetch(&lt;code&gt;/api/auth/switch-role&lt;/code&gt;, {&lt;br&gt;
    method: "POST",&lt;br&gt;
    body: JSON.stringify({ role: newRole }),&lt;br&gt;
  });&lt;/p&gt;

&lt;p&gt;const data = await res.json();&lt;br&gt;
  if (data.token) {&lt;br&gt;
    Cookies.set("authToken", data.token);&lt;br&gt;
    dispatch(setCredentials({ ...data.user, token: data.token, role: newRole }));&lt;br&gt;
  }&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;This ensures:&lt;br&gt;
All old credentials are flushed&lt;br&gt;
New role gets fresh permissions&lt;br&gt;
Backend validates the switch securely&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Backend Role Validation (Strapi or Express)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you use Strapi, you can customize the controller like this:&lt;/p&gt;

&lt;p&gt;// /extensions/users-permissions/controllers/auth.js&lt;br&gt;
module.exports = {&lt;br&gt;
  async switchRole(ctx) {&lt;br&gt;
    const { role } = ctx.request.body;&lt;br&gt;
    const user = ctx.state.user;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (!user) return ctx.unauthorized("Not logged in");

// Verify that the user can have this role
const allowed = ["buyer", "seller"];
if (!allowed.includes(role)) return ctx.badRequest("Invalid role");

const updatedUser = await strapi.query("plugin::users-permissions.user").update({
  where: { id: user.id },
  data: { role },
});

// Issue a new JWT for the updated role
const token = strapi.plugins["users-permissions"].services.jwt.issue({
  id: updatedUser.id,
  role,
});

ctx.send({ user: updatedUser, token });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;},&lt;br&gt;
};&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Securely Handle Client Sync&lt;/strong&gt;&lt;br&gt;
On the Next.js side, I used a useEffect listener to ensure state consistency:&lt;/p&gt;

&lt;p&gt;useEffect(() =&amp;gt; {&lt;br&gt;
  const token = Cookies.get("authToken");&lt;br&gt;
  if (!token) {&lt;br&gt;
    dispatch(logout());&lt;br&gt;
  }&lt;br&gt;
}, [dispatch]);&lt;/p&gt;

&lt;p&gt;This prevents UI from showing stale credentials after switching or logout.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;🧠Key Takeaways&lt;br&gt;
✅Treat role switching like a re-login, not a simple toggle.&lt;br&gt;
🧩Always issue a new token when roles change.&lt;br&gt;
💡Keep Redux, Cookies, and Server Auth in perfect sync.&lt;br&gt;
⚙️Cache invalidation matters — clear localStorage/sessionStorage when switching.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧱 Use backend role validation to prevent privilege escalation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Author&lt;br&gt;
SHAHADAT SIDDIKEE SHAWON &lt;br&gt;
Full-Stack Developer | System Designer | DevOps Enthusiast&lt;br&gt;
&lt;a href="https://www.linkedin.com/in/shahadat-siddikee-shawon/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; • &lt;a href="https://github.com/SHAWON0c" rel="noopener noreferrer"&gt;GitHub &lt;/a&gt;• &lt;a href="https://dev.tourl"&gt;Dev.to&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>architecture</category>
      <category>role</category>
    </item>
  </channel>
</rss>
