I'm using Keycloak as my OAuth2 Authorization Server and I configured an OAuth2 Resource Server for Multitenancy following this official example on GitHub. The current Tenant is resolved considering the Issuer field of the JWT token. Hence the token is verified against the JWKS exposed at the corresponding OpenID Connect well known endpoint.
This is my Security Configuration:
@EnableWebSecurity
@RequiredArgsConstructor
@EnableAutoConfiguration(exclude = UserDetailsServiceAutoConfiguration.class)
public class OrganizationSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final TenantService tenantService;
private List<Tenant> tenants;
@PostConstruct
public void init() {
this.tenants = this.tenantService.findAllWithRelationships();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.oauth2ResourceServer()
.authenticationManagerResolver(new MultiTenantAuthenticationManagerResolver(this.tenants));
}
}
and this is my custom AuthenticationManagerResolver:
public class MultiTenantAuthenticationManagerResolver implements AuthenticationManagerResolver<HttpServletRequest> {
private final AuthenticationManagerResolver<HttpServletRequest> resolver;
private List<Tenant> tenants;
public MultiTenantAuthenticationManagerResolver(List<Tenant> tenants) {
this.tenants = tenants;
List<String> trustedIssuers = this.tenants.stream()
.map(Tenant::getIssuers)
.flatMap(urls -> urls.stream().map(URL::toString))
.collect(Collectors.toList());
this.resolver = new JwtIssuerAuthenticationManagerResolver(trustedIssuers);
}
@Override
public AuthenticationManager resolve(HttpServletRequest context) {
return this.resolver.resolve(context);
}
}
Now, because of the design of org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver.TrustedIssuerJwtAuthenticationManagerResolver
which is private, the only way I can think in order to extract a custom principal is to reimplement everything that follows:
- TrustedIssuerJwtAuthenticationManagerResolver
- the returned AuthenticationManager
- the AuthenticationConverter
- the CustomAuthenticationToken which extends JwtAuthenticationToken
- the CustomPrincipal
To me it seems a lot of Reinventing the wheel, where my only need would be to have a custom Principal.
The examples that I found don't seem to suit my case since they refer to OAuth2Client or are not tought for Multitenancy.
- https://www.baeldung.com/spring-security-oauth-principal-authorities-extractor
- How to extend OAuth2 principal
Do I really need to reimplement all such classes/interfaes or is there a smarter approach?