Describe the bug
After upgrading to com.azure.spring:azure-spring-boot-starter-active-directory:3.1.0 I am running into dependency issues. I am using the org.springframework.security:spring-security-oauth2-resource-server dependency to extract the roles claim from the JWT in order to set users' GrantedAuthorities.
It seems that AADWebAppConfiguration is not being loaded because BearerTokenAuthenticationToken is present (as shown in the stack trace below)
Exception or Stack Trace
***************************
APPLICATION FAILED TO START
***************************
Description:
Field repo in com.azure.spring.aad.webapp.AADWebSecurityConfigurerAdapter required a bean of type 'com.azure.spring.aad.webapp.AADWebAppClientRegistrationRepository' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
The following candidates were found but could not be injected:
- Bean method 'clientRegistrationRepository' in 'AADWebAppConfiguration' not loaded because @ConditionalOnMissingClass found unwanted class 'org.springframework.security.oauth2.server.resource.BearerTokenAuthenticationToken'
Action:
Consider revisiting the entries above or defining a bean of type 'com.azure.spring.aad.webapp.AADWebAppClientRegistrationRepository' in your configuration.
To Reproduce
Start from azure-spring-boot-sample-active-directory-webapp and add the following dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
Now extend the Spring WebSecurityConfig to add information to the users based on the JWT:
public class WebSecurityConfig extends AADWebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests((requests) -> requests.anyRequest().authenticated())
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(new AADJwtBearerTokenAuthenticationConverter());
}
}
Expected behavior
I would expect to be able to add GrantedAuthorities to users based on the Azure AD groups that are returned in the token.
Setup (please complete the following information):
Information Checklist
Kindly make sure that you have added all the following information above and checkoff the required fields otherwise we will treat the issuer as an incomplete report
Thank you for your feedback. Tagging and routing to the team member best able to assist.
Hi, @ppartarr
Please refer to this comment: https://github.com/Azure/azure-sdk-for-java/issues/17860#issuecomment-756550816
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests((requests) -> requests.anyRequest().authenticated())
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(new AADJwtBearerTokenAuthenticationConverter()); // Change here.
}
You can use new AADJwtBearerTokenAuthenticationConverter("roles", "ROLE_") instead of new AADJwtBearerTokenAuthenticationConverter()
Hi @chenrujun
My use case falls under accessing a web application but I've been using spring's oauth2Login() & oauth2ResourceServer() in the same application. Until now I needed the oauth2ResourceServer() to convert the roles claim to GrantedAuthorities.
Since the migration I cannot use oauth2ResourceServer() anymore because of the reasons posted above. I cannot get rid of the oauth2Login() and only use a oauth2ResourceServer() as you suggest in https://github.com/Azure/azure-sdk-for-java/issues/17860#issuecomment-756550816 because as mentioned in the aad starter resource server docs:
This scenario no support login
The docs for accessing a web application show that the Web application can
evaluate permissions based on groups or role
but how am I supposed to do this without a resource server?
I have read through the web app sample and all I can find is authorization based on roles
Our application has a very granular authority mapping for users based on the active directory groups they belong to. We have a 1 to many mapping where 1 AD groups maps to N authorities. These authorities may overlap from one group to another.
It isn't an option for us to create one AD group for every permission since this would be unmanageable for us and for our clients.
Any ideas with how I can move forward with this?
Hi, @ppartarr .
Here is my suggestion:
Use accessing a web application . web-application and resource-server work in the same spring-boot application is not support for aad-starter. And now we do not have plan to support it.
Use roles to authorize. Currently web-application only use groups to create grantedAuthority. Use roles to create grantedAuthority is not supported yet, we plan to support it in next release. Please wait next release.
Hi, I stumbled across this issue recently. I was able to configure oAuth2Login with the following customized UserService to convert roles into authorities.
Please advise whether this customization is OK or not.
@Component
public class CustomOidcUserService extends AADOAuth2UserService {
public CustomOidcUserService(AADAuthenticationProperties properties) {
super(properties);
}
@Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser user = super.loadUser(userRequest);
Object _roles = user.getAttribute("roles");
if (null == _roles) {
return user;
}
if (!List.class.isInstance(_roles)) {
return user;
}
List<String> roles = (List<String>) _roles;
Collection<GrantedAuthority> authorities = new HashSet<>();
if (null != user.getAuthorities()) {
authorities.addAll(user.getAuthorities());
}
roles.stream().forEach(role -> authorities.add(new SimpleGrantedAuthority("ROLE_" + role)));
return new DefaultOidcUser(authorities, user.getIdToken(), user.getUserInfo());
}
}
and then in the WebSecurityConfig
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class WebSecurityConfig extends AADWebSecurityConfigurerAdapter {
private final CustomOidcUserService userService;
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.oauth2Login().userInfoEndpoint().oidcUserService(userService);
http.authorizeRequests().antMatchers(
ProxyConstants.BUY_PATH_PREFIX + "/*/" + "/*",
"/error/**").permitAll()
.anyRequest().authenticated();
}
}
Hi @totakura , thanks for your great advice on this feature. The pr has been merged and you could expect our next release of 3.3.0.
Closing this issue, because the feature has been released in 3.3.0: https://github.com/Azure/azure-sdk-for-java/blob/azure-spring-boot-starter-active-directory_3.3.0/sdk/spring/azure-spring-boot-starter-active-directory/CHANGELOG.md
Thanks for the workaround @totakura and thanks for the work on the PR @yiliuTo - it's much appreciated!