I have a problem with springboot security. I do not why but the latter do not working with AUTH0 token. Let me start by saying that I'm learning AUTH0 and this is my first demo. In AUTH0 I have created an application and an API with permission read:message. Then I created a user and created the role for this user as 'admin'. I then gave the 'admin' role the 'read:message' permissions of my API and assigned the role to the created user. Finally I authorized the API to the application.
In my springboot app I have created a security config:
@EnableWebSecurity
public class SecurityConfig {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class.getName());
@Value("${auth0.audience}")
private String audience;
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().csrf().disable();
http.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/private/**").authenticated()
.mvcMatchers("/api/private-scoped").hasAuthority("SCOPE_read:message")
.and()
.cors()
.and()
.oauth2ResourceServer().jwt();
return http.build();
}
@Bean
JwtDecoder jwtDecoder(){
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromIssuerLocation(issuer);
OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);
jwtDecoder.setJwtValidator(withAudience);
return jwtDecoder;
}
}
The audience validator:
public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
private final String audience;
public AudienceValidator(String audience){
this.audience = audience;
}
@Override
public OAuth2TokenValidatorResult validate(Jwt token) {
OAuth2Error error = new OAuth2Error("invalid_token", "L'audience richiesto è mancante", null);
if (token.getAudience().contains(audience)) {
return OAuth2TokenValidatorResult.success();
}
return OAuth2TokenValidatorResult.failure(error);
}
}
And the api controller:
@RestController
@RequestMapping(path = "/api", produces = MediaType.APPLICATION_JSON_VALUE)
@CrossOrigin(origins = "*")
public class APIController {
@GetMapping(value = "/public")
public Message publicEndpoint() {
return new Message("All good. You DO NOT need to be authenticated to call /api/public.");
}
@GetMapping(value = "/private")
public Message privateEndpoint() {
return new Message("All good. You can see this because you are Authenticated.");
}
@GetMapping(value = "/private-scoped")
public Message privateScopedEndpoint() {
return new Message("All good. You can see this because you are Authenticated with a Token granted the 'read:messages' scope");
}
}
In postman I make the following call to get the token:
curl --location 'https://dev-tkjcpdzq3rhabjp1.us.auth0.com/oauth/token' \
--header 'content-type: application/json' \
--header 'Cookie: did=s%3Av0%3A602acbc0-731c-11ee-b98a- 95a049e9e317.nODjWjq1tEeLZzXXtNkzFz6p88pwZ3hhvtROw5YLWu4; did_compat=s%3Av0%3A602acbc0-731c-11ee-b98a-95a049e9e317.nODjWjq1tEeLZzXXtNkzFz6p88pwZ3hhvtROw5YLWu4' \
--data '{"client_id":"FlzXFNGz3Hr9F54usq8D3Es9lDAVEueR","client_secret":"hZslREfUgifNU4vqIp36Qo3GttLTRWDCC9TsgC0fIgsyyJ27Iq63C-M3M7MUrXr4","audience":"http://myAPI/api","grant_type":"client_credentials"}'
and then I make the following call to my spring boot app:
curl --location 'http://localhost:8080/app/api/private-scoped' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Inl5R0V0MHJseDBjUFVRUXFEc3JXdyJ9.eyJpc3MiOiJodHRwczovL2Rldi10a2pjcGR6cTNyaGFianAxLnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJGbHpYRk5HejNIcjlGNTR1c3E4RDNFczlsREFWRXVlUkBjbGllbnRzIiwiYXVkIjoiaHR0cDovL215QVBJL2FwaSIsImlhdCI6MTY5ODIzOTcyMiwiZXhwIjoxNjk4MzI2MTIyLCJhenAiOiJGbHpYRk5HejNIcjlGNTR1c3E4RDNFczlsREFWRXVlUiIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.ljA4UqAuwdBZzEAxcnc8L7Nzt3HDeTQxEDnQKuPhOFEPaJ_LqDsjQ1SOcpz7_tYNuf7aa_5bX4TernyN5o4iFTkg1ECiv2_OnsdKS8u7kNr0rUB9cofL6xXtaxwl4-ofUxUq_uEBnu7NZaRC7TT6X6PzNGdWd_gPgIzAMRAZa99tsM2DGEPYAbRkLZ7cqGyoDivemJnqMH5LUon-bt0otBp64ta0O45LZ8IIDprHcQ9BusRW5v02evBBANN5LT6GKxo6Y0KrugL04ec2mCRvvz-FW318A9djBAE-Lo1gVPHaB4njsFcHdtcxesHe9ja-qbG2x1ccckPOgTrzIciEhw' \
--header 'Cookie: JSESSIONID=FA60C42C23DC34ECAD41D542EB480383'
but the latter always replies to me with code 403. Where am I going wrong?
I solved the problem rewriting the security class in this way:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class.getName());
@Value("${auth0.audience}")
private String apiAudience;
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();
JwtWebSecurityConfigurer
.forRS256(apiAudience, issuer)
.configure(http)
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/public").permitAll()
.antMatchers(HttpMethod.GET, "/api/private").authenticated()
.antMatchers(HttpMethod.GET, "/api/private-scoped").hasAuthority("read:message");
}
}
I also deleted the audience class and in the Auth0 settings, in the Machine to Machine tab of my API I checked the permissions checkbox. Now everything works correctly.