spring-projects / spring-projects/spring-security

Stub the call to OpenID configuration in an `oauth2Client` `@SpringBootTest`

Open
#17,385 2 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

Easy way to avoid the call to an OP /.well-known/openid-configuration in a @SpringBootTest of an application configured with oauth2Client (or oauth2Login).

Context

When working with mocked Authentication instances (using MockMvc request post processors, WebTestClient mutators, or test annotations), no communication with the authorization server should be needed. However, in the case of a @SpringBootTest for an application configured for OAuth2 with an OP issuer URI, the OpenID configuration is fetched eagerly during application context initialisation.

I found two options, but neither is as convenient as declaring a @MockitoBean for a Spring Boot @ConditionalOnMissingBean like we can do for the (Reactive)JwtDecoder in an oauth2ResourceServer:

  • Testcontainers, but this is super slow and completely overkill to provide no more than OpenID configuration
  • Wiremock, lighter and faster than Testcontainers, but still requires additional dependencies, adds some overhead, and requires more configuration than a @MockitoBean (declare a static WireMockServer, a @DynamicPropertySource to bind the issuer URI to this server base URL, and @BeforeAll/@AfterAll to start & stop the mocked server)
Edit

Found a @TestConfiguration that should fit most needs without faking an IDP (super fast startup):

@TestConfiguration
@Import({OAuth2ClientProperties.class})
public class MockedOAuth2ClientTestConfiguration {
  @Bean
  ClientRegistrationRepository clientRegistrationRepository(
      OAuth2ClientProperties oauth2ClientProperties) {

    return new InMemoryClientRegistrationRepository(
        oauth2ClientProperties.getRegistration().entrySet().stream().map(entry -> {
          var registrationId = entry.getKey();
          var registration = entry.getValue();
          return ClientRegistration
              .withRegistrationId(registrationId)
              .authorizationGrantType(
                  new AuthorizationGrantType(registration.getAuthorizationGrantType()))
              .clientId(registration.getClientId())
              .clientSecret(registration.getClientSecret())
              .tokenUri("https://test.idp/token")
              .authorizationUri("https://test.idp/auth")
              .redirectUri("https://client/oauth2/%s/code".formatted(registrationId))
              .build();
        }).toList());
  }
}

With this conf, the default InMemoryClientRegistrationRepository instantiation won't prevent the app from starting, and beans looking for a registration at startup will find 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 inspecting the OAuth2 client auto-configuration and the eager OpenID configuration lookup during application context initialization; the issue names no source files or tests. Done means an @SpringBootTest using oauth2Client or oauth2Login can use mocked authentication without contacting the authorization server, while application beans can still find a client registration.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring-boot
Domain
authentication, security, testing
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.