summaryrefslogtreecommitdiffstats
path: root/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2013-03-26 11:19:06 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2013-03-26 11:19:06 -0700
commit3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb (patch)
treec15816c3d7f6e74334553f2ff98605ce1c22c538 /samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
parent5e9014f36b2d53b8e419918675df636540ea24e2 (diff)
parente6f7409f4caceb7bc2a5b4ddbcb1a4097af340f2 (diff)
downloadDotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.zip
DotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.tar.gz
DotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.tar.bz2
Move to HttpClient throughout library.
Diffstat (limited to 'samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs')
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs292
1 files changed, 162 insertions, 130 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
index 013e66b..16f1c92 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
@@ -11,71 +11,80 @@ namespace DotNetOpenAuth.ApplicationBlock {
using System.Globalization;
using System.IO;
using System.Net;
+ using System.Net.Http;
+ using System.Net.Http.Headers;
+ using System.Runtime.Serialization.Json;
+ using System.Threading;
+ using System.Threading.Tasks;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
+ using Newtonsoft.Json.Linq;
+
/// <summary>
/// A consumer capable of communicating with Twitter.
/// </summary>
- public static class TwitterConsumer {
+ public class TwitterConsumer : Consumer {
/// <summary>
/// The description of Twitter's OAuth protocol URIs for use with actually reading/writing
/// a user's private Twitter data.
/// </summary>
- public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription {
- RequestTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- UserAuthorizationEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/authorize", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- AccessTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
- };
+ public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription(
+ "https://api.twitter.com/oauth/request_token",
+ "https://api.twitter.com/oauth/authorize",
+ "https://api.twitter.com/oauth/access_token");
/// <summary>
/// The description of Twitter's OAuth protocol URIs for use with their "Sign in with Twitter" feature.
/// </summary>
- public static readonly ServiceProviderDescription SignInWithTwitterServiceDescription = new ServiceProviderDescription {
- RequestTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- UserAuthorizationEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/authenticate", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- AccessTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
- };
+ public static readonly ServiceProviderDescription SignInWithTwitterServiceDescription = new ServiceProviderDescription(
+ "https://api.twitter.com/oauth/request_token",
+ "https://api.twitter.com/oauth/authenticate",
+ "https://api.twitter.com/oauth/access_token");
/// <summary>
/// The URI to get a user's favorites.
/// </summary>
- private static readonly MessageReceivingEndpoint GetFavoritesEndpoint = new MessageReceivingEndpoint("http://twitter.com/favorites.xml", HttpDeliveryMethods.GetRequest);
+ private static readonly Uri GetFavoritesEndpoint = new Uri("http://twitter.com/favorites.xml");
/// <summary>
/// The URI to get the data on the user's home page.
/// </summary>
- private static readonly MessageReceivingEndpoint GetFriendTimelineStatusEndpoint = new MessageReceivingEndpoint("http://twitter.com/statuses/friends_timeline.xml", HttpDeliveryMethods.GetRequest);
+ private static readonly Uri GetFriendTimelineStatusEndpoint = new Uri("https://api.twitter.com/1.1/statuses/home_timeline.json");
- private static readonly MessageReceivingEndpoint UpdateProfileBackgroundImageEndpoint = new MessageReceivingEndpoint("http://twitter.com/account/update_profile_background_image.xml", HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);
+ private static readonly Uri UpdateProfileBackgroundImageEndpoint = new Uri("http://twitter.com/account/update_profile_background_image.xml");
- private static readonly MessageReceivingEndpoint UpdateProfileImageEndpoint = new MessageReceivingEndpoint("http://twitter.com/account/update_profile_image.xml", HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);
+ private static readonly Uri UpdateProfileImageEndpoint = new Uri("http://twitter.com/account/update_profile_image.xml");
- private static readonly MessageReceivingEndpoint VerifyCredentialsEndpoint = new MessageReceivingEndpoint("http://api.twitter.com/1/account/verify_credentials.xml", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);
+ private static readonly Uri VerifyCredentialsEndpoint = new Uri("http://api.twitter.com/1/account/verify_credentials.xml");
/// <summary>
- /// The consumer used for the Sign in to Twitter feature.
+ /// Initializes a new instance of the <see cref="TwitterConsumer"/> class.
/// </summary>
- private static WebConsumer signInConsumer;
-
- /// <summary>
- /// The lock acquired to initialize the <see cref="signInConsumer"/> field.
- /// </summary>
- private static object signInConsumerInitLock = new object();
+ public TwitterConsumer() {
+ this.ServiceProvider = ServiceDescription;
+ this.ConsumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"];
+ this.ConsumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"];
+ this.TemporaryCredentialStorage = HttpContext.Current != null
+ ? (ITemporaryCredentialStorage)new CookieTemporaryCredentialStorage()
+ : new MemoryTemporaryCredentialStorage();
+ }
/// <summary>
- /// Initializes static members of the <see cref="TwitterConsumer"/> class.
+ /// Initializes a new instance of the <see cref="TwitterConsumer"/> class.
/// </summary>
- static TwitterConsumer() {
- // Twitter can't handle the Expect 100 Continue HTTP header.
- ServicePointManager.FindServicePoint(GetFavoritesEndpoint.Location).Expect100Continue = false;
+ /// <param name="consumerKey">The consumer key.</param>
+ /// <param name="consumerSecret">The consumer secret.</param>
+ public TwitterConsumer(string consumerKey, string consumerSecret)
+ : this() {
+ this.ConsumerKey = consumerKey;
+ this.ConsumerSecret = consumerSecret;
}
/// <summary>
@@ -88,137 +97,160 @@ namespace DotNetOpenAuth.ApplicationBlock {
}
}
+ public static Consumer CreateConsumer(bool forWeb = true) {
+ string consumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"];
+ string consumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"];
+ if (IsTwitterConsumerConfigured) {
+ ITemporaryCredentialStorage storage = forWeb ? (ITemporaryCredentialStorage)new CookieTemporaryCredentialStorage() : new MemoryTemporaryCredentialStorage();
+ return new Consumer(consumerKey, consumerSecret, ServiceDescription, storage) {
+ HostFactories = new TwitterHostFactories(),
+ };
+ } else {
+ throw new InvalidOperationException("No Twitter OAuth consumer key and secret could be found in web.config AppSettings.");
+ }
+ }
+
/// <summary>
- /// Gets the consumer to use for the Sign in to Twitter feature.
+ /// Prepares a redirect that will send the user to Twitter to sign in.
/// </summary>
- /// <value>The twitter sign in.</value>
- private static WebConsumer TwitterSignIn {
- get {
- if (signInConsumer == null) {
- lock (signInConsumerInitLock) {
- if (signInConsumer == null) {
- signInConsumer = new WebConsumer(SignInWithTwitterServiceDescription, ShortTermUserSessionTokenManager);
- }
- }
- }
+ /// <param name="forceNewLogin">if set to <c>true</c> the user will be required to re-enter their Twitter credentials even if already logged in to Twitter.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The redirect message.
+ /// </returns>
+ public static async Task<Uri> StartSignInWithTwitterAsync(bool forceNewLogin = false, CancellationToken cancellationToken = default(CancellationToken)) {
+ var redirectParameters = new Dictionary<string, string>();
+ if (forceNewLogin) {
+ redirectParameters["force_login"] = "true";
+ }
+ Uri callback = MessagingUtilities.GetRequestUrlFromContext().StripQueryArgumentsWithPrefix("oauth_");
+
+ var consumer = CreateConsumer();
+ consumer.ServiceProvider = SignInWithTwitterServiceDescription;
+ Uri redirectUrl = await consumer.RequestUserAuthorizationAsync(callback, cancellationToken: cancellationToken);
+ return redirectUrl;
+ }
- return signInConsumer;
+ /// <summary>
+ /// Checks the incoming web request to see if it carries a Twitter authentication response,
+ /// and provides the user's Twitter screen name and unique id if available.
+ /// </summary>
+ /// <param name="completeUrl">The URL that came back from the service provider to complete the authorization.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// A tuple with the screen name and Twitter unique user ID if successful; otherwise <c>null</c>.
+ /// </returns>
+ public static async Task<Tuple<string, int>> TryFinishSignInWithTwitterAsync(Uri completeUrl = null, CancellationToken cancellationToken = default(CancellationToken)) {
+ var consumer = CreateConsumer();
+ consumer.ServiceProvider = SignInWithTwitterServiceDescription;
+ var response = await consumer.ProcessUserAuthorizationAsync(completeUrl ?? HttpContext.Current.Request.Url, cancellationToken: cancellationToken);
+ if (response == null) {
+ return null;
}
+
+ string screenName = response.ExtraData["screen_name"];
+ int userId = int.Parse(response.ExtraData["user_id"]);
+ return Tuple.Create(screenName, userId);
}
- private static InMemoryTokenManager ShortTermUserSessionTokenManager {
- get {
- var store = HttpContext.Current.Session;
- var tokenManager = (InMemoryTokenManager)store["TwitterShortTermUserSessionTokenManager"];
- if (tokenManager == null) {
- string consumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"];
- string consumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"];
- if (IsTwitterConsumerConfigured) {
- tokenManager = new InMemoryTokenManager(consumerKey, consumerSecret);
- store["TwitterShortTermUserSessionTokenManager"] = tokenManager;
- } else {
- throw new InvalidOperationException("No Twitter OAuth consumer key and secret could be found in web.config AppSettings.");
- }
- }
+ public async Task<JArray> GetUpdatesAsync(AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ if (String.IsNullOrEmpty(accessToken.Token)) {
+ throw new ArgumentNullException("accessToken.Token");
+ }
- return tokenManager;
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
+ using (var response = await httpClient.GetAsync(GetFriendTimelineStatusEndpoint, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ string jsonString = await response.Content.ReadAsStringAsync();
+ var json = JArray.Parse(jsonString);
+ return json;
+ }
}
}
- public static XDocument GetUpdates(ConsumerBase twitter, string accessToken) {
- IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(GetFriendTimelineStatusEndpoint, accessToken);
- return XDocument.Load(XmlReader.Create(response.GetResponseReader()));
- }
+ public async Task<XDocument> GetFavorites(AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ if (String.IsNullOrEmpty(accessToken.Token)) {
+ throw new ArgumentNullException("accessToken.Token");
+ }
- public static XDocument GetFavorites(ConsumerBase twitter, string accessToken) {
- IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(GetFavoritesEndpoint, accessToken);
- return XDocument.Load(XmlReader.Create(response.GetResponseReader()));
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
+ using (HttpResponseMessage response = await httpClient.GetAsync(GetFavoritesEndpoint, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ return XDocument.Parse(await response.Content.ReadAsStringAsync());
+ }
+ }
}
- public static XDocument UpdateProfileBackgroundImage(ConsumerBase twitter, string accessToken, string image, bool tile) {
- var parts = new[] {
- MultipartPostPart.CreateFormFilePart("image", image, "image/" + Path.GetExtension(image).Substring(1).ToLowerInvariant()),
- MultipartPostPart.CreateFormPart("tile", tile.ToString().ToLowerInvariant()),
- };
- HttpWebRequest request = twitter.PrepareAuthorizedRequest(UpdateProfileBackgroundImageEndpoint, accessToken, parts);
- request.ServicePoint.Expect100Continue = false;
- IncomingWebResponse response = twitter.Channel.WebRequestHandler.GetResponse(request);
- string responseString = response.GetResponseReader().ReadToEnd();
- return XDocument.Parse(responseString);
+ public async Task<XDocument> UpdateProfileBackgroundImageAsync(AccessToken accessToken, string image, bool tile, CancellationToken cancellationToken) {
+ var imageAttachment = new StreamContent(File.OpenRead(image));
+ imageAttachment.Headers.ContentType = new MediaTypeHeaderValue("image/" + Path.GetExtension(image).Substring(1).ToLowerInvariant());
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, UpdateProfileBackgroundImageEndpoint);
+ var content = new MultipartFormDataContent();
+ content.Add(imageAttachment, "image");
+ content.Add(new StringContent(tile.ToString().ToLowerInvariant()), "tile");
+ request.Content = content;
+ request.Headers.ExpectContinue = false;
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
+ using (HttpResponseMessage response = await httpClient.SendAsync(request, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ string responseString = await response.Content.ReadAsStringAsync();
+ return XDocument.Parse(responseString);
+ }
+ }
}
- public static XDocument UpdateProfileImage(ConsumerBase twitter, string accessToken, string pathToImage) {
+ public Task<XDocument> UpdateProfileImageAsync(AccessToken accessToken, string pathToImage, CancellationToken cancellationToken = default(CancellationToken)) {
string contentType = "image/" + Path.GetExtension(pathToImage).Substring(1).ToLowerInvariant();
- return UpdateProfileImage(twitter, accessToken, File.OpenRead(pathToImage), contentType);
+ return this.UpdateProfileImageAsync(accessToken, File.OpenRead(pathToImage), contentType, cancellationToken);
}
- public static XDocument UpdateProfileImage(ConsumerBase twitter, string accessToken, Stream image, string contentType) {
- var parts = new[] {
- MultipartPostPart.CreateFormFilePart("image", "twitterPhoto", contentType, image),
- };
- HttpWebRequest request = twitter.PrepareAuthorizedRequest(UpdateProfileImageEndpoint, accessToken, parts);
- IncomingWebResponse response = twitter.Channel.WebRequestHandler.GetResponse(request);
- string responseString = response.GetResponseReader().ReadToEnd();
- return XDocument.Parse(responseString);
+ public async Task<XDocument> UpdateProfileImageAsync(AccessToken accessToken, Stream image, string contentType, CancellationToken cancellationToken = default(CancellationToken)) {
+ var imageAttachment = new StreamContent(image);
+ imageAttachment.Headers.ContentType = new MediaTypeHeaderValue(contentType);
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, UpdateProfileImageEndpoint);
+ var content = new MultipartFormDataContent();
+ content.Add(imageAttachment, "image", "twitterPhoto");
+ request.Content = content;
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
+ using (HttpResponseMessage response = await httpClient.SendAsync(request, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ string responseString = await response.Content.ReadAsStringAsync();
+ return XDocument.Parse(responseString);
+ }
+ }
}
- public static XDocument VerifyCredentials(ConsumerBase twitter, string accessToken) {
- IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(VerifyCredentialsEndpoint, accessToken);
- return XDocument.Load(XmlReader.Create(response.GetResponseReader()));
+ public async Task<XDocument> VerifyCredentialsAsync(AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
+ using (var response = await httpClient.GetAsync(VerifyCredentialsEndpoint, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ using (var stream = await response.Content.ReadAsStreamAsync()) {
+ return XDocument.Load(XmlReader.Create(stream));
+ }
+ }
+ }
}
- public static string GetUsername(ConsumerBase twitter, string accessToken) {
- XDocument xml = VerifyCredentials(twitter, accessToken);
+ public async Task<string> GetUsername(AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ XDocument xml = await this.VerifyCredentialsAsync(accessToken, cancellationToken);
XPathNavigator nav = xml.CreateNavigator();
return nav.SelectSingleNode("/user/screen_name").Value;
}
- /// <summary>
- /// Prepares a redirect that will send the user to Twitter to sign in.
- /// </summary>
- /// <param name="forceNewLogin">if set to <c>true</c> the user will be required to re-enter their Twitter credentials even if already logged in to Twitter.</param>
- /// <returns>The redirect message.</returns>
- /// <remarks>
- /// Call <see cref="OutgoingWebResponse.Send"/> or
- /// <c>return StartSignInWithTwitter().<see cref="MessagingUtilities.AsActionResult">AsActionResult()</see></c>
- /// to actually perform the redirect.
- /// </remarks>
- public static OutgoingWebResponse StartSignInWithTwitter(bool forceNewLogin) {
- var redirectParameters = new Dictionary<string, string>();
- if (forceNewLogin) {
- redirectParameters["force_login"] = "true";
- }
- Uri callback = MessagingUtilities.GetRequestUrlFromContext().StripQueryArgumentsWithPrefix("oauth_");
- var request = TwitterSignIn.PrepareRequestUserAuthorization(callback, null, redirectParameters);
- return TwitterSignIn.Channel.PrepareResponse(request);
- }
+ private class TwitterHostFactories : IHostFactories {
+ private static readonly IHostFactories underlyingFactories = new DefaultOAuthHostFactories();
- /// <summary>
- /// Checks the incoming web request to see if it carries a Twitter authentication response,
- /// and provides the user's Twitter screen name and unique id if available.
- /// </summary>
- /// <param name="screenName">The user's Twitter screen name.</param>
- /// <param name="userId">The user's Twitter unique user ID.</param>
- /// <returns>
- /// A value indicating whether Twitter authentication was successful;
- /// otherwise <c>false</c> to indicate that no Twitter response was present.
- /// </returns>
- public static bool TryFinishSignInWithTwitter(out string screenName, out int userId) {
- screenName = null;
- userId = 0;
- var response = TwitterSignIn.ProcessUserAuthorization();
- if (response == null) {
- return false;
+ public HttpMessageHandler CreateHttpMessageHandler() {
+ return new WebRequestHandler();
}
- screenName = response.ExtraData["screen_name"];
- userId = int.Parse(response.ExtraData["user_id"]);
+ public HttpClient CreateHttpClient(HttpMessageHandler handler = null) {
+ var client = underlyingFactories.CreateHttpClient(handler);
- // If we were going to make this LOOK like OpenID even though it isn't,
- // this seems like a reasonable, secure claimed id to allow the user to assume.
- OpenId.Identifier fake_claimed_id = string.Format(CultureInfo.InvariantCulture, "http://twitter.com/{0}#{1}", screenName, userId);
-
- return true;
+ // Twitter can't handle the Expect 100 Continue HTTP header.
+ client.DefaultRequestHeaders.ExpectContinue = false;
+ return client;
+ }
}
}
}