Sabu Shakya
Sabu Shakya

Reputation: 354

LinkedIn access token exchange with keycloak token

I'm trying to integrate signup/login from linkedIn in my application which currently uses keycloak for authentication. I've implemented my flow in this way:

  1. My frontend redirects to linkedIn for sign in.
  2. LinkedIn then redirects to my callback in backend with authorization code.
  3. Then my backend application exchanges the code for access token and fetches userinfo from linkedIn and does necessary process.
  4. My backend manually creates a user in keycloak with federated identities set to linkedin.
  5. Invoke keycloaks token exchange api to exchange the token from LinkedIn with jwt from keycloak.

Here's an example code:

public JwtTokenResponse processOAuthCallbackAndGetToken(String code) {
    // Step 1: Exchange authorization code for an access token
    String accessToken = exchangeAuthorizationCodeForAccessToken(code);

    // Step 2: Fetch user info from LinkedIn
    Map<String, Object> userInfo = fetchLinkedInUserProfile(accessToken);

    // Step 3: Create users in app
    if (userInfo == null) {
      throw new SomeException("Error occurred while fetching user info from LinkedIn.");
    }

    String email = (String) userInfo.get("email");
    // create user in the application if needed

    // Step 4: Create use in keycloak with federatedIdentity

    log.info("User Info from LinkedIn ::: {}", userInfo);
    Map<String, Object> federatedIdentity = new HashMap<>();
    federatedIdentity.put("identityProvider", "linkedin");
    federatedIdentity.put("userId", userInfo.get("sub")); // LinkedIn user ID
    federatedIdentity.put("userName", email);

    userService.createKeycloakUserWithFederatedIdentity(agentByEmail.getUser(), federatedIdentity);

    log.info("Created user in keycloak with federated identity ::: {}", federatedIdentity);

    // Step 5: Exchange LinkedIn access_token with jwt
    return keycloakAuthServerConnector.getTokenFromLinkedInAccessToken(accessToken);
  }
....
  private static final String GRANT_TYPE_OAUTH_TOKEN_EXCHANGE =
      "urn:ietf:params:oauth:grant-type:token-exchange";
  private static final String REQUESTED_TOKEN_TYPE =
      "urn:ietf:params:oauth:token-type:access_token";
  public static final String PROTOCOL_OPENID_CONNECT_TOKEN = "/protocol/openid-connect/token";

public com.agencyheight.dto.onboardingv2.auth.JwtTokenResponse getTokenFromLinkedInAccessToken(
      String accessToken) {
    log.info("Requesting token with linkedIn access token :: {}", accessToken);

    return webClient
        .post()
        .uri(PROTOCOL_OPENID_CONNECT_TOKEN)
        .contentType(MediaType.APPLICATION_FORM_URLENCODED)
        .body(
            BodyInserters.fromFormData("client_id", clientId)
                .with("client_secret", clientSecret)
                .with("grant_type", GRANT_TYPE_OAUTH_TOKEN_EXCHANGE)
                .with("subject_token", accessToken)
                .with("subject_issuer", "linkedin")
                .with("requested_token_type", REQUESTED_TOKEN_TYPE))
        .retrieve()
        .bodyToMono(com.agencyheight.dto.onboardingv2.auth.JwtTokenResponse.class)
        .block();
  }

I'm getting this response from keycloak when trying to exchange token. Status : 400 Bad Request

{
    "error": "invalid_token",
    "error_description": "invalid token"
}

I have added the necessary configuration in keycloak: enter image description here I've enabled the token exchange permission as well and set the policy, as per this document : Granting permission for the exchange

enter image description here

What am I missing or what is causing the error?

Upvotes: 0

Views: 41

Answers (1)

Sabu Shakya
Sabu Shakya

Reputation: 354

So the issue is that linkedIn's LinkedIn OpenID Connect, which provides opaque token, is supported only starting Keycloak's version 22 and greater New LinkedIn OpenID Connect social provider. I was using keycloak version 19 and worked on upgrading!

Upvotes: 0

Related Questions