JwtTokenRefresher.cs
1 using System;
2 using System.Net;
3 using System.Net.Http;
4 using System.Threading;
5 using System.Threading.Tasks;
6 
7 namespace Microsoft.Bot.Connector
8 {
9  public class JwtTokenRefresher : DelegatingHandler
10  {
11  private readonly MicrosoftAppCredentials credentials;
12 
14  : base()
15  {
16  if (credentials == null)
17  {
18  throw new ArgumentNullException(nameof(credentials));
19  }
20  this.credentials = credentials;
21  }
22 
23  protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
24  {
25  HttpResponseMessage response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
26 
27  // possibly a transient "token expiration" failure
28  if (response.StatusCode == HttpStatusCode.Unauthorized)
29  {
30  response.Dispose();
31  // this call might throw if the Microsoft login service returns an oauth failure
32  var token = await credentials.GetTokenAsync(true).ConfigureAwait(false);
33  // adds token to outgoing request
34  await credentials.ProcessHttpRequestAsync(request, cancellationToken).ConfigureAwait(false);
35  // retry request with refreshed token
36  response = await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
37  }
38 
39  // since we've ruled out transient "token expiration" failure, or we've found a permanent Forbidden failure
40  // this failure will come from a downstream system like channel connector or state service rather than the Microsoft login service
41  // then throw an exception with additional context
42  // this centralizes the handling for this StatusCode here rather than the autorest-generated clients
43  if (response.StatusCode == HttpStatusCode.Forbidden || response.StatusCode == HttpStatusCode.Unauthorized)
44  {
45  using (response)
46  {
47  try
48  {
49  response.EnsureSuccessStatusCode();
50  }
51  catch (Exception error)
52  {
53  var statusCode = response.StatusCode;
54  var reasonPhrase = response.ReasonPhrase;
55  throw new UnauthorizedAccessException($"Authorization for Microsoft App ID {credentials.MicrosoftAppId} failed with status code {statusCode} and reason phrase '{reasonPhrase}'", error);
56  }
57  }
58  }
59 
60  return response;
61  }
62  }
63 }
override async Task< HttpResponseMessage > SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
JwtTokenRefresher(MicrosoftAppCredentials credentials)