springkeycloakunauthorizedconfidential

spring boot rest app secured by keycloak 18 return always error 401 Unauthorized


I'm developing microservices some of them with spring boot web and other with spring boot data rest and I want secure them with keycloak server 18 through OpenId protocol. The microservices endpoint can be accessed from frontend adding in the "Authorization header" the bearer token obtained from post request to the url http://localhost:8280/auth/realms/--realm_name--/.well-known/openid-configuration inserting in the body request the key client_id, username, password, grant_type, client_secret.

I have create 1.a realm, 2.a client named "springboot-mc-dev" (with Access Type = confidential;, "Root URL" and "Valid Redirect URIs" both setted to "http://localhost:8490", "Standard Flow Enabled", "Direct Access Grants Enabled", "Service Accounts Enabled" and "Authorization Enabled" setted to "ON"), 3.before a role inside client ("named springboot-mc-dev-role" composite False) and after a role inside realm (named always "springboot-mc-dev-role" composite true that is associated Client Roles "springboot-mc-dev-role" of client springboot-mc-dev), 4.user mapped to role "springboot-mc-dev-role" in "realm roles"

After I have imported the following dependency in parent pom.xml

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java-version>1.8</java-version>
    <keycloak.version>18.0.2</keycloak.version>
</properties>
<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    <version>${keycloak.version}</version>
</dependency>
    
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.keycloak.bom</groupId>
            <artifactId>keycloak-adapter-bom</artifactId>
            <version>${keycloak.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Here there is the code of SecurityConfig.java class

package it.organization.project.microservice.datamart.config.security;

import org.keycloak.adapters.KeycloakConfigResolver;
import org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver;
import org.keycloak.adapters.springsecurity.KeycloakConfiguration;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;

@KeycloakConfiguration
@Configuration
@EnableWebSecurity
@Import(KeycloakSpringBootConfigResolver.class) 
@EnableGlobalMethodSecurity(jsr250Enabled = true) 
@ConditionalOnProperty(name = "ms-security-enable", havingValue = "true") 
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
    grantedAuthorityMapper.setPrefix("ROLE_");

    KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
    //keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);
    keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
    auth.authenticationProvider(keycloakAuthenticationProvider);
}

@Bean
@Override
/*protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
    return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}*/
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
    return new NullAuthenticatedSessionStrategy();
}
    
@Override
protected void configure(HttpSecurity http) throws Exception {
  super.configure(http);
  http
      .authorizeRequests()
      .antMatchers("/**").hasRole("springboot-mc-dev-role")
      //.anyRequest().hasRole("springboot-mc-dev-role")
      //.anyRequest().//authenticated()
      //permitAll()
      
      ;
  http.csrf().disable();
}

@Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
    //public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
    return new KeycloakSpringBootConfigResolver();
}

}

this is the code of main class

package it.organization.project.microservice.datamart;

import java.security.Security;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@SpringBootApplication
@EnableCaching
public class MAINDataMartApplication {

public static void main(String[] args) {
    Security.addProvider(new BouncyCastleProvider());
    SpringApplication.run(MAINDataMartApplication.class, args);
}

}

and last there is the application.yml with keycloak settings

#KEYCLOAK CONFIGURATION

keycloak:
  auth-server-url: http://localhost:8280/auth
  #ssl-required: external
  realm: realmname
  bearer-only: true
  #public-client: true
  use-resource-role-mappings: true
  resource: springboot-mc-dev
  credentials:
    secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx

this is the result

Text

What is wrong?


Solution

  • Keycloak spring adapter is deprecated. Don't use it.

    Have look at those tutorials for alternatives.

    We are missing important info to answer preecisely:

    There can be few reasons for 401 Unauthorized:

    If you're using Keycloak 18 with Quarkus, it is likely that your conf should reference http://localhost:8280 as issuer (and not http://localhost:8280/auth).

    So, look at Postman console, open your access-token with a tool like https://jwt.io and compare values you find there with what you have in spring conf and logs.