spring-projects / spring-projects/spring-security

As a developer, I want test OAuth2ResourceServer-based authorization without authentication

Open
#17,829 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage type: enhancement
Dominant language
Java
Stars
9.6k
Forks
6.3k
Avg merge
2d 11h
Merged PRs (30d)
52

Description

Expected Behavior

Consider the following security config, taken from documentation

public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(authorize -> authorize
                .requestMatchers("/messages/**").access(hasScope("message:read"))
                .anyRequest().authenticated()
            )
            .oauth2ResourceServer(oauth2 -> oauth2
                .jwt(jwt -> jwt
                    .jwtAuthenticationConverter(myConverter())
                )
            );
        return http.build();
}

I expect the following snippet should work out of the box (or with minimal effort)

@WebMvcTest
...
mockMvc.perform(
  get("/messages/foo").with(jwt().jwt(builder -> builder.claim("message", "read").build())))
  .andExpect(status().isOk());

Current Behavior

  1. Vanilla

application.properties: spring.security.oauth2.resourceserver.jwt.issuer-uri= ${ISSUER_URI}

Caused by: org.springframework.util.PlaceholderResolutionException: Could not resolve placeholder 'ISSUER_URI' in value "${ISSUER_URI}"

  1. Vanilla + excludeAutoConfiguration = OAuth2ResourceServerAutoConfiguration.class

No qualifying bean of type 'org.springframework.security.oauth2.jwt.JwtDecoder' available
Context

  1. Vanulla + excludeAutoConfiguration = OAuth2ResourceServerAutoConfiguration.class + @MockitoBean JwtDecoder jwtDecoder;

BUILD SUCCESS

Context

Security is authentication and authorization.
Very often external service - here ISSUER_URI - is responsible for authentication.
But authorization - here "/messages/**").access(hasScope("message:read") - is completely under developer control.
That is a reason why developer wants test something he control and does not test something he is not responsible.

Although @WebMvcTest is Annotation that can be used for a Spring MVC test that focuses only on Spring MVC components., the annotation is enriched (if possible) with some default security.
It is (more or less) reasonable to extent scope of @WebMvcTest for testing authorization (controlled), but not for authentication (uncontrolled)

Unfortunately, in 2020 in https://github.com/spring-projects/spring-boot/issues/19823 it was reported that OAuth2AuthorizedClientArgumentResolver is not automatically added to @WebMvcTest. Instead of adding the resolver, autoconfiguration for OAuth2Client was added and for OAuth2Resource also, although OAuth2Resource is not related to OAuth2AuthorizedClientArgumentResolver

Finally, a developer which want to test his authorization code with @WebMvcTest, must focus on tame emerging authentication.

In 2022 in https://github.com/spring-projects/spring-security/issues/11784 (smaller number, later issue) it was reported that @WebMvcTest unnecessary awakes OAuth2Client authentication.

But it was too late. Phrase from Javadoc By default, tests annotated with @WebMvcTest will also auto-configure Spring Security closes a discussion. Fairly speaking, it should be told that @WebMvcTest awakes OAuth2Client to register OAuth2AuthorizedClientArgumentResolver.

Conclusion
In my opinion authentication OAuth2ResourceServerAutoConfiguration in @WebMvcTest is a mistake. The same applies to other autoconfigurations responsible for authentication.

  1. If someone wants test his authentication, he is be responsible for adding OAuth2ResourceServerAutoConfiguration to his WebMvcTest.
  2. If someone wants test his RestController, which consumes JwtAuthenticationToken or OAuth2User, he should be able to do this without taming emerging authentication
  3. If someone wants test his authorization, it should be simple and supported by spring-security-test. For OAuth2Resource, it could be something like
public class MockOAuth2ResourceAutoConfiguration {

    @Component
    @ConditionalOnClass(JwtDecoder.class)
    @ConditionalOnMissingBean(JwtDecoder.class)
    static class TestJwtDecoder implements JwtDecoder {
        @Override
        public Jwt decode(String token) throws JwtException {
            return null;
        }
    }
}

It seems overengineering, but analogous MockOAuth2ClientAutoConfiguration should register OAuth2AuthorizedClientArgumentResolver and probably more (I don't know OAuth2Client well)

If the enhancement is accepted, I may implement it.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by examining @WebMvcTest and OAuth2ResourceServerAutoConfiguration behavior, then compare the existing OAuth2Client test auto-configuration referenced in the issue. Determine how authorization tests using jwt() can run without issuer or JwtDecoder setup; done when the proposed behavior is covered by focused tests and existing tests remain passing.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
authentication, authorization, security, testing
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.