0

I am trying to fetch the user details of a logged in user :

I am passing the access token collected from :

https://localhost:1099/auth/realms/myrealm/protocol/openid-connect/token

while calling this endpoint :

import org.keycloak.KeycloakSecurityContext;
import org.keycloak.representations.AccessToken;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.Collections;
import java.util.Map;

@RestController
public class userDetails {

    @GetMapping(value = "/myresources", produces = MediaType.APPLICATION_JSON_VALUE)
    public String handleUserInfoRequest(HttpServletRequest request) {
        System.out.println("Using HttpServletRequest to inspect authentication...");

        // Get the HttpSession from the request
        HttpSession session = request.getSession();

        // Get the Keycloak security context from the session attribute
        KeycloakSecurityContext securityContext = (KeycloakSecurityContext) session.getAttribute(KeycloakSecurityContext.class.getName());

        if (securityContext != null) {
            AccessToken token = securityContext.getToken();

            // Extract details from the token
            String userId = token.getId();
            String userName = token.getPreferredUsername();

            // Print security context details
            String securityContextDetails = "User ID: " + userId + "\n" +
                                           "Username: " + userName;

            System.out.println("Security Context Details:\n" + securityContextDetails);

            // Return a response
            return securityContextDetails;
        } else {
            return "No Keycloak Security Context found";
        }
    }


}

I have also tried to get the security context from httpServletRequest for secured users using this instead of the session as done above :

  // Get the Keycloak security context from the request attribute
        KeycloakSecurityContext securityContext = (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());

However, I am getting "No Keycloak Security Context found" or security context is null, principal is null. How can I get the user details i.e. "preferrred_username" from the access token. Iam not using spring security and I am using embedded springboot keycloak version 21.0.0 . Thanks in advance for all the inputs.

jane
  • 211
  • 9
  • 30

1 Answers1

1

Keycloak adapter for spring was deprecated long ago. See this other answer for alternatives.

Once your security conf is fixed, you should have an Authentication instance in the security context. The type of this Authentication will depend on the request being

  • anonymous => AbstractAuthenticationToken
  • authorized with a session on an OAuth2 client with oauth2Login => OAuth2AuthenticationToken having an OAuth2User as principal (in case of OIDC authorization servers like Keycloak, this interface is further specialized to OidcUser)
  • authorized with an access token on an OAuth2 resource server => the default Authentication depends on the conf (and can be easily replaced with a custom implementation like Keycloak adapters were doing):
    • JwtAuthenticationToken when a JWT decoder is used
    • BearerTokenAuthentication when introspection is used (usually with "opaque" tokens, even if you can introspect a JWT)

The easiest way access this Authentication is with "magic" parameter for your controller method:

@GetMapping("/me")
public Map<String, Object> getMe(Authentication auth) {
    // authorized request to a resource server with JWT decoder
    if (auth instance of JwtAuthenticationToken jwt) {
        return jwt.getTokenAttributes();
    }
    // authorized request to a resource server with introspection
    if (auth instance of BearerTokenAuthentication bearerToken) {
        return bearerToken.getTokenAttributes();
    }
    // authorized request to an OpenID client 
    if (auth instanceof OAuth2AuthenticationToken oauth && oauth.getPrincipal() instanceof OAuth2AuthenticatedPrincipal user) {
        return user.getAttributes();
    }
    // most probably an unauthorized request (anonymous)
    return Map.of();
}

Of course, you can also use the SecurityContextHolder.getContext().getAuthentication() static accessor, but it is probably not as convenient.

ch4mp
  • 6,622
  • 6
  • 29
  • 49
  • Thanks for the inputs. I dont plan to use 'org.springframework.boot:spring-boot-starter-oauth2-client'. In my example, the code snippet manually retrieve the KeycloakSecurityContext from the HttpSession and extract user details from the AccessToken, without directly utilizing the Keycloak Spring Security Adapter. I am using springboot 2. Is there an another way to pull the currently logged in user details, without making any change to the security config ? I am just trying to pull the user details, a controller mapping is not mandatory, just a method to achieve it. @ch4mp – jane Aug 31 '23 at 17:58
  • Also, I am not using 'keycloak-spring-security-adapter' as well in my project. – jane Aug 31 '23 at 18:01
  • I can pull the userinfo using "auth/realms/myrealm/protocol/openid-connect/userinfo" - However, I will have to feed the accessToken and I cannot find a way to get the accessToken of the currently logged in user. – jane Aug 31 '23 at 18:06
  • And with how exactly is that `KeycloakSecurityContext` (defined in keycloak-core) instantiated if you're not using the deprecated adapters? How is the HttpSession created if you're not using spring-security-oauth2-client (the boot starter is just a helper to configure this one)? How is this session populated with a `KeycloakSecurityContext` instance? Also you have to decide if you want requests to be authorized with sessions (with user details in `HttpSession`) or with access tokens (with user details as token claims)... – ch4mp Aug 31 '23 at 18:44
  • I'm gonna make my own promotion again (already did in the answer I linked), but you should probably read my [OAuth2 essentials](https://github.com/ch4mpy/spring-addons/tree/master/samples/tutorials#oauth_essentials) to clarify what you really want to configure. – ch4mp Aug 31 '23 at 18:47
  • I have nothing but "spring_security_core" in build.gradle of my project. Its inspired from this : https://github.com/thomasdarimont/embedded-spring-boot-keycloak-server/blob/master/embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/support/DynamicJndiContextFactoryBuilder.java i am using accessTokens to verify users. I will give aread to your linked post. Authentication instance is always null in my case : Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - – jane Aug 31 '23 at 19:00
  • securityContextHolder is always null. Link to what I tried : https://goonlinetools.com/snapshot/code/#n8ttl6dohbiapwv20zhti – jane Aug 31 '23 at 19:00
  • is there a way to achieve it using Keycloak adapter, as I am still in springboot2 – jane Aug 31 '23 at 19:03
  • from the main readme of the repo you linked "The idea is to have a variant of Keycloak-X but based on Spring Boot instead of Quarkus". So basically, you are using a sample to **build an authorization server** (a Keycloak server). I hardly believe this is what you need -I'm almost sure you already have a Keycloak instance somewhere). Take a breath and learn OAuth2 basics before trying to use it. – ch4mp Aug 31 '23 at 19:07
  • **Forget about Keycloak adapters, it was deprecated two years ago.** Just understand OAuth2 actors responsibilities (client, resource server, authorization server and resource owner) and how flows work (a minimum of authorization_code, client_credentials and refresh_token). Once you understood that, you'll be able to adapt what you'll find in my tutorials to your older boot versions. – ch4mp Aug 31 '23 at 19:12
  • Thank you, I will go through them all. I will sove this problem some way or the other. I can see that there is no explicit security configi n my setup, something similar to this : https://www.baeldung.com/keycloak-embedded-in-spring-boot-app. I can see the OIDC endpoints. let me continue to try some other way on why the SecurityContect is null even after the user is authenticated. Thnaks, have a good day ahead. – jane Aug 31 '23 at 19:30
  • also, keycloak eventlisteners are able to pull all the info such as user_id, preferred_username etc thats embedded in the token – jane Aug 31 '23 at 19:34