В этом проекте я создал сервис jwt-аутентификации по этому тз:
Необходимо реализовать часть сервиса аутентификации. Сервис должен предоставлять четыре конечные точки API:
- на получение пары токенов (access и refresh) для пользователя с идентификатором (GUID) указанным в параметре запроса;
- на обновление пары токенов;
- на получение GUID текущего пользователя (роут должен быть защищен);
- на деавторизацию пользователя (поле выполнения этого запроса с
access
токеном, пользователю больше не должен быть доступен роут на получение его GUID и операция обновления токенов).
Требования к access
токену:
- Формат токена - JWT,
- Алгоритм для подписи токена - SHA512,
- Хранить токен в базе строго запрещено.
Требования к refresh
токену
- Формат токена - произвольный.
- Передаваться токен должен только в формате
base64
. - Хранить токен в базе строго в виде
bcrypt
хеша. - Токен должен быть защищен от повторного использования.
- Токен должен быть защищен от изменений на стороне клиента.
Требования к операции refresh
- Операцию
refresh
можно выполнить только той парой токенов, которая была выдана вместе. - Необходимо запретить операцию обновления токенов при изменении
User-Agent
. При этом, после неудачной попытки выполнения операции, неоходимо деавторизовать пользователя, который попытался выполнить обновление токенов. - При попытке обновления токенов с нового IP необходимо отправить POST-запрос на заданный
webhook
с информацией о попытке входа со стороннего IP. Запрещать операцию в данном случае не нужно.
Увидеть ручки и их краткое описание можно в api/openapi.yaml
Я реализовал jwt по описанию с википедии, а вот для refresh я придумал кое-что своё, чтобы обеспечить работу пары токенов только вместе я убрал из refresh header, а payload заменил на sha512 кеш access, чтобы защитить ключ от изменения я добавил к нему подпись, а потом перевёл в base64 (не в base64url, т.к. в условии говорилось именно про base64)
Также в условии ничего не говорилось про хранение ключей, но я посчитал, что если уж мы храним данные в бд, то нам будет полезно сохранять не только сессии, но и ключи, чтобы продолжать с ними работать
Для демонстрации работы у меня написан интеграционный тест (без проверки вебхуков и проверки user-agent), но для его работы нужно откомментировать несколько строчек в docker-compose.yml и поменять хост в конфиге с db на localhost
(я написал его, как и половину Makefile для себя, поэтому оно не обязано работать по одной кнопке)