JWT (JSON Web Token) автоматическое продление срока действия


Мне бы хотелось осуществить проверки подлинности-проверка подлинности на основе нашего нового API-интерфейса REST. Но поскольку срок действия установлен в токене, можно ли автоматически продлить его? Я не хочу, чтобы пользователи должны были входить в систему после каждых X минут, если они активно использовали приложение в этот период. Это было бы огромным провалом UX.

но продление срока действия создает новый токен (и старый все еще действителен до его истечения). И создание нового токена после каждого запроса звучит глупо для меня. Звучит как проблема безопасности, когда одновременно действует более одного токена. Конечно, я мог бы аннулировать старый используемый, используя черный список, но мне нужно было бы хранить токены. И одним из преимуществ JWT является отсутствие хранения.

Я нашел, как Auth0 решил его. Они используют не только токен JWT, но и токен обновления: https://docs.auth0.com/refresh-token

но опять же, чтобы реализовать это (без Auth0) мне нужно будет хранить токены обновления и сохраняйте их срок годности. В чем же тогда реальная выгода? Почему бы не иметь только один токен (не JWT) и сохранить срок действия на сервере?

есть ли другие варианты? С помощью проверки подлинности не подходит для этого сценария?

9 379

9 ответов:

Я работаю в Auth0, и я участвовал в разработке функции токена обновления.

все зависит от типа приложения и вот наш рекомендуемый подход.

web-приложений

хорошим шаблоном является обновление токена до истечения срока его действия.

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

для обновления маркера вашему API нужна новая конечная точка, которая получает действительный, не истекший JWT и возвращает тот же подписанный JWT с новым полем истечения срока действия. Затем веб-приложение будет хранить маркер где-то.

мобильные/приложения

большинство нативных приложений войти один раз и только один раз.

идея заключается в том, что токен обновления никогда не истекает, и это может быть всегда обменять на действительный вышлю.

проблема с токеном, который никогда не истекает, заключается в том, что никогда значит никогда. Что делать, если вы потеряете свой телефон? Таким образом, он должен быть идентифицирован пользователем каким-то образом, и приложение должно обеспечить способ отзыва доступа. Мы решили использовать имя устройства, например, "iPad maryo". Затем пользователь может зайти в приложение и отозвать доступ к "iPad maryo".

другой подход заключается в отмене токена обновления конкретное событие. Интересным событием является смена пароля.

мы считаем, что JWT не полезен для этих случаев использования, поэтому мы используем случайную генерируемую строку и храним ее на нашей стороне.

в случае, когда вы сами обрабатываете auth (т. е. не используете провайдера, такого как Auth0), может работать следующее:

  1. выпустить токен JWT с относительно коротким сроком действия, скажем, 15 минут.
  2. приложение проверяет дату истечения срока действия токена перед любой транзакцией, требующей токена (Токен содержит дату истечения срока действия). Если токен истек, то он сначала просит API "обновить" токен (это делается прозрачно для UX).
  3. API получает запрос на обновление токена, но сначала проверяет базу данных пользователей, чтобы узнать, установлен ли флаг "reauth" для этого профиля пользователя (маркер может содержать идентификатор пользователя). Если флаг присутствует, то обновление маркера отклоняется, в противном случае выдается новый маркер.
  4. повторить.

флаг 'reauth' в бэкэнде базы данных будет установлен, когда, например, пользователь сбросит свой пароль. Флаг удаляется, когда пользователь входит в систему в следующий раз.

кроме того, допустим, у вас есть политика, в соответствии с которой пользователь должен вход по крайней мере один раз в 72 часа. В этом случае ваша логика обновления маркера API также проверит дату последнего входа пользователя из пользовательской базы данных и запретит/разрешит обновление маркера на этой основе.

я возился при перемещении наших приложений в HTML5 с RESTful API в бэкэнде. Решение, которое я придумал, было:

  1. клиент выдается с токеном с временем сеанса 30 минут (или независимо от обычного времени сеанса на стороне сервера) при успешном входе в систему.
  2. таймер на стороне клиента создается для вызова службы для обновления маркера до истечения его времени. Новый токен заменит существующий в будущих вызовах.

Как вы можете видеть, что это уменьшает частые запросы токена обновления. Если пользователь закрывает браузер / приложение до запуска вызова маркера обновления, предыдущий маркер истечет во времени, и пользователю придется повторно войти в систему.

более сложная стратегия может быть реализована для удовлетворения бездействия пользователя (например, пренебречь открытой вкладкой браузера). В этом случае вызов маркера обновления должен включать ожидаемое время истечения срока действия, которое не должно превышать определенное время сеанса. Приложения следите за последним взаимодействием пользователя соответственно.

Мне не нравится идея установки длительного срока действия, поэтому этот подход может плохо работать с собственными приложениями, требующими менее частой аутентификации.

альтернативным решением для аннулирования JWTs, без какого-либо дополнительного безопасного хранения на бэкэнде, является реализация нового jwt_version целочисленный столбец в таблице users. Если пользователь хочет выйти из системы или истечь срок действия существующих токенов, они просто увеличивают

хороший вопрос - и есть богатство информации в самом вопросе.

статьи обновить токены: когда их использовать и как они взаимодействуют с JWTs дает хорошую идею для этого сценария. Некоторые моменты:-

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

посмотрите auth0 / angular-jwt angularjs

для веб-API. читайте включить токены обновления OAuth в приложении AngularJS с помощью ASP .NET Web API 2 и Owin

Я фактически реализовал это в PHP, используя клиент Guzzle, чтобы сделать клиентскую библиотеку для api, но концепция должна работать для других платформ.

в основном, я выпускаю два токена, короткий (5 минут) один и длинный, который истекает через неделю. Клиентская библиотека использует промежуточное программное обеспечение для попытки одного обновления короткого маркера, если она получает ответ 401 на некоторый запрос. Затем он снова попробует исходный запрос, и если он смог обновить, получит правильный ответ, прозрачно для пользователя. Если это не удалось, он просто отправит 401 пользователю.

Если короткий токен истек, но все еще аутентичен, а длинный токен действителен и аутентичен, он обновит короткий токен, используя специальную конечную точку на службе, которую аутентифицирует длинный токен (это единственное, для чего он может использоваться). Затем он будет использовать короткий токен для получения нового длинного токена, тем самым продлевая его еще на неделю каждый раз, когда он обновляет короткий токен знак.

этот подход также позволяет нам отменить доступ в течение не более 5 минут, что приемлемо для нашего использования без необходимости хранить черный список токенов.

Late edit: перечитывая этот месяц после того, как он был свеж в моей голове, я должен отметить, что вы можете отменить доступ при обновлении короткого токена, потому что он дает возможность для более дорогих вызовов (например, позвонить в базу данных, чтобы узнать, был ли пользователь запрещен), не платя за него при каждом вызове к вашим услугам.

jwt-autorefresh

если вы используете узел (React / Redux / Universal JS), вы можете установить npm i -S jwt-autorefresh.

эта библиотека планирует обновление токенов JWT на вычисленное пользователем количество секунд до истечения срока действия токена доступа (на основе утверждения exp, закодированного в токене). Он имеет обширный набор тестов и проверяет довольно много условий, чтобы гарантировать, что любая странная деятельность сопровождается описательным сообщением о неправильных конфигурациях из вашего окружающая среда.

полный пример реализации

import autorefresh from 'jwt-autorefresh'

/** Events in your app that are triggered when your user becomes authorized or deauthorized. */
import { onAuthorize, onDeauthorize } from './events'

/** Your refresh token mechanism, returning a promise that resolves to the new access tokenFunction (library does not care about your method of persisting tokens) */
const refresh = () => {
  const init =  { method: 'POST'
                , headers: { 'Content-Type': `application/x-www-form-urlencoded` }
                , body: `refresh_token=${localStorage.refresh_token}&grant_type=refresh_token`
                }
  return fetch('/oauth/token', init)
    .then(res => res.json())
    .then(({ token_type, access_token, expires_in, refresh_token }) => {
      localStorage.access_token = access_token
      localStorage.refresh_token = refresh_token
      return access_token
    })
}

/** You supply a leadSeconds number or function that generates a number of seconds that the refresh should occur prior to the access token expiring */
const leadSeconds = () => {
  /** Generate random additional seconds (up to 30 in this case) to append to the lead time to ensure multiple clients dont schedule simultaneous refresh */
  const jitter = Math.floor(Math.random() * 30)

  /** Schedule autorefresh to occur 60 to 90 seconds prior to token expiration */
  return 60 + jitter
}

let start = autorefresh({ refresh, leadSeconds })
let cancel = () => {}
onAuthorize(access_token => {
  cancel()
  cancel = start(access_token)
})

onDeauthorize(() => cancel())

отказ от ответственности: я хранитель

Как насчет такого подхода:

  • для каждого запроса клиента сервер сравнивает время истечения маркера с (currentTime-lastAccessTime)
  • Если expirationTime , он изменяет последнее lastAccessedTime на currentTime.
  • в случае бездействия в браузере в течение времени, превышающего время экспирации или в случае, если окно браузера было закрыто и expirationTime > (currentTime - lastAccessedTime), а затем сервер может истечь токен и попросить пользователя снова войти в систему.

в этом случае нам не требуется дополнительная конечная точка для обновления токена. Был бы признателен за любую обратную связь.

я решил эту проблему, добавив переменную в данные токена:

softexp - I set this to 5 mins (300 seconds)

Я expiresIn опция для моего желаемого времени, прежде чем пользователь будет вынужден снова войти в систему. Мой установлен на 30 минут. Это должно быть больше, чем значение softexp.

когда мое клиентское приложение отправляет запрос на сервер API (где требуется токен, например. страница списка клиентов), сервер проверяет, является ли отправленный маркер все еще действительным или нет на основе его первоначального срока действия (expiresIn) значение. Если это неверно, сервер ответит со статусом, определенным для этой ошибки, например. INVALID_TOKEN.

если токен все еще действителен на основе expiredIn значение, но оно уже превысило softexp значение, сервер будет отвечать с отдельным статусом для этой ошибки, например. EXPIRED_TOKEN:

(Math.floor(Date.now() / 1000) > decoded.softexp)

на стороне клиента, если он получил EXPIRED_TOKEN ответ, он должен автоматически обновить маркер, отправив запрос на обновление на сервер. Это прозрачно к пользователь и автоматически заботится о клиентском приложении.

метод обновления на сервере должен проверить, действителен ли токен:

jwt.verify(token, secret, (err, decoded) => {})

сервер откажется продлевать токены, если он не выполнил вышеуказанный метод.