reactjskeycloakopenid-connectkeycloak-js

ReactJS Keycloak PKCE sending code_verifier with token post request after authentication


I am trying to implement PKCE flow with keycloak and reactJS. I am generating code_challange and code verifier in my react client application.

I have adapter configuration as follows :

{
  "realm": "demo",
  "auth-server-url": "http://localhost:8080/auth/",
  "issuer": "http://localhost:8080/auth/realms/demo",
  "scope": "openid profile email offline_access",
  "code-challenge-method": "S256",
  "ssl-required": "external",
  "resource": "javascript-app",
  "verify-token-audience": true,
  "use-resource-role-mappings": true,
  "confidential-port": 0,
  "enable-pkce": true,
  "public-client": true,
  "pkce-method": "S256",
  "response-type": "code"
}

I am generating the auth URL as follows :

// _kc is keycloak client adapter instance 
function generateUrl() {
  const { code_verifier, code_challenge } = pkceChallenge();
  const URL = _kc.createLoginUrl();
  const CODE_CHALLENGE = "&code_challenge=" + code_challenge;
  const CODE_CHALLENGE_METHOD = "&code_challenge_method=S256";
  const CODE_VERIFIER = "code_verifier" + code_verifier;
  return URL + CODE_CHALLENGE + CODE_VERIFIER + CODE_CHALLENGE_METHOD;
  window.open(authorizationUrl, "_self");
}

So far everything works fine. But when it is redirect to the app from keycloak auth page the subsequent post request to get access token is failing with the following error : url : http://localhost:8080/auth/realms/demo/protocol/openid-connect/token

{error: "invalid_grant", error_description: "PKCE code verifier not specified"}

It is clear that the code_verifier is not being passed with the form data. Due to this client keycloak instance is failing to initiate after redirecting.

How to fix this issue by updating the token access request in keycloak to pass code_verifier and code along with the form data ?


Solution

  • In case to mimic PKCE flow manually one way is to retrieve code from the redirect url after authentication. You can get the code using javascript window objects params. But it doesn't seems to be a cool way.

    In the above code keycloak PKCE flow is invoked manually. It can be done with keycloak instance itself by setting pkceMethod: "S256", with keycloak inint() methode. once its done use keycloak login() methode to proceed with PKCE flow. it will automatically generate code_verifier and code_challenge.

    It will look something like this :

    export function initKeycloak() {
      _kc.init({
        onLoad: "check-sso",
        pkceMethod: "S256", // <- important
        silentCheckSsoRedirectUri:
          window.location.origin + "/silent-check-sso.html",
      });
    }
    

    This initialization can be done right at the loading of the app or component itself.

    Instead of generating a URL for login use keycloak login function:

    const doLogin = async () => {
      initKeycloak();
      _kc.login();
    };
    

    setting the pkce properties in the adapter json as below is not enough to implement the flow.

      "code-challenge-method": "S256",
      "pkce-method": "S256",
    

    Read more : https://www.keycloak.org/docs/16.1/securing_apps/#javascript-adapter-reference

    init(options) from - https://github.com/keycloak/keycloak-documentation/blob/main/securing_apps/topics/oidc/javascript-adapter.adoc

    Another way is to use any OIDC third party libraries which will do the job. However it is not needed as keycloak provides the feature with its client adapters.

    In short to implement PKCE keycloak in React

    1. ensure pkce settings are enabled in keycloak console.
    2. Use keycloak client adapter.
    3. ensure the keycloak instance in initialized with init() methode and with pkceMethod: "S256",
    4. use keycloak login() method. before login() ensure keycloak.init() is invoked.