реализация токенов обновления с помощью angular и express-jwt

Я хочу реализовать концепцию скользящего истечения срока действия с веб-токенами json, используя angular, nodejs и express-jwt. Я немного запутался в том, как это сделать, и изо всех сил пытаюсь найти какой-либо пример токенов обновления или другой материал, относящийся к сеансам с этими технологиями/фреймворками.

Несколько вариантов, о которых я думал, были

  • Генерация нового токена с каждым запросом после первоначального входа в систему
  • Отслеживание выпущенного токена на стороне сервера

Но я честно не уверен, пожалуйста, помогите


person user979441    schedule 27.10.2014    source источник
comment
У меня в основном тот же вопрос, и я опубликовал свой подход первого прохода по адресу: " title=" истечение срока действия jsonwebtoken по сравнению с истечением времени выдачи"> stackoverflow.com/questions/27408762/   -  person rgwozdz    schedule 10.12.2014
comment
вы можете использовать gist.github.com/Mirodil/952e5932c284a2d205db   -  person Mirodil    schedule 17.08.2015


Ответы (1)


Мне удалось реализовать этот сценарий.

Что я сделал...

На сервере:

-Включить конечную точку API для входа. Эта конечная точка ответит веб-токеном Json в заголовке. Клиентская сторона должна перехватить его (с помощью перехватчиков $http) и сохранить (я использую локальное хранилище). Клиент также будет управлять обновленными токенами, отправленными сервером.

-При каждом запросе к серверу настройте промежуточное ПО для экспресс-проверки токена. Сначала я попробовал модуль express-jwt, но jsonwebtoken мне подошел.

Для определенных маршрутов вы можете отключить промежуточное ПО. В этом случае вход и выход.

var jwtCheck = auth.verifyJWT;
jwtCheck.unless = unless;
app.use('/api', jwtCheck.unless({path: [
    '/api/auth/signin',
    '/api/auth/signout'
]}));

- Промежуточное программное обеспечение verifyJWT всегда отвечает токеном в заголовке. Если токен необходимо обновить, вызывается функция обновления.

jwtLib — это моя собственная библиотека, в которой живет код для создания, обновления и получения токенов jwt.

function(req, res, next) {
    var newToken,
        token = jwtLib.fetch(req.headers);

    if(token) {
        jwt.verify(token, config.jwt.secret, {
            secret: config.jwt.secret
        }, function(err, decoded) {
            if(err) {
                return res.status(401).send({
                    message: 'User token is not valid'
                });
            }
            //Refresh: If the token needs to be refreshed gets the new refreshed token
            newToken = jwtLib.refreshToken(decoded);
            if(newToken) {
                // Set the JWT refreshed token in http header
                res.set('Authorization', 'Bearer ' + newToken);
                next();
            } else {
                res.set('Authorization', 'Bearer ' + token);
                next();
            }
        });
    } else {
        return res.status(401).send({
            message: 'User token is not present'
        });
    }
};

-Функция обновления (jwtLib). Поскольку аргументу требуется декодированный токен, см. выше, что jsonwebtoken разрешает декодирование при вызове jwt.verify().

Если во время входа вы создаете токен со сроком действия 4 часа и сроком обновления 1 час (1 * 60 * 60 = 3600 секунд), это означает, что токен будет обновлен, если пользователь неактивен в течение 3 часов или более. , но не более 4 часов, так как в этом случае процесс проверки завершится неудачно (окно обновления 1 час). Это позволяет избежать создания нового токена при каждом запросе, только если срок действия токена истечет в этом временном окне.

module.exports.refreshToken = function(decoded) {
    var token_exp,
        now,
        newToken;

    token_exp = decoded.exp;
    now = moment().unix().valueOf();

    if((token_exp - now) < config.jwt.TOKEN_REFRESH_EXPIRATION) {
        newToken = this.createToken(decoded.user);
        if(newToken) {
            return newToken;
        }
    } else {
       return null;
    }
};

На клиенте (Angularjs):

-Включить клиентскую сторону для входа в систему. Это вызывает конечную точку сервера. Я использую базовую аутентификацию Http, закодированную с помощью base64. Вы можете использовать угловой модуль base64 для кодирования электронной почты: пароль. Обратите внимание, что в случае успеха я не сохраняю токен в localStorage или Cookie. Это будет управляться http Interceptor.

//Base64 encode Basic Authorization (email:password)
$http.defaults.headers.common.Authorization = 'Basic ' + base64.encode(credentials.email + ':' + credentials.password);
return $http.post('/api/auth/signin', {skipAuthorization: true});

-Настройте перехватчики http для отправки токена на сервер при каждом запросе и сохранения токена в ответе. Если получен обновленный токен, его необходимо сохранить.

// Config HTTP Interceptors
angular.module('auth').config(['$httpProvider',
    function($httpProvider) {
        // Set the httpProvider interceptor
        $httpProvider.interceptors.push(['$q', '$location', 'localStorageService', 'jwtHelper', '$injector',
            function($q, $location, localStorageService, jwtHelper, $injector) {
                return {
                    request: function(config) {
                        var token = localStorageService.get('authToken');
                        config.headers = config.headers || {};

                        if (token && !jwtHelper.isTokenExpired(token)) {
                            config.headers.Authorization = 'Bearer ' + token;
                        }
                        return config;
                    },
                    requestError: function(rejection) {
                        return $q.reject(rejection);
                    },
                    response: function(response) {
                        //JWT Token: If the token is a valid JWT token, new or refreshed, save it in the localStorage
                        var Authentication = $injector.get('Authentication'),
                            storagedToken = localStorageService.get('authToken'),
                            receivedToken = response.headers('Authorization');
                        if(receivedToken) {
                            receivedToken = Authentication.fetchJwt(receivedToken);
                        }
                        if(receivedToken && !jwtHelper.isTokenExpired(receivedToken) && (storagedToken !== receivedToken)) {

                            //Save Auth token to local storage
                            localStorageService.set('authToken', receivedToken);
                        }
                        return response;
                    },
                    responseError: function(rejection) {
                        var Authentication = $injector.get('Authentication');
                        switch (rejection.status) {
                            case 401:
                                // Deauthenticate the global user
                                Authentication.signout();
                                break;
                            case 403:
                                // Add unauthorized behaviour
                                break;
                        }

                        return $q.reject(rejection);
                    }
                };
            }
        ]);
    }
]);
person almoraleslopez    schedule 17.12.2014
comment
В этом решении кажется, что вы просто выпускаете новый JWT, если срок действия старого истек. Это нарушает всю безопасность JWT, который нельзя использовать после истечения срока действия, даже для обновления и выпуска нового токена. Сервер действительно должен был выпустить другой токен, называемый токеном обновления, который он сохранил бы в базе данных и отправил его клиенту. Токен обновления будет использоваться для получения нового токена доступа JWT после истечения срока действия предыдущего. - person stephen; 24.05.2017
comment
refreshToken() вызывается только в том случае, если пользователь аутентифицирован, поэтому проблем с безопасностью нет. refreshToken() создает новый токен, если срок действия находится во временном окне TOKEN_REFRESH_EXPIRATION, в противном случае возвращает значение null. В любом случае, этот фрагмент кода был написан некоторое время назад. Это все еще действует, но, возможно, какой-то другой подход может быть лучше. - person almoraleslopez; 29.05.2017
comment
@almoraleslopez, когда кто-то украл ваш токен, хакер будет аутентифицирован и сможет обновляться без ограничений - person AgBorkowski; 03.11.2017
comment
@AgBorkowski токен обновления будет так же легко украсть, если он обрабатывается клиентом, поэтому не обеспечивает дополнительной защиты. Мое понимание токенов обновления заключается в том, что они дают вам возможность выйти из системы через определенный период времени (скажем, 1 день, в то время как jwt обновляется более регулярно) и возможность заставить кого-то снова войти в систему. т. е. вы отмечаете обновление токен как недействительный в БД. Стивен прав, хотя. Мы должны вернуться к базе данных, чтобы проверить, что пользователю по-прежнему разрешен доступ каждый раз, когда истекает срок действия JWT. В противном случае может быть просто один долгоживущий JWT, который опасен. - person Jonathan Pritchard; 26.05.2018