DEV Community

Shahadat Siddikee Shawon
Shahadat Siddikee Shawon

Posted on

Users could act as both Buyers and Sellers — and they could switch roles instantly from the navbar

 🧩 When Users Switch Roles (Buyer ↔ Seller): Handling Dynamic Authentication the Smart Way

🧠 A real-world lesson from building a marketplace app with Next.js, Redux Toolkit, and Strapi.

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

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

⚠️ What Went Wrong

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).

This caused:

Unauthorized errors on protected routes
Wrong API data (seller dashboard showing buyer stats)
“Ghost login” — user still authenticated as previous role

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

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

🛠️ The Solution — Step by Step
1. Separate Auth Context per Role

Instead of one authSlice, I split logic like this:

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

const authSlice = createSlice({
name: "auth",
initialState,
reducers: {
setCredentials: (state, action) => {
const { user, token, role } = action.payload;
state.user = user;
state.token = token;
state.role = role;
},
logout: (state) => {
state.user = null;
state.token = null;
state.role = null;
},
},
});

2. On Role Switch → Clean Up & Re-authenticate

const handleRoleSwitch = async (newRole: "buyer" | "seller") => {
dispatch(logout()); // clear Redux + cookies
Cookies.remove("authToken");

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

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

This ensures:
All old credentials are flushed
New role gets fresh permissions
Backend validates the switch securely

3. Backend Role Validation (Strapi or Express)

If you use Strapi, you can customize the controller like this:

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

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 });
Enter fullscreen mode Exit fullscreen mode

},
};

4. Securely Handle Client Sync
On the Next.js side, I used a useEffect listener to ensure state consistency:

useEffect(() => {
const token = Cookies.get("authToken");
if (!token) {
dispatch(logout());
}
}, [dispatch]);

This prevents UI from showing stale credentials after switching or logout.

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

🧱 Use backend role validation to prevent privilege escalation.

Author
SHAHADAT SIDDIKEE SHAWON
Full-Stack Developer | System Designer | DevOps Enthusiast
LinkedInGitHub Dev.to

Top comments (0)