Search code examples
oauth-2.0oauthaccess-tokenauth0

Why is Spring Security not properly reading my Auth0 OAuth permissions using hasAuthority?


I have the following…

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.oauth2Login(AbstractHttpConfigurer::disable)
                .oauth2ResourceServer(AbstractHttpConfigurer::disable)
                .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests((authz) -> authz
                        .requestMatchers(
                                new AntPathRequestMatcher("/actuator/**")
                        ).hasAuthority("SCOPE_read:actuators")
                        .anyRequest().authenticated()
                );
        return http.build();
    }
    @Bean
    JwtDecoder jwtDecoder(OAuth2ResourceServerProperties properties, @Value("${auth0.audience}") String audience) {
        /*
         * By default, Spring Security does not validate the "aud" claim of the token,
         * to ensure that this token is
         * indeed intended for our app. Adding our own validator is easy to do:
         */

        String issuerUri = properties.getJwt().getIssuerUri();
        NimbusJwtDecoder jwtDecoder = JwtDecoders.fromOidcIssuerLocation(issuerUri);

        OAuth2TokenValidator<Jwt> audienceValidator = AudienceValidator.of(audience);
        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri);
        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

        jwtDecoder.setJwtValidator(withAudience);

        return jwtDecoder;
    }

And the following config…

auth0:
  audience: ...
spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: ...

I am trying to call the endpoint with curl http://localhost:8080/actuator -v -H "Authorization: Bearer ..." and curl http://localhost:8080/actuator -v -H "Authorization: OAuth ..."

I have confirmed the access token has

{
  "iss": "...",
  "sub": "...",
  "aud": [
    "...",
    "..."
  ],
  "iat": ...,
  "exp": ...,
  "azp": "...",
  "scope": "openid profile email",
  "permissions": [
    "read:actuators"
  ]
}

The aud and iss match but when I go to the site I get a 403. If I make it permit-all then it works fine.

What am I missing here?

I also tried to add this but the same issue...

.oauth2ResourceServer((oauth2) -> oauth2
                        .jwt((jwt) ->jwt.decoder(jwtDecoder()))
                )

Solution

  • You have to enable resource server configuration for access token to be used when building the security context.

    You also need a Converter<Jwt, ? extends AbstractAuthenticationToken> to convert the content of a private claim into Spring authorities. JwtAuthenticationConverter is the default implementation for this interface and configurable to some extent (will work if you want to use only one claim directly under root node, which seems to be your case for now). Just expose a bean of that type with overriden configuration to read the permissions claim.

    You can find samples in my tutorials, all work with Auth0 (as well as Keycloak, and Amazon Cognito).