Backend Architecture

Securing Full-Stack Web Applications: Implementing Role-Based Access Control (RBAC) with JWTs

Best practices for securing modern web applications: stateless JSON Web Tokens, refresh token rotation, HttpOnly cookie storage, and defensive authorization guards.

Authentication verifies who a user is; authorization determines what they are allowed to do. In modern enterprise applications with multiple administrative tiers, role-based access control (RBAC) must be enforced defensively on every single endpoint.

Storing authentication tokens improperly or relying solely on front-end route guards creates severe vulnerabilities. Here is how to implement a secure, stateless RBAC architecture.


1. Where Should JWTs Be Stored?

  • LocalStorage / SessionStorage: Vulnerable to XSS (Cross-Site Scripting). Any malicious script or compromised npm package executing on your page can read localStorage.getItem('token') and transmit it to an external server.
  • HttpOnly, Secure, SameSite Cookies: The Industry Standard. HttpOnly cookies cannot be accessed via client-side JavaScript (document.cookie), completely mitigating XSS token theft.

2. Dual-Token Architecture: Access Token + Refresh Token Rotation

Never issue an access token with a multi-day or multi-week lifespan. If an access token is compromised, the attacker retains access until expiration.

The Dual-Token Lifecycle:

  1. Access Token: Short-lived (10–15 minutes). Sent in memory or authorization header.
  2. Refresh Token: Long-lived (7–14 days). Stored in an HttpOnly, SameSite=Strict cookie.
  3. Refresh Token Rotation: Each time a client requests a new access token using a refresh token, the server invalidates the used refresh token and issues a brand-new refresh token. If a stolen refresh token is used twice, the server detects token reuse and immediately invalidates all active sessions for that user.

3. Spring Security RBAC Implementation Example

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/", "/public/").permitAll()
.requestMatchers("/api/admin/").hasRole("ADMIN")
.requestMatchers("/api/manager/
").hasAnyRole("ADMIN", "MANAGER")
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

return http.build();
}
}


```


#Security#JWT#RBAC#Node.js#Spring Security
Shashank Dubey

Written by Shashank Dubey

Software Engineer specializing in Java Spring Boot, Node.js, React, distributed microservices, and enterprise cloud systems at National Informatics Centre (NIC), Government of India.

View Full Bio & Credentials →


Related In-Depth Engineering Guides