Ответ
Согласно спецификации RFC 6750 (Bearer Token Usage) и общепринятой практике, для просроченного (или недействительного) токена должен возвращаться статус 401 Unauthorized.
Почему именно 401, а не 403?
401 Unauthorizedозначает, что запрос не содержит корректных учетных данных для аутентификации. Сервер "не знает", кто вы. Просроченный токен — это некорректные учетные данные.403 Forbiddenозначает, что сервер понял, кто вы (аутентификация успешна), но у вас недостаточно прав для доступа к ресурсу. Это другая ситуация.
Правильная реализация в ASP.NET Core:
-
В обработчике аутентификации (например, JWT Bearer): При проверке токена, если он просрочен, схема аутентификации автоматически завершится с ошибкой, и фреймворк вернет
401. -
Для явного возврата с дополнительной информацией (в контроллере или middleware):
[Authorize] public class SecureController : ControllerBase { [HttpGet("data")] public IActionResult GetData() { // Проверка, выполненная кастомной логикой if (HttpContext.User.IsTokenExpired()) { // Рекомендуется также добавить заголовок WWW-Authenticate Response.Headers.WWWAuthenticate = "Bearer error="invalid_token", error_description="The access token expired""; return Unauthorized(new { error = "invalid_token", message = "Token has expired" }); } return Ok("Sensitive data"); } } -
Использование
[Authorize]с политиками: Можно создать политику, которая проверяет не только наличие, но и валидность/срок токена.services.AddAuthorization(options => { options.AddPolicy("ValidToken", policy => policy.RequireAssertion(context => context.User.Identity?.IsAuthenticated == true && !context.User.IsTokenExpired())); });Затем использовать
[Authorize(Policy = "ValidToken")]. При неудаче политики также вернется401.
Ключевой вывод: Всегда используйте 401 Unauthorized для проблем с аутентификацией (неверный/просроченный/отозванный токен). Используйте 403 Forbidden только когда пользователь аутентифицирован, но его роли или claims не позволяют выполнить действие.
Ответ 18+ 🔞
А, слушай, вот этот ваш RFC 6750 — он, конечно, документ, но там всё по полочкам разложено, как в аптеке. Суть-то проще пареной репы.
Представь, ты приходишь в закрытый клуб, суёшь охраннику свою карточку, а он смотрит и такой: «Мужик, у тебя членство вчера кончилось, просрочено, нахуй». Это 401 Unauthorized. Он тебя не узнаёт, твоя карточка — хуйня, а не удостоверение. Ты для системы — никто, пустое место.
А вот 403 Forbidden — это когда ты уже внутри, с валидной картой, пролез в VIP-зону, но пытаешься зайти в служебный сортир, куда пускают только уборщиц с блядским уровнем доступа. Тебя узнали, но доступ не дали — иди нахуй, не положено.
Так вот, просроченный токен — это однозначно первый случай. Ты не авторизован, точка. Возвращаем 401, и всё тут.
Теперь смотри, как это в коде выглядит, без этих ваших заумных терминов.
В основном, если ты используешь стандартную аутентификацию через AddJwtBearer, то за тебя всё уже сделали. Токен просрочился — схема аутентификации фейлится, и фреймворк автоматом отправит 401. Красота.
Но если ты хочешь добавить немного информативности, чтобы фронтенд-ребята не гадали, что случилось, можно и ручками потыкать. Например, в контроллере:
[Authorize] // Это чтобы просто так любой не лез
public class MySecretController : ControllerBase
{
[HttpGet("my-data")]
public IActionResult GetMyData()
{
// Допустим, у тебя есть свой метод проверки
if (User.IsTokenExpiredLikeADog())
{
// Канонично ещё вот этот заголовок добавить, по RFC
Response.Headers.WWWAuthenticate = "Bearer error="invalid_token", error_description="The token expired, man"";
// И отдать 401 с пояснением
return Unauthorized(new { error = "invalid_token", message = "Токен сдох, обнови, брат" });
}
return Ok("Вот тебе секретные данные, держи.");
}
}
Можно вообще заморочиться и сделать свою политику для [Authorize]. Типа так:
services.AddAuthorization(options =>
{
options.AddPolicy("NotExpiredToken", policy =>
policy.RequireAssertion(context =>
context.User.Identity?.IsAuthenticated == true &&
!context.User.IsTokenExpired() // Твоя кастомная проверка
));
});
И потом вешать на метод [Authorize(Policy = "NotExpiredToken")]. Не прошёл проверку по политике — опять же, получишь 401.
Итог, коротко и на пальцах:
- Токен просрочен, невалидный, отозванный, его вообще нет? — 401 Unauthorized. «Я тебя не знаю, иди нахуй».
- Токен есть, он живой, всё ок, но у тебя нет прав на этот конкретный метод/ресурс? — 403 Forbidden. «Я тебя знаю, но ты сюда не лезь, у тебя прав нет».
Вот и вся философия. Не усложняй, блядь.