Hello everyone
,
I recently worked on a small side project written in Java with the Spring Framework and I had difficulties authenticating to an external OAuth2 client using spring security. The solution to my problem was clear after I perused the code and documentation of the Spring OAuth Client package.
I thought that I'll have to write custom classes to configure it for my specific need but it turned out that my issues was strictly configuration related.
The issues was that I had a Confidential OAuth2 client and the default configuration of Spring Security assumes that the client is Public.
The RFC describes the client types as follows:
And the configuration that I was missing was client_secret_post.
Once that was completed I only had to add the following SecurityConfig class:
And when I want to authenticate users using my external OAuth client, I only have to redirect them to an internal link in order to trigger the authentication flow implemented by Spring Security:
The final step is to configure the external OAuth client with the following URL:
This will technically complete the authentication flow. What will happen next is that Spring will try to grab the user data from the user-info-uri, in my case the API is secured by the access token and it will respond with a "me" object that looks like this:
What I had to do was to specify me as the user-name attribute as follows:
Then I can access all those fields from a controller like so:
That's about it, I hope this article helped!
Thanks for reading!

I recently worked on a small side project written in Java with the Spring Framework and I had difficulties authenticating to an external OAuth2 client using spring security. The solution to my problem was clear after I perused the code and documentation of the Spring OAuth Client package.
I thought that I'll have to write custom classes to configure it for my specific need but it turned out that my issues was strictly configuration related.
The issues was that I had a Confidential OAuth2 client and the default configuration of Spring Security assumes that the client is Public.
The RFC describes the client types as follows:
- The Confidential client type is for applications that can keep the client secret safe, for example a back-end web application.
- The Public client type is for applications that cannot keep their client secrets safe, for example a mobile applications or front end application. The user can always browse the source code and obtain the secrets.
Code:
# OAuth2 Client Configuration
spring.security.oauth2.client.registration.nuculabs.client-id=xxx
spring.security.oauth2.client.registration.nuculabs.client-secret=xxx
spring.security.oauth2.client.registration.nuculabs.scope=user:read,read:user
spring.security.oauth2.client.registration.nuculabs.authorizationGrantType=authorization_code
spring.security.oauth2.client.registration.nuculabs.redirectUri=http://localhost:8080/login/oauth2/code/nuculabs
spring.security.oauth2.client.registration.nuculabs.client-authentication-method=client_secret_post
spring.security.oauth2.client.provider.nuculabs.authorization-uri=https://www.example.com/oauth2/authorize
spring.security.oauth2.client.provider.nuculabs.token-uri=https://www.example.com/api/oauth2/token
spring.security.oauth2.client.provider.nuculabs.user-info-uri=https://www.example.com/api/me
spring.security.oauth2.client.provider.nuculabs.user-name-attribute=me
And the configuration that I was missing was client_secret_post.
Once that was completed I only had to add the following SecurityConfig class:
Java:
package dev.nuculabs.xenchat.xenchat.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
// OAuth2AuthorizationCodeGrantRequestEntityConverter
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/error", "/webjars/**", "/login").permitAll()
.requestMatchers("/").authenticated()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.defaultSuccessUrl("/", true)
)
.logout(logout -> logout
.logoutSuccessUrl("/login")
.permitAll()
);
return http.build();
}
}
And when I want to authenticate users using my external OAuth client, I only have to redirect them to an internal link in order to trigger the authentication flow implemented by Spring Security:
Code:
/oauth2/authorization/nuculabs
The final step is to configure the external OAuth client with the following URL:
Code:
http://localhost:8080/login/oauth2/code/nuculabs
This will technically complete the authentication flow. What will happen next is that Spring will try to grab the user data from the user-info-uri, in my case the API is secured by the access token and it will respond with a "me" object that looks like this:
JavaScript:
{
"me": {
"about": "I’m Denis, a Software Engineer living in Romania. I’m passionate about cloud computing and software development ✨.",
"activity_visible": false,
"alert_optout": [],
"allow_post_profile": "members",
"allow_receive_news_feed": "members",
"allow_send_personal_conversation": "members",
"allow_view_identities": "members",
"allow_view_profile": "members",
"name": "Denis",
...
}
What I had to do was to specify me as the user-name attribute as follows:
Code:
spring.security.oauth2.client.provider.nuculabs.user-name-attribute=me
Then I can access all those fields from a controller like so:
Java:
package dev.nuculabs.xenchat.xenchat.controller;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.LinkedHashMap;
@Controller
public class HomeController {
@GetMapping("/")
public String home(@AuthenticationPrincipal OAuth2User principal, Model model) {
LinkedHashMap<String, Object> userData = principal.getAttribute("me");
// Add user attributes to the model
model.addAttribute("name", userData.get("username"));
model.addAttribute("email", userData.get("email"));
model.addAttribute("title", userData.get("custom_title"));
model.addAttribute("profileUrl", userData.get("view_url"));
model.addAttribute("isModerator", userData.get("is_moderator"));
// Add all attributes for debugging
model.addAttribute("attributes", principal.getAttributes());
return "home";
}
@GetMapping("/login")
public String login() {
return "login";
}
}
That's about it, I hope this article helped!

Thanks for reading!
Last edited: