
1. Overview
We often need to implement OAuth2 Single Sign-on in applications. With it, users logged in once can enjoy accessing other apps without logging in again and again. Typically, it has an authorization server that manages the authentication part, and this is usually third-party.
Testing in such a situation becomes difficult. To overcome this, we need to mock the authorization server. In this tutorial, we’ll learn two ways to mock and bypass the OAuth2 SSO in Spring apps.
First, we’ll create a simple Spring Boot application with OAuth2 SSO enabled with Keycloak as the authorization server. Then, we’ll learn two ways to fake OAuth2 SSO using test cases.
2. OAuth2 in Spring App
In this section, we’ll create a small and simple Spring Boot application with OAuth2 SSO where the authorization server will be Keycloak. We’ll keep this section brief as our main goal here is to learn how to fake OAuth2 SSO.
Let’s create a Spring Boot app with the following dependencies from start.spring.io:
- spring-boot-starter-oauth2-client
- spring-boot-starter-security
- spring-boot-starter-web
- spring-boot-starter-test
- spring-security-test
Now, let’s create the REST endpoint resource and this would be protected. No one can access these without being authenticated:
@GetMapping("/")
public String get() {
return "Login Success";
}
This API simply returns a message Login Success. Now, let’s learn how to secure the application with OAuth2 configuration.
2.1. Configuring OAuth2
Now, let’s learn about configuring the application with OAuth2 settings.
First of all, let’s create a class that handles security and OAuth2-related configurations:
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.authorizeHttpRequests(a ->
a.requestMatchers(
new AntPathRequestMatcher("/login"),
new AntPathRequestMatcher("/oauth2/**"),
new AntPathRequestMatcher("/openid-connect"),
new AntPathRequestMatcher("/error"),
new AntPathRequestMatcher("/css/**"),
new AntPathRequestMatcher("/js/**"),
new AntPathRequestMatcher("/images/**"),
new AntPathRequestMatcher("/assets/**"))
.permitAll()
.anyRequest().authenticated())
.oauth2Login(customizer -> customizer.successHandler(successHandler()))
.build();
}
public AuthenticationSuccessHandler successHandler() {
SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
handler.setDefaultTargetUrl("/");
return handler;
}
}
Here, we configure the SecurityFilterChain bean to authenticate every request except some endpoints like login, oauth2, error, etc. The login type is OAuth2. The successHandler() ensures that after successful login, it’ll redirect flow to a REST endpoint of the application returning a message Login Success.
Now, let’s add some properties related to OAuth2 and the authorization server in application.yaml file. The authorization server is called provider in the application.yaml file:
spring:
security:
oauth2:
client:
registration:
keycloak:
client-id: my-client
scope: openid,profile,email
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
provider:
keycloak:
issuer-uri: http://localhost:8787/realms/my-realm
Here, we configure our Spring Boot app for the authorization server. We register a Keycloak client, my-client, with scopes for openid, profile, and email, enabling OpenID Connect-based authentication. The configuration sets the authorization-grant-type to authorization-code.
It dynamically constructs the redirect-URI using placeholders, enabling the application to handle the OAuth2 callback correctly after authentication. The issuer-uri under provider points to the Keycloak realm my-realm, which is essential for discovering Keycloak’s OAuth2 endpoints.
We can configure Keycloak as an authorization server by following this documentation. We just have to match client-id and issuer-url while configuring Keycloak. Although this isn’t required for tests, we can play with it and try to access the API and see authentication in action.
3. Faking the OAuth2 SSO
In this section, we’ll learn two ways to fake OAuth2 SSO. One way is to bypass the authentication altogether, and the second way is to mock the authorization server. In both scenarios, we don’t need to run Keycloak while running our test cases.
3.1. Bypassing Authentication With MockMvc
To bypass the authentication, we need a dummy provider that can be registered as a client by the OAuth2AuthorizedClientService bean. To do that, let’s create a test configuration for this dummy provider:
@TestConfiguration
public class NoOAuth2Config {
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
ClientRegistration registration = ClientRegistration
.withRegistrationId("dummy")
.clientId("test-client")
.clientSecret("test-secret")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
.authorizationUri("http://localhost/fake-auth")
.tokenUri("http://localhost/fake-token")
.userInfoUri("http://localhost/fake-userinfo")
.userNameAttributeName("sub")
.clientName("Dummy Client")
.build();
return new InMemoryClientRegistrationRepository(registration);
}
@Bean
public OAuth2AuthorizedClientService authorizedClientService(
ClientRegistrationRepository clientRegistrationRepository) {
return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
}
}
The above test configuration creates a client and registers it with the OAuth2AuthorizedClientService. If we look carefully at clientRegistrationRepository(), it has all the properties Spring needs to register clients. We can verify with the properties in the application.yaml file from earlier.
Now, let’s write the test case:
@Import(NoOAuth2Config.class)
@ActiveProfiles("test")
@AutoConfigureMockMvc
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
class FakingOauth2SsoIntegrationTest {
@Autowired
MockMvc mockMvc;
@Test
void whenBypssingTheOAuthWithSpringConfig_thenItShouldBeAbleToLogin() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/")
.with(oauth2Login()))
.andExpect(status().isOk());
}
}
Here, we auto-configure MockMvc and import the NoOAuthConfig configuration class in this Spring Boot test.
The oauth2login() is part of SecurityMockMvcRequestPostProcessors and establishes a SecurityContext that has an OAuth2AuthenticationToken for the Authentication, an OAuth2User as the principal, and an OAuth2AuthorizedClient in the session. This is how we bypass the authentication.
3.2. Fake an OAuth2 SSO Service With WireMock
To mock the authorization server, we’ll use WireMock:
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8-standalone</artifactId>
<version>2.35.1</version>
<scope>test</scope>
</dependency>
The latest version of WireMock is available in the Maven Repository.
Now, let’s add the following configuration in the application-test.yaml file, which is similar to the main application.yaml file content:
spring:
security:
oauth2:
client:
registration:
wiremock:
client-id: my-client
client-secret: my-secret
redirect-uri: "{baseUrl}/login/oauth2/code/{registrationId}"
authorization-grant-type: authorization_code
scope: openid
provider: wiremock
provider:
wiremock:
issuer-uri: http://localhost:8787/realms/my-realm
To mock the authorization server, we’ll mock one API /.well-known/openid-configuration, a JSON file that an OpenID Connect provider serves over HTTPS at a well-known URL, providing all the configuration details a client needs to integrate with it. It allows client applications (like web or mobile apps) to automatically discover auth endpoints, token endpoints, supported features, public keys, scopes, claims, etc.
Let’s add the code to mock the endpoint in the authorization server:
static WireMockServer wireMockServer;
@BeforeAll
static void setup() {
wireMockServer = new WireMockServer(8080);
configureFor(8080);
wireMockServer.start();
stubFor(get(urlEqualTo("/realms/my-realm/.well-known/openid-configuration"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json")
.withBody("""
{
"issuer": "http://localhost:8787/realms/my-realm",
"authorization_endpoint": "http://localhost:8787/realms/my-realm/oauth/authorize",
"token_endpoint": "http://localhost:8787/realms/my-realm/oauth/token",
"userinfo_endpoint": "http://localhost:8787/realms/my-realm/userinfo",
"jwks_uri": "http://localhost:8787/realms/my-realm/.well-known/jwks.json",
"response_types_supported": [
"code",
"token",
"id_token",
"code token",
"code id_token",
"token id_token",
"code token id_token",
"none"
],
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"scopes_supported": [
"openid",
"email",
"profile"
]
}
""")));
}
@AfterAll
static void tearDown() {
wireMockServer.stop();
}
Here, we set up a WireMock server to mock Keycloak for testing OAuth2 login flows. It runs on port 8787 and define a stub to intercept requests to /.well-known/openid-configuration under the my-realm realm. The stub returns a mock JSON response that mimics a real Keycloak configuration, including endpoints for authorization, token exchange, and user info.
This setup makes the application believe it’s interacting with a real Keycloak server during tests. Finally, the tearDown() stops the WireMock server after all tests are complete.
Now, let’s write the test case:
@Test
void whenAuthServerIsMocked_thenItShouldBeAbleToLogin() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.get("/")
.with(oauth2Login()))
.andExpect(status().isOk());
}
4. Conclusion
In this article, we learned how to set up a basic OAuth2 Single Sign-On (SSO) using Keycloak as the authorization server. We then explored how to simulate authentication when writing test cases.
In the first approach, we bypassed authentication using Spring Security configurations alone. In the second approach, we used the WireMock API to mock the authorization server without running an actual auth server.
All the code examples used in this article are available over on GitHub.
The post Faking OAuth2 Single Sign-on in Spring first appeared on Baeldung.