Bảo mật Web API bằng Client Credentials trong IdentityServer4

Mô hình Client Credentials là kịch bản cơ bản nhất trong IdentityServer4 (IS4), thường được sử dụng cho các tương tác máy-với-máy (machine-to-machine), nơi không có sự hiện diện của người dùng cuối. Trong hướng dẫn này, chúng ta sẽ xây dựng một hệ thống gồm ba thành phần chính: IdentityServer (Trạm cấp phát Token), Resource API (Tài nguyên cần bảo vệ) và Client (Ứng dụng yêu cầu truy cập).

1. Thiết lập dự án IdentityServer

Đầu tiên, tạo một dự án ASP.NET Core Empty để đóng vai trò là IdentityServer. Dự án này sẽ quản lý các định nghĩa về API và Client.

Cài đặt thư viện:

Install-Package IdentityServer4

Cấu hình tài nguyên và Client (Config.cs):

Chúng ta cần định nghĩa các phạm vi (Scopes) mà API cung cấp và thông tin định danh của ứng dụng khách.

public static class AuthConfig
{
    // Định nghĩa các API Resource
    public static IEnumerable<ApiResource> GetApiResources()
    {
        return new List<ApiResource>
        {
            new ApiResource("backend_api", "Hệ thống quản lý Backend")
        };
    }

    // Cấu hình Client truy cập
    public static IEnumerable<Client> GetClients()
    {
        return new List<Client>
        {
            new Client
            {
                ClientId = "service_worker_app",
                // Sử dụng cơ chế Client Credentials
                AllowedGrantTypes = GrantTypes.ClientCredentials,
                // Mã bảo mật để xác thực Client
                ClientSecrets =
                {
                    new Secret("key_bao_mat_2024".Sha256())
                },
                // Các tài nguyên mà Client này được phép truy cập
                AllowedScopes = { "backend_api" }
            }
        };
    }
}

Cấu hình trong Startup.cs:

Đăng ký IdentityServer vào hệ thống Dependency Injection và Middleware.

public void ConfigureServices(IServiceCollection services)
{
    var authBuilder = services.AddIdentityServer()
        .AddDeveloperSigningCredential() // Sử dụng chứng chỉ tạm thời cho phát triển
        .AddInMemoryApiResources(AuthConfig.GetApiResources())
        .AddInMemoryClients(AuthConfig.GetClients());
}

public void Configure(IApplicationBuilder app)
{
    app.UseIdentityServer();
}

2. Xây dựng Resource API

API này sẽ chứa các dữ liệu nhạy cảm mà chỉ những Client có Token hợp lệ mới được truy xuất. Giả sử API chạy tại http://localhost:5001.

Tạo Controller bảo mật:

[Route("api/secure")]
[Authorize]
public class SecureDataController : ControllerBase
{
    [HttpGet]
    public IActionResult GetData()
    {
        // Trả về các Claims có trong Token để kiểm tra
        return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
    }
}

Cấu hình xác thực JWT Bearer:

API cần biết cách xác thực Token được gửi tới từ IdentityServer.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddAuthentication("Bearer")
        .AddJwtBearer("Bearer", options =>
        {
            options.Authority = "http://localhost:5000"; // Địa chỉ IdentityServer
            options.RequireHttpsMetadata = false;
            options.Audience = "backend_api"; // Khớp với ApiResource đã định nghĩa
        });
}

public void Configure(IApplicationBuilder app)
{
    app.UseRouting();
    app.UseAuthentication();
    app.UseAuthorization();
    app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}

3. Xây dựng ứng dụng Client (Console App)

Client sẽ thực hiện quy trình: Khám phá Endpoint -> Yêu cầu Token -> Gọi API với Token đó.

Cài đặt thư viện hỗ trợ:

Install-Package IdentityModel

Triển khai mã nguồn gọi API:

static async Task Main(string[] args)
{
    var httpClient = new HttpClient();

    // 1. Khám phá các endpoint từ IdentityServer
    var discovery = await httpClient.GetDiscoveryDocumentAsync("http://localhost:5000");
    if (discovery.IsError) throw new Exception(discovery.Error);

    // 2. Yêu cầu Access Token bằng ClientId và ClientSecret
    var tokenResponse = await httpClient.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
    {
        Address = discovery.TokenEndpoint,
        ClientId = "service_worker_app",
        ClientSecret = "key_bao_mat_2024",
        Scope = "backend_api"
    });

    if (tokenResponse.IsError) 
    {
        Console.WriteLine($"Lỗi lấy token: {tokenResponse.Error}");
        return;
    }

    Console.WriteLine($"Token nhận được: {tokenResponse.AccessToken}");

    // 3. Sử dụng Token để gọi API bảo mật
    var apiClient = new HttpClient();
    apiClient.SetBearerToken(tokenResponse.AccessToken);

    var apiResponse = await apiClient.GetAsync("http://localhost:5001/api/secure");
    if (!apiResponse.IsSuccessStatusCode)
    {
        Console.WriteLine($"Lỗi truy cập API: {apiResponse.StatusCode}");
    }
    else
    {
        var content = await apiResponse.Content.ReadAsStringAsync();
        Console.WriteLine("Dữ liệu từ API:");
        Console.WriteLine(content);
    }
}

4. Cơ chế hoạt động

Khi ứng dụng Client khởi chạy, nó không trực tiếp gửi thông tin xác thực đến API. Thay vào đó, nó gửi ClientId và ClientSecret đến IdentityServer. Nếu thông tin chính xác, IdentityServer trả về một chuỗi JWT (JSON Web Token).

Client đính kèm Token này vào header Authorization: Bearer [token] khi gọi API. Phía API sẽ gửi Token này (hoặc sử dụng public key từ Discovery Endpoint) để xác minh tính hợp lệ. Nếu thành công, tài nguyên sẽ được trả về.

Thẻ: IdentityServer4 ASP.NET Core Web API OAuth2 Security

Đăng vào ngày 31 tháng 10 lúc 08:47