Search code examples
authorizationspring-security-oauth2oauth2client

Is it ok to add access_token authorities to the OAuth2LoginAuthenticationToken?


I have a simple spring boot application with two services - ui and resource. I trying to configure oauth2+oidc authentication using uaa server.

When I login in the ui service, spring security creates authentication result (in OidcAuthorizationCodeAuthenticationProvider) using id_token and it doesn't contain any scopes except openid. When the authentication result is created it contains only one authority - ROLE_USER so a can't use authorization on the client side.

Is is ok to override OidcUserService and add to the user's authorities scopes from the access_token to check access on the client side?

    @Override
    public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
        OidcUser user = super.loadUser(userRequest);
        Collection<? extends GrantedAuthority> authorities = buildAuthorities(
                user,
                userRequest.getAccessToken().getScopes()
        );
        return new DefaultOidcUser(
                authorities,
                userRequest.getIdToken(),
                user.getUserInfo()
        );
    }

Security configuration:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .mvcMatchers("/protected/**").hasAuthority("SCOPE_protected")
                .anyRequest().authenticated()
                .and()
            .oauth2Login()
                .userInfoEndpoint().oidcUserService(oidcUserService())
                .and()
            ...

It works but I'm not sure it's a good idea.


Solution

  • It is the approach as outlined in the Spring Security documentation, so the approach is fine. The only thing is that when I have implemented it, I didn't add all the scopes to the authorities set - I pulled out the specific claim that had the role information - a custom groups claim that I configured in the identity provider's authorization server.

    I include some example code for how to do this with Spring Webflux as most examples show how to do it with Spring MVC as per your code.

    note: I'm very inexperienced with using reactor!

    public class CustomClaimsOidcReactiveOAuth2UserService implements ReactiveOAuth2UserService<OidcUserRequest, OidcUser> {
    
        private final OidcReactiveOAuth2UserService service = new OidcReactiveOAuth2UserService();
    
        public Mono<OidcUser> loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
            log.debug("inside CustomClaimsOidcReactiveOAuth2UserService..");
    
            Mono<OidcUser> mOidcUser = service.loadUser(userRequest);
    
            return mOidcUser
                    .log()
                    .cast(DefaultOidcUser.class)
                    .map(DefaultOidcUser::getClaims)
                    .flatMapIterable(Map::entrySet)
                    .filter(entry -> entry.getKey().equals("groups"))
                    .flatMapIterable(roleEntry -> (JSONArray) roleEntry.getValue())
                    .map(roleString -> {
                        log.debug("roleString={}", roleString);
                        return new OidcUserAuthority((String) roleString, userRequest.getIdToken(), null);
                    })
                    .collect(Collectors.toSet())
                    .map(authorities -> {
                        log.debug("authorities={}", authorities);
                        return new DefaultOidcUser(authorities, userRequest.getIdToken());
                    });
        }
    }
    ...
    @Bean
    ReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService() {
        return new CustomClaimsOidcReactiveOAuth2UserService();
    }