Skip to content

Override Points

IAM follows one contract everywhere: every capability is opt-out with a safe default, and every default is a bean your application can replace. Define your own bean of the seam type and IAM’s default steps aside.

Seam Default Replace it when
PermissionChecker full three-tier resolution you need custom decision logic or an external PDP
PermissionCache Caffeine in-memory Hazelcast, Infinispan, your cache fabric
IamTenantDataSourceRegistry properties-backed pools dynamic tenant fleets, vault-managed credentials
SecurityFilterChain IAM’s JWT chain your app owns the chain, composes IAM’s filters
Repository ports (16) Spring Data JPA adapters alternative persistence for any entity
RateLimitStore in-memory distributed limiting
@Configuration
class AccessConfig {
@Bean
fun permissionCache(hz: HazelcastInstance): PermissionCache = object : PermissionCache {
override fun get(tenantId: UUID, userId: UUID): ResolvedPermissions? = /* ... */
override fun put(tenantId: UUID, userId: UUID, permissions: ResolvedPermissions) { /* ... */ }
override fun evict(tenantId: UUID, userId: UUID) { /* ... */ }
override fun evictTenant(tenantId: UUID) { /* ... */ }
}
}
@Bean
fun tenantDataSourceRegistry(vault: VaultTemplate): IamTenantDataSourceRegistry =
IamTenantDataSourceRegistry { tenantId ->
pools.computeIfAbsent(tenantId) { buildPool(vault.credentialsFor(it)) }
}

(fun interface — a lambda is a valid registry.)

Every repository sits behind a port. Replace the adapter for a single aggregate without touching the rest:

@Bean
@Primary
fun auditLogRepositoryPort(mongo: MongoTemplate): AuditLogRepositoryPort =
MongoAuditLogAdapter(mongo) // audit goes to Mongo; everything else stays JPA

Everything big also has a flag: iam.enabled, iam.cache.enabled, iam.rate-limit.enabled, iam.audit.enabled, iam.oauth2.enabled, iam.two-factor.enabled, iam.multi-tenancy.enabled, iam.resources.enabled. Disabled never means “missing bean” — it means a no-op implementation, so your code never null-checks a capability.