Ankur Singhal
Ankur Singhal

Reputation: 26077

KEYCLOAK - Refresh/update token not working

I have single page application that is built using Angularjs and integrated with Keycloak for authentication and authorization.

I am able to login into my application, get loggedin user roles etc. goes The moment refresh token call, it always returns in my else case, and user logout of the application. Though the token valid time is set very high.

I need to update the token, if user has opened the app. In case of failure or expire token i need to logout the user. if (refreshed) always returns false.

Below is the piece of code i am using.

var __env = {};

        Object.assign(__env, window.__env);

        var keycloakConfig = {
            "url" : __env.keycloakUrl,
            "realm" : __env.keycloakRealm,
            "clientId" : __env.keycloakClientId,
            "credentials" : {
            "secret" : __env.keycloakSecret
            }
        };
var keycloak = Keycloak(keycloakConfig);
        keycloak.init({
            onLoad : 'login-required'
        }).success(function(authenticated) {
                 if(authenticated){                  
                        keycloak.loadUserInfo().success(function(userInfo) {
                        bootstrapAngular(keycloak, userInfo, roles);
                    });
            }
        });

function bootstrapAngular(keycloak, userInfo, roles) {
        angular.module('myApp').run(
                function($rootScope, $http, $interval, $cookies) {
                    var updateTokenInterval = $interval(function() {
                        // refresh token if it's valid for less then 15 minutes
                    keycloak.updateToken(15).success(
                                function(refreshed) {
                                    if (refreshed) {
                                        $cookies.put('X-Authorization-Token',
                                                keycloak.token);
                                    }else{
                                        $rootScope.logoutApp();
                                    }
                                });
                    }, 600000);
                    updateTokenInterval;
                    $cookies.put('X-Authorization-Token', keycloak.token);

                    $rootScope.logoutApp = function() {
                        $cookies.remove('X-Authorization-Token');
                        $interval.cancel(updateTokenInterval);
                        keycloak.logout();
                    };
    }
}

Upvotes: 16

Views: 57779

Answers (7)

  Vasiles Petrides
Vasiles Petrides

Reputation: 1

I was experiencing the same issue. For me the problem was in the keycloak.init() configuration. I was using the flow: "implicit" which is not recommended for refreshing tokens as the refresh token is never sent to the client. The solution was to use flow: "hybrid", for which the keycloak documentation states:

This flow requires the client to have both the Standard Flow and Implicit Flow enabled in the Admin Console. The Keycloak server then sends both the code and tokens to your application. The access token can be used immediately while the code can be exchanged for access and refresh tokens. Similar to the implicit flow, the hybrid flow is good for performance because the access token is available immediately. But, the token is still sent in the URL, and the security vulnerability mentioned earlier may still apply.

You may read more here: https://www.keycloak.org/docs/latest/securing_apps/#_javascript_implicit_flow

Finally, I also had to follow the recommended answer here because the token expiration values (on the Admin Panel and the keycloak-js adapter) were not set correctly.

Upvotes: 0

PPr
PPr

Reputation: 81

keycloak.onAuthSuccess = function() { alert('authenticated'); }

keycloak.onTokenExpired = function () {
            keycloak.updateToken(5)
            .then(function (refreshed) {
                if (refreshed) {
                    alert('Token refreshed');
                  // write any code you required here
                } else {
                    alert('Token is still valid now');
                }
            }).catch(function () {
                alert('Failed to refresh the token, or the session has expired');
            }); }
            keycloak.init({ onLoad: 'login-required' }).then(function (authenticated) {..... }

Upvotes: 1

Alex Gontcharov
Alex Gontcharov

Reputation: 119

In standard flow I noticed that Token expiration is coming form Access Token Lifespan and Refresh Token expiration from SSO Session Idle. And it is recommended that Access Token Lifespan should be shorter then SSO time out. Should it be the other way around? Also Access Token Lifespan can be overwritten in Clients Settings tab under Advanced Settings.

Upvotes: 0

Charith Jayasanka
Charith Jayasanka

Reputation: 4792

(refreshed) returns false only if your token is not expired. So you're trying to refresh the token when it has not yet expired.

set " Access Token Lifespan " to 1 minute in the Keycloak realm you're using, then try the following code to check the status of refreshed again

keycloak.onTokenExpired = ()=>{
            console.log('expired '+new Date());
            keycloak.updateToken(50).success((refreshed)=>{
                if (refreshed){
                    console.log('refreshed '+new Date());
                }else {
                    console.log('not refreshed '+new Date());
                }
            }).error(() => {
                 console.error('Failed to refresh token '+new Date());
            });
            }

Upvotes: 11

Einsamer
Einsamer

Reputation: 1087

I take a look on their sample code:

        /**
     * If the token expires within `minValidity` seconds, the token is refreshed.
     * If the session status iframe is enabled, the session status is also
     * checked.
     * @returns A promise to set functions that can be invoked if the token is
     *          still valid, or if the token is no longer valid.
     * @example
     * ```js
     * keycloak.updateToken(5).success(function(refreshed) {
     *   if (refreshed) {
     *     alert('Token was successfully refreshed');
     *   } else {
     *     alert('Token is still valid');
     *   }
     * }).error(function() {
     *   alert('Failed to refresh the token, or the session has expired');
     * });
     */

So I think your token is still valid, that's why the updateToken return false.

Upvotes: 4

Atropo
Atropo

Reputation: 12531

I couldn't find explained it in the API docs but the timeout argument of keycloak.updateToken() function is expressed in seconds, not in minutes.

So if the Access Token Lifespan on server is at the default value of 5 minutes, you should use a value less than 300 seconds. I learned it doing some experiments.

//Update the token when will last less than 3 minutes
keycloak.updateToken(180)

Btw I suggest you to use a Lifespan longer than 5 minutes for the token.

In your code You never see the token refreshed because the refresh is never triggered in the 15 seconds window in which will work.

Upvotes: 17

Sébastien Blanc
Sébastien Blanc

Reputation: 3239

Have you looked at our Angular sample app https://github.com/keycloak/keycloak/blob/master/examples/demo-template/angular-product-app/src/main/webapp/js/app.js ? It could give you some tips maybe.

Upvotes: -1

Related Questions