ASP.NET Core のための gRPC での認証と承認

作成者: James Newton-King

サンプル コードを表示またはダウンロードする (ダウンロード方法)

gRPC サービスを呼び出しているユーザーを認証する

gRPC を ASP.NET Core 認証と共に使用して、ユーザーを各呼び出しに関連付けることができます。

以下に、gRPC と ASP.NET Core 認証を使用する Program.cs の例を示します。

app.UseRouting();

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

app.MapGrpcService<GreeterService>();

Note

ASP.NET Core 認証ミドルウェアを登録する順序が重要です。 必ず UseRouting の後に UseAuthenticationUseAuthorization を呼び出し、その後に UseEndpoints を呼び出します。

呼び出し時にアプリによって使用される認証メカニズムは、構成する必要があります。 認証の構成は、Program.cs に追加され、アプリで使用される認証メカニズムによって異なるものとなります。

認証が設定されると、ServerCallContext を介して gRPC サービス メソッドでユーザーにアクセスできます。

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

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

ベアラー トークン認証

クライアントは認証のためのアクセス トークンを提供できます。 サーバーはトークンを検証し、それを使用してユーザーを識別します。

サーバーでは、JWT ベアラー ミドルウェアを使用してベアラー トークン認証が構成されます。

.NET gRPC クライアントでは、Metadata コレクションを使用すれば、呼び出しでトークンを送信することができます。 Metadata コレクション内のエントリは、gRPC 呼び出しで、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;
}

CallCredentials を使用してベアラー トークンを設定する

チャネルで ChannelCredentials を構成することは、gRPC 呼び出しを使用してトークンをサービスに送信するもう 1 つの方法です。 ChannelCredentials には CallCredentials を含めることができます。これにより、Metadata を自動的に設定することができます。

CallCredentials を使用する利点:

  • 認証はチャネルで一元的に構成されます。 トークンを gRPC 呼び出しに手動で指定する必要はありません。
  • CallCredentials.FromInterceptor コールバックは非同期です。 呼び出し資格情報では、必要に応じて外部システムから資格情報トークンをフェッチできます。 コールバック内の非同期メソッドでは、AuthInterceptorContextCancellationToken を使用する必要があります。

注意

CallCredentials はチャネルが TLS でセキュリティ保護されている場合にのみ適用されます。 セキュリティで保護されていない接続を経由した認証ヘッダーの送信はセキュリティに影響を及ぼすため、運用環境では実行しないでください。 アプリでは、チャネルで UnsafeUseInsecureChannelCallCredentials を設定することで、この動作を無視し、常に CallCredentials を使用するように、アプリを構成することができます。

次の例の資格情報では、すべての 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;
}

gRPC クライアント ファクトリでのベアラー トークン

gRPC クライアント ファクトリでは、AddCallCredentials を使用してベアラー トークンを送信するクライアントを作成できます。 このメソッドは、Grpc.Net.ClientFactory バージョン 2.46.0 以降で使用できます。

AddCallCredentials に渡されたデリゲートは、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;
    });

依存関係の挿入 (DI) は、AddCallCredentials と組み合わせることができます。 オーバーロードによって、IServiceProvider がデリゲートに渡されます。これは、スコープ指定された、一時的なサービスを使用して DI から構築されたサービスを取得するために使用できます。

次のようなアプリを考えてみましょう。

  • ベアラー トークンを取得するためのユーザー定義 ITokenProviderITokenProvider は、スコープ付きの有効期間を使って DI に登録されています。
  • gRPC サービスと Web API コントローラーに挿入するクライアントを作成するように gRPC クライアント ファクトリが構成されています。
  • gRPC 呼び出しには、ベアラー トークンを取得するために ITokenProvider を使う必要があります。
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}");
    }));

上記のコードでは次の操作が行われます。

  • ITokenProvider および AppTokenProvider を定義します。 これらの型によって、gRPC 呼び出しの認証トークンの解決が処理されます。
  • スコープ指定されたライフタイムの DI に AppTokenProvider 型を登録します。 スコープ内の最初の呼び出しのみが計算で必要となるように、AppTokenProvider によってトークンがキャッシュされます。
  • GreeterClient 型をクライアント ファクトリに登録します。
  • このクライアント用に、AddCallCredentials を構成します。 デリゲートは、呼び出しが行われるたびに実行されます。また、デリゲートを使用すると、ITokenProvider によって返されたトークンがメタデータに追加されます。

クライアント証明書認証

別の方法として、クライアントから認証のためにクライアント証明書を提供することもできます。 証明書の認証は、ASP.NET Core に到達するずっと前に TLS レベルで実行されます。 要求が ASP.NET Core に到達すると、クライアント証明書の認証パッケージによって、証明書を ClaimsPrincipal に解決できるようになります。

Note

クライアント証明書を受け入れるようにサーバーを構成します。 Kestrel、IIS、および Azure でのクライアント証明書の受け入れについては、「ASP.NET Core で証明書認証を構成する」を参照してください。

.NET gRPC クライアントでは、クライアント証明書は HttpClientHandler に追加されます。これがその後、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);
}

その他の認証メカニズム

ASP.NET Core でサポートされている多くの認証メカニズムを、gRPC で使用できます。

  • Microsoft Entra ID
  • クライアント証明書
  • Identityサーバー
  • JWT トークン
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

サーバーで認証を構成することの詳細については、ASP.NET Core 認証に関するページを参照してください。

認証を使用するための gRPC クライアントの構成は、使おうとしている認証メカニズムによって異なります。 前述したベアラー トークンとクライアント証明書の例では、gRPC 呼び出しで認証メタデータを送信するように gRPC クライアントを構成できるいくつかの方法を示しています。

  • 厳密に型指定された gRPC クライアントは内部的に HttpClient を使用します。 HttpClientHandler 上で、またはカスタムの HttpMessageHandler インスタンスを HttpClient に追加することで、認証を構成します。
  • それぞれの gRPC 呼び出しに、省略可能な CallOptions 引数があります。 オプションのヘッダー コレクションを使用して、カスタム ヘッダーを送信できます。

Note

gRPC で Windows 認証 (NTLM/Kerberos/ネゴシエート) を使用することはできません。 gRPC には HTTP/2 が必要で、HTTP/2 では Windows 認証がサポートされていません。

サービスやサービス メソッドにアクセスするユーザーを承認する

既定では、認証されていないユーザーが、サービスのすべてのメソッドを呼び出すことができます。 認証を要求するには、サービスに [Authorize] 属性を適用します。

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

コンストラクターの引数と [Authorize] 属性のプロパティを使用して、特定の承認ポリシーに一致するユーザーのみにアクセスを制限できます。 たとえば、MyAuthorizationPolicy というカスタム承認ポリシーがある場合は、次のコードを使用して、そのポリシーに一致するユーザーだけがサービスにアクセスできるようにします。

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

個々のサービス メソッドに [Authorize] 属性を適用することもできます。 現在のユーザーが、メソッドとクラスの両方に適用されているポリシーと一致しない場合は、呼び出し元にエラーが返されます。

[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) ..
    }
}

その他の技術情報

サンプル コードを表示またはダウンロードする (ダウンロード方法)

gRPC サービスを呼び出しているユーザーを認証する

gRPC を ASP.NET Core 認証と共に使用して、ユーザーを各呼び出しに関連付けることができます。

以下に、gRPC と ASP.NET Core 認証を使用する Startup.Configure の例を示します。

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

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

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

Note

ASP.NET Core 認証ミドルウェアを登録する順序が重要です。 必ず UseRouting の後に UseAuthenticationUseAuthorization を呼び出し、その後に UseEndpoints を呼び出します。

呼び出し時にアプリによって使用される認証メカニズムは、構成する必要があります。 認証の構成は、Startup.ConfigureServices に追加され、アプリで使用される認証メカニズムによって異なるものとなります。

認証が設定されると、ServerCallContext を介して gRPC サービス メソッドでユーザーにアクセスできます。

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

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

ベアラー トークン認証

クライアントは認証のためのアクセス トークンを提供できます。 サーバーはトークンを検証し、それを使用してユーザーを識別します。

サーバーでは、JWT ベアラー ミドルウェアを使用してベアラー トークン認証が構成されます。

.NET gRPC クライアントでは、Metadata コレクションを使用すれば、呼び出しでトークンを送信することができます。 Metadata コレクション内のエントリは、gRPC 呼び出しで、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;
}

CallCredentials を使用してベアラー トークンを設定する

チャネルで ChannelCredentials を構成することは、gRPC 呼び出しを使用してトークンをサービスに送信するもう 1 つの方法です。 ChannelCredentials には CallCredentials を含めることができます。これにより、Metadata を自動的に設定することができます。

CallCredentials を使用する利点:

  • 認証はチャネルで一元的に構成されます。 トークンを gRPC 呼び出しに手動で指定する必要はありません。
  • CallCredentials.FromInterceptor コールバックは非同期です。 呼び出し資格情報では、必要に応じて外部システムから資格情報トークンをフェッチできます。 コールバック内の非同期メソッドでは、AuthInterceptorContextCancellationToken を使用する必要があります。

注意

CallCredentials はチャネルが TLS でセキュリティ保護されている場合にのみ適用されます。 セキュリティで保護されていない接続を経由した認証ヘッダーの送信はセキュリティに影響を及ぼすため、運用環境では実行しないでください。 アプリでは、チャネルで UnsafeUseInsecureChannelCallCredentials を設定することで、この動作を無視し、常に CallCredentials を使用するように、アプリを構成することができます。

次の例の資格情報では、すべての 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;
}

gRPC クライアント ファクトリでのベアラー トークン

gRPC クライアント ファクトリでは、AddCallCredentials を使用してベアラー トークンを送信するクライアントを作成できます。 このメソッドは、Grpc.Net.ClientFactory バージョン 2.46.0 以降で使用できます。

AddCallCredentials に渡されたデリゲートは、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;
    });

依存関係の挿入 (DI) は、AddCallCredentials と組み合わせることができます。 オーバーロードによって、IServiceProvider がデリゲートに渡されます。これは、スコープ指定された、一時的なサービスを使用して DI から構築されたサービスを取得するために使用できます。

次のようなアプリを考えてみましょう。

  • ベアラー トークンを取得するためのユーザー定義 ITokenProviderITokenProvider は、スコープ付きの有効期間を使って DI に登録されています。
  • gRPC サービスと Web API コントローラーに挿入するクライアントを作成するように gRPC クライアント ファクトリが構成されています。
  • gRPC 呼び出しには、ベアラー トークンを取得するために ITokenProvider を使う必要があります。
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}");
    }));

上記のコードでは次の操作が行われます。

  • ITokenProvider および AppTokenProvider を定義します。 これらの型によって、gRPC 呼び出しの認証トークンの解決が処理されます。
  • スコープ指定されたライフタイムの DI に AppTokenProvider 型を登録します。 スコープ内の最初の呼び出しのみが計算で必要となるように、AppTokenProvider によってトークンがキャッシュされます。
  • GreeterClient 型をクライアント ファクトリに登録します。
  • このクライアント用に、AddCallCredentials を構成します。 デリゲートは、呼び出しが行われるたびに実行されます。また、デリゲートを使用すると、ITokenProvider によって返されたトークンがメタデータに追加されます。

クライアント証明書認証

別の方法として、クライアントから認証のためにクライアント証明書を提供することもできます。 証明書の認証は、ASP.NET Core に到達するずっと前に TLS レベルで実行されます。 要求が ASP.NET Core に到達すると、クライアント証明書の認証パッケージによって、証明書を ClaimsPrincipal に解決できるようになります。

Note

クライアント証明書を受け入れるようにサーバーを構成します。 Kestrel、IIS、および Azure でのクライアント証明書の受け入れについては、「ASP.NET Core で証明書認証を構成する」を参照してください。

.NET gRPC クライアントでは、クライアント証明書は HttpClientHandler に追加されます。これがその後、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);
}

その他の認証メカニズム

ASP.NET Core でサポートされている多くの認証メカニズムを、gRPC で使用できます。

  • Microsoft Entra ID
  • クライアント証明書
  • Identityサーバー
  • JWT トークン
  • OAuth 2.0
  • OpenID Connect
  • WS-Federation

サーバーで認証を構成することの詳細については、ASP.NET Core 認証に関するページを参照してください。

認証を使用するための gRPC クライアントの構成は、使おうとしている認証メカニズムによって異なります。 前述したベアラー トークンとクライアント証明書の例では、gRPC 呼び出しで認証メタデータを送信するように gRPC クライアントを構成できるいくつかの方法を示しています。

  • 厳密に型指定された gRPC クライアントは内部的に HttpClient を使用します。 HttpClientHandler 上で、またはカスタムの HttpMessageHandler インスタンスを HttpClient に追加することで、認証を構成します。
  • それぞれの gRPC 呼び出しに、省略可能な CallOptions 引数があります。 オプションのヘッダー コレクションを使用して、カスタム ヘッダーを送信できます。

Note

gRPC で Windows 認証 (NTLM/Kerberos/ネゴシエート) を使用することはできません。 gRPC には HTTP/2 が必要で、HTTP/2 では Windows 認証がサポートされていません。

サービスやサービス メソッドにアクセスするユーザーを承認する

既定では、認証されていないユーザーが、サービスのすべてのメソッドを呼び出すことができます。 認証を要求するには、サービスに [Authorize] 属性を適用します。

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

コンストラクターの引数と [Authorize] 属性のプロパティを使用して、特定の承認ポリシーに一致するユーザーのみにアクセスを制限できます。 たとえば、MyAuthorizationPolicy というカスタム承認ポリシーがある場合は、次のコードを使用して、そのポリシーに一致するユーザーだけがサービスにアクセスできるようにします。

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

個々のサービス メソッドに [Authorize] 属性を適用することもできます。 現在のユーザーが、メソッドとクラスの両方に適用されているポリシーと一致しない場合は、呼び出し元にエラーが返されます。

[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) ..
    }
}

その他の技術情報