title: grpc-dotnet authentication jwt bearer token
body: <p>I am looking for some feedback or guidance.  I have built a grpc server and client in dotnet core 3.1.  Only this client will be talking to the server, but still need to protect the server endpoints.  I am trying to implement authentication security on it and the jwt bearer token seems like a good path.  I am trying to follow the example <a href="https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/grpc/authn-and-authz/sample/" rel="nofollow noreferrer">here</a>(which came from this <a href="https://docs.microsoft.com/en-us/aspnet/core/grpc/authn-and-authz?view=aspnetcore-3.1" rel="nofollow noreferrer">MS Doc</a>) which seems like a pretty simple implementation, however seems insecure for production use since anyone can hit the generate token endpoint.  I figured it could modify and implement a shared secret that is passed on the get token request.  Does this seem like a good approach or other thoughts?</p>
  <p>Besides this part, I have a question about the client side code that I don't quite understand.  At the start of the client, it establishes a grpc channel by calling the CreateAuthenticatedChannel() which appears to only add the bearer token if the _token is set.  You don't get the token until the client starts up and hit number three, but I don't see where the channel or client is rebuilt to include the metadata token value.  The sample appears to work to but don't understand it.</p>
  <pre><code>class Program
  {
      // The port number(5001) must match the port of the gRPC server.
      private const string Address = &quot;localhost:5001&quot;;
  
      private static string _token;
  
      static async Task Main(string[] args)
      {
          var channel = CreateAuthenticatedChannel($&quot;https://{Address}&quot;);
          var client = new Ticketer.TicketerClient(channel);
  
          Console.WriteLine(&quot;gRPC Ticketer&quot;);
          Console.WriteLine();
          Console.WriteLine(&quot;Press a key:&quot;);
          Console.WriteLine(&quot;1: Get available tickets&quot;);
          Console.WriteLine(&quot;2: Purchase ticket&quot;);
          Console.WriteLine(&quot;3: Authenticate&quot;);
          Console.WriteLine(&quot;4: Exit&quot;);
          Console.WriteLine();
  
          var exiting = false;
          while (!exiting)
          {
              var consoleKeyInfo = Console.ReadKey(intercept: true);
              switch (consoleKeyInfo.KeyChar)
              {
                  case '1':
                      await GetAvailableTickets(client);
                      break;
                  case '2':
                      await PurchaseTicket(client);
                      break;
                  case '3':
                      _token = await Authenticate();
                      break;
                  case '4':
                      exiting = true;
                      break;
              }
          }
  
          Console.WriteLine(&quot;Exiting&quot;);
      }
  
      private static GrpcChannel CreateAuthenticatedChannel(string address)
      {
          var credentials = CallCredentials.FromInterceptor((context, metadata) =&gt;
          {
              if (!string.IsNullOrEmpty(_token))
              {
                  metadata.Add(&quot;Authorization&quot;, $&quot;Bearer {_token}&quot;);
              }
              return Task.CompletedTask;
          });
  
          // SslCredentials is used here because this channel is using TLS.
          // Channels that aren't using TLS should use ChannelCredentials.Insecure instead.
          var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
          {
              Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
          });
          return channel;
      }
  
      private static async Task&lt;string&gt; Authenticate()
      {
          Console.WriteLine($&quot;Authenticating as {Environment.UserName}...&quot;);
          var httpClient = new HttpClient();
          var request = new HttpRequestMessage
          {
              RequestUri = new Uri($&quot;https://{Address}/generateJwtToken?name={HttpUtility.UrlEncode(Environment.UserName)}&quot;),
              Method = HttpMethod.Get,
              Version = new Version(2, 0)
          };
          var tokenResponse = await httpClient.SendAsync(request);
          tokenResponse.EnsureSuccessStatusCode();
  
          var token = await tokenResponse.Content.ReadAsStringAsync();
          Console.WriteLine(token);
          Console.WriteLine(&quot;Successfully authenticated.&quot;);
  
          return token;
      }
  
      private static async Task PurchaseTicket(Ticketer.TicketerClient client)
      {
          Console.WriteLine(&quot;Purchasing ticket...&quot;);
          try
          {
              var response = await client.BuyTicketsAsync(new BuyTicketsRequest { Count = 1 });
              if (response.Success)
              {
                  Console.WriteLine(&quot;Purchase successful.&quot;);
              }
              else
              {
                  Console.WriteLine(&quot;Purchase failed. No tickets available.&quot;);
              }
          }
          catch (Exception ex)
          {
              Console.WriteLine(&quot;Error purchasing ticket.&quot; + Environment.NewLine + ex.ToString());
          }
      }
  
      private static async Task GetAvailableTickets(Ticketer.TicketerClient client)
      {
          Console.WriteLine(&quot;Getting available ticket count...&quot;);
          var response = await client.GetAvailableTicketsAsync(new Empty());
          Console.WriteLine(&quot;Available ticket count: &quot; + response.Count);
      }
  }
  </code></pre>
  <p>}</p>