Note
Используемые технологии:
- Go
- JWT
- PostgreSQL
- Docker
- Medods - Technical Assessment
Написать часть сервиса аутентификации.
Два REST маршрута:
Первый маршрут выдает пару Access, Refresh токенов для пользователя с идентификатором (GUID) указанным в параметре запроса
POST http://localhost:8080/api/v1/auth/login/{GUID}
Пример запроса POST http://localhost:8080/api/v1/auth/login/cec24247-497f-48f2-8a93-4ccdc2fdd65b
Тело
(пусто)
Пример ответа
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2OTk5MTcsImlhdCI6MTczMzY5OTYxNywiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiMDBlMWFhNGMtN2YxMi00Y2Y4LWIyNGEtYmU1YTM2MjgyNTZjIn0.s9IfgsetfB1HzArmbAz3Rlh2Z4sGA5u2spcB3TT4Q9DFlDFLu9v7R-_kmHfeW1ugEwUnhpOQeja3FDNeHzuMIg",
"refreshToken": "AOGqTH8STPiySr5aNiglbKwSAAE="
}
Реализация ./auth/internal/chi/authcontroller.go#L285
POST http://localhost:8080/api/v1/auth/refresh
Пример запроса POST http://localhost:8080/api/v1/auth/refresh
Тело
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2OTk5MTcsImlhdCI6MTczMzY5OTYxNywiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiMDBlMWFhNGMtN2YxMi00Y2Y4LWIyNGEtYmU1YTM2MjgyNTZjIn0.s9IfgsetfB1HzArmbAz3Rlh2Z4sGA5u2spcB3TT4Q9DFlDFLu9v7R-_kmHfeW1ugEwUnhpOQeja3FDNeHzuMIg",
"refreshToken": "AOGqTH8STPiySr5aNiglbKwSAAE="
}
Пример ответа
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2OTk5ODIsImlhdCI6MTczMzY5OTY4MiwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiMDNjYzA2ZDMtNzc4OS00NjQ2LWIzNzYtOTU1NjU4NmFiNzcxIn0.Xx_9FPHK4nsUyS18F1t5bM56EbLPZ4tE_XWQtvMir6KEqm5GQKRgGoKuflZBJwfsNoKmzxGvEXYZvFGeykkZpQ",
"refreshToken": "A8wG03eJRkazdpVWWGq3cawSAAE="
}
Реализация ./auth/internal/chi/authcontroller.go#L302
Access токен
- Тип JWT
- Aлгоритм SHA512
- Для подписывания JWT токена используется алгоритм HMAC-SHA512
- Хранить в базе строго запрещено
Refresh токен
-
Тип произвольный
-
Формат передачи base64
- С учетом лимита на создание bcrypt хеша в 72 символа, максимальное количество байт которое сможет хранить в себе base64 строка (72 * 3/4) 54 байта
- На идентификатор (UUID) уходит 16 байт
- На IP адрес (
netip.Addr
) уходит используется 4 или 16 байт в зависимости от версии IPv4 или IPv6
- Реализация ./auth/internal/jwt/jwtservice.go#L62
- С учетом лимита на создание bcrypt хеша в 72 символа, максимальное количество байт которое сможет хранить в себе base64 строка (72 * 3/4) 54 байта
-
Хранится в базе исключительно в виде bcrypt хеша
- Из факта соления через bcrypt следует, что при Refresh операции нельзя найти запись в бд исключительно по Refresh токену
- Значит, нужно дополнительно хранить какой-нибудь идентификатор в Payload
- Создание экземпляра для хранения в бд ./auth/internal/chi/authcontroller.go#L471
- Из факта соления через bcrypt следует, что при Refresh операции нельзя найти запись в бд исключительно по Refresh токену
-
Должен быть защищен от изменения на стороне клиента
- Из факта хеширования через bcrypt следует, что подписывать токен (как в JWT) не нужно - проверка целостности осуществляется через bcrypt
- Во время Referesh операции bcrypt хеш передаваемого Refresh токена сравнивается с хешом в базе данных
- Из факта хеширования через bcrypt следует, что подписывать токен (как в JWT) не нужно - проверка целостности осуществляется через bcrypt
-
Должен быть защищен от попыток повторного использования
- У хранимых в базе данных Refresh токенов есть поле
Active
, на котором висит ограничение "у пользователя может быть только один активный токен"- Создание нового токена требует отзыва предыдущих
- У хранимых в базе данных Refresh токенов есть поле
-
Access, Refresh токены обоюдно связаны, Refresh операцию для Access токена можно выполнить только тем Refresh токеном который был выдан вместе с ним
- Во время Referesh операции у Access и Refresh токенов проверяется одинаковый ли у них jti
-
Payload токенов должен содержать сведения об ip адресе клиента, которому он был выдан
- В обоих токенах есть поле для ip заполняемое по данным из
chi/middleware RealIP
- В обоих токенах есть поле для ip заполняемое по данным из
-
В случае, если ip адрес изменился, при рефреш операции нужно послать email warning на почту юзера (для упрощения можно использовать моковые данные)
- Реализация ./auth/internal/smtp/mailservice.go
Будет плюсом, если получится использовать Docker и покрыть код тестами.
- Set up environment variables
cp .env.example .env
- Run
docker-compose
docker-compose up --build
Installing uninstalled (but imported) dependencies
(cd auth && go mod tidy)
Run all tests
(cd auth && go test ./...)
Patterns:
- DDD - Domain Driven Design
- "Ensure that you solve valid problem in the optimal way. After that implement the solution in a way that your business will understand without any extra translation from technical language needed"
- Applied to:
- Root package auth.go
- DIP - Dependency Inversion Principle
- "D" in SOLID
- "High-level modules should not depend on low-level modules. Both should depend on abstractions"
- "Abstractions should not depend upon details. Details should depend upon abstractions"
- Applied to:
- Internal modules implementing DIP
- Auth Controller authcontroller.go
- Auth Service authservice.go
- Internal modules implementing DIP
- "D" in SOLID
- CQRS - Command and Query Responsibility Segregation
- "Every method should either be a command that performs an action, or a query that returns data to the caller, but not both"
- Applied to:
- Auth Service authservice.go
Reference:
- https://go.dev/tour/list
- https://www.youtube.com/watch?v=8uiZC0l4Ajw
- https://www.reddit.com/r/golang/comments/1310xxl/comment/jhymmry/
- https://www.gobeyond.dev/standard-package-layout/
- https://www.reddit.com/r/golang/comments/wbawx5/comment/ii5m2ox/
- https://threedots.tech/post/ddd-lite-in-go-introduction/
- https://security.stackexchange.com/questions/79577/whats-the-difference-between-hmac-sha256key-data-and-sha256key-data
- https://stackoverflow.com/a/54378384
- https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
- https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
- https://security.stackexchange.com/questions/39849/does-bcrypt-have-a-maximum-password-length
Body
{
"email": "[email protected]",
"password": "Hello1234!"
}
Example response:
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2OTkyMTQsImlhdCI6MTczMzY5ODkxNCwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiOTBmOWEwZTQtZDU1MC00MjM1LTg0ZTgtM2Q3YmQzNjAyODUwIn0.hbORB2206F0CIlsscxxtY1Pm1AQ2q4yQKghck7HBAjX7lCMUmjtxMKkXQgiLBrVToKWUmobMGs1wj1LLW13leg",
"refreshToken": "kPmg5NVQQjWE6D1702AoUKwSAAE="
}
Body
{
"email": "[email protected]",
"password": "hello1234"
}
Example response:
{
"code": 422,
"message": {
"errors": [
{
"field": "password",
"message": "password must contain at least one uppercase letter, one lowercase letter, one number, and one special character"
}
]
}
}
Body
{
"email": "[email protected]",
"password": "Hello1234!"
}
Example response:
{
"code": 409,
"message": "user with this email already exists"
}
Body
{
"email": "[email protected]"
}
Example response:
{
"code": 422,
"message": {
"errors": [
{
"field": "password",
"message": "password is required"
}
]
}
}
Body
{
"hi": "hello"
}
Example response:
{
"code": 400,
"message": "json: unknown field \"hi\""
}
Body
{
"email": "[email protected]",
"password": 1234568798
}
Example response:
{
"code": 400,
"message": "json: cannot unmarshal number into Go struct field CreateUserDto.password of type string"
}
Body
{
"email": "[email protected]",
"password": "Hello1234!"
}
Example response:
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2Mzc4NTcsImlhdCI6MTczMzYzNzU1NywiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiMDgxNmIyMjctYjk5Ny00ZDQyLTlmY2MtNzUwMmU3MmRjZTRlIiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.wXGy0c_iO1rw4XfZJwJpYg7cu6i1ZGKLqwr8GaRKgGC-V3Mntzan580ZNeurA0SW7LUl2770BaLmhRpy4wNf_A",
"refreshToken": "CBayJ7mXTUKfzHUC5y3OTqwSAAE="
}
Body
{
"email": "[email protected]",
"password": "wrongpass"
}
Example response:
{
"code": 403,
"message": "crypto/bcrypt: hashedPassword is not the hash of the given password"
}
Body
{
"email": "[email protected]",
"password": "Hello1234!"
}
Example response:
{
"code": 404,
"message": "user with email [email protected] not found: sql: no rows in result set"
}
- Requires header
Authorization: Bearer eyJhb...
Headers
Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2OTk3NjUsImlhdCI6MTczMzY5OTQ2NSwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiZDIwODA4ZWYtYWVlNS00MzI0LTg4ZTYtOGU5OTMyYjM3NmQ0In0.k4EnfkiENdBBk0SMvyjLLqgwgTyEc9YVg6dtyvmnX8A9YIk2-7SQPTzwLzPn3dPKUuVu-PxDa1ul9gq8emZ3LA
Example response:
{
"uuid": "239a0eb8-ca36-4930-b087-81a1d6532005",
"email": "[email protected]"
}
Headers (bad accessToken)
Authorization: Bearer BADxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2OTk3NjUsImlhdCI6MTczMzY5OTQ2NSwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiZDIwODA4ZWYtYWVlNS00MzI0LTg4ZTYtOGU5OTMyYjM3NmQ0In0.k4EnfkiENdBBk0SMvyjLLqgwgTyEc9YVg6dtyvmnX8A9YIk2-7SQPTzwLzPn3dPKUuVu-PxDa1ul9gq8emZ3LA
Example response:
{
"code": 403,
"message": "error verifying Authorization header: token signature is invalid: signature is invalid"
}
- Requires header
Authorization: Bearer eyJhb...
Headers
Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2MzgwMjgsImlhdCI6MTczMzYzNzcyOCwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiZjk5YjkyNTMtNGIwYi00ZDFlLTg5NmUtOWVjYmMxNzJjNWUwIiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.EYGs2uu7KXIwOJT_xf1bQ1xjxJvMrfhG0gwu67d89mZEJnvV7TWwyp3WmB3UOQSppRALkzxTV9fkdcpp19wEJw
Example response:
[
{
"email": "[email protected]"
}
]
Headers
(empty)
Example response:
{
"code": 403,
"message": "error verifying Authorization header: Authorization header must be provided"
}
Headers (expired access token)
Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2MzgwMjgsImlhdCI6MTczMzYzNzcyOCwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiZjk5YjkyNTMtNGIwYi00ZDFlLTg5NmUtOWVjYmMxNzJjNWUwIiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.EYGs2uu7KXIwOJT_xf1bQ1xjxJvMrfhG0gwu67d89mZEJnvV7TWwyp3WmB3UOQSppRALkzxTV9fkdcpp19wEJw
Example response:
{
"code": 403,
"message": "error verifying Authorization header: token has invalid claims: token is expired"
}
POST /api/v1/auth/login/898be767-f66f-494d-be9a-c1be85548bb7
Body
(empty)
Example response:
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2Mzg1NTIsImlhdCI6MTczMzYzODI1MiwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiNzhkN2EwNTEtYjU0ZS00NDczLWI5MTEtYjE0YzIwZjI5OGJjIiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.9Y7pkxA0iTsp2XX8sbDh3oBtHOPbFTztet-QsMTUkH4mlE3MGjqTekoiqtAphXKEjBN-EIbKJqLeZI5wa6uKtw",
"refreshToken": "eNegUbVORHO5EbFMIPKYvKwSAAE="
}
POST /api/v1/auth/login/malformed-f66f-494d-be9a-c1be85548bb7
Body
(empty)
Example response:
{
"code": 400,
"message": "invalid UUID format: invalid UUID length: 37"
}
POST /api/v1/auth/login/00000000-f66f-494d-be9a-c1be85548bb7
Body
(empty)
Example response:
{
"code": 404,
"message": "user not found: sql: no rows in result set"
}
Body
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2Mzg3MTUsImlhdCI6MTczMzYzODQxNSwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiNTI3NTdlNjQtMDlkNi00MWI5LTg4YjgtZjFkMDljNzRhN2IyIiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.g7Zl5_MXMN3IyURgR3VIHjgZZrq1o6YEp3odrSsoQV8YDBj2iqhxEHOkcdRVQsai1xjH53f5pyw1867CQQf4Lg",
"refreshToken": "UnV+ZAnWQbmIuPHQnHSnsqwSAAE="
}
Example response:
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2Mzg3NDUsImlhdCI6MTczMzYzODQ0NSwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiZmY5N2FlNjMtZjdjMC00NTAyLWIwYmEtNzY4YjAyNmI5N2I3Iiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.4zNEUcQCt0FR03se6x0UN_hYVqRrvBKtjCwGj6OI-k1glfSFdpRfripXFAshNI6RyH07hhZOVNB7oEBJwZH-hg",
"refreshToken": "/5euY/fARQKwunaLAmuXt6wSAAE="
}
Body (missing refreshToken)
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2Mzg3MTUsImlhdCI6MTczMzYzODQxNSwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiNTI3NTdlNjQtMDlkNi00MWI5LTg4YjgtZjFkMDljNzRhN2IyIiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.g7Zl5_MXMN3IyURgR3VIHjgZZrq1o6YEp3odrSsoQV8YDBj2iqhxEHOkcdRVQsai1xjH53f5pyw1867CQQf4Lg"
}
Example response:
{
"code": 403,
"message": "crypto/bcrypt: hashedPassword is not the hash of the given password"
}
Body (missing accessToken)
{
"refreshToken": "UnV+ZAnWQbmIuPHQnHSnsqwSAAE="
}
Example response:
{
"code": 400,
"message": "token is malformed: token contains an invalid number of segments"
}
Body (bad accessToken)
{
"accessToken": "badhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2Mzg4NTMsImlhdCI6MTczMzYzODU1MywiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiN2IxMDFkNDAtZDZhOC00OTNkLWE3NjMtYzNjNjlmYjQ5YTdhIiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.U-TDrcRpRzMoao3gB6D8lLiOKnZf81ZLGUx1NLRNI7TNgyimaYXUftu7NMdPMGqaN3VmCHQsY6LXptoBUASBaQ",
"refreshToken": "exAdQNaoST2nY8PGn7SaeqwSAAE="
}
Example response:
{
"code": 400,
"message": "token is malformed: could not JSON decode header: invalid character 'm' looking for beginning of value"
}
Body (bad refreshToken)
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2Mzg4NTMsImlhdCI6MTczMzYzODU1MywiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiN2IxMDFkNDAtZDZhOC00OTNkLWE3NjMtYzNjNjlmYjQ5YTdhIiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.U-TDrcRpRzMoao3gB6D8lLiOKnZf81ZLGUx1NLRNI7TNgyimaYXUftu7NMdPMGqaN3VmCHQsY6LXptoBUASBaQ",
"refreshToken": "baddQNaoST2nY8PGn7SaeqwSAAE="
}
Example response:
{
"code": 403,
"message": "crypto/bcrypt: hashedPassword is not the hash of the given password"
}
Body (revoked refreshToken)
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2Mzg5NDEsImlhdCI6MTczMzYzODY0MSwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiNjVhMmIwNzQtMjk2NC00OWZkLWE0ZmQtZWUyM2ZhZTNmMWIzIiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.YNJVAB7ICxTrTvxCu06OuJiGZLjhyGdOduoYzWmdRLYpzuoxh1kAt8Y-iIYxoYqVPmFHMqWTTpFB7Bfix_HXdA",
"refreshToken": "ZaKwdClkSf2k/e4j+uPxs6wSAAE="
}
Example response:
{
"code": 403,
"message": "crypto/bcrypt: hashedPassword is not the hash of the given password"
}
Body (new refreshToken with old accessToken)
{
"accessToken": "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2Mzg5ODMsImlhdCI6MTczMzYzODY4MywiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiNGJjZWU0NjYtZTcxNC00NzE2LWFkODMtNjQyN2RhZmMwZGFmIiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.0uyY8_Zc2oCXQPspeDE88zo2u5AIL0pbroqpT5niTvONnu6xGjt-ZNXXCmBqfvR86WxGcPp2IdAcKj5TsDpGbA",
"refreshToken": "HGYsNpzcS4iLfmhamWjdqKwSAAE="
}
Example response:
{
"code": 403,
"message": "jti in accessToken and refreshToken do not match"
}
GET /api/v1/auth/898be767-f66f-494d-be9a-c1be85548bb7
Example response:
{
"email": "[email protected]"
}
GET /api/v1/auth/badbe767-f66f-494d-be9a-c1be85548bb7
Example response:
{
"code": 404,
"message": "user not found: sql: no rows in result set"
}
- Requires header
Authorization: Bearer eyJhb...
Body
{
"email": "[email protected]",
"password": "NewPass123!"
}
Example response:
{
"uuid": "898be767-f66f-494d-be9a-c1be85548bb7",
"email": "[email protected]"
}
Body
{
"email": "[email protected]"
}
Example response:
{
"uuid": "898be767-f66f-494d-be9a-c1be85548bb7",
"email": "[email protected]"
}
Body
{
"uuid": "000000-f66f-494d-be9a-c1be85548bb7",
"email": "[email protected]"
}
Example response:
{
"code": 400,
"message": "json: unknown field \"uuid\""
}
Body
{}
Example response:
{
"uuid": "898be767-f66f-494d-be9a-c1be85548bb7",
"email": "[email protected]"
}
Body
{
"email": "bad_new_email",
"password": "bad_new_pass"
}
Example response:
{
"code": 422,
"message": {
"errors": [
{
"field": "email",
"message": "invalid email format"
},
{
"field": "password",
"message": "password must contain at least one uppercase letter, one lowercase letter, one number, and one special character"
}
]
}
}
- Requires header
Authorization: Bearer eyJhb...
DELETE http://localhost:8080/api/v1/auth/898be767-f66f-494d-be9a-c1be85548bb7
Headers
Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2MzkzMDUsImlhdCI6MTczMzYzOTAwNSwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiNjQyNThmZjgtYmFmNC00OTQ4LTliNWMtNTVjNTExOWEyZjI3Iiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.t3GeawGNFsRBiPRzD6OU8ZQAWm66ZgO-kNXdMeistDnng3nAohw1qV_Gmdtjj8Wb4Z_8vgRzy2FdvVBz48NKYg
Example response (204):
(empty)
DELETE http://localhost:8080/api/v1/auth/898be767-f66f-494d-be9a-c1be85548bb7
Headers
(empty)
Example response:
{
"code": 403,
"message": "error verifying Authorization header: token is malformed: token contains an invalid number of segments"
}
DELETE http://localhost:8080/api/v1/auth/898be767-f66f-494d-be9a-c1be85548bb7
Headers (expired token)
Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2MzkzMDUsImlhdCI6MTczMzYzOTAwNSwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiNjQyNThmZjgtYmFmNC00OTQ4LTliNWMtNTVjNTExOWEyZjI3Iiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.t3GeawGNFsRBiPRzD6OU8ZQAWm66ZgO-kNXdMeistDnng3nAohw1qV_Gmdtjj8Wb4Z_8vgRzy2FdvVBz48NKYg
Example response:
{
"code": 403,
"message": "error verifying Authorization header: token has invalid claims: token is expired"
}
DELETE http://localhost:8080/api/v1/auth/00000000-f66f-494d-be9a-c1be85548bb7
Headers (expired token)
Authorization: Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MzM2MzkzMDUsImlhdCI6MTczMzYzOTAwNSwiaXAiOiIxNzIuMTguMC4xIiwianRpIjoiNjQyNThmZjgtYmFmNC00OTQ4LTliNWMtNTVjNTExOWEyZjI3Iiwic3ViIjoiODk4YmU3NjctZjY2Zi00OTRkLWJlOWEtYzFiZTg1NTQ4YmI3In0.t3GeawGNFsRBiPRzD6OU8ZQAWm66ZgO-kNXdMeistDnng3nAohw1qV_Gmdtjj8Wb4Z_8vgRzy2FdvVBz48NKYg
Example response:
{
"code": 404,
"message": "error deleting user: user not found"
}