Search code examples
springspring-bootspring-securitykeycloakspring-security-oauth2

Spring Security OAuth2 with Keycloak - Accessing User Information


I have a Spring Security OAuth2 with Keycloak setup.

On Client application side the artifacts look like this:

application.yml

server.port: 8182

spring:
    security:
        oauth2:
            client:
                registration:
                    keycloak:
                        client-id: myclient-ac
                        client-secret: 81e3fd9f-52ce-4549-8ea9-ae53e754da89
                        authorization-grant-type: authorization_code
                        redirect-uri: http://localhost:8182/login/oauth2/code/myclient-ac
                        scope: openid
                provider:
                    keycloak:
                        issuer-uri: http://localhost:8180/auth/realms/myrealm
                        #authorization-uri: http://localhost:8180/auth/realms/myrealm/protocol/openid-connect/auth
                        #token-uri: http://localhost:8180/auth/realms/myrealm/protocol/openid-connect/token
                        #user-info-uri: http://localhost:8180/auth/realms/myrealm/protocol/openid-connect/userinfo
                        #jwk-set-uri: http://localhost:8180/auth/realms/myrealm/protocol/openid-connect/certs
                        #user-name-attribute: preferred_username

SecurityConfig.java

@EnableWebSecurity
public class SecurityConfig {
    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests().anyRequest().permitAll()
            .and()
            .oauth2Login().disable()
            .oauth2Client();
            
        return http.build();
    }
}

WebClientConfig.java

@Configuration
public class WebClientConfig {
    @Bean
    WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = 
            new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        
        return WebClient.builder()
            .apply(oauth2Client.oauth2Configuration())
            .build();
    }    

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
        ClientRegistrationRepository clientRegistrationRepository,
        OAuth2AuthorizedClientRepository authorizedClientRepository) {

        OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .authorizationCode()
            .refreshToken()
            .build();

        DefaultOAuth2AuthorizedClientManager authorizedClientManager = 
            new DefaultOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientRepository);
        
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }
}

MyRestControllerClient.java

@RestController
public class MyRestControllerClient {
    private static final Logger LOGGER = LoggerFactory.getLogger(MyRestControllerClient.class);
    
    @Autowired
    private WebClient webClient;     
    
    @GetMapping("/helloworld")
    public String helloworld(@RegisteredOAuth2AuthorizedClient("keycloak") OAuth2AuthorizedClient authorizedClient) {
        String body = webClient
            .get()
            .uri("http://localhost:8181/helloworld")
            .attributes(oauth2AuthorizedClient(authorizedClient))
            .retrieve()
            .bodyToMono(String.class)
            .block();
        
        LOGGER.info(body);
        
        return body;
    } 
    
    @GetMapping("/oidc-principal")
    public OidcUser getOidcUserPrincipal(@AuthenticationPrincipal OidcUser principal) {
        return principal;
    }    
}

Accessing http://localhost:8182/helloworld results in being redirected to login page at Keycloak. Providing username and password, I can successfully access my /helloworld endpoint.

I want to access also the user information, according to 4.1. Accessing User Information at https://www.baeldung.com/spring-security-openid-connect it can be done in the REST controller via

@GetMapping("/oidc-principal")
public OidcUser getOidcUserPrincipal(@AuthenticationPrincipal OidcUser principal) {
    return principal;
}

Adding this endpoint to my REST controller, and accessing it after the /helloworld endpoint, results in principal being null.

How to access the user information?


Solution

  • I changed this

    @EnableWebSecurity
    public class SecurityConfig {
        @Bean
        SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http
                .authorizeRequests().anyRequest().permitAll()
                .and()
                .oauth2Login().disable()
                .oauth2Client();
                
            return http.build();
        }
    }
    

    to

    .oauth2Login()
    .and()
    .oauth2Client();
    

    now principal is full of information.

    But what's the difference now? OAuth2 worked with .oauth2Login().disable() as expected, the behavior is the same, I get redirected to the login page at Keycloak, and the login happens with or without .disable()