Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Keycloak: Get User ID

I have developed a Spring Boot Webservice and use Keycloak for Access Management. The website stores some userdata in a database. I try to connect these data with the user logged in.

At the moment I store the username with the data. But I like to store the user id instead the username. How can I do that?

I try to get SecurityContext by this:

@Bean
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public KeycloakSecurityContext getKeycloakSecurityContext() {
    return ((KeycloakPrincipal<KeycloakSecurityContext>) getRequest().getUserPrincipal()).getKeycloakSecurityContext();
}

But I get an error:

There was an unexpected error (type=Internal Server Error, status=500).
Error creating bean with name 'scopedTarget.getKeycloakSecurityContext'
defined in com.SiteApplication: Bean instantiation via factory method
failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [org.keycloak.KeycloakSecurityContext]: Factory method
'getKeycloakSecurityContext' threw exception; nested exception is
java.lang.ClassCastException:
org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken
cannot be cast to org.keycloak.KeycloakPrincipal

Is this the right way? What is missing?

Thank you!

like image 438
Daniel Pomrehn Avatar asked Aug 21 '17 17:08

Daniel Pomrehn


People also ask

How do spring boots work with Keycloaks?

The Keycloak Spring Boot adapter capitalizes on Spring Boot's auto-configuration, so all we need to do is add the Keycloak Spring Boot starter to our project. The following embedded containers are supported now and don't require any extra dependencies if using Spring Boot Keycloak Starter: Tomcat.

What is spring Keycloak?

Keycloak is an open-source Identity and Access Management solution administered by RedHat and developed in Java by JBoss. In this tutorial, we'll learn how to set up a Keycloak server embedded in a Spring Boot application. This makes it easy to start up a pre-configured Keycloak server.

How do you get a Keycloak access token?

Navigate to the Postman Authorization tab of your request. From the Type dropdown menu, select OAuth 2.0: Click on the Get New Access Token button that will open a dialog box for configuring the identity server (Keycloak in our case).


2 Answers

I found a much simpler solution than the above:

    @GetMapping
    public ResponseEntity getEndpoint(String someParam, HttpServletRequest request) {
        KeycloakAuthenticationToken principal = (KeycloakAuthenticationToken) request.getUserPrincipal();
        String userId = principal.getAccount().getKeycloakSecurityContext().getIdToken().getSubject();

        //....do something

        return new ResponseEntity(HttpStatus.OK);
    }

I think the exception you are getting above is because you are trying to cast getRequest().getUserPrincipal() to KeycloakPrincipal<KeycloakSecurityContext> while it is of type KeycloakAuthenticationToken, so ((KeycloakAuthenticationToken) getRequest().getUserPrincipal()) would work.

like image 88
zakaria amine Avatar answered Oct 12 '22 08:10

zakaria amine


I've done something similar in our code.

public class AuthenticationTokenProcessingFilter extends GenericFilterBean {

        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

            if (!(request instanceof HttpServletRequest)) {
                throw new RuntimeException("Expecting a HTTP request");
            }

            RefreshableKeycloakSecurityContext context = (RefreshableKeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());

            if (context == null) {
                handleNoSecurityContext(request, response, chain);
                return;
            }

            AccessToken accessToken = context.getToken();
            Integer userId = Integer.parseInt(accessToken.getOtherClaims().get("user_id").toString());

            chain.doFilter(request, response);
        }

}

Before you can do this you must add the user_id to the access tokens being issued by keycloak. You can do this through a mapper as shown in the screenshot below.

configuration for property mapper

Also, don't forgot to add the processing filter from above to your application lifecycle by adding a @Bean method to your application class.

@Configuration
@EnableAutoConfiguration(exclude = {FallbackWebSecurityAutoConfiguration.class, SpringBootWebSecurityConfiguration.class, DataSourceAutoConfiguration.class})
@ComponentScan
@EnableAsync
public class MyServiceClass {

    public static void main(String[] args) {
        Properties properties = new Properties();
        new SpringApplicationBuilder(MyServiceClass.class)
                .properties(properties)
                .bannerMode(Banner.Mode.OFF)
                .run(args);
    }


    @Bean
    public AuthenticationTokenProcessingFilter authFilter() {
        return new AuthenticationTokenProcessingFilter();
    }

}
like image 12
Jerry Saravia Avatar answered Oct 12 '22 08:10

Jerry Saravia