Created
November 2, 2015 02:58
-
-
Save weitzhandler/4071da688485cdb49502 to your computer and use it in GitHub Desktop.
A HttpClient that handles bearer tokens
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.Owin.Security.OAuth; | |
using System.Net.Http.Formatting; | |
using System.Net.Http.Headers; | |
using System.Reflection; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace System.Net.Http | |
{ | |
public class OAuthHttpClient : HttpClient | |
{ | |
public OAuthHttpClient() | |
: base(new OAuthHttpClientHandler()) | |
{ | |
var type = typeof(HttpMessageInvoker); | |
var flags = BindingFlags.Instance | BindingFlags.NonPublic; | |
var field = type.GetField("handler", flags) | |
?? type.GetField("_handler", flags); | |
Handler = (OAuthHttpClientHandler)field.GetValue(this); | |
Handler.Client = this; | |
} | |
public string ClientId { get; set; } | |
public string ClientSecret { get; set; } | |
public string UserName { get; set; } | |
public string Password { get; set; } | |
public string TokenUrl { get; set; } | |
public string AuthenticationType { get; set; } = OAuthDefaults.AuthenticationType; | |
public BearerToken Token { get; set; } | |
public BearerToken CurrentToken { get; set; } | |
public bool IsValid | |
{ | |
get | |
{ | |
return CurrentToken != null && !CurrentToken.HasExpired; | |
} | |
} | |
private readonly OAuthHttpClientHandler Handler; | |
private class OAuthHttpClientHandler : HttpClientHandler | |
{ | |
public OAuthHttpClient Client { get; set; } | |
public virtual async Task RequestTokenAsync() | |
{ | |
if (Client.IsValid) | |
return; | |
var data = new | |
{ | |
grant_type = "password", | |
client_id = Client.ClientId, | |
client_secret = Client.ClientSecret, | |
username = Client.UserName, | |
password = Client.Password | |
}; | |
var time = DateTime.UtcNow; | |
var tokenUrl = new Uri(Client.BaseAddress, Client.TokenUrl ?? "/token"); | |
var response = await base.SendAsync(new HttpRequestMessage(HttpMethod.Post, tokenUrl) { Content = GetObjectContent(data) }, CancellationToken.None); | |
var result = await response.Content.ReadAsAsync<dynamic>(); | |
Client.Token = new BearerToken(result.access_token, result.token_type, time, TimeSpan.FromSeconds(result.expires_in)); | |
} | |
private static ObjectContent<T> GetObjectContent<T>(T value) => new ObjectContent<T>(value, new JsonMediaTypeFormatter()); | |
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) | |
{ | |
await RequestTokenAsync(); | |
request.Headers.Authorization = new AuthenticationHeaderValue(Client.AuthenticationType, Client.CurrentToken.Token); | |
return await base.SendAsync(request, cancellationToken); | |
} | |
} | |
} | |
public class BearerToken | |
{ | |
public BearerToken(string token, string type, DateTime issuedUtc, TimeSpan validity) | |
{ | |
_Token = token; | |
_Type = type; | |
_IssuedUtc = issuedUtc; | |
_Validity = validity; | |
} | |
private readonly string _Type; | |
public string Type | |
{ | |
get | |
{ | |
return _Type; | |
} | |
} | |
private readonly string _Token; | |
public string Token | |
{ | |
get | |
{ | |
return _Token; | |
} | |
} | |
private readonly DateTime _IssuedUtc; | |
public DateTime Issued | |
{ | |
get | |
{ | |
return _IssuedUtc; | |
} | |
} | |
private readonly TimeSpan _Validity; | |
public TimeSpan Validity | |
{ | |
get | |
{ | |
return _Validity; | |
} | |
} | |
public DateTime ExpiryUtc | |
{ | |
get | |
{ | |
return Issued.Add(Validity); | |
} | |
} | |
public bool HasExpired | |
{ | |
get | |
{ | |
return DateTime.UtcNow > ExpiryUtc; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment