9 Replies Latest reply on Jan 9, 2015 1:06 AM by Victor Soria

    KeyCloak, Errai and bearer Token

    Victor Soria Newbie

      Hi all,
      The English is not my native language, but will try to make myself understood.

      I'm building an application that uses Errai 3.1.0 for the view(modification of "Errai Security Demo"), Dropwizard for the rest backend (using the new Keycloak 1.1.0-Final Jetty 9.2.x Adapter) and Keycloak 1.1.0 Final appliance.


      Everything was happiness until trying to make a REST call with security active in the backend(works fine without security adapter), Then I discovered that I had to enable CORS not only in dropwizard filters, Keycloak also needed to activate CORS.


      I see keycloak/examples/cors at master · keycloak/keycloak · GitHub project and proceeded to set equivalent on my own project, i needed a obtain a bearer token in errai client and send Authentication header "Authentication: Bearer %#$%#$%$#%#$" to my dropwizard backend.


      In the angular.js example (github link) they use interceptor:


      module.factory('authInterceptor', function($q, Auth) {
      return {
      request: function (config) {
      var deferred = $q.defer();
      if (Auth.authz.token) {
      Auth.authz.updateToken(5).success(function() {
      config.headers = config.headers || {};
      config.headers.Authorization = 'Bearer ' + Auth.authz.token;
      deferred.resolve(config);
      }).error(function() {
      deferred.reject('Failed to refresh token');
      });
      }
      return deferred.promise;
      }
      };
      });
      
      
      

       

      In my Errai code:

       

      public class MyCacheInterceptor implements RestClientInterceptor {

        @Override

        public void aroundInvoke(final RestCallContext context) {

          RequestBuilder builder = context.getRequestBuilder();

          builder.setHeader("Authorization", "Bearer eyJhbGciOiJSUzI1NiJ9...kHCIEIoqNt1YAesBN8W6-8X4k");

          context.proceed();

        }

      }

       

      And its work's... i get the message "token has expired" (I capture a token that had already expired and hardcode this for test purposes).


      i need the bearer token (in angular they use Auth.authz.updateToken(5)), it is possible to have this information Errai? if this is not possible, I would give some clue as to where I should play the code?


      Regards.


        • 1. Re: KeyCloak, Errai and bearer Token
          Christian Sadilek Master

          Hi Victor!

           

          Yes, that's possible using Keycloak's Javascript Adapter:

          http://docs.jboss.org/keycloak/docs/1.1.0.Beta2/userguide/html/ch07.html#javascript-adapter

           

          You could for example add keycloak.js to your host page and have the token update:

          keycloak.updateToken(30).success(function() {
            
          }).error(function() {
              alert('failed to refresh token');
          );
          

           

          Then in your client-side interceptor:

           

          public class MyKeycloakInterceptor implements RestClientInterceptor {
            @Override
            public void aroundInvoke(final RestCallContext context) {
              RequestBuilder builder = context.getRequestBuilder();
              builder.setHeader("Authorization", "Bearer " + getToken());
              context.proceed();
            }
          
           private native String getToken /*-{
             return $wnd.keycloak.token;    
           }-*/;
          }
          
          • 2. Re: Re: KeyCloak, Errai and bearer Token
            Victor Soria Newbie

            Hi Christian.

             

            Thanks for the suggestion, i tried create a Keycloak Object and call to init function:

             

                <script>
                    var keycloak = Keycloak({
                                        url: 'http://localhost:8080/auth',
                                        realm: 'demo',
                                        clientId: 'xplanner-frontend'
                                    });
                    keycloak.init({ onLoad: 'login-required' }).success(function(authenticated) {
                        alert(authenticated ? 'authenticated' : 'not authenticated');
                    }).error(function() {
                        alert('failed to initialize');
                    });                       
                </script> 
            

             

            I navigate to http://localhost:8080/errai-security-demo and the result is the text "Bad Request" and the new url is: http://localhost:8080/errai-security-demo/WelcomePage?prompt=none&code=Zl2xp7Am4RYHFxtuCVS-_xpJKzVaH3xx5Xuac-IhsuY.8a7b6…

             

            I read the documentation of Javascript Adapter and i see "It works in the same way as other application adapters except that your browser is driving the OAuth redirect protocol rather than the server.".

            I see de Network console in chrome:

            redirect.png

            I am tempted to think that there is compatibility problem with javascript adapter and protected resources Errai security or a compatibility problem with Errai navigation.

            • 3. Re: Re: Re: KeyCloak, Errai and bearer Token
              Victor Soria Newbie

              Hi Christian.


              in my desperation I made the following:


              1. i try to add a method getToken in AuthenticationService interface and KeycloakAuthenticationService class, the code in the class is:


                  public String getToken() {
                      return keycloakSecurityContext.getTokenString();
                  }
              

               

              *I saw the need to change other classes With dummies getToken's methods.

               

              2. for test purposes i add EventHandler in Welcome.java (in "Errai Security Demo")

               

                @EventHandler("obtieneRutina")
                public void onObtieneRutina(ClickEvent e) {
                    String idRutina = this.idRutina.getText();
                    
                    authCaller.call(new RemoteCallback<String>() {
              
              
                    @Override
                    public void callback(final String token) {
                     Storage stockStore = null;
                     stockStore = Storage.getLocalStorageIfSupported();
                     Window.alert(token);
                     if (stockStore != null) {
                          stockStore.setItem("token", token);
                        }
                    }
                  }, new BusErrorCallback() {
              
              
                    @Override
                    public boolean error(Message message, Throwable throwable) {
                      userLabel.setText(ANONYMOUS);
                      return true;
                    }
                  }).getToken();
                    
                    routinesServiceCaller.call(new RemoteCallback<Rutina>() {
              
              
                        @Override
                        public void callback(Rutina response) {
                            Window.alert(response.toString());
                            rutina.setNombre(response.getNombre());
                        }
                    }).routinesIdRoutineGet(idRutina);
                  
                }
              

               

              3. Change the interceptor:

                @Override
                public void aroundInvoke(final RestCallContext context) {
                    
                    String token = "nani";
                    Storage stockStore = null;
                    stockStore = Storage.getLocalStorageIfSupported();
                    if (stockStore != null) {
                        token = stockStore.getItem("token");
                    }
              
              
                    RequestBuilder builder = context.getRequestBuilder();
                    builder.setHeader("Authorization", "Bearer " + token);
                    context.proceed();
              
              
                }
              

               

              Then the call is successful.

               

              however I am not happy with such a dirty solution, starting with I had to modify their sources and do not know the consequences, besides I could not inject the AuthenticationService in the interceptor, and I had to resort to local storage to store the token.

               

              any suggestions?

               

              Regards.

               

              Victor Soria.

              • 4. Re: Re: Re: Re: KeyCloak, Errai and bearer Token
                Christian Sadilek Master

                Hi Victor!

                 

                Yes, these are two different approaches:

                 

                1. Using keycloak.js and driving the OAuth protocol from the browser (see https://developers.google.com/accounts/docs/OAuth2#clientside). I don't know why the redirect failed because I can only see part of the failed URL in your screenshot. One thing you can try is commenting out var erraiPushStateEnabled = true; or setting it to false.

                 

                2. Using errai-security-keycloak and driving the OAuth protocol from the server (see https://developers.google.com/accounts/docs/OAuth2#webserver). You seem to have this working now! One thing we could do is make the token accessible through a user property. That would be a one line addition to Errai (see https://github.com/errai/errai/blob/master/errai-security/errai-security-keycloak/src/main/java/org/jboss/errai/security/keycloak/KeycloakAuthenticationService.java#L186). Do you want to send us a pull request for this? You could then @Inject the User instance in your interceptor (given that it's IOC managed i.e. annotated with @Dependent) and call user.getProperty("token"). The reason you can't inject the AuthenticationService in your interceptor is that the AuthenticationService is server-side and the interceptor is client-side (it runs in your browser).

                 

                Cheers,

                Christian

                1 of 1 people found this helpful
                • 5. Re: Re: Re: Re: Re: KeyCloak, Errai and bearer Token
                  Christian Sadilek Master

                  Another thought: Are you sure you even need the bearer token in your auth header? Since you're already authenticated and have a cookie maybe the problem is just that the cookie isn't sent along with the cross domain request. You can try this to verify:

                   

                  public class MyKeycloakInterceptor implements RestClientInterceptor {
                    @Override
                    public void aroundInvoke(final RestCallContext context) {
                      RequestBuilder builder = context.getRequestBuilder();
                     // make sure cookies are sent along
                      builder.setIncludeCredentials(true);
                      context.proceed();
                    }
                  }
                  
                  

                   

                  In your CORS setup you also have to set this header Access-Control-Allow-Credentials: true

                  • 6. Re: Re: Re: Re: Re: KeyCloak, Errai and bearer Token
                    Victor Soria Newbie

                    Hi Christian! Happy New Year!

                     

                    I could write before by celebrations, which by the way have been very good.

                     

                    Now i finish reading the documentation on github with respect to the pull request (I'm new to github for anything other than make "git clone" ) and send it.

                     

                    I try setIncludeCredentials option, however it did not work and got this output :

                     

                    XMLHttpRequest cannot load http://localhost:1111/routines/448ca53f-8145-433b-bcc0-f5f74f630886.

                      The request was redirected to 'http://localhost:8080/auth/realms/demo/protocol/openid-connect/login?client…cc0-f5f74f630886&state=0%2F43dcc86e-f774-49ba-aca9-434b1842ab31&login=true',

                      which is disallowed for cross-origin requests that require preflight.

                     

                    for this comment option "setBearerOnly (true)" in my jetty adapter(because "Errai Security Demo" not send the header "Authetication: Bearer " + token ).

                     

                    I tried to do the equivalent of cors project example: keycloak/examples/cors at master · keycloak/keycloak · GitHub apparently this one contemplates the use of bearertoken to use cors, although I will continue investigating if I find any alternative because it never hurts, maybe it's a matter of using "setIncludeCredentials (true)" option in "ErraiSecurityDemo" and try some other settings in the adapter del backend (Keycloak JettyAdapter in dropwizard), but do not know enough the inner workings of Errai-security-keycloak (and not the internal behavior of keycloak), I not venture to say that could be done. For now the bearertoken aproximation for "CORS example" of keycloak team works well if we get the token.

                     

                    Regards.

                     

                    Victor.

                    • 7. Re: KeyCloak, Errai and bearer Token
                      Victor Soria Newbie

                      Hi Christian,

                       

                      I have not sent the pull request; i  detect another problem, if I, inject User object, the token is not updated on each call to the interceptor, when the token_ expires, I get an error message; that does not happen if I run the method getToken(Modification of the AuthenticationService) unfortunately include getToken method involves affect other jars such as class implement all AuthenticationService.

                      Another problem that was thinking is that if I, inject the user object and included the token_ in properties, this is a fairly large String and adds many bytes that not everyone may need; On the other hand, injecting user_properties for each call rest, is much more information than I need.

                       

                       

                      So for now I'll stay with the modification that adds the method getToken (i annotated @Dependet the interceptor and inject the service); however I do not think send an pull request as the method getToken affect other files that have to do with keycloak and in this case the token is the stuff of keycloak.

                       

                       

                      On the other hand my need is too specific and may need to stop that units are those that take into consideration whether it is worthwhile to include this in the code and what would be the best alternative to do, so generare one feature request.

                       

                      sorry for my English, I need urgently a course, reading is easy for me but writing is complicated me too. I hope you have understood the idea

                      • 8. Re: KeyCloak, Errai and bearer Token
                        Christian Sadilek Master

                        Hi Victor,

                         

                        I think your solution using a getToken() method is fine given it doesn't exhibit the same token expiry problem (did you verify that? what keeps it up-to-date?). You're more than welcome to send us a PR. If you're worried that it's too specific to Keycloak you can create an interface KeycloakAuthenticationService that inherits from AuthenticationService and have the service (KeycloakAuthenticationServiceImpl) implement that instead.

                         

                        Reading this thread again it does seem that using keycloak.js is a better approach for your use case. So, it might be worth investigating this more.

                         

                        Cheers,

                        Christian

                        • 9. Re: Re: KeyCloak, Errai and bearer Token
                          Victor Soria Newbie

                          Hello Christian

                           

                          Sorry for the delay in answering, I've been with a lot of workload and this is a personal project, but hey now return to the load.

                          I tested again to see if the token is refreshed; and indeed, if it does; not really that way because KeycloakAuthenticationService analyzing the code and can not find the code where the refresh ago.

                          It seems that you were running an instance of RefreshableKeycloakSecurityContext but do not see where. At the moment I can not analyze it more deeply; I further progress in the visual part of the project. But as you can volvere to immerse myself in those sources.

                          I like Errai and Errai Security, with respect to each other as could be Angular + JS adapter.

                          Besides the JS Keycloak Adapter have this disadvantage :

                           

                          The Keycloak Server comes with a Javascript library you can use to secure pure HTML/Javascript applications. It works in the same way as other application adapters except that your browser is driving the OAuth redirect protocol rather than the server.

                          The disadvantage of using this approach is that you end up having a non-confidential, public client. This can be mitigated by registering valid redirect URLs. You are still vulnerable if somebody hijacks the IP/DNS name of your pure HTML/Javascript application though.

                           

                          Regards,

                           

                          Victor Soria.