Autentikasi dan otorisasi di gRPC untuk ASP.NET Core

Oleh James Newton-King

Menampilkan atau mengunduh kode sampel (cara mengunduh)

Mengautentikasi pengguna yang memanggil layanan gRPC

gRPC dapat digunakan dengan autentikasi ASP.NET Core untuk mengaitkan pengguna dengan setiap panggilan.

Berikut ini adalah contoh Program.cs yang menggunakan autentikasi gRPC dan ASP.NET Core:

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapGrpcService<GreeterService>();

Catatan

Urutan tempat Anda mendaftarkan middleware autentikasi ASP.NET Core penting. Selalu panggil UseAuthentication dan UseAuthorization sesudah UseRouting dan sebelum UseEndpoints.

Mekanisme autentikasi yang digunakan aplikasi Anda selama panggilan perlu dikonfigurasi. Konfigurasi autentikasi ditambahkan dan Program.cs akan berbeda tergantung pada mekanisme autentikasi yang digunakan aplikasi Anda.

Setelah autentikasi disiapkan, pengguna dapat diakses dalam metode layanan gRPC melalui ServerCallContext.

public override Task<BuyTicketsResponse> BuyTickets(
    BuyTicketsRequest request, ServerCallContext context)
{
    var user = context.GetHttpContext().User;

    // ... access data from ClaimsPrincipal ...
}

Autentikasi token pembawa

Klien dapat menyediakan token akses untuk autentikasi. Server memvalidasi token dan menggunakannya untuk mengidentifikasi pengguna.

Di server, autentikasi token pembawa dikonfigurasi menggunakan middleware Pembawa JWT.

Di klien .NET gRPC, token dapat dikirim dengan panggilan dengan menggunakan Metadata koleksi. Entri dalam Metadata koleksi dikirim dengan panggilan gRPC sebagai header HTTP:

public bool DoAuthenticatedCall(
    Ticketer.TicketerClient client, string token)
{
    var headers = new Metadata();
    headers.Add("Authorization", $"Bearer {token}");

    var request = new BuyTicketsRequest { Count = 1 };
    var response = await client.BuyTicketsAsync(request, headers);

    return response.Success;
}

Atur token pembawa dengan CallCredentials

Mengonfigurasi ChannelCredentials pada saluran adalah cara alternatif untuk mengirim token ke layanan dengan panggilan gRPC. Dapat ChannelCredentials mencakup CallCredentials, yang menyediakan cara untuk mengatur Metadatasecara otomatis .

Manfaat menggunakan CallCredentials:

  • Autentikasi dikonfigurasi secara terpusat pada saluran. Token tidak perlu disediakan secara manual ke panggilan gRPC.
  • Panggilan CallCredentials.FromInterceptor balik tidak sinkron. Kredensial panggilan dapat mengambil token kredensial dari sistem eksternal jika diperlukan. Metode asinkron di dalam panggilan balik harus menggunakan CancellationToken pada AuthInterceptorContext.

Catatan

CallCredentials hanya diterapkan jika saluran diamankan dengan TLS. Mengirim header autentikasi melalui koneksi yang tidak aman memiliki implikasi keamanan dan tidak boleh dilakukan di lingkungan produksi. Aplikasi dapat mengonfigurasi saluran untuk mengabaikan perilaku ini dan selalu menggunakan CallCredentials dengan mengatur UnsafeUseInsecureChannelCallCredentials di saluran.

Info masuk dalam contoh berikut mengonfigurasi saluran untuk mengirim token dengan setiap panggilan gRPC:

private static GrpcChannel CreateAuthenticatedChannel(ITokenProvder tokenProvider)
{
    var credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
    {
        var token = await tokenProvider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    });

    var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
    {
        Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
    });
    return channel;
}

Token pembawa dengan pabrik klien gRPC

Pabrik klien gRPC dapat membuat klien yang mengirim token pembawa menggunakan AddCallCredentials. Metode ini tersedia di Grpc.Net.ClientFactory versi 2.46.0 atau yang lebih baru.

Delegasi yang diteruskan ke AddCallCredentials dijalankan untuk setiap panggilan gRPC:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

Injeksi dependensi (DI) dapat dikombinasikan dengan AddCallCredentials. Kelebihan beban diteruskan IServiceProvider ke delegasi, yang dapat digunakan untuk membuat layanan dari DI menggunakan layanan tercakup dan sementara.

Pertimbangkan aplikasi yang memiliki:

  • Ditentukan pengguna ITokenProvider untuk mendapatkan token pembawa. ITokenProvider terdaftar di DI dengan masa pakai tercakup.
  • Pabrik klien gRPC dikonfigurasi untuk membuat klien yang disuntikkan ke layanan gRPC dan pengontrol API Web.
  • Panggilan gRPC harus digunakan ITokenProvider untuk mendapatkan token pembawa.
public interface ITokenProvider
{
    Task<string> GetTokenAsync(CancellationToken cancellationToken);
}

public class AppTokenProvider : ITokenProvider
{
    private string _token;

    public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
    {
        if (_token == null)
        {
            // App code to resolve the token here.
        }

        return _token;
    }
}
builder.Services.AddScoped<ITokenProvider, AppTokenProvider>();

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials(async (context, metadata, serviceProvider) =>
    {
        var provider = serviceProvider.GetRequiredService<ITokenProvider>();
        var token = await provider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    }));

Kode sebelumnya:

  • ITokenProvider Mendefinisikan dan AppTokenProvider. Jenis ini menangani penyelesaian token autentikasi untuk panggilan gRPC.
  • Mendaftarkan AppTokenProvider jenis dengan DI dalam masa pakai tercakup. AppTokenProvider cache token sehingga hanya panggilan pertama dalam cakupan yang diperlukan untuk menghitungnya.
  • Mendaftarkan jenis dengan GreeterClient pabrik klien.
  • AddCallCredentials Mengonfigurasi untuk klien ini. Delegasi dijalankan setiap kali panggilan dilakukan dan menambahkan token yang dikembalikan oleh ITokenProvider ke metadata.

Autentikasi sertifikat klien

Klien dapat menyediakan sertifikat klien untuk autentikasi. Autentikasi sertifikat terjadi di tingkat TLS, jauh sebelum ASP.NET Core. Ketika permintaan memasuki ASP.NET Core, paket autentikasi sertifikat klien memungkinkan Anda menyelesaikan sertifikat ke ClaimsPrincipal.

Catatan

Konfigurasikan server untuk menerima sertifikat klien. Untuk informasi tentang menerima sertifikat klien di Kestrel, IIS, dan Azure, lihat Mengonfigurasi autentikasi sertifikat di ASP.NET Core.

Di klien .NET gRPC, sertifikat klien ditambahkan ke HttpClientHandler yang kemudian digunakan untuk membuat klien gRPC:

public Ticketer.TicketerClient CreateClientWithCert(
    string baseAddress,
    X509Certificate2 certificate)
{
    // Add client cert to the handler
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(certificate);

    // Create the gRPC channel
    var channel = GrpcChannel.ForAddress(baseAddress, new GrpcChannelOptions
    {
        HttpHandler = handler
    });

    return new Ticketer.TicketerClient(channel);
}

Mekanisme autentikasi lainnya

Banyak mekanisme autentikasi yang didukung ASP.NET Core bekerja dengan gRPC:

  • Microsoft Entra ID
  • Sertifikat Klien
  • IdentityServer
  • JWT Token
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

Untuk informasi selengkapnya tentang mengonfigurasi autentikasi di server, lihat autentikasi ASP.NET Core.

Mengonfigurasi klien gRPC untuk menggunakan autentikasi akan bergantung pada mekanisme autentikasi yang Anda gunakan. Contoh token pembawa dan sertifikat klien sebelumnya menunjukkan beberapa cara klien gRPC dapat dikonfigurasi untuk mengirim metadata autentikasi dengan panggilan gRPC:

  • Klien gRPC yang sangat ditik menggunakan HttpClient secara internal. Autentikasi dapat dikonfigurasi pada HttpClientHandler, atau dengan menambahkan instans kustom HttpMessageHandler ke HttpClient.
  • Setiap panggilan gRPC memiliki argumen opsional CallOptions . Header kustom dapat dikirim menggunakan koleksi header opsi.

Catatan

Autentikasi Windows (NTLM/Kerberos/Negosiasi) tidak dapat digunakan dengan gRPC. gRPC memerlukan HTTP/2, dan HTTP/2 tidak mendukung Autentikasi Windows.

Mengotorisasi pengguna untuk mengakses layanan dan metode layanan

Secara default, semua metode dalam layanan dapat dipanggil oleh pengguna yang tidak diabaikan. Untuk memerlukan autentikasi, terapkan [Authorize] atribut ke layanan:

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
}

Anda dapat menggunakan argumen konstruktor dan properti [Authorize] atribut untuk membatasi akses hanya ke pengguna yang cocok dengan kebijakan otorisasi tertentu. Misalnya, jika Anda memiliki kebijakan otorisasi kustom yang disebut MyAuthorizationPolicy, pastikan hanya pengguna yang cocok dengan kebijakan tersebut yang dapat mengakses layanan menggunakan kode berikut:

[Authorize("MyAuthorizationPolicy")]
public class TicketerService : Ticketer.TicketerBase
{
}

Metode layanan individual juga dapat [Authorize] menerapkan atribut . Jika pengguna saat ini tidak cocok dengan kebijakan yang diterapkan ke metode dan kelas, kesalahan dikembalikan ke pemanggil:

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
    public override Task<AvailableTicketsResponse> GetAvailableTickets(
        Empty request, ServerCallContext context)
    {
        // ... buy tickets for the current user ...
    }

    [Authorize("Administrators")]
    public override Task<BuyTicketsResponse> RefundTickets(
        BuyTicketsRequest request, ServerCallContext context)
    {
        // ... refund tickets (something only Administrators can do) ..
    }
}

Sumber Daya Tambahan:

Menampilkan atau mengunduh kode sampel (cara mengunduh)

Mengautentikasi pengguna yang memanggil layanan gRPC

gRPC dapat digunakan dengan autentikasi ASP.NET Core untuk mengaitkan pengguna dengan setiap panggilan.

Berikut ini adalah contoh Startup.Configure yang menggunakan autentikasi gRPC dan ASP.NET Core:

public void Configure(IApplicationBuilder app)
{
    app.UseRouting();

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGrpcService<GreeterService>();
    });
}

Catatan

Urutan tempat Anda mendaftarkan middleware autentikasi ASP.NET Core penting. Selalu panggil UseAuthentication dan UseAuthorization sesudah UseRouting dan sebelum UseEndpoints.

Mekanisme autentikasi yang digunakan aplikasi Anda selama panggilan perlu dikonfigurasi. Konfigurasi autentikasi ditambahkan dan Startup.ConfigureServices akan berbeda tergantung pada mekanisme autentikasi yang digunakan aplikasi Anda.

Setelah autentikasi disiapkan, pengguna dapat diakses dalam metode layanan gRPC melalui ServerCallContext.

public override Task<BuyTicketsResponse> BuyTickets(
    BuyTicketsRequest request, ServerCallContext context)
{
    var user = context.GetHttpContext().User;

    // ... access data from ClaimsPrincipal ...
}

Autentikasi token pembawa

Klien dapat menyediakan token akses untuk autentikasi. Server memvalidasi token dan menggunakannya untuk mengidentifikasi pengguna.

Di server, autentikasi token pembawa dikonfigurasi menggunakan middleware Pembawa JWT.

Di klien .NET gRPC, token dapat dikirim dengan panggilan dengan menggunakan Metadata koleksi. Entri dalam Metadata koleksi dikirim dengan panggilan gRPC sebagai header HTTP:

public bool DoAuthenticatedCall(
    Ticketer.TicketerClient client, string token)
{
    var headers = new Metadata();
    headers.Add("Authorization", $"Bearer {token}");

    var request = new BuyTicketsRequest { Count = 1 };
    var response = await client.BuyTicketsAsync(request, headers);

    return response.Success;
}

Atur token pembawa dengan CallCredentials

Mengonfigurasi ChannelCredentials pada saluran adalah cara alternatif untuk mengirim token ke layanan dengan panggilan gRPC. Dapat ChannelCredentials mencakup CallCredentials, yang menyediakan cara untuk mengatur Metadatasecara otomatis .

Manfaat menggunakan CallCredentials:

  • Autentikasi dikonfigurasi secara terpusat pada saluran. Token tidak perlu disediakan secara manual ke panggilan gRPC.
  • Panggilan CallCredentials.FromInterceptor balik tidak sinkron. Kredensial panggilan dapat mengambil token kredensial dari sistem eksternal jika diperlukan. Metode asinkron di dalam panggilan balik harus menggunakan CancellationToken pada AuthInterceptorContext.

Catatan

CallCredentials hanya diterapkan jika saluran diamankan dengan TLS. Mengirim header autentikasi melalui koneksi yang tidak aman memiliki implikasi keamanan dan tidak boleh dilakukan di lingkungan produksi. Aplikasi dapat mengonfigurasi saluran untuk mengabaikan perilaku ini dan selalu menggunakan CallCredentials dengan mengatur UnsafeUseInsecureChannelCallCredentials di saluran.

Info masuk dalam contoh berikut mengonfigurasi saluran untuk mengirim token dengan setiap panggilan gRPC:

private static GrpcChannel CreateAuthenticatedChannel(ITokenProvder tokenProvider)
{
    var credentials = CallCredentials.FromInterceptor(async (context, metadata) =>
    {
        var token = await tokenProvider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    });

    var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
    {
        Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
    });
    return channel;
}

Token pembawa dengan pabrik klien gRPC

Pabrik klien gRPC dapat membuat klien yang mengirim token pembawa menggunakan AddCallCredentials. Metode ini tersedia di Grpc.Net.ClientFactory versi 2.46.0 atau yang lebih baru.

Delegasi yang diteruskan ke AddCallCredentials dijalankan untuk setiap panggilan gRPC:

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

Injeksi dependensi (DI) dapat dikombinasikan dengan AddCallCredentials. Kelebihan beban diteruskan IServiceProvider ke delegasi, yang dapat digunakan untuk membuat layanan dari DI menggunakan layanan tercakup dan sementara.

Pertimbangkan aplikasi yang memiliki:

  • Ditentukan pengguna ITokenProvider untuk mendapatkan token pembawa. ITokenProvider terdaftar di DI dengan masa pakai tercakup.
  • Pabrik klien gRPC dikonfigurasi untuk membuat klien yang disuntikkan ke layanan gRPC dan pengontrol API Web.
  • Panggilan gRPC harus digunakan ITokenProvider untuk mendapatkan token pembawa.
public interface ITokenProvider
{
    Task<string> GetTokenAsync(CancellationToken cancellationToken);
}

public class AppTokenProvider : ITokenProvider
{
    private string _token;

    public async Task<string> GetTokenAsync(CancellationToken cancellationToken)
    {
        if (_token == null)
        {
            // App code to resolve the token here.
        }

        return _token;
    }
}
services.AddScoped<ITokenProvider, AppTokenProvider>();

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials(async (context, metadata, serviceProvider) =>
    {
        var provider = serviceProvider.GetRequiredService<ITokenProvider>();
        var token = await provider.GetTokenAsync(context.CancellationToken);
        metadata.Add("Authorization", $"Bearer {token}");
    }));

Kode sebelumnya:

  • ITokenProvider Mendefinisikan dan AppTokenProvider. Jenis ini menangani penyelesaian token autentikasi untuk panggilan gRPC.
  • Mendaftarkan AppTokenProvider jenis dengan DI dalam masa pakai tercakup. AppTokenProvider cache token sehingga hanya panggilan pertama dalam cakupan yang diperlukan untuk menghitungnya.
  • Mendaftarkan jenis dengan GreeterClient pabrik klien.
  • AddCallCredentials Mengonfigurasi untuk klien ini. Delegasi dijalankan setiap kali panggilan dilakukan dan menambahkan token yang dikembalikan oleh ITokenProvider ke metadata.

Autentikasi sertifikat klien

Klien dapat menyediakan sertifikat klien untuk autentikasi. Autentikasi sertifikat terjadi di tingkat TLS, jauh sebelum ASP.NET Core. Ketika permintaan memasuki ASP.NET Core, paket autentikasi sertifikat klien memungkinkan Anda menyelesaikan sertifikat ke ClaimsPrincipal.

Catatan

Konfigurasikan server untuk menerima sertifikat klien. Untuk informasi tentang menerima sertifikat klien di Kestrel, IIS, dan Azure, lihat Mengonfigurasi autentikasi sertifikat di ASP.NET Core.

Di klien .NET gRPC, sertifikat klien ditambahkan ke HttpClientHandler yang kemudian digunakan untuk membuat klien gRPC:

public Ticketer.TicketerClient CreateClientWithCert(
    string baseAddress,
    X509Certificate2 certificate)
{
    // Add client cert to the handler
    var handler = new HttpClientHandler();
    handler.ClientCertificates.Add(certificate);

    // Create the gRPC channel
    var channel = GrpcChannel.ForAddress(baseAddress, new GrpcChannelOptions
    {
        HttpHandler = handler
    });

    return new Ticketer.TicketerClient(channel);
}

Mekanisme autentikasi lainnya

Banyak mekanisme autentikasi yang didukung ASP.NET Core bekerja dengan gRPC:

  • Microsoft Entra ID
  • Sertifikat Klien
  • IdentityServer
  • JWT Token
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

Untuk informasi selengkapnya tentang mengonfigurasi autentikasi di server, lihat autentikasi ASP.NET Core.

Mengonfigurasi klien gRPC untuk menggunakan autentikasi akan bergantung pada mekanisme autentikasi yang Anda gunakan. Contoh token pembawa dan sertifikat klien sebelumnya menunjukkan beberapa cara klien gRPC dapat dikonfigurasi untuk mengirim metadata autentikasi dengan panggilan gRPC:

  • Klien gRPC yang sangat ditik menggunakan HttpClient secara internal. Autentikasi dapat dikonfigurasi pada HttpClientHandler, atau dengan menambahkan instans kustom HttpMessageHandler ke HttpClient.
  • Setiap panggilan gRPC memiliki argumen opsional CallOptions . Header kustom dapat dikirim menggunakan koleksi header opsi.

Catatan

Autentikasi Windows (NTLM/Kerberos/Negosiasi) tidak dapat digunakan dengan gRPC. gRPC memerlukan HTTP/2, dan HTTP/2 tidak mendukung Autentikasi Windows.

Mengotorisasi pengguna untuk mengakses layanan dan metode layanan

Secara default, semua metode dalam layanan dapat dipanggil oleh pengguna yang tidak diabaikan. Untuk memerlukan autentikasi, terapkan [Authorize] atribut ke layanan:

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
}

Anda dapat menggunakan argumen konstruktor dan properti [Authorize] atribut untuk membatasi akses hanya ke pengguna yang cocok dengan kebijakan otorisasi tertentu. Misalnya, jika Anda memiliki kebijakan otorisasi kustom yang disebut MyAuthorizationPolicy, pastikan hanya pengguna yang cocok dengan kebijakan tersebut yang dapat mengakses layanan menggunakan kode berikut:

[Authorize("MyAuthorizationPolicy")]
public class TicketerService : Ticketer.TicketerBase
{
}

Metode layanan individual juga dapat [Authorize] menerapkan atribut . Jika pengguna saat ini tidak cocok dengan kebijakan yang diterapkan ke metode dan kelas, kesalahan dikembalikan ke pemanggil:

[Authorize]
public class TicketerService : Ticketer.TicketerBase
{
    public override Task<AvailableTicketsResponse> GetAvailableTickets(
        Empty request, ServerCallContext context)
    {
        // ... buy tickets for the current user ...
    }

    [Authorize("Administrators")]
    public override Task<BuyTicketsResponse> RefundTickets(
        BuyTicketsRequest request, ServerCallContext context)
    {
        // ... refund tickets (something only Administrators can do) ..
    }
}

Sumber Daya Tambahan: