Yash/Backend Engineer
Interview / Contact
Back to Blog

Implementing OAuth 2.0 with Spring Security: A Practical Guide

10 min readYaswanth Reddy Koduru
OAuth 2.0Spring SecurityJWTAuthenticationSecurity

Implementing OAuth 2.0 with Spring Security: A Practical Guide

Security isn't optional in enterprise systems. At WageNest and ChildEra, I implemented comprehensive OAuth 2.0 authentication with Spring Security. Here's what I learned.

Why OAuth 2.0?

Traditional session-based auth doesn't work well for:

  • Microservices architectures
  • Mobile applications
  • Third-party integrations
  • Stateless APIs

OAuth 2.0 with JWT tokens solves these problems.

Architecture Overview

Components:

  1. Authorization Server - Issues tokens
  2. Resource Servers - Your microservices
  3. Client Applications - React frontend, mobile apps
  4. Token Store - Redis for token validation

Implementation Steps

Step 1: Authorization Server Setup

@Configuration
@EnableAuthorizationServer
public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
            .withClient("payroll-web-app")
            .secret(passwordEncoder().encode("secret"))
            .authorizedGrantTypes("authorization_code", "refresh_token")
            .scopes("read", "write")
            .accessTokenValiditySeconds(3600)
            .refreshTokenValiditySeconds(86400);
    }
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
            .tokenStore(tokenStore())
            .accessTokenConverter(accessTokenConverter())
            .authenticationManager(authenticationManager);
    }
}

Step 2: JWT Token Configuration

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setSigningKey("your-secret-key"); // Use proper key management in production
    return converter;
}

@Bean
public TokenStore tokenStore() {
    return new JwtTokenStore(accessTokenConverter());
}

Step 3: Resource Server Security

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers("/api/public/**").permitAll()
            .antMatchers("/api/admin/**").hasRole("ADMIN")
            .antMatchers("/api/**").authenticated()
            .and()
            .csrf().disable();
    }
}

Step 4: Method-Level Security

@RestController
@RequestMapping("/api/payroll")
public class PayrollController {
    
    @PreAuthorize("hasRole('PAYROLL_ADMIN')")
    @PostMapping("/process")
    public ResponseEntity<PayrollResult> processPayroll(@RequestBody PayrollRequest request) {
        // Only users with PAYROLL_ADMIN role can access
        return ResponseEntity.ok(payrollService.process(request));
    }
    
    @PreAuthorize("hasPermission(#employeeId, 'Employee', 'READ')")
    @GetMapping("/employee/{employeeId}")
    public ResponseEntity<Employee> getEmployee(@PathVariable Long employeeId) {
        // Custom permission evaluator checks if user can access this employee
        return ResponseEntity.ok(employeeService.getEmployee(employeeId));
    }
}

Role-Based Access Control (RBAC)

Implemented granular permissions:

Roles:

  • SUPER_ADMIN - Full system access
  • TENANT_ADMIN - Tenant-level admin
  • PAYROLL_MANAGER - Can process payroll
  • EMPLOYEE - Self-service access

Custom Permission Evaluator:

@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
    @Override
    public boolean hasPermission(Authentication auth, Object targetId, Object targetType, Object permission) {
        if (targetType.equals("Employee")) {
            // Check if user can access this employee
            UserDetails user = (UserDetails) auth.getPrincipal();
            return employeeAccessService.canAccess(user, (Long) targetId);
        }
        return false;
    }
}

Security Best Practices

1. Token Expiration

  • Access tokens: 1 hour
  • Refresh tokens: 24 hours
  • Rotate refresh tokens on use

2. HTTPS Everywhere

All token exchanges over HTTPS only

3. Token Storage

  • Never store tokens in localStorage
  • Use httpOnly cookies or secure storage
  • Clear tokens on logout

4. Rate Limiting

Prevent brute force attacks:

@Bean
public RateLimiter rateLimiter() {
    return RateLimiter.create(10.0); // 10 requests per second
}

Results

After implementing OAuth 2.0:

  • Security incidents: Reduced to zero
  • Unauthorized access attempts: Blocked 100%
  • Token-based auth: Seamless across mobile and web
  • Audit compliance: Full request traceability

Common Pitfalls

  1. Exposing tokens in URLs - Use headers instead
  2. Long-lived tokens - Shorter is better
  3. Missing CSRF protection - Still needed for cookies
  4. Poor error messages - Don't leak security details
  5. Not validating redirect URIs - Security risk

Need help securing your Spring Boot microservices? Let's talk security architecture.