Search code examples
javaspring-bootspring-securityoauth-2.0amazon-cognito

Spring security on endpoint using Cognito IAM role?


I am trying to restrict specific endpoints on a Spring boot service depending on what role they have set in the OAuth2 credentials.

This is the endpoint

@RestController
@RequestMapping("/api/admin")
public class AdminController {

    @GetMapping(produces = "application/json")
    public TestResponse get() {
        return new TestResponse("Admin API Response");
    }
}

This is then secured using SecurityConfiguration bean

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf()
            .and()
            .authorizeRequests()
            .antMatchers("/login", "/", "/home", "/logout", "/ping").permitAll()
            .antMatchers("/api/admin").hasRole("arn:aws:iam::xxxxxx:role/spring-sso-test-ADMIN")
            .antMatchers("/api/user").hasRole("arn:aws:iam::xxxxxx:role/spring-sso-test-USER")
            .and()
            .oauth2Login()
            .and()
            .logout()
            .logoutSuccessUrl("/logout");
    }
}

I debugged the Principal and can see the correct IAM role in the list of attributes cognito:roles list

aws cognito spring security principal

However when I hit the endpoint I get a HTTP 403 Unauthorized. Meaning that the user has authenticated successfully, but Spring does not recognize or understand the attributes or how to map them?

I tried using the @Secured annotation but that didn't change anything.

@Secured("arn:aws:iam::xxxxxx:role/spring-sso-test-ADMIN")
@GetMapping(produces = "application/json")
public TestResponse get() {
        return new TestResponse("Admin API Response");
}

How do I allow this to work using an IAM role defined in AWS Cognito?


Solution

  • When you use the hasRole DSL method, Spring Security adds the ROLE_ prefix to your authority. So, the authority arn:aws:iam::xxxxxx:role/spring-sso-test-ADMIN will become ROLE_arn:aws:iam::xxxxxx:role/spring-sso-test-ADMIN.

    You should use the hasAuthority method instead.

    Additionally, you should take the cognito:roles from the attributes and add in the authorities, since it's the property that Spring Security will query to get the authorities.

    To map the authorities you can use a OAuth2UserService:

    
    @Bean
    SecurityFilterChain app(HttpSecurity http) throws Exception {
        http
                .oauth2Login(oauth2 -> oauth2
                    .userInfoEndpoint(userInfo -> userInfo
                        .oidcUserService(this.oidcUserService())
                        ...
                    )
                );
        return http.build();
    }
    
    private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
        // your custom implementation
    }
    

    More details in the documentation.