AngularJS refresh token

Salutare si la multi ani. Vin cu o intrebare si sper ca ma puteti ajuta.
Pe front-end folosesc angularjs si pe partea de back-end symfony. Autentificarea se face cu FOSUserBundle. Ideea e ca cand initializez un controller fac sa zicem 3 ajax call-uri imi vine la primul call response 401 ca si cum a expirat token-ul fac un call pentru refresh token insa celelalte 2 call-uri care au ramas nu se actualizeaza token-ul. Am incercat cu $httpBuffer, cu http interceptor si tot nu reusesc.
Mai jos este codul pentru Interceptor, fac update la refresh token si mai incerc odata call-ul cu noul token insa imi da da in console de mai multe ori ca apeleaza refresh_token, si primul imi returneaza 200 insa restul imi da invalid_token, dar eu vreau sa se opreasca dupa primul call

$provide.factory('MyHttpInterceptor', function ($q, $rootScope, OAuthToken, $injector, $location, $cookies, $window) {
        return {
            request: function(config) {
                config.headers = config.headers || {};
                if (OAuthToken.getAccessToken()) {
                    // if (config.url.substring(0, RESOURCE_URL.length) !== RESOURCE_URL) {
                        config.headers.Authorization = 'Bearer ' + OAuthToken.getAccessToken();
                    // }
                }
                return config;
            },
            responseError: function(response) {
                var $http = $injector.get('$http');
                var deferred = $q.defer();

                // If is ignored http request (token)
                if(response.config.ignore401) {
                    return response;
                }

                if(!OAuthToken.getRefreshToken()) {
                    $location.path('user/login');
                }

                switch (response.status) {
                    case 401:
                        $http({
                            method: 'GET',
                            ignore401: true,
                            url: $rootScope.baseUrl + "/oauth/v2/token?client_id=" + $rootScope.clientId + "&client_secret=" + $rootScope.clientSecret + "&grant_type=refresh_token&refresh_token=" + OAuthToken.getRefreshToken(),
                        }).success(function (data) {
                            // Redirect to login page
                            if(!data) {
                                OAuthToken.removeToken();
                                $location.path('user/login');
                                return;
                            }
                            if (data.access_token && data.refresh_token && data.expires_in) {
                                // Set new token
                                OAuthToken.setToken({
                                    access_token: data.access_token,
                                    expires_in: data.expires_in,
                                    refresh_token: data.refresh_token
                                });

                                // Try again the request with the new token
                                response.config.headers.Authorization = 'Bearer ' + data.access_token;
                                $injector.get("$http")(response.config).then(function (resp) {
                                    deferred.resolve(resp);
                                }, function (resp) {
                                    deferred.reject();
                                });
                            }

                        }).error(function (err) {
                            deferred.reject();
                        });
                        return deferred.promise;
                        break;
                    default:
                        // OAuthToken.removeToken();
                        deferred.reject();
                        // $location.path('user/login');
                        break;
                }
                return response || $q.when(response);
            }
        };
    });

Daca requesturile se fac concurent nu are de ce sa se faca update la token, toate trei sunt pornite si la un moment dat se face update, cel mai probabil dupa ce sau executat deja.

Nu cred ca abordarea asta e cea mai buna, poate sa genereze diferite erori. Mai repede a-si face o functie separata care se re-apeleaza la fiecare x minute si face refresh la token in fundal fara sa fie nevoie de o logica complicata de retry globala in interceptor.

Partea asta de interceptor nu cred a fost gandita pentru operatiuni de genul asta, mai repede pentru manipularea rapida de date (adaugi ceva in header, modifici ceva in continut, faci o logare etc…) dar nu sa faci chestii complexe in ea.

2 Likes

La primul 401 setezi doua variabile, una cu timestamp si una cu true daca nu e setata si faci refresh la token. La celelalte 401 o sa vezi ca e true variabila si timestampul e mai vechi de x minute sau nu e setat deloc, si le pui intr-un queue pana iti vine tokenul inapoi. Cand vine tokenul inapoi, lansezi toate requesturile din queue (inclusiv cel in care ai facut refresh) si pui false variabila setata.

Ceva de genul:

case 401:
  if (isTokenRefresh) {
    queue.add(request);
  } else {
    if (!tokenTimestamp || now() - tokenTimestamp  > x minute) {
      queue.add(request);
      isTokenRefresh = true;
      refresh token --- > {
        isTokenRefresh = false
        tokenTimestamp = now()
        for each request in queue -> send request
        empty queue
      }
    } else {
      resend request with token
    }
  }
1 Like

iti poti face un serviciu care sa ruleze de fiecare data inainte sa porneasca aplicatia. (nu mai stiu exact cum, nu m-am mai atins de angular de vreo 4 ani). si teoretic in serviciul ala verifici daca tokenul e expirat sau daca-i pe cale sa expire. daca-i pe cale sa expire faci un request pentru revalidare, daca nu continui normal cu aplicatia.

metoda lui adrian e un hack urat.

si ce faci daca expira in timp ce foloseste aplicatia ?

Salut !
Am cautat intrebarea ta si am gasit asta pe stack overflow

plus

sper sa te ajute !