Skip to main content

EAF IAM Client SDK

The EAF IAM Client SDK provides seamless integration with the ACCI EAF Identity and Access Management (IAM) service, enabling Spring Boot applications to authenticate users, enforce role-based access control (RBAC), and maintain tenant context across multi-tenant environments.

Featuresโ€‹

  • ๐Ÿ” JWT Token Validation: Automatic validation of EAF-issued JWT tokens
  • ๐Ÿข Multi-tenant Context: Automatic tenant context propagation
  • ๐Ÿ›ก๏ธ Spring Security Integration: Seamless integration with Spring Security
  • ๐Ÿ“‹ Role-Based Access Control: Support for @PreAuthorize annotations and programmatic role checking
  • โš™๏ธ Auto-Configuration: Zero-configuration setup with Spring Boot auto-configuration
  • ๐Ÿงช Test Support: Comprehensive testing utilities and examples

Quick Startโ€‹

1. Add Dependencyโ€‹

Add the EAF IAM Client SDK to your Spring Boot project:

dependencies {
implementation("com.axians.eaf:eaf-iam-client")
}

2. Configure Propertiesโ€‹

Add the following properties to your application.yml:

eaf:
iam:
enabled: true
base-url: 'http://localhost:8080'
issuer-uri: 'http://localhost:8080'
audience: 'eaf-services'

3. Secure Your Endpointsโ€‹

Use Spring Security annotations to protect your endpoints:

@RestController
@RequestMapping("/api/products")
class ProductController {

@GetMapping
@PreAuthorize("hasRole('TENANT_USER')")
fun getProducts(): List<Product> {
// Your business logic here
return productService.findAll()
}

@PostMapping
@PreAuthorize("hasRole('TENANT_ADMIN')")
fun createProduct(@RequestBody product: Product): Product {
return productService.create(product)
}
}

4. Access User Contextโ€‹

Access the current user and tenant context in your services:

@Service
class ProductService {

fun findAll(): List<Product> {
val authentication = SecurityContextHolder.getContext().authentication as EafAuthentication
val tenantId = authentication.tenantId
val userId = authentication.principal.userId

// Use tenant context for data isolation
return productRepository.findByTenantId(tenantId)
}
}

Configuration Propertiesโ€‹

PropertyDescriptionDefaultRequired
eaf.iam.enabledEnable/disable EAF IAM integrationtrueNo
eaf.iam.base-urlBase URL of the IAM service-Yes
eaf.iam.issuer-uriJWT token issuer URI-Yes
eaf.iam.audienceExpected JWT audience-Yes

Authentication Flowโ€‹

sequenceDiagram
participant Client
participant App as Your Application
participant Filter as JwtAuthenticationFilter
participant IAM as IAM Service

Client->>App: Request with JWT token
App->>Filter: Process request
Filter->>Filter: Extract JWT from Authorization header
Filter->>IAM: Validate token via /api/v1/auth/introspect
IAM->>Filter: Return user details and roles
Filter->>Filter: Create EafAuthentication object
Filter->>App: Set SecurityContext
App->>Client: Process request with user context

Role Mappingโ€‹

The SDK automatically maps IAM service roles to Spring Security authorities:

IAM RoleSpring AuthorityDescription
TENANT_ADMINROLE_TENANT_ADMINFull administrative access within tenant
TENANT_USERROLE_TENANT_USERStandard user access within tenant
SUPER_ADMINROLE_SUPER_ADMINSystem-wide administrative access

Programmatic Role Checkingโ€‹

Beyond annotations, you can check roles programmatically:

@Service
class BusinessService {

fun performSensitiveOperation() {
val authentication = SecurityContextHolder.getContext().authentication as EafAuthentication
val principal = authentication.principal as EafPrincipal

when {
principal.hasRole("TENANT_ADMIN") -> {
// Admin-specific logic
}
principal.hasRole("TENANT_USER") -> {
// User-specific logic
}
else -> {
throw AccessDeniedException("Insufficient permissions")
}
}
}
}

Error Handlingโ€‹

The SDK provides comprehensive error handling:

Common Error Scenariosโ€‹

ScenarioHTTP StatusResponse
Missing token401{"error": "unauthorized", "message": "No authentication token provided"}
Invalid token401{"error": "invalid_token", "message": "Token validation failed"}
Expired token401{"error": "token_expired", "message": "Authentication token has expired"}
Insufficient permissions403{"error": "access_denied", "message": "Insufficient permissions"}

Custom Error Handlingโ€‹

You can customize error responses by providing your own AuthenticationEntryPoint:

@Configuration
class SecurityConfig {

@Bean
fun authenticationEntryPoint(): AuthenticationEntryPoint {
return AuthenticationEntryPoint { request, response, authException ->
response.contentType = "application/json"
response.status = HttpServletResponse.SC_UNAUTHORIZED
response.writer.write("""{"error": "custom_unauthorized", "message": "${authException.message}"}""")
}
}
}

Testingโ€‹

Unit Testingโ€‹

Test your secured endpoints using Spring Security test utilities:

@WebMvcTest(ProductController::class)
class ProductControllerTest {

@Autowired
private lateinit var mockMvc: MockMvc

@Test
@WithMockUser(roles = ["TENANT_USER"])
fun `should allow access to users with TENANT_USER role`() {
mockMvc.perform(get("/api/products"))
.andExpect(status().isOk)
}

@Test
@WithMockUser(roles = ["TENANT_ADMIN"])
fun `should allow product creation for admins`() {
mockMvc.perform(
post("/api/products")
.contentType(MediaType.APPLICATION_JSON)
.content("""{"name": "Test Product"}""")
).andExpect(status().isCreated)
}
}

Integration Testingโ€‹

For integration tests, you can mock the IAM service or use test containers:

@SpringBootTest
@TestPropertySource(properties = [
"eaf.iam.base-url=http://localhost:8080",
"eaf.iam.issuer-uri=http://localhost:8080",
"eaf.iam.audience=test-audience"
])
class ProductIntegrationTest {

@Test
fun `should authenticate valid JWT token`() {
// Your integration test logic
}
}

Troubleshootingโ€‹

Common Issuesโ€‹

Issue: NoSuchBeanDefinitionException for security components Solution: Ensure @EnableWebSecurity is present and auto-configuration is not excluded

Issue: Token validation fails with 401 Solution: Verify IAM service URL and ensure the service is accessible

Issue: Roles not recognized in @PreAuthorize Solution: Check role mapping and ensure roles are prefixed with ROLE_

Debug Loggingโ€‹

Enable debug logging to troubleshoot authentication issues:

logging:
level:
com.axians.eaf.iam.client: DEBUG
org.springframework.security: DEBUG

Architectureโ€‹

The EAF IAM Client SDK follows clean architecture principles:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Your Application โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ EAF IAM Client SDK โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ Security โ”‚ โ”‚ Auto Config โ”‚ โ”‚
โ”‚ โ”‚ Filter โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ Spring Security โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ IAM Service โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Best Practicesโ€‹

  1. Always validate tenant context: Ensure data isolation by checking tenant ID
  2. Use method-level security: Prefer @PreAuthorize over URL-based security
  3. Handle authentication errors gracefully: Provide meaningful error messages
  4. Test security configurations: Include security tests in your test suite
  5. Monitor authentication metrics: Track authentication success/failure rates

Migration Guideโ€‹

If you're migrating from a custom authentication solution:

  1. Replace custom filters with EAF IAM Client SDK auto-configuration
  2. Update role checks to use Spring Security authorities
  3. Migrate user context access to use EafAuthentication and EafPrincipal
  4. Update tests to use Spring Security test utilities

Supportโ€‹

For issues and questions: