Uploaded image for project: 'Red Hat build of Keycloak'
  1. Red Hat build of Keycloak
  2. RHBK-3482

Parallel token exchange fails if client session is expired [GHI#41427]

XMLWordPrintable

    • False
    • Hide

      None

      Show
      None
    • False

      Before reporting an issue

      [x] I have read and understood the above terms for submitting issues, and I understand that my issue may be closed without action if I do not follow them.

      Area

      token-exchange

      Describe the bug

      I have experienced this in token-exchange:v1, but I think other areas might also be affected.
      When exchanging a token from client-A into one from client-B, if there is an existing client session for client-B in the database and this session is expired, the ClientSessionPersistentChangelogBasedTransaction.getSessionEntityFromPersister will try to remove the existing session from the database. If the same token exchange is called twice in parallel, there is a high probability that one of them will fail with a StaleObjectStateException because the other one has already deleted the session.

      I believe this failure can be avoided, the exception can just be ignored if the session has already been deleted from the database.

      It does not matter if both calls end up on the same server (in a clustered environment) or not.

      Tested in RHBK 26.2.5 and upstream 26.3.2 (both latest at the time of writing).

      Version

      RHBK 26.2.5, 26.3.2

      Regression

      [ ] The issue is a regression

      Expected behavior

      All token exchanges succeed

      Actual behavior

      one of the exchanges fails with HTTP 500

      How to Reproduce?

      For local testing, I reduced the client session timeouts to 30s idle / 1m max and ran the following script a couple of times with >30s pause in between:

      #!/usr/bin/env bash
      
      token="$(<get token for client-A>)"
      
      curl -X POST -sS --location "http://localhost:8080/auth/realms/<realm>/protocol/openid-connect/token" \
      

      -H "Content-Type: application/x-www-form-urlencoded" \
      -d 'client_id=client-B&client_secret=client-B-secret&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&subject_token='"$token" -i > curl1.txt &

      curl -X POST -sS --location "http://localhost:8080/auth/realms/<realm>/protocol/openid-connect/token" \
      

      -H "Content-Type: application/x-www-form-urlencoded" \
      -d 'client_id=client-B&client_secret=client-B-secret&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&subject_token='"$token" -i > curl2.txt &

      wait
      
      cat curl*.txt
      

      Anything else?

      No response

              Unassigned Unassigned
              pvlha Pavel Vlha
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Created:
                Updated:
                Resolved: