Skip to content

Instantly share code, notes, and snippets.

@zmts
Last active May 16, 2022
Embed
What would you like to do?
Про токены, JSON Web Tokens (JWT), аутентификацию и авторизацию. Token-Based Authentication

Про токены, JSON Web Tokens (JWT), аутентификацию и авторизацию. Token-Based Authentication

Last major update: 25.08.2020

  • Что такое авторизация/аутентификация
  • Где хранить токены
  • Как ставить куки ?
  • Процесс логина
  • Процесс рефреш токенов
  • Кража токенов/Механизм контроля токенов
  • Зачем все это ? JWT vs Cookie sessions

Основа:

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

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

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

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

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

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

JSON Web Token (JWT) — содержит три блока, разделенных точками: заголовок(header), набор полей (payload) и сигнатуру. Первые два блока представлены в 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.

При попытке хакером подменить данные в header'ре или payload'е, токен станет не валидным, поскольку сигнатура не будет соответствовать изначальным значениям. А возможность сгенерировать новую сигнатуру у хакера отсутствует, поскольку секретный ключ для зашифровки лежит на сервере.

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

refresh token - выдается сервером по результам успешной аутентификации и используется для получения новой пары access/refresh токенов. Храним исключительно в httpOnly куке.

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

Поскольку токены(а данном случае access) это не зашифрованная информация крайне не рекомендуется хранить в них какую либо sensitive data (passwords, payment credentials, etc...)

Роль рефреш токенов и зачем их хранить в БД. Рефреш на сервере хранится для учета доступа и инвалидации краденых токенов. Таким образом сервер наверняка знает о клиентах которым стоит доверять(кому позволено авторизоваться). Если не хранить рефреш токен в БД то велика вероятность того что токены будут бесконтрольно гулять по рукам злоумышленников. Для отслеживания которых нам придется заводить черный список и периодически чистить его от просроченных. В место этого мы храним лимитированный список белых токенов для каждого юзера отдельно и в случае кражи у нас уже есть механизм противодействия(описано ниже).

Как ставить куки ?

Для того что бы refreshToken кука была успешно уставленна и отправлена браузером, адреса эндпоинтов аутентификации(/api/auth/login, /api/auth/refresh-tokens, /api/auth/logout) должны располагася в доменном пространстве сайта. Тоесть для домена super.com на сервере ставим куку с такими опциями:

{
    domain: '.super.com',
    path: '/api/auth'
}

Таким образом кука установится в браузер и прийдет на все эндпоинты по адресу super.com/api/auth/<any-path>

Если у нас монолит и за аутентификацию отвечает один и тот-же API, тут проблем не должно быть. Но если за аутентификацию отвечает отдельный микросервис, прячем его средствами nginx по выше указанному пути (super.com/api/auth).

# пример настройки nginx конфига(только основые настройки)
server {
    listen 80;
    server_name super.com;
    # SPA/Front-end
    location / {
        try_files $uri /index.html;
        root /var/www/frontend/dist;
        index index.html;
    }
    # Main API
    location /api {
        proxy_pass http://111.111.111.111:7000;
    }
    # Auth API
    location /api/auth {
        proxy_redirect http://222.222.222.222:7000   /auth/;
        proxy_pass http://222.222.222.222:7000;
    }
}

Логин, создание сессии/токенов (api/auth/login):

  1. Пользователь логинится в приложении, передавая логин/пароль и fingerprint браузера (ну или некий иной уникальный идентификатор устройства если это не браузер)
  2. Сервер проверят подлинность логина/пароля
  3. В случае удачи создает и записывает сессию в БД { userId: uuid, refreshToken: uuid, expiresIn: int, fingerprint: string, ... } (схема таблицы ниже)
  4. Создает access token
  5. Отправляет клиенту access и refresh token uuid (взятый из выше созданной сессии)
Set-Cookie: refreshToken='c84f18a2-c6c7-4850-be15-93f9cbaef3b3'; HttpOnly // для браузера
{
  body: { 
    accessToken: 'eyJhbGciOiJIUzUxMiIsI...',
    refreshToken: 'c84f18a2-c6c7-4850-be15-93f9cbaef3b3' // для мобильных приложений
  }
}
  1. Клиент сохраняет токены(access в памяти приложения, refresh сетится как кука автоматом)

На что нужно обратить внимание при установке refresh куки:

  • maxAge куки ставим равную expiresIn из выше созданной сессии
  • В path ставим корневой роут auth контроллера (/api/auth) это важно, таким образом токен получат только те хендлеры которым он нужен(/api/auth/logout и /api/auth/rerfesh-tokens), остальные обойдутся(нечего зря почём отправлять sensitive data).

Стоит заметить, что процесс добавления сессии в таблицу должен имеет свои меры безопасности. При добавлении стоит проверять сколько рефреш-сессий всего есть у юзера и, если их слишком много или юзер конектится одновременно из нескольких подсетей, стоит предпринять меры. Имплементируя данную проверку, я проверяю только что бы юзер имел максимум до 5 одновременных рефреш-сессий максимум, и при попытке установить следующую удаляю предыдущие. Все остальные проверки на ваше усмотрение в зависимости от задачи.

Таким образом если юзер залогинился на пяти устройствах, рефреш токены будут постоянно обновляться и все счастливы. Но если с аккаунтом юзера начнут производить подозрительные действия(попытаются залогинится более чем на 5'ти устройствах) система сбросит все сессии(рефреш токены) кроме последней.

Перед каждым запросом клиент предварительно проверяет время жизни access token'а (да берем expiresIn прямо из JWT в клиентском приложении) и если оно истекло шлет запрос на обновление токенов. Для большей уверенности можем обновлять токены на несколько секунд раньше. То есть кейс когда API получит истекший access токен практически исключен.

Что такое fingerprint ? Это инструмент отслеживания браузера вне зависимости от желания пользователя быть идентифицированным. Это хеш сгенерированный js'ом на базе неких уникальных параметров/компонентов браузера. Преимущество fingerprint'a в том что он нигде персистентно не хранится и генерируется только в момент логина и рефреша.

В случае если клиент не браузер, а мобильное приложение, в качестве fingerprint используем любую уникальную строку(тот же uuid) персистентно хранящуюся на устройстве.

Рефреш токенов (api/auth/refresh-tokens):

Для использования возможности аутентификации на более чем одном девайсе необходимо хранить все рефреш токены по каждому юзеру. Я храню это список в PostgreSQL таблице(а надо бы в Redis'е). В процессе каждого логина создается запись с IP/Fingerprint и другой мета информацией, так званая рефреш-сессия.

CREATE TABLE refreshSessions (
    "id" SERIAL PRIMARY KEY,
    "userId" uuid REFERENCES users(id) ON DELETE CASCADE,
    "refreshToken" uuid NOT NULL,
    "ua" character varying(200) NOT NULL, /* user-agent */
    "fingerprint" character varying(200) NOT NULL,
    "ip" character varying(15) NOT NULL,
    "expiresIn" bigint NOT NULL,
    "createdAt" timestamp with time zone NOT NULL DEFAULT now()
);
  1. Клиент(фронтенд) проверяет перед запросом не истекло ли время жизни access token'на
  2. Если истекло клиент делает запрос на POST auth/refresh-tokens { fingerprint: string } в body и соответственно refreshToken куку.
  3. Сервер получает запись рефреш-сессии по UUID'у рефреш токена
  4. Сохраняет текущую рефреш-сессию в переменную и удаляет ее из таблицы
  5. Проверяет текущую рефреш-сессию:
    1. Не истекло ли время жизни
    2. На соответствие старого fingerprint'a полученного из текущей рефреш-сессии с новым полученным из тела запроса
  6. В случае негативного результата бросает ошибку TOKEN_EXPIRED/INVALID_REFRESH_SESSION
  7. В случае успеха создает новую рефреш-сессию и записывает ее в БД
  8. Создает access token
  9. Отправляет клиенту access и refresh token uuid (взятый из выше созданной рефреш-сессии)
Set-Cookie: refreshToken='c84f18a2-c6c7-4850-be15-93f9cbaef3b3'; HttpOnly // для браузера
{
  body: { 
    accessToken: 'eyJhbGciOiJIUzUxMiIsI...',
    refreshToken: 'c84f18a2-c6c7-4850-be15-93f9cbaef3b3' // для мобильных приложений
  }
}

Tip: Для отправки запроса с куками для axios есть опция { withCredentials: true }

Ключевой момент:

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

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

В случае кражи access токена и refresh куки:

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

В случае кражи access токена, refresh куки и fingerprint'а:

Стащить все авторизационные данные это не из легких задач, но все же допустим этот кейс как крайний и наиболее неудобный с точки зрения UX (без примера в кодовой базе supra-api-nodejs).

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

  • Хранить IP или Subnet залогиненного клиента
  1. Хакер воспользовался access token'ом
  2. Закончилось время жизни access token'на
  3. Хакер отправляет refresh куку и fingerprint
  4. Сервер проверяет IP хакера, хакер идет лесом

UX минус: нужно логинится с каждого нового IP.

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

UX минус: в каждом случае когда сервер не будет находить рефреш токен - будут сбрасиватся все сессии юзера на всех устройствах.

Зачем все это ? JWT vs Cookie sessions

Зачем этот весь геморой ? Почему не юзать старые добрые cookie sessions ? Чем не угодили куки ?

  • Куки подвержены CSRF: https://habr.com/ru/company/oleg-bunin/blog/412855 https://www.youtube.com/watch?v=x5AuK_IbJlg
  • Нативыным приложениям для сматфонов удобнее работать с токенами. Да есть хаки для работы с куки, но это не нативная поддержка
  • Куки в микросерисной архитектуре использовать не вариант. Напомню зачастую микросервисы раскиданы на разных доменах, а куки не поддерживают кросc-доменные запросы
  • В микросерисной архитектуре JWT позволяет каждому сервису независимо от сервера авторизации верифицировать access токен (через публичный ключ)
  • При использовании cookie sessions программист зачастую надеется на то, что предоставил фреймворк и оставляет как есть
  • При использовании jwt мы видим проблему с безопасностью и стараемся предусмотреть механизмы контроля в случае каржи авторизационных данных. При использовании cookie сессий программист зачастую даже не задумывается что сессия может быть скомпрометирована
  • На каждом запросе использование JWT избавляет бекенд от одного запроса в БД(или кеш) за данными пользователя(userId, email, etc.)

В итоге:

  • access токены храним исключительно в памяти клиентского приложения. Не в глобально доступной переменной аля window.accessToken а в замыкании
  • refresh токен храним исключительно в httpOnly куке
  • Механизмы контроля при угоне sensitive data в наличии
  • Взяли лучшее из обеих технологий, максимально обезопасились от CSRF/XSS
  • Добавьте в компанию ко всему CSP заголовки и SameSite=Strict флаг для кук и ждите прихода злодеев

p.s. Каждой задаче свой подход. Юзайте в небольших/средних монолитах cookie sessions и не парьтесь. Ну или на ваш вкус :)


Имплементация:

Front-end:

Back-end:

Info:

And why JWT is bad


Комментарии периодически подчищаются

@zmts
Copy link
Author

zmts commented Mar 31, 2021

@RuslanTheKing ответ на ваш вопрос в первой части статьи

@denyrt
Copy link

denyrt commented Apr 16, 2021

Насчёт безопасности.
Проверка айпи адреса кроме рефреш токена и прочих метаданных обезопасит юзера, но если он постоянно прокся использует.
Есть вариант с настройками Security для юзера которые он может менять только имея рефреш токен и все метаданные, а ещо лучше как в гитхабе, после авторизации повторной (чтобы хакер не мог их менять если он угнал access_token). А далее юзер просто опционально может отменить проверку по айпи чтобы он спокойно через прокси сидел себе и это будет как раз таки на его страх и риск.

@denyrt
Copy link

denyrt commented Apr 16, 2021

Насчёт безопасности.
Проверка айпи адреса кроме рефреш токена и прочих метаданных обезопасит юзера, но если он постоянно прокся использует.
Есть вариант с настройками Security для юзера которые он может менять только имея рефреш токен и все метаданные, а ещо лучше как в гитхабе, после авторизации повторной (чтобы хакер не мог их менять если он угнал access_token). А далее юзер просто опционально может отменить проверку по айпи чтобы он спокойно через прокси сидел себе и это будет как раз таки на его страх и риск.

Ну и конечно же список активных сессий для юзера держать которые он так же может смотреть. Если делает логаут. То для текущего рефреш токен или сразу для всех. Можно так же сделать возможность для kill selected session, но её вместе с авторизацией как-то сделать, чтобы хакер не мог угнать токен и офнуть сессию владельца аккаунта. Ну в общем, если сделать некоторые параметры опциональными то проблема проксей и прочего спокойно решается. Та и зависит от сервиса, если у меня пет проект в котором ну никак не может быть у юзера информации которая полезна хакеру то и оверинженирить тоже не стоит. Если речь идёт о плажетах и прочее то конечно лучше подумать более детально над всеми существующими способами аутентификации и смотреть не только в сторону jwt. Так же функционал системы можно разделить на разные части и чтобы использовать более защищенные функции то какую-то доп. аутентификацию делать, типа двухфакторки или что-то ещо придумать.

@zmts
Copy link
Author

zmts commented Apr 23, 2021

@fix-it

Здесь т.к. рефреш-сессии в бд нет и максимально точный userId из него получить нельзя, то откуда узнать userId для которого надо удалить все рефреш-сессии из бд?

Да ты прав. Спасибо правильно подметил. Решение тому есть. Нет времени на реализацию. Обязательно потом отпишу и имплементирую.

Выходит userId можно взять из payload access токена (с валидной подписью).

Рефреш токана проходит только по рефреш токену. Ответ - нет, нельзя.

Нормально ли пользоваться payload'ом access токена в этом случае?

Нет

@zmts
Copy link
Author

zmts commented Apr 23, 2021

@denyrt

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

Можно чуть подробнее про Security и про гитхаб. Нужно больше подробностей

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

Тоже очень здравая идея. Можно будет потом добавить. Спасибо!

@denyrt
Copy link

denyrt commented Apr 23, 2021

@denyrt

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

Можно чуть подробнее про Security и про гитхаб. Нужно больше подробностей

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

Тоже очень здравая идея. Можно будет потом добавить. Спасибо!

Ну например ввести пароль для некоторых действий. Например я хочу сделать killsession для сессии на телефоне, находясь за ПК. И как раз пароль добавит безопасности т.к. если хакер знает пароль то и красть сессию ни к чему. Так что при краже сессии нормальный вариант. Ну или к авторизированному юзеру добавить Policy дополнительный. Например хочу доступ к более защищенным настройкам и чтобы убедиться что я не хакер, ввожу пароль и получаю дополнительный токен на 10 минут который привязан к айпи. Что-то такое в общем.

А насчёт гитхаба, там есть вкладка в настройках репозитория и когда пытаешься туда войти, то просит ввести пароль свой от аккаунта. Repositoty -> Settings -> Manage Access вроде как.

@denyrt
Copy link

denyrt commented Apr 23, 2021

Ну или каким-то боком двухфакторку прикрутить к всему этому. В общем всякое можно придумать, стоит от задачи отталкиваться. Можно даже несколько Policy разных использовать и как-то все комбинировать если нужно.

@mvalitov
Copy link

mvalitov commented May 7, 2021

А как быть с тем, что юзер сделал logout, access token у него протухнет через небольшое время, а вот refresh token то останется в таблице ещё на 60 дней. Т.е будет занимать место при проверке условия "разрешаем одновременный логин только с пяти устройств". Может при logout надо удалить refresh токены с таким же fingerprint?

@zmts
Copy link
Author

zmts commented May 7, 2021

@mvalitov так и есть рефреш токен удаляеться https://github.com/zmts/supra-api-nodejs/blob/master/modules/auth/actions/LogoutAction.js

По логике у одного рефреш токена может быть один уникальный fingerprint. Для надежности можешь сделать еще удаление по fingerprint up to u

@dcompet03
Copy link

dcompet03 commented May 9, 2021

Спасибо за отличную статью!
Есть отдельные рекомендации где хранить рефреш токены и как их отправлять в случае с мобильными приложениями?

@zmts
Copy link
Author

zmts commented May 11, 2021

@dcompet03 в мобайле не спец :)

@RuslanTheKing
Copy link

RuslanTheKing commented May 12, 2021

В мобильном приложении access token храним в памяти приложения, так при выгрузке приложения из памяти, или перезагрузке access token теряется. А refresh token храним либо в текстовом файле в директории приложения, либо в SQLite базе данных которое использует ваше приложения. В первом случае, доступ к файлу будет иметь только ваше приложение, если только нет ROOT, во втором случае SQLite имеет возможность шифрования данных.
По деректория для IOS можно почитать тут https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html
При первом запуске приложения или при рестарте проверяем наличие refresh token - а в текстовом файле, или в бд приложения, если он есть, то пытаемся получить access token. При успешном получении открываем главный интерфейс приложения, при отсутствии refresh token открываем интерфейс аутентификации/регистрации.

@dcompet03
Copy link

dcompet03 commented May 13, 2021

@RuslanTheKing спасибо большое

@darkdealnet
Copy link

darkdealnet commented May 13, 2021

то есть хакер не может скопировать из токена fingerprint и вставить его в refresh схему? может же...
fingerprint не нужно хранить в payload. Fingerprint нужно хранить в базе и передавать его только при авторизации/регистрации

@zartdinov
Copy link

zartdinov commented May 13, 2021

@austrokhart CSRF подразумевает под собой отправку кукис, а api/auth/refresh-tokens помимо кукис требует также отпечаток пальцев браузера в body. Так что нет не подвержен.

Вообще, вроде в /api/auth/logout и /api/auth/rerfesh-tokens есть момент с CSRF, но ничего страшного, пользователя просто будет разлогинивать, поэтому скорей всего лучше обрабатывать только POST-методы чтобы всякие <img src=".../logout" /> не срабатывали, но это мелочь

@tz4678
Copy link

tz4678 commented Jun 9, 2021

у пользователя просто должно храниться время последнего изменения пароля, если токен создан РАНЕЕ чем был изменен пароль, то он НЕ ВАЛИДЕН.

@zmts
Copy link
Author

zmts commented Jun 13, 2021

у пользователя просто должно храниться время последнего изменения пароля, если токен создан РАНЕЕ чем был изменен пароль, то он НЕ ВАЛИДЕН.

После того как юзер меняет пароль система(API) должна вылогинить юзера и удалить все его сессии

@zmts
Copy link
Author

zmts commented Jun 13, 2021

Сделал подобные refresh сессии на PostgreSQL. Есть ли смысл переделать под Redis? Если да, то как возможно представить таблицу refreshSessions описанную в статье? Как я понимаю, лишь одной хеш-таблицей обойтись не получится.

@fickmann В статье так и написано юзеть Redis или еще что-то быстро действующее. Храните как хотите это вне контекста данной статьи.

p.s.
комменты зачищены, тролли забанены

@zmts
Copy link
Author

zmts commented Jun 13, 2021

то есть хакер не может скопировать из токена fingerprint и вставить его в refresh схему? может же...

@darkdealnet В токене не хранится fingerprint. Он генерится в момент логина. В статье это написано.

@brutalsignature
Copy link

brutalsignature commented Jun 20, 2021

@zmts Правильно ли я понял, что если у куки для рефреш сесии закончилось время, то в таблице сессий, запись для которой выпущена была эта кука, просто останется в таблице и будет лежать там всегда?

В смысле когда пропадает кука для рефреша мы и рефрешить посути уже не можем, нам только логиниться заново. Соответсвтенно в таблице, строка где есть refreshToken равный пропашей куки так и останется навсегда.

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

@kochetkov-ma
Copy link

kochetkov-ma commented Jun 23, 2021

Идёт 2021 год, а люди пишут свои реализации авторизации на JWT... Начиная со средних проектов нужно юзать сервер авторизации oauth2 / openid, например бесплатный Keycloak. Свои попытки реализации всегда будут менее безопасные.

@brutalsignature
Copy link

brutalsignature commented Jun 23, 2021

@kochetkov-ma
я сначала хотел юзать OAuth2, но я не разобрался до конца, например как сделать аутентификацию не только через гугл или фейсбук но и через email + password, то есть локально аутентифицирватся через OAuth2, но я так и не разобрался....

я видел OAuth0, но там сервера платные в определенныех условиях. если есть возможность сделать так через OAuth2 то я бы с радостью так сделал. может вы подскажете где почитать или объясните как это делается, более понятным языком? если конечно вам не сложно. спасибо!

@RuslanTheKing
Copy link

RuslanTheKing commented Jun 23, 2021

@kochetkov-ma
я сначала хотел юзать OAuth2, но я не разобрался до конца, например как сделать аутентификацию не только через гугл или фейсбук но и через email + password, то есть локально аутентифицирватся через OAuth2, но я так и не разобрался....

я видел OAuth0, но там сервера платные в определенныех условиях. если есть возможность сделать так через OAuth2 то я бы с радостью так сделал. может вы подскажете где почитать или объясните как это делается, более понятным языком? если конечно вам не сложно. спасибо!

Совет: решайте вашу задачу на основе той технологии, в которой сейчас разбираетесь!
Если бы инженеры хотели бы сделать мерседес таким, какой он есть сейчас, мы бы не увидели ни одного мерседеса. Сделайте так как можете, потом разберетесь в OAuth2, адаптируете и доработаете. Главное не стойте на месте, делайте!

@sklyar
Copy link

sklyar commented Jul 22, 2021

При обновлении страницы или при первом "заходе" как мы узнаем что пользователь аутентифицирован?
Пытаться сходить в api/auth/refresh-tokens?

@zmts
Copy link
Author

zmts commented Jul 26, 2021

При обновлении страницы или при первом "заходе" как мы узнаем что пользователь аутентифицирован?
Пытаться сходить в api/auth/refresh-tokens?

Да

@r0hack
Copy link

r0hack commented Dec 9, 2021

"Куки в микросерисной архитектуре использовать не вариант. Напомню зачастую микросервисы раскиданы на разных доменах, а куки не поддерживают кросc-доменные запросы" - А как же CORS ?

@kbondarets
Copy link

kbondarets commented Feb 17, 2022

Еще никогда не встречал такого понятного описания, автору спасибо!

@sklyar
Copy link

sklyar commented Mar 1, 2022

@r0hack
"Куки в микросерисной архитектуре использовать не вариант. Напомню зачастую микросервисы раскиданы на разных доменах, а куки не поддерживают кросc-доменные запросы" - А как же CORS ?

Обычно есть API Gateway, который распределяет запросы между внутренними сервисами.

@Seterium
Copy link

Seterium commented Mar 20, 2022

Автору большое спасибо за статью, однако у меня есть вопрос:

  1. Хакер воспользовался access token'ом
  2. Закончилось время жизни access token'на

Как можно обезопасить пользователя в этот период жизни access_token'a? Довольно маловероятная ситуация что токен украли и тут же смогли им воспользоваться, но все же. Если refresh_token можно отозвать (удалить из БД) то access_token ревокнуть никак нельзя (в бд не хранится ведь).

@Devoter
Copy link

Devoter commented Apr 12, 2022

Как можно обезопасить пользователя в этот период жизни access_token'a? Довольно маловероятная ситуация что токен украли и тут же смогли им воспользоваться, но все же. Если refresh_token можно отозвать (удалить из БД) то access_token ревокнуть никак нельзя (в бд не хранится ведь).

Можно. Например, access token может хранить в payload id сессии, этот id добавляем в black list на время жизни превышающее время жизни access token, при каждом запросе проверяем список. Другой вариант - хранить все сессии в том же редисе и проверять на соответствие сессии, правда, тогда это уже совсем ничем не будет отличаться от классической схемы.

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