Enhancement:
spring.security.oauth2.resourceserver.jwt.issuer-uri configuration property currently accepts a single value.
In some cases (like multi-tenant environments), resource-servers accept identities issued by more than just one authorization-server.
It would ease spring-boot apps security configuration if spring.security.oauth2.resourceserver.jwt.issuer-uri could accept an array of issuers.
Currently I have to use a private property for that purpose and that leads to quite some confusion for developers used to spring-boot one.
Notes:
- properties and yaml files syntax would allow such a modification (String to String[]) without breaking existing config files
- OAuth2ResourceServerProperties$Jwt backward compatibility can be achieved with:
public class OAuth2ResourceServerProperties {
public static class Jwt {
// modified private property
private String[] issuerUris;
// untouched existing public interface
/**
* @deprecated Use {@link Jwt#getIssuerUris()} instead.
*/
@Deprecated
public String getIssuerUri() {
if (this.issuerUris == null || this.issuerUris.length == 0) {
return null;
}
if (this.issuerUris.length > 1) {
throw new RuntimeException("Jwt#getIssuerUris() must be used when more than one issuer is configured");
}
return this.issuerUris[0];
}
// added functionnality
public String[] getIssuerUris() {
return this.issuerUris;
}
public void setIssuerUri(String... issuerUris) {
this.issuerUris = issuerUris;
}
}
}
**Comment From: emisbalu**
I (we as a team) would like to take this up
**Comment From: EduColnago**
Is this issue available? I would like to try to do this
**Comment From: ch4mpy**
https://spring.io/blog/2022/06/01/spring-security-oauth-reaches-end-of-life
I suggest you get a look at alternatives for spring-boot resource-servers auto-configuration. Maybe [this one I wrote](https://github.com/ch4mpy/spring-addons) ?
**Comment From: wilkinsona**
@ch4mpy Spring Security itself provides OAuth support these days and Spring Boot provides auto-configuration for it. IMO, there's no need to look for an alternative.
**Comment From: wilkinsona**
@EduColnago thanks for the offer but we're not quite sure how to handle this one yet. Please watch the ideal-for-contribution label.
**Comment From: wilkinsona**
Given the Spring Security team's position on multiple issuer URIs and their decision to decline https://github.com/spring-projects/spring-security/issues/10943, I'm not sure that we should do anything in Spring Boot. IMO, it does not make sense to allow multiple URIs to be configured when Spring Boot's auto-configuration will only use one of them. This wouldn't address the confusion described in the description above, it would just change its source. I also don't think it makes sense for Spring Boot to support multiple issuer URIs if that requires us to maintain a composite `JwtDecoder` implementation that delegates to decoders created from the issuer URIs. IMO, such a class should be maintained as part of Spring Security so that all users can benefit from it, not just those using Spring Boot.
Flagging for team attention to see what everyone else thinks.
/cc @jzheaux
**Comment From: ch4mpy**
> @ch4mpy Spring Security itself provides OAuth support these days and Spring Boot provides auto-configuration for it. IMO, there's no need to look for an alternative.
@wilkinsona I had read the blog post too fast. My bad. But, actually I can find some reasons to look for alternatives to OpenID resource-server auto-configuration with `spring-boot-starter-oauth2-resource-server`:
- it does not support multi issuers out of the box (reason for this ticket)
- it populates security-context with `JwtAuthenticationToken` which has very limited interface for claims. Nowadays, most authorization servers being OIDC, an `Authentication` implementation exposing OpenID claims would be usefull (at least when what we provide in configuration is a `.well-known/openid-configuration`).
- authorities parsing is made from `scope`claim, which is mostly useless and must be overriden in almost any app
- CORS config must be added in the code. "Pure" resource-servers (REST APIs without embeded client) always need CORS. It would be convenient to do it from properties without any java config.
- defaults to 302 (redirect to login) when authentication is missing or invalid. A probably better default for resource-servers would be 401 (unauthorized). Login management should be a client concern (not one of resource-server).
- frequent resource-server flags must be set in the code too. It could be done from properties (without Java config). I'm thinking of:
- anonymous enabling (for public resources)
- stateless session-management (user state in the token)
- CSRF disabling
- maybe some permit-all routes (public resources accessible to anonymous) and a more restrictive default access (to isAuthenticated()?)
This are not just speculations, it is possible to achieve all of above with just a few properties: I do it [there](https://github.com/ch4mpy/spring-addons/tree/master/samples/tutorials/resource-server_with_oauthentication)
with this app:
```java
@SpringBootApplication
public class ResourceServerWithOAuthenticationApplication {
public static void main(String[] args) {
SpringApplication.run(ResourceServerWithOAuthenticationApplication.class, args);
}
@EnableGlobalMethodSecurity(prePostEnabled = true)
public static class WebSecurityConfig {
}
}
and that config
# shoud be set to where your authorization-server is
com.c4-soft.springaddons.security.token-issuers[0].location=https://localhost:9443/auth/realms/master
# shoud be configured with a list of private-claims this authorization-server puts user roles into
# below is default Keycloak conf for a `spring-addons` client with client roles mapper enabled
com.c4-soft.springaddons.security.token-issuers[0].authorities.claims=realm_access.roles,resource_access.spring-addons.roles
com.c4-soft.springaddons.security.cors[0].path=/greet/**
com.c4-soft.springaddons.security.permit-all=/actuator/health/readiness,/actuator/health/liveness,/v3/api-docs/**
# use IDE auto-completion or see SpringAddonsSecurityProperties javadoc for complete configuration properties list
Bootyful, isn't it?
As you could guess from com.c4-soft.springaddons.security.token-issuers being an array, it supports as many authorization-servers as needed, each with its own granted-authorities mapping: which claim(s) to get it from and with which basic processing (case and prefix).
composite JwtDecoder implementation that delegates to decoders created from the issuer URIs
I do it with JwtIssuerAuthenticationManagerResolver or ReactiveAuthenticationManagerResolver (credits go to spring-security team who told me that it is the expected way to do it, in the ticket you linked)
Comment From: jzheaux
@ch4mpy is correct that Spring Security ships with a component that supports multiple issuer URIs, primarily to support multi-tenant applications. Hypothetically, Boot could publish a JwtIssuerAuthenticationManagerResolver instead of a JwtDecoder.
This would have wider-reaching consequences than simply allowing issuer-uri to be a list, though. In fact, it would likely lead to something that looks more like the OAuth2 Client Boot configuration since each issuer could easily have different algorithm and audience expectations. While again, that's possible, I'm inclined to keep the Boot properties focused on single-tenancy, which is the most common use case.
Note that the reason the OAuth2 Client properties allow for declaring more than one client is to support multiple identity providers, like Google and Facebook. The fact that they can be used for multi-tenancy is a nice additional benefit, but not the primary use case.
c4soft chooses a multi-tenant approach and I appreciate the fact that there is an open-source solution out there based on Spring Security's resource server that enables this for those who need it.
By the way, @ch4mpy, I appreciate the list of features you generated above, that's really helpful. You and I have discussed some of those already, and I'd welcome further collaboration to find what's a good fit for adding to Spring Security.
Comment From: philwebb
Thanks @jzheaux. Given the comments above I don't think we should try to support multiple JWT issuer URIs. Thanks anyway for the suggestion and thanks for open-sourcing your springaddons project.
Comment From: ch4mpy
the reason the OAuth2 Client properties allow for declaring more than one client is to support multiple identity providers, like Google and Facebook
@jzheaux @philwebb how is the spring-boot app serving resources to such clients supposed to handle end-users identity with a single configured authorization-server in that case? Should it choose Google or Facebook?
I appreciate the list of features you generated above, that's really helpful. You and I have discussed some of those already, and I'd welcome further collaboration to find what's a good fit for adding to Spring Security.
Those features are mostly configuration of existing spring-security elements, reason for me listing it here in spring-boot.
There are only two things I use which are not from spring-security:
- OAuth2 Authentication implementation exposing any type of "attribute" set, including OpenID claims (took me quite a few iteration to come to something as simple and usable since my rejected ticket to refactor AbstractOAuth2Token)
- Test annotations, which were rejected too (I still haven't found how to use MockMvc post-processors or WebClient mutators to unit-test secured @Components such as @Service and @Repository)
I'd welcome further collaboration to find what's a good fit for adding to Spring Security
Do not easitate to open tickets on https://github.com/ch4mpy/spring-addons if you find something useful for the comunity, I'll do my best to help you migrate corresponding code from this repo to spring-security or spring-boot ones.
Comment From: jzheaux
Should it choose Google or Facebook?
Neither. Google's token is for protecting Google's resource servers. Or, IOW, if the resources being served are on Google resource servers, then you would use Google's token.
Comment From: ch4mpy
Google's token is for protecting Google's resource servers
Only? https://developers.google.com/identity/sign-in/web/backend-auth
Beyond what Google offers as OAuth2 authentication, there are online OIDC providers out there which allow to define audience(s).
I have quite a few (dozens) resource-servers at hand that have to accept various issuers either because - they serve clients from various editors, some sharing the same authorization-server and some having their own - they serve a single client with different groups of users, each group having it's own identity provider (internal and external users are stored in different databases and are not authorized by the same server)
Comment From: jzheaux
Google's documentation refers to flowing the user's identity to a server, not to authorizing a client's request for resources protected by that server. If you haven't already, please see the link I posted in my previous comment. I also feel like the maintainers' comments in this thread are a good representation of my position. That said, we might be getting off-topic discussing how various providers interpret the spec.
The resource server arrangements you describe sound like classic multi-tenancy examples. I feel I already stated my position on changing Boot's properties from being single-tenant to multi-tenant, and I've got nothing more to add. Should multi-tenant resource servers become quite common, we can always take another look.
Comment From: jasonrberk
https://github.com/spring-projects/spring-security/issues/8648#issuecomment-638585827 mostly solved my issue with having multi tenant issuers