Enterprise Identity Management: Integrating WSO2 Identity Server with Java Applications

WSO2 Identity Server is a powerful, open-source IAM solution that provides identity federation, SSO, adaptive authentication, and API security. This guide covers complete integration with Java applications using OIDC, SAML, and SCIM.

Architecture Overview

Java Applications → WSO2 IS → (Identity Providers)
↑
(OIDC/SAML/SCIM
Integrations)

Step 1: WSO2 Identity Server Setup

Docker Deployment

# docker-compose.yml
version: '3.8'
services:
wso2-is:
image: wso2/wso2is:6.1.0
ports:
- "9443:9443"
- "9763:9763"
environment:
- CARBON_HOST=localhost
- CARBON_HTTPS_PORT=9443
volumes:
- ./repository/conf:/home/wso2carbon/wso2is-6.1.0/repository/conf
- ./repository/deployment:/home/wso2carbon/wso2is-6.1.0/repository/deployment
networks:
- wso2-network
wso2-is-db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wso2is
MYSQL_USER: wso2carbon
MYSQL_PASSWORD: wso2carbon
volumes:
- mysql_data:/var/lib/mysql
networks:
- wso2-network
volumes:
mysql_data:
networks:
wso2-network:

Programmatic Service Provider Configuration

// src/main/java/com/company/wso2/WSO2Initializer.java
package com.company.wso2;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Base64;
/**
* Programmatic configuration of WSO2 Identity Server
*/
@Component
public class WSO2Initializer {
@Value("${wso2.is.base-url:https://localhost:9443}")
private String baseUrl;
@Value("${wso2.is.admin-username:admin}")
private String adminUsername;
@Value("${wso2.is.admin-password:admin}")
private String adminPassword;
public void createServiceProvider(String spName, String callbackUrl) {
try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost post = new HttpPost(baseUrl + "/t/carbon.super/api/server/v1/service-providers");
// Basic Authentication
String auth = adminUsername + ":" + adminPassword;
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes());
post.setHeader("Authorization", "Basic " + encodedAuth);
post.setHeader("Content-Type", "application/json");
JSONObject spConfig = new JSONObject();
spConfig.put("name", spName);
spConfig.put("description", "Java Application Service Provider");
JSONObject inboundConfig = new JSONObject();
JSONObject oidcConfig = new JSONObject();
// OIDC Configuration
oidcConfig.put("clientId", spName + "_client");
oidcConfig.put("clientSecret", generateClientSecret());
oidcConfig.put("grantTypes", new String[]{"authorization_code", "refresh_token", "password"});
oidcConfig.put("callbackUrl", callbackUrl);
inboundConfig.put("oidc", oidcConfig);
spConfig.put("inboundConfiguration", inboundConfig);
StringEntity entity = new StringEntity(spConfig.toString());
post.setEntity(entity);
try (CloseableHttpResponse response = client.execute(post)) {
if (response.getStatusLine().getStatusCode() == 201) {
System.out.println("Service Provider created successfully: " + spName);
} else {
System.err.println("Failed to create Service Provider: " + response.getStatusLine().getStatusCode());
}
}
} catch (Exception e) {
throw new RuntimeException("Failed to create WSO2 Service Provider", e);
}
}
private String generateClientSecret() {
return java.util.UUID.randomUUID().toString().replace("-", "").substring(0, 16);
}
}

Step 2: Spring Boot OIDC Integration

Dependencies

<!-- pom.xml -->
<dependencies>
<!-- Spring Security OAuth2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<!-- WSO2 Identity Server -->
<dependency>
<groupId>org.wso2.is</groupId>
<artifactId>org.wso2.carbon.identity.oauth.stub</artifactId>
<version>6.1.0</version>
</dependency>
<!-- HTTP Client -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>

Application Configuration

# application.yml
spring:
security:
oauth2:
client:
registration:
wso2:
client-id: ${WSO2_CLIENT_ID:java_app_client}
client-secret: ${WSO2_CLIENT_SECRET:secret}
scope: openid,profile,email
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/wso2"
provider:
wso2:
issuer-uri: ${WSO2_ISSUER_URI:https://localhost:9443/oauth2/token}
authorization-uri: ${WSO2_AUTHORIZATION_URI:https://localhost:9443/oauth2/authorize}
token-uri: ${WSO2_TOKEN_URI:https://localhost:9443/oauth2/token}
user-info-uri: ${WSO2_USERINFO_URI:https://localhost:9443/oauth2/userinfo}
jwk-set-uri: ${WSO2_JWKS_URI:https://localhost:9443/oauth2/jwks}
user-name-attribute: sub
wso2:
identity-server:
base-url: https://localhost:9443
tenant: carbon.super
admin-username: admin
admin-password: admin
server:
port: 8080
ssl:
trust-store: classpath:wso2truststore.jks
trust-store-password: wso2carbon

Security Configuration

// src/main/java/com/company/config/WSO2SecurityConfig.java
package com.company.config;
import com.company.wso2.WSO2UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class WSO2SecurityConfig {
private final WSO2UserService wso2UserService;
private final WSO2JwtFilter wso2JwtFilter;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(cors -> cors.disable())
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> 
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// Authorization rules
.authorizeHttpRequests(authz -> authz
.requestMatchers("/", "/public/**", "/login**", "/error").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN")
.requestMatchers("/api/**").authenticated()
.anyRequest().authenticated()
)
// OAuth2 Login configuration
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.failureUrl("/login?error=true")
.userInfoEndpoint(userInfo -> 
userInfo.oidcUserService(wso2UserService))
)
// OAuth2 Resource Server for JWT
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter()))
)
// Add custom JWT filter
.addFilterBefore(wso2JwtFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
}

Step 3: WSO2 OIDC User Service

// src/main/java/com/company/wso2/WSO2UserService.java
package com.company.wso2;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
@Slf4j
@RequiredArgsConstructor
public class WSO2UserService extends OidcUserService {
private final WSO2AdminClient wso2AdminClient;
@Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser oidcUser = super.loadUser(userRequest);
try {
return processOidcUser(oidcUser, userRequest);
} catch (Exception e) {
log.error("Failed to process OIDC user", e);
throw new OAuth2AuthenticationException("User processing failed");
}
}
private OidcUser processOidcUser(OidcUser oidcUser, OidcUserRequest userRequest) {
String username = oidcUser.getPreferredUsername();
Map<String, Object> claims = oidcUser.getClaims();
// Extract roles from WSO2 claims
Collection<GrantedAuthority> authorities = extractAuthorities(claims);
// Create custom user with additional claims
return new WSO2OidcUser(oidcUser, authorities, username);
}
@SuppressWarnings("unchecked")
private Collection<GrantedAuthority> extractAuthorities(Map<String, Object> claims) {
try {
// WSO2 roles are typically in the "groups" claim or custom claims
List<String> roles = (List<String>) claims.get("groups");
if (roles == null) {
roles = Collections.emptyList();
}
return roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))
.collect(Collectors.toList());
} catch (Exception e) {
log.warn("Failed to extract authorities from claims", e);
return Collections.emptyList();
}
}
/**
* Custom OIDC User implementation for WSO2
*/
public static class WSO2OidcUser implements OidcUser {
private final OidcUser delegate;
private final Collection<GrantedAuthority> authorities;
private final String username;
public WSO2OidcUser(OidcUser delegate, Collection<GrantedAuthority> authorities, String username) {
this.delegate = delegate;
this.authorities = authorities;
this.username = username;
}
@Override
public Map<String, Object> getClaims() {
return delegate.getClaims();
}
@Override
public OidcUserInfo getUserInfo() {
return delegate.getUserInfo();
}
@Override
public OidcIdToken getIdToken() {
return delegate.getIdToken();
}
@Override
public String getName() {
return username;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public Map<String, Object> getAttributes() {
return delegate.getAttributes();
}
}
}

Step 4: JWT Token Processing

// src/main/java/com/company/wso2/WSO2JwtFilter.java
package com.company.wso2;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;
@Component
@Slf4j
@RequiredArgsConstructor
public class WSO2JwtFilter extends OncePerRequestFilter {
private final ObjectMapper objectMapper;
@Value("${wso2.identity-server.base-url}")
private String wso2BaseUrl;
@Override
protected void doFilterInternal(HttpServletRequest request, 
HttpServletResponse response, 
FilterChain filterChain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String token = authHeader.substring(7);
try {
WSO2User user = validateAndParseToken(token);
setAuthenticationContext(user, token);
} catch (Exception e) {
log.error("JWT validation failed", e);
sendErrorResponse(response, "Invalid token");
return;
}
}
filterChain.doFilter(request, response);
}
private WSO2User validateAndParseToken(String token) {
try {
Claims claims = Jwts.parserBuilder()
.setSigningKeyResolver(new WSO2SigningKeyResolver(wso2BaseUrl))
.build()
.parseClaimsJws(token)
.getBody();
return extractUserFromClaims(claims);
} catch (Exception e) {
throw new RuntimeException("Token validation failed", e);
}
}
@SuppressWarnings("unchecked")
private WSO2User extractUserFromClaims(Claims claims) {
String username = claims.getSubject();
String email = claims.get("email", String.class);
List<String> roles = claims.get("groups", List.class);
if (roles == null) {
roles = List.of("USER"); // Default role
}
List<SimpleGrantedAuthority> authorities = roles.stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.toUpperCase()))
.collect(Collectors.toList());
return WSO2User.builder()
.username(username)
.email(email)
.roles(roles)
.authorities(authorities)
.claims(claims)
.build();
}
private void setAuthenticationContext(WSO2User user, String token) {
UsernamePasswordAuthenticationToken authentication = 
new UsernamePasswordAuthenticationToken(user, token, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
private void sendErrorResponse(HttpServletResponse response, String message) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
String errorResponse = objectMapper.writeValueAsString(
java.util.Map.of("error", "UNAUTHORIZED", "message", message)
);
response.getWriter().write(errorResponse);
}
@Data
@Builder
public static class WSO2User {
private String username;
private String email;
private List<String> roles;
private List<SimpleGrantedAuthority> authorities;
private Claims claims;
public boolean hasRole(String role) {
return roles.stream()
.anyMatch(r -> r.equalsIgnoreCase(role));
}
}
}

Step 5: WSO2 Admin Client

// src/main/java/com/company/wso2/WSO2AdminClient.java
package com.company.wso2;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Component
@Slf4j
public class WSO2AdminClient {
@Value("${wso2.identity-server.base-url}")
private String baseUrl;
@Value("${wso2.identity-server.admin-username}")
private String adminUsername;
@Value("${wso2.identity-server.admin-password}")
private String adminPassword;
@Value("${wso2.identity-server.tenant:carbon.super}")
private String tenant;
private String getAuthHeader() {
String auth = adminUsername + ":" + adminPassword;
return "Basic " + Base64.getEncoder().encodeToString(auth.getBytes());
}
public List<String> getUserRoles(String username) {
try (CloseableHttpClient client = HttpClients.createDefault()) {
String url = baseUrl + "/t/" + tenant + "/scim2/Users?filter=userNameEq" + username;
HttpGet get = new HttpGet(url);
get.setHeader("Authorization", getAuthHeader());
try (CloseableHttpResponse response = client.execute(get)) {
if (response.getStatusLine().getStatusCode() == 200) {
String responseBody = new String(response.getEntity().getContent().readAllBytes());
JSONObject jsonResponse = new JSONObject(responseBody);
return extractRolesFromSCIMResponse(jsonResponse);
}
}
} catch (Exception e) {
log.error("Failed to get user roles", e);
}
return Collections.emptyList();
}
private List<String> extractRolesFromSCIMResponse(JSONObject scimResponse) {
try {
JSONArray resources = scimResponse.getJSONArray("Resources");
if (resources.length() > 0) {
JSONObject user = resources.getJSONObject(0);
JSONArray groups = user.optJSONArray("groups");
if (groups != null) {
return IntStream.range(0, groups.length())
.mapToObj(groups::getJSONObject)
.map(group -> group.getString("display"))
.collect(Collectors.toList());
}
}
} catch (Exception e) {
log.warn("Failed to extract roles from SCIM response", e);
}
return Collections.emptyList();
}
public boolean createUser(String username, String password, String email, List<String> roles) {
try (CloseableHttpClient client = HttpClients.createDefault()) {
String url = baseUrl + "/t/" + tenant + "/scim2/Users";
HttpPost post = new HttpPost(url);
post.setHeader("Authorization", getAuthHeader());
post.setHeader("Content-Type", "application/json");
JSONObject user = new JSONObject();
user.put("userName", username);
user.put("password", password);
JSONArray emails = new JSONArray();
JSONObject emailObj = new JSONObject();
emailObj.put("value", email);
emailObj.put("primary", true);
emails.put(emailObj);
user.put("emails", emails);
// Add roles as groups
if (roles != null && !roles.isEmpty()) {
JSONArray groups = new JSONArray();
for (String role : roles) {
JSONObject group = new JSONObject();
group.put("value", getRoleId(role)); // You need to map role names to IDs
group.put("display", role);
groups.put(group);
}
user.put("groups", groups);
}
StringEntity entity = new StringEntity(user.toString());
post.setEntity(entity);
try (CloseableHttpResponse response = client.execute(post)) {
return response.getStatusLine().getStatusCode() == 201;
}
} catch (Exception e) {
log.error("Failed to create user", e);
return false;
}
}
private String getRoleId(String roleName) {
// This should be implemented to fetch actual role IDs from WSO2
// For now, returning a placeholder
return roleName.toLowerCase().replace(" ", "-") + "-id";
}
}

Step 6: REST API Controllers

// src/main/java/com/company/controller/AuthController.java
package com.company.controller;
import com.company.wso2.WSO2JwtFilter.WSO2User;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequiredArgsConstructor
@Slf4j
public class AuthController {
@GetMapping("/login")
public String loginPage() {
return "login";
}
@GetMapping("/dashboard")
public String dashboard(@AuthenticationPrincipal OidcUser oidcUser, Model model) {
if (oidcUser != null) {
model.addAttribute("user", oidcUser.getAttributes());
model.addAttribute("username", oidcUser.getPreferredUsername());
model.addAttribute("roles", oidcUser.getAuthorities());
}
return "dashboard";
}
@GetMapping("/api/user/profile")
@ResponseBody
public Map<String, Object> getUserProfile(@AuthenticationPrincipal OidcUser oidcUser) {
Map<String, Object> profile = new HashMap<>();
if (oidcUser != null) {
profile.put("username", oidcUser.getPreferredUsername());
profile.put("email", oidcUser.getEmail());
profile.put("name", oidcUser.getFullName());
profile.put("roles", oidcUser.getAuthorities().stream()
.map(auth -> auth.getAuthority().replace("ROLE_", ""))
.toList());
}
return profile;
}
}
// Protected API Controller
@RestController
@RequestMapping("/api")
public class ApiController {
@GetMapping("/public/info")
public Map<String, String> publicInfo() {
return Map.of("message", "This is public information");
}
@GetMapping("/user/data")
public Map<String, String> userData(@AuthenticationPrincipal WSO2JwtFilter.WSO2User user) {
return Map.of(
"message", "Hello " + user.getUsername(),
"role", user.getRoles().get(0)
);
}
@GetMapping("/admin/data")
public Map<String, String> adminData(@AuthenticationPrincipal WSO2JwtFilter.WSO2User user) {
if (!user.hasRole("ADMIN")) {
throw new RuntimeException("Access denied");
}
return Map.of(
"message", "Admin access granted",
"user", user.getUsername()
);
}
}

Step 7: SAML Integration (Alternative to OIDC)

// src/main/java/com/company/wso2/WSO2SAMLConfig.java
package com.company.wso2;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class WSO2SAMLConfig {
// SAML Configuration would go here
// This is a simplified example
/*
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.anyRequest().authenticated()
)
.saml2Login(saml2 -> saml2
.loginProcessingUrl("/saml2/login")
.idpLoginProcessingUrl("/saml2/SSO")
)
.saml2Logout(saml2 -> saml2
.logoutRequest(req -> req.logoutUrl("/saml2/logout"))
);
return http.build();
}
*/
}
// SAML User Details Service
@Component
public class SAMLUserDetailsServiceImpl implements SAMLUserDetailsService {
@Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
String username = credential.getNameID().getValue();
List<GrantedAuthority> authorities = new ArrayList<>();
// Extract attributes from SAML assertion
Attribute roleAttribute = credential.getAttribute("Role");
if (roleAttribute != null) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + roleAttribute.getAttributeValues()[0].toString()));
}
return new User(username, "", authorities);
}
}

Step 8: Adaptive Authentication

// src/main/java/com/company/wso2/adaptive/AdaptiveAuthService.java
package com.company.wso2.adaptive;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@Service
@Slf4j
public class AdaptiveAuthService {
@Value("${wso2.identity-server.base-url}")
private String wso2BaseUrl;
public Map<String, Object> evaluateAuthentication(HttpServletRequest request, 
String username, 
Map<String, String> context) {
Map<String, Object> authContext = new HashMap<>();
authContext.put("username", username);
authContext.put("ipAddress", getClientIpAddress(request));
authContext.put("userAgent", request.getHeader("User-Agent"));
authContext.put("timestamp", System.currentTimeMillis());
authContext.putAll(context);
// Risk assessment
int riskScore = calculateRiskScore(authContext);
authContext.put("riskScore", riskScore);
// Determine authentication level
String authLevel = determineAuthLevel(riskScore);
authContext.put("requiredAuthLevel", authLevel);
log.info("Adaptive auth evaluation - User: {}, Risk: {}, Auth Level: {}", 
username, riskScore, authLevel);
return authContext;
}
private int calculateRiskScore(Map<String, Object> context) {
int score = 0;
// Example risk factors
String ipAddress = (String) context.get("ipAddress");
if (isSuspiciousIp(ipAddress)) {
score += 30;
}
String userAgent = (String) context.get("userAgent");
if (isSuspiciousUserAgent(userAgent)) {
score += 20;
}
// Add more risk factors as needed
return Math.min(score, 100);
}
private String determineAuthLevel(int riskScore) {
if (riskScore >= 70) {
return "HIGH"; // Require MFA
} else if (riskScore >= 30) {
return "MEDIUM"; // Additional verification
} else {
return "LOW"; // Standard authentication
}
}
private boolean isSuspiciousIp(String ipAddress) {
// Implement IP reputation check
return ipAddress.startsWith("192.168.") || 
ipAddress.startsWith("10.") ||
ipAddress.equals("127.0.0.1");
}
private boolean isSuspiciousUserAgent(String userAgent) {
// Implement user agent analysis
return userAgent == null || userAgent.isEmpty() || 
userAgent.contains("bot") || userAgent.contains("crawler");
}
private String getClientIpAddress(HttpServletRequest request) {
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty()) {
return xForwardedFor.split(",")[0];
}
return request.getRemoteAddr();
}
}

Step 9: Testing

Test Configuration

// src/test/java/com/company/config/TestWSO2Config.java
@Configuration
@TestPropertySource(properties = {
"wso2.identity-server.base-url=http://localhost:9443",
"wso2.identity-server.admin-username=admin",
"wso2.identity-server.admin-password=admin"
})
public class TestWSO2Config {
@Bean
@Primary
public WSO2AdminClient mockWSO2AdminClient() {
WSO2AdminClient client = mock(WSO2AdminClient.class);
when(client.getUserRoles(anyString())).thenReturn(List.of("USER", "ADMIN"));
return client;
}
}
// src/test/java/com/company/controller/ApiControllerTest.java
@WebMvcTest(ApiController.class)
@Import(TestWSO2Config.class)
class ApiControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockUser(roles = "USER")
void testUserEndpoint() throws Exception {
mockMvc.perform(get("/api/user/data")
.header("Authorization", "Bearer mock-token"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message").exists());
}
@Test
@WithMockUser(roles = "ADMIN")
void testAdminEndpoint() throws Exception {
mockMvc.perform(get("/api/admin/data"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message").value("Admin access granted"));
}
@Test
void testPublicEndpoint() throws Exception {
mockMvc.perform(get("/api/public/info"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.message").value("This is public information"));
}
}

Configuration for Production

application-prod.yml

spring:
security:
oauth2:
client:
registration:
wso2:
client-id: ${WSO2_CLIENT_ID}
client-secret: ${WSO2_CLIENT_SECRET}
scope: openid,profile,email,address,phone
provider:
wso2:
issuer-uri: ${WSO2_ISSUER_URI}
authorization-uri: ${WSO2_AUTHORIZATION_URI}
token-uri: ${WSO2_TOKEN_URI}
user-info-uri: ${WSO2_USERINFO_URI}
wso2:
identity-server:
base-url: ${WSO2_BASE_URL}
tenant: ${WSO2_TENANT:carbon.super}
admin-username: ${WSO2_ADMIN_USERNAME}
admin-password: ${WSO2_ADMIN_PASSWORD}
logging:
level:
com.company.wso2: DEBUG
org.springframework.security: INFO

Best Practices

  1. Security
  • Use HTTPS in production
  • Validate JWT signatures properly
  • Implement proper CORS configuration
  • Use secure cookie settings for sessions
  1. Performance
  • Cache WSO2 public keys
  • Use connection pooling for admin API calls
  • Implement rate limiting
  1. Error Handling
  • Graceful handling of WSO2 downtime
  • Proper error messages for users
  • Comprehensive logging
  1. Monitoring
  • Log authentication events
  • Monitor token expiration
  • Track failed authentication attempts

Conclusion

WSO2 Identity Server integration provides:

  • Enterprise-grade IAM: Robust identity and access management
  • Multiple Protocols: OIDC, SAML, OAuth2 support
  • Extensibility: Custom authentication flows and extensions
  • Security: Comprehensive security features

Implementation steps:

  1. Set up WSO2 Identity Server
  2. Configure OIDC client in WSO2
  3. Integrate Spring Security OAuth2
  4. Implement JWT validation
  5. Add admin client for user management
  6. Implement adaptive authentication
  7. Add testing and monitoring

This setup provides a secure, scalable identity management solution suitable for enterprise Java applications.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper