Uploaded image for project: 'Keycloak'
  1. Keycloak
  2. KEYCLOAK-9468

Improve keycloak-authz.js to automatically exchange UMA tickets and refresh tokens

    Details

      Description

      I was thinking that keycloak-authz.js adapter can provide some utility, which will provide refreshing of RPT tokens (in case they are expired) and also exchanging UMA tickets, which were returned from resource-server for new RPT.

      For example adapter can have some utility like "rptProvider", which will do something like this (it will be better to have proper state diagram, but hopefully you won't be lost in those conditions. Imagine that from the point 1, you can go to 1.1 or 1.2):

      1) check if there is existing RPT stored. If yes, it will:
      1.1) Check if existing RPT is expired. If yes, it will:
      1.1.1) try to refresh RPT. If refresh success, then adapter will store refreshed RPT and go to (1.2)
      1.1.2) If refresh fails, adapter will delete the existing RPT and go to step 2
      1.2) If existing RPT is not expired, adapter will just call that particular "onSuccess" callback method with the RPT
      2) If there is no RPT, adapter will use it's accessToken to call authorization API
      2.1) If calling authorization API fails, there should be "onAuthzError" callback called with the error message sent to it as argument (For example "request_submitted", so that caller is aware that request was saved on KC side to be approved by the resource owner)
      2.2) If calling authorization API succeeds, we will store RPT and go to (1.2)

      — The "onSuccess" callback will usually invoke the REST service with RPT, but service can return UMA ticket in case that RPT is missing some permissions. In that case, it should call some builtin function provided by the authz client, which will:
      3) Try to "parse" the UMA ticket from the response.
      3.1) If it's not there, we need to call some "onOtherError" callback method
      3.2) If it's there, we will use that UMA ticket to call authorization API - hence go again to step 2

      Some pseudo-code how the usage of it can look like:

      var rptProvider = keycloakAuthzClient.getRptProvider();
       
      // This is the maximum timeout allowed before "rpt" needs to be refreshed
      rptProvider.setTokenMinimumTimeToLive(10);
       
      // The "rpt" is guaranteed to NOT be expired. However it may not contain permissions needed to invoke resource-server
      rptProvider.onSuccess = function(rpt) {
          // Just call the REST service
          var url = 'http://localhost:8080/resource-server';
       
          var req = new XMLHttpRequest();
          req.open('GET', url, true);
          req.setRequestHeader('Accept', 'application/json');
          req.setRequestHeader('Authorization', 'Bearer ' + rpt);
       
          req.onreadystatechange = function () {
              if (req.readyState == 4) {
                  if (req.status == 200) {
                      alert('Success');
                  } else if (req.status == 401) {
                      // We MAY have UMA ticket in the response. Let's just call "rptProvider.setUmaResponse" and then re-call "run" .
                      // Internally, the adapter will try to parse UMA ticket from the response and exchange this UMA ticket for the RPT from KC server
                      rptProvider.setUmaResponse(req);
                      rptProvider.run();               
                  }
              }
          }
       
          req.send();   
                      
      });
       
      // This callback is called when the call to KC authorization API failed.
      // The authzError can be for example "request_submitted", so the caller is aware that request was created on Keycloak side and needs to be approved by the resource owner
      rptProvider.onAuthzError(function(authzError) {
       
      });
       
      // This callback is called on any other error. For example when resource-server returns some other error response than "401" with UMA ticket
      rptProvider.onOtherError(function(errorDetails) {
       
      });
       
      // This will trigger the described flow
      rptProvider.run();
      

        Gliffy Diagrams

          Attachments

            Issue Links

              Activity

                People

                • Assignee:
                  Unassigned
                  Reporter:
                  mposolda Marek Posolda
                • Votes:
                  0 Vote for this issue
                  Watchers:
                  2 Start watching this issue

                  Dates

                  • Created:
                    Updated: