Search code examples
javaspring-bootspring-securitygoogle-oauthspring-security-oauth2

How to correctly set role for User in oauth2 app spring boot


I'm struggling with spring security after implemented oauth2 in my application. That's my first experience, but will be glad to hear nice or anger comments. That's the way to improve.

Problem: During the logging in my application I see that I retrieve from google oidcuser object of DefaultOidcUser class. This object has collection of authorities with 4 items: enter image description here

Honestly, I don't understand why I get ROLE_USER for this user in my system, cos actually it has ADMIN role in my system (role is created by me).

Generally I have 3 roles in my app: User, Manager, Admin. But can't understand how to correctly set particular role for special user.

Because of this, when I put smth like:

  .antMatchers("/api/**").hasRole(Role.ADMIN.name())
  .antMatchers("/administration/**").hasRole(Role.ADMIN.name())

I get 403 cos as I understand I get ROLE_USER for each user.

Can you please help me to set roles for each User but from my db (not this default ROLSER_USER from google)? Also I read about GrantedAuthorities and mapper of grantedAuthorities, but it's a bit unclear for me.

I will be glad to hear any opinion. any useful link to improve my knowledge about this. Cos main goal: to understand how it works.

Other classes of my oauth2 implementation below:

@Getter
@Setter
@RequiredArgsConstructor
public class CustomOidcUser implements OidcUser {

    private final OidcUser oidcUser;
    private String email;
    private String firstName;
    private String lastName;

    @Override
    public Map<String, Object> getClaims() {
        return oidcUser.getClaims();
    }
    @Override
    public OidcUserInfo getUserInfo() {
        return oidcUser.getUserInfo();
    }

    @Override
    public OidcIdToken getIdToken() {
        return oidcUser.getIdToken();
    }

    @Override
    public Map<String, Object> getAttributes() {
        return oidcUser.getAttributes();
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return oidcUser.getAuthorities();
    }

    @Override
    public String getName() {
        return oidcUser.getName();
    }

}
@Service
@RequiredArgsConstructor
public class CustomOidcUserService extends OidcUserService {

    private final UserRepository repository;
    private static final String GOOGLE_KEY_LASTNAME = "family_name";
    private static final String GOOGLE_KEY_FIRSTNAME = "given_name";

    @Override
    public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
        final OidcUser oidcUser = super.loadUser(userRequest);
        CustomOidcUser newUser =  new CustomOidcUser(oidcUser);

        String email = oidcUser.getAttributes().get("email").toString();
        User user = createUserIfNoExist(email, oidcUser);
        newUser.setEmail(email);
        newUser.setFirstName(user.getFirstName());
        newUser.setLastName(user.getLastName());

        return newUser;
    }

    public User createUserIfNoExist(String email, OidcUser oidcUser) {
        return repository.findByEmail(email)
                .orElseGet(() -> {
                    User newUser = new User();
                    newUser.setEmail(email);
                    newUser.setRole(Role.USER);
                    newUser.setStartWorkAt(LocalDate.now());
                    newUser.setLastLoginDate(LocalDateTime.now());
    ...

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

  private final AuthProvider authProvider;
  private final OnSuccessHandler onSuccessHandler;
  private final CustomOidcUserService customOidcUserService;
  private final UserService userService;

  protected void configure(HttpSecurity http) throws Exception {
    SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
    successHandler.setUseReferer(true);
    successHandler.setDefaultTargetUrl("/success");
    successHandler.setAlwaysUseDefaultTargetUrl(true);

    http
        .addFilterAfter(new UserHasManagerFilter(userService), BasicAuthenticationFilter.class)
        .cors()
        .and()
          .csrf()
          .disable()
          .authorizeRequests()
          .antMatchers("/api/**").hasRole(Role.ADMIN.name())
          .antMatchers("/administration/**").hasRole(Role.ADMIN.name())
        .and()
            .httpBasic()
        .and()
          .formLogin()
          .loginPage("/unauthorized")
          .loginProcessingUrl("/login")
          .defaultSuccessUrl("/success")
          .failureUrl("/failed")
          .successHandler(successHandler)
          .usernameParameter("email")
          .passwordParameter("password")
          .permitAll()
        .and()
          .logout()
          .logoutUrl("/logout")
          .logoutSuccessUrl("/logout_success")
          .permitAll()
        .and()
            .oauth2Login()

              .loginPage("/")
              .userInfoEndpoint(userInfoEndpoint ->
                      userInfoEndpoint.oidcUserService(customOidcUserService)
                      .customUserType(CustomOidcUser.class, "google")
//                      .userAuthoritiesMapper(userAuthoritiesMapper())
              )

              .defaultSuccessUrl("/dashboard", true)
              .successHandler(onSuccessHandler)
              .failureHandler(authenticationFailureHandler())
            .permitAll()
        .and()
        .authorizeRequests()
        .antMatchers("/**")
        .permitAll();
  }

Solution

  • For OIDC by default Spring will map scopes and the default USER role to granted Authorities on OidcUser object. Your simply returning this in your CustomOidcUser

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return oidcUser.getAuthorities();
    }
    

    You need to create a custom GrantedAuthorities collection in your CustomOidcUser object, and then your CustomOidcUserService you need to add the roles from your database to this collection manually.

    Collection<GrantedAuthority> authorities ....
    authorities.add(new SimpleGrantedAuthority("ROLE_...."));