Введение в OAuth 2.0 и JWT на C#
Современные веб-приложения и API требуют надежной системы авторизации. Два самых популярных стандарта — OAuth 2.0 (протокол делегирования доступа) и JWT (JSON Web Token, формат токена) — стали основой безопасности в экосистеме .NET. В этой статье мы разберем, как реализовать OAuth 2.0 и JWT авторизацию на C# в ASP.NET Core, создадим пример сервера авторизации и защитим API.
Независимо от того, создаете ли вы монолитное приложение или микросервисную архитектуру, понимание этих механизмов критически важно. OAuth 2.0 определяет роли (клиент, сервер авторизации, ресурсный сервер), а JWT служит компактным и самодостаточным способом передачи данных о пользователе.
Основы OAuth 2.0: потоки и роли
Что такое OAuth 2.0?
OAuth 2.0 — это протокол авторизации, который позволяет сторонним приложениям получать ограниченный доступ к ресурсам пользователя без передачи его учетных данных. Вместо логина и пароля используются токены доступа (access tokens).
Основные роли в OAuth 2.0
- Resource Owner — пользователь, владеющий данными.
- Client — приложение, которое запрашивает доступ.
- Authorization Server — сервер, выдающий токены после аутентификации.
- Resource Server — API, защищающее ресурсы и проверяющее токены.
Популярные потоки (grant types)
- Authorization Code — для серверных приложений (наиболее безопасный).
- Client Credentials — для межсерверного взаимодействия (без пользователя).
- Implicit — устаревший, не рекомендуется.
- Refresh Token — для получения нового access token без повторного ввода пароля.
JWT (JSON Web Token): структура и принцип работы
Из чего состоит JWT?
JWT — это строка, состоящая из трех частей, разделенных точками:
header.payload.signature
- Header — тип токена и алгоритм подписи (например, HS256 или RS256).
- Payload — данные (claims): идентификатор пользователя, роли, срок действия и т.д.
- Signature — подпись, которая верифицирует, что токен не был изменен.
Преимущества JWT для C#
- Самодостаточность — все данные о пользователе внутри токена.
- Легкая проверка на стороне ресурсного сервера без обращения к БД.
- Поддержка в .NET через библиотеку
Microsoft.AspNetCore.Authentication.JwtBearer.
Реализация OAuth 2.0 + JWT на C# в ASP.NET Core
1. Настройка сервера авторизации (выдача JWT)
Создадим минимальный сервер, который принимает логин/пароль и возвращает JWT. Для простоты используем симметричный ключ (HS256).
// Program.cs — сервер авторизации
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "MyAuthServer",
ValidAudience = "MyApiClient",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SuperSecretKey123!@#$%"))
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
app.MapPost("/token", (HttpContext context) =>
{
// Здесь должна быть проверка логина/пароля из БД
var claims = new[]
{
new Claim(ClaimTypes.Name, "user@example.com"),
new Claim(ClaimTypes.Role, "User")
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SuperSecretKey123!@#$%"));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "MyAuthServer",
audience: "MyApiClient",
claims: claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return new { token = new JwtSecurityTokenHandler().WriteToken(token) };
});
app.Run();
2. Защита API с помощью JWT (Resource Server)
Теперь создадим защищенный API, который принимает только запросы с валидным JWT.
// Program.cs — защищенный API
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "MyAuthServer",
ValidAudience = "MyApiClient",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SuperSecretKey123!@#$%"))
};
});
builder.Services.AddAuthorization();
builder.Services.AddControllers();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/api/secure", (HttpContext context) =>
{
var userName = context.User.Identity?.Name;
return Results.Ok(new { message = $"Привет, {userName}! Доступ разрешен." });
}).RequireAuthorization();
app.Run();
3. Работа с Refresh Token
Access token обычно живет недолго (15-30 минут). Для продления сессии используется refresh token — долгоживущий токен, который хранится на сервере (например, в БД).
// Пример выдачи refresh token
public class TokenService
{
private static readonly Dictionary<string, string> _refreshTokens = new(); // В реальности — БД
public static string GenerateRefreshToken()
{
var randomBytes = new byte[32];
using var rng = System.Security.Cryptography.RandomNumberGenerator.Create();
rng.GetBytes(randomBytes);
return Convert.ToBase64String(randomBytes);
}
public static void SaveRefreshToken(string userId, string refreshToken)
{
_refreshTokens[refreshToken] = userId; // Хранить в БД с датой