OAuth 2.0 и JWT авторизация на C#: практическое руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: зачем нужны OAuth 2.0 и JWT в современных приложениях?

В современной веб-разработке безопасность данных и управление доступом выходят на первый план. Каждый день миллионы пользователей входят в приложения через Google, Facebook или корпоративные системы, не передавая свои пароли напрямую. Именно здесь в игру вступают протоколы OAuth 2.0 и технология JWT (JSON Web Tokens).

OAuth 2.0 — это отраслевой стандарт для делегирования доступа. Он позволяет приложениям получать ограниченный доступ к ресурсам пользователя без раскрытия его учетных данных. JWT, в свою очередь, представляет собой компактный и самодостаточный формат токена, который содержит всю необходимую информацию для проверки подлинности и авторизации.

В этой статье мы на практике разберем, как реализовать OAuth 2.0 и JWT авторизацию на C# с использованием ASP.NET Core. Вы узнаете, как настроить сервер авторизации, сгенерировать токены и защитить свои API-эндпоинты. Все примеры кода готовы к использованию в реальных проектах.

1. Настройка сервера авторизации с IdentityServer4

Для реализации OAuth 2.0 мы воспользуемся популярной библиотекой IdentityServer4 — это фреймворк для ASP.NET Core, который реализует протоколы OpenID Connect и OAuth 2.0. Начнем с создания минимального сервера авторизации.

Шаг 1: Установка пакетов и создание проекта

Создайте новый проект ASP.NET Core Web Application (пустой шаблон) и установите необходимые NuGet-пакеты:

dotnet add package IdentityServer4
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Шаг 2: Конфигурация клиентов и ресурсов

Определите тестовых пользователей, клиентов и защищенные ресурсы. Создайте класс Config.cs:

public static class Config
{
    public static IEnumerable<IdentityResource> IdentityResources =>
        new[] { new IdentityResources.OpenId(), new IdentityResources.Profile() };

    public static IEnumerable<ApiScope> ApiScopes =>
        new[] { new ApiScope("api1", "My API") };

    public static IEnumerable<Client> Clients =>
        new[]
        {
            new Client
            {
                ClientId = "client",
                AllowedGrantTypes = GrantTypes.ClientCredentials,
                ClientSecrets = { new Secret("secret".Sha256()) },
                AllowedScopes = { "api1" }
            }
        };
}

Шаг 3: Регистрация сервисов в Startup.cs

Настройте IdentityServer в методе ConfigureServices:

public void ConfigureServices(IServiceCollection services)
{
    services.AddIdentityServer()
        .AddInMemoryIdentityResources(Config.IdentityResources)
        .AddInMemoryApiScopes(Config.ApiScopes)
        .AddInMemoryClients(Config.Clients)
        .AddTestUsers(TestUsers.Users);

    services.AddAuthentication()
        .AddJwtBearer("Bearer", options =>
        {
            options.Authority = "https://localhost:5001";
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateAudience = false
            };
        });
}

В методе Configure добавьте вызов app.UseIdentityServer().

2. Реализация JWT токенов: генерация и валидация

JWT токен состоит из трех частей: заголовок (header), полезная нагрузка (payload) и подпись (signature). В .NET генерация и проверка JWT осуществляется с помощью встроенных механизмов.

Генерация JWT токена вручную

Иногда требуется создать токен вручную, например, для тестирования. Используйте следующий код:

using System.IdentityModel.Tokens.Jwt;
using Microsoft.IdentityModel.Tokens;
using System.Security.Claims;
using System.Text;

public string GenerateJwtToken(string userId, string role)
{
    var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("MySuperSecretKey123!"));
    var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

    var claims = new[]
    {
        new Claim(JwtRegisteredClaimNames.Sub, userId),
        new Claim(ClaimTypes.Role, role),
        new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
    };

    var token = new JwtSecurityToken(
        issuer: "https://myapp.com",
        audience: "https://myapp.com",
        claims: claims,
        expires: DateTime.UtcNow.AddHours(1),
        signingCredentials: credentials
    );

    return new JwtSecurityTokenHandler().WriteToken(token);
}

Валидация JWT токена

Проверка токена происходит автоматически при настройке JWT-аутентификации, но вы можете сделать это вручную:

public ClaimsPrincipal ValidateToken(string token)
{
    var tokenHandler = new JwtSecurityTokenHandler();
    var key = Encoding.UTF8.GetBytes("MySuperSecretKey123!");

    var validationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidIssuer = "https://myapp.com",
        ValidAudience = "https://myapp.com",
        IssuerSigningKey = new SymmetricSecurityKey(key)
    };

    return tokenHandler.ValidateToken(token, validationParameters, out _);
}

3. Защита API-эндпоинтов с помощью OAuth 2.0

Теперь, когда сервер авторизации настроен, защитим несколько API-эндпоинтов. Создадим контроллер, который требует валидный JWT токен.

Создание защищенного контроллера

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class DataController : ControllerBase
{
    // Доступен только для авторизованных пользователей
    [HttpGet]
    public IActionResult GetProtectedData()
    {
        return Ok(new { Message = "Вы получили защищенные данные!" });
    }

    // Доступен только для пользователей с ролью Admin
    [HttpGet("admin")]
    [Authorize(Roles = "Admin")]
    public IActionResult GetAdminData()
    {
        return Ok(new { Message = "Только для администраторов!" });
    }

    // Анонимный доступ
    [AllowAnonymous]
    [HttpGet("public")]
    public IActionResult GetPublicData()
    {
        return Ok(new { Message = "Публичные данные" });
    }
}

Получение токена и вызов API

Клиентское приложение должно сначала получить токен, а затем передавать его в заголовке Authorization:

// Получение токена через HTTP запрос
var client = new HttpClient();
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
    Address = "https://localhost:5001/connect/token",
    ClientId = "client",
    ClientSecret = "secret",                

Блоги

Рекомендаций по книгам