Skip to content

Instantly share code, notes, and snippets.

@apache888
Forked from zmts/tokens.md
Created September 29, 2017 07:54
Show Gist options
  • Select an option

  • Save apache888/634b95ce4529fb1b0c20a0506e0fb00d to your computer and use it in GitHub Desktop.

Select an option

Save apache888/634b95ce4529fb1b0c20a0506e0fb00d to your computer and use it in GitHub Desktop.
Про токены, JSON Web Tokens (JWT), аутентификацию и авторизацию

Token-Based Authentication(JWT)

Основы:

Аутентификация(authentication, от греч. αὐθεντικός [authentikos] – реальный, подлинный; от αὐθέντης [authentes] – автор) - это процесс проверки учётных данных пользователя (логин/пароль). Проверка подлинности пользователя путём сравнения введённого им пароля с паролем, сохранённым в базе данных пользователей;

Авторизация(authorization — разрешение, уполномочивание) - это проверка прав пользователя на доступ к определенным ресурсам.

Например после аутентификации юзер sasha получает право обращатся и получать от ресурса "super.com/vip" некие данные. Во время обращения юзера sasha к ресурсу vip система авторизации проверит имеет ли право юзер обращатся к этому ресурсу (проще говоря переходить по неким разрешенным ссылкам)

  1. Юзер c емайлом sasha_gmail.com успешно прошел аутентификацию
  2. Сервер посмотрел в БД какая роль у юзера
  3. Сервер сгенерил юзеру токен с указанной ролью
  4. Юзер заходит на некий ресурс
  5. Сервер смотрит на права(роль) юзера в токене и соотвественно пропускает или отсекает запрос

Собственно п.5 и есть процесс авторизации.

Дабы не путатся с понятиями Authentication/Authorization можно использовать псевдонимы checkPassword/checkRole(я так сделал в своей API)

JSON Web Token (JWT) — содержит три блока, разделенных точками: заголовок, набор полей (claims) и подпись. Первые два блока представлены в JSON-формате и дополнительно закодированы в формат base64. Набор полей содержит произвольные пары имя/значения, притом стандарт JWT определяет несколько зарезервированных имен (iss, aud, exp и другие). Подпись может генерироваться при помощи и симметричных алгоритмов шифрования, и асимметричных. Кроме того, существует отдельный стандарт, отписывающий формат зашифрованного JWT-токена.

Пример подписанного JWT токена (после декодирования 1 и 2 блоков):

{ «alg»: «HS256», «typ»: «JWT» }.{ «iss»: «auth.myservice.com», «aud»: «myservice.com», «exp»: «1435937883», «userName»: «John Smith», «userRole»: «Admin» }.S9Zs/8/uEGGTVVtLggFTizCsMtwOJnRhjaQ2BMUQhcY

Токены предоставляют собой средство авторизации для каждого запроса от клиента к серверу. Токены генерируются на сервере основываясь на секретном ключе(который хранится на сервере) и payload'e. Токен в итоге хранится на клиенте и используется при необходимости авторизации како-го либо запроса. Такое решение отлично подходит при разаработке SPA.

access token - используется для авторизации запросов и хранения дополнительной информации о пользователе (аля user_id, user_role или еще что либо, эту информацию также называет payload)

refresh token - выдается сервером по результам успешной аутентификации и используется для получения нового access token'a и обновления refresh token'a

Каждый токен имеет свой срок жизни, например access: 30мин, refresh: 60дней

Поскольку токены это не зашифрованная информация крайне не рекомендуется хранить в них такую информацию как пароли

Схема использования токенов:

  1. Пользователь логинится в приложении, передавая логин/пароль на сервер
  2. Сервер проверят подлинность логина/пароля, в случае удачи генерирует и отправляет клиенту два токена(access и refresh token)
  3. Приложение сохраняет токены и использует access token для последующией авторизации запросов
  4. Когда время жизни access token'а закончилось и он приходит на сервер с истекшим сроком жизни, сервер отвечает "Token expired", далее клиент использует refresh token чтобы обновить ОБА токена и продолжить использовать новый access token

Ключевой момент что в момент рефреша то есть обновления access token'a обновляются ОБА токена. Но как же refresh token может сам себя обновить, он ведь создается только после успешной аунтефикации ? refresh token в момент рефреша сравнивает себя с тем refresh token'ом который лежит в БД и вслучае успеха, а также если у него не истек срок, система рефрешит токены. Внимание при обновлении refresh token продливается также и его срок жизни.

Возникает вопрос зачем refresh token'y срок жизни, если он обновляется каждый раз при обновлении access token'a ? Это сделано на случай если юзер будет в офлайне более 60 дней, тогда прийдется заново вбить логин/пароль.

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

Схема рефреша токенов:

  1. Клиент отправляет запрос с интекшим access token'ом
  2. Сервер отвечает { success: false, accessTokenError: 'TokenExpiredError' }
  3. Клиент отправляет на auth/refresh-token URL refresh token
  4. Сервер сравнивает refresh token от клиента с refresh token'ом лежащим в БД
  5. Сервер проверяет действителен ли срок действия refresh token'а
  6. В случае успеха сервер: 6.1. Пересоздает и записывает refresh token в БД 6.2. Создает новый access token 6.3. Отправляет оба токена клиенту
  7. Клиент пробует повторить запрос к API c новым access token'ом

В случае кражи(обоих токенов):

  1. Хакер воспользовался access token'ом
  2. Закончилось время жизни access token'на
  3. Клиент хакера отправляет refresh token
  4. Хакер получает новую пару токенов
  5. На сервере создается новая пара токенов("от хакера")
  6. Юзер пробует зайти на сервер >> обнаруживается что токены невалидны
  7. Сервер перенаправляет юзера на форму аутентификации
  8. Юзер вводит логин/пароль
  9. Создается новая пара токенов >> пара токенов "от хакера" становится не валидна

Проблема: Поскольку refresh token продлевает срок своей жизни каждый раз при рефреше токенов >> хакер пользуется токенами до тех пор пока юзер не залогинится.

В случае паранои:

  • хранить список валидных IP, deviceID, fingerprint браузера, генерить рандомный randomUserID
  • дополнительно шифровать токены (в nodejs например crypt >> aes-256)

Чтиво:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment