diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2013-02-25 21:26:04 -0800 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2013-02-25 21:26:04 -0800 |
commit | 38a1162c5cbaea035e655dc9accd92f9de5019ed (patch) | |
tree | 489ba7dfa106d5b0a8878ac386f2d2130bdf6b21 /samples/DotNetOpenAuth.ApplicationBlock | |
parent | 10fc3ad3a7feda0cb5ab64aabe2e26bbce94595a (diff) | |
download | DotNetOpenAuth-38a1162c5cbaea035e655dc9accd92f9de5019ed.zip DotNetOpenAuth-38a1162c5cbaea035e655dc9accd92f9de5019ed.tar.gz DotNetOpenAuth-38a1162c5cbaea035e655dc9accd92f9de5019ed.tar.bz2 |
OAuth 1.0 Consumers are now *much* simpler, entirely avoiding channels.
Build breaks in other projects, however.
Diffstat (limited to 'samples/DotNetOpenAuth.ApplicationBlock')
5 files changed, 93 insertions, 334 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj index 9f74693..19f26b5 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj +++ b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj @@ -101,9 +101,6 @@ <Compile Include="GoogleConsumer.cs" /> <Compile Include="HttpAsyncHandlerBase.cs" /> <Compile Include="InMemoryClientAuthorizationTracker.cs" /> - <Compile Include="InMemoryTokenManager.cs"> - <SubType>Code</SubType> - </Compile> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="TwitterConsumer.cs" /> <Compile Include="Util.cs" /> @@ -138,6 +135,10 @@ <Project>{60426312-6AE5-4835-8667-37EDEA670222}</Project> <Name>DotNetOpenAuth.Core</Name> </ProjectReference> + <ProjectReference Include="..\..\src\DotNetOpenAuth.OAuth.Common\DotNetOpenAuth.OAuth.Common.csproj"> + <Project>{115217c5-22cd-415c-a292-0dd0238cdd89}</Project> + <Name>DotNetOpenAuth.OAuth.Common</Name> + </ProjectReference> <ProjectReference Include="..\..\src\DotNetOpenAuth.OAuth.Consumer\DotNetOpenAuth.OAuth.Consumer.csproj"> <Project>{B202E40D-4663-4A2B-ACDA-865F88FF7CAA}</Project> <Name>DotNetOpenAuth.OAuth.Consumer</Name> diff --git a/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs index cd3c5fe..2302837 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs @@ -4,8 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.ApplicationBlock -{ +namespace DotNetOpenAuth.ApplicationBlock { using System; using System.Collections.Generic; using System.Diagnostics; @@ -29,17 +28,15 @@ namespace DotNetOpenAuth.ApplicationBlock /// <summary> /// A consumer capable of communicating with Google Data APIs. /// </summary> - public static class GoogleConsumer - { + public class GoogleConsumer : Consumer { /// <summary> /// The Consumer to use for accessing Google data APIs. /// </summary> - public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription { - RequestTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest), - UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthAuthorizeToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest), - AccessTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetAccessToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest), - TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() }, - }; + public static readonly ServiceProviderDescription ServiceDescription = + new ServiceProviderDescription( + "https://www.google.com/accounts/OAuthGetRequestToken", + "https://www.google.com/accounts/OAuthAuthorizeToken", + "https://www.google.com/accounts/OAuthGetAccessToken"); /// <summary> /// A mapping between Google's applications and their URI scope values. @@ -72,8 +69,7 @@ namespace DotNetOpenAuth.ApplicationBlock /// The many specific authorization scopes Google offers. /// </summary> [Flags] - public enum Applications : long - { + public enum Applications : long { /// <summary> /// The Gmail address book. /// </summary> @@ -155,23 +151,7 @@ namespace DotNetOpenAuth.ApplicationBlock Maps = 0x8000, } - /// <summary> - /// The service description to use for accessing Google data APIs using an X509 certificate. - /// </summary> - /// <param name="signingCertificate">The signing certificate.</param> - /// <returns>A service description that can be used to create an instance of - /// <see cref="DesktopConsumer"/> or <see cref="WebConsumer"/>. </returns> - public static ServiceProviderDescription CreateRsaSha1ServiceDescription(X509Certificate2 signingCertificate) { - if (signingCertificate == null) { - throw new ArgumentNullException("signingCertificate"); - } - - return new ServiceProviderDescription { - RequestTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest), - UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthAuthorizeToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest), - AccessTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetAccessToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest), - TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new RsaSha1ConsumerSigningBindingElement(signingCertificate) }, - }; + public GoogleConsumer() { } /// <summary> @@ -184,40 +164,12 @@ namespace DotNetOpenAuth.ApplicationBlock /// A task that completes with the asynchronous operation. /// </returns> /// <exception cref="System.ArgumentNullException">consumer</exception> - public static async Task RequestAuthorizationAsync(WebConsumer consumer, Applications requestedAccessScope, CancellationToken cancellationToken = default(CancellationToken)) { - if (consumer == null) { - throw new ArgumentNullException("consumer"); - } - + public Task<Uri> RequestUserAuthorizationAsync(Applications requestedAccessScope, CancellationToken cancellationToken = default(CancellationToken)) { var extraParameters = new Dictionary<string, string> { { "scope", GetScopeUri(requestedAccessScope) }, }; Uri callback = Util.GetCallbackUrlFromContext(); - var request = await consumer.PrepareRequestUserAuthorizationAsync(callback, extraParameters, null, cancellationToken); - var redirectingResponse = await consumer.Channel.PrepareResponseAsync(request, cancellationToken); - redirectingResponse.Send(); - } - - /// <summary> - /// Requests authorization from Google to access data from a set of Google applications. - /// </summary> - /// <param name="consumer">The Google consumer previously constructed using <see cref="CreateWebConsumer" /> or <see cref="CreateDesktopConsumer" />.</param> - /// <param name="requestedAccessScope">The requested access scope.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns> - /// The URI to redirect to and the request token. - /// </returns> - /// <exception cref="System.ArgumentNullException">consumer</exception> - public static Task<Tuple<Uri, string>> RequestAuthorizationAsync(DesktopConsumer consumer, Applications requestedAccessScope, CancellationToken cancellationToken = default(CancellationToken)) { - if (consumer == null) { - throw new ArgumentNullException("consumer"); - } - - var extraParameters = new Dictionary<string, string> { - { "scope", GetScopeUri(requestedAccessScope) }, - }; - - return consumer.RequestUserAuthorizationAsync(extraParameters, null, cancellationToken); + return this.RequestUserAuthorizationAsync(callback, extraParameters, cancellationToken); } /// <summary> @@ -232,14 +184,10 @@ namespace DotNetOpenAuth.ApplicationBlock /// An XML document returned by Google. /// </returns> /// <exception cref="System.ArgumentNullException">consumer</exception> - public static async Task<XDocument> GetContactsAsync(ConsumerBase consumer, string accessToken, int maxResults = 25, int startIndex = 1, CancellationToken cancellationToken = default(CancellationToken)) { - if (consumer == null) { - throw new ArgumentNullException("consumer"); - } - + public async Task<XDocument> GetContactsAsync(AccessToken accessToken, int maxResults = 25, int startIndex = 1, CancellationToken cancellationToken = default(CancellationToken)) { // Enable gzip compression. Google only compresses the response for recognized user agent headers. - Mike Lim var handler = new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip }; - using (var httpClient = consumer.CreateHttpClient(accessToken, handler)) { + using (var httpClient = this.CreateHttpClient(accessToken, handler)) { var request = new HttpRequestMessage(HttpMethod.Get, GetContactsEndpoint); request.Content = new FormUrlEncodedContent( new Dictionary<string, string>() { @@ -255,7 +203,7 @@ namespace DotNetOpenAuth.ApplicationBlock } } - public static async Task PostBlogEntryAsync(ConsumerBase consumer, string accessToken, string blogUrl, string title, XElement body, CancellationToken cancellationToken) { + public async Task PostBlogEntryAsync(AccessToken accessToken, string blogUrl, string title, XElement body, CancellationToken cancellationToken) { string feedUrl; var getBlogHome = WebRequest.Create(blogUrl); using (var blogHomeResponse = getBlogHome.GetResponse()) { @@ -285,7 +233,7 @@ namespace DotNetOpenAuth.ApplicationBlock var request = new HttpRequestMessage(HttpMethod.Post, feedUrl); request.Content = new StreamContent(ms); request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/atom+xml"); - using (var httpClient = consumer.CreateHttpClient(accessToken)) { + using (var httpClient = this.CreateHttpClient(accessToken)) { using (var response = await httpClient.SendAsync(request, cancellationToken)) { if (response.StatusCode == HttpStatusCode.Created) { // Success diff --git a/samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs b/samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs deleted file mode 100644 index 35f6c08..0000000 --- a/samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs +++ /dev/null @@ -1,147 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="InMemoryTokenManager.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.ApplicationBlock { - using System; - using System.Collections.Generic; - using System.Diagnostics; - using DotNetOpenAuth.OAuth.ChannelElements; - using DotNetOpenAuth.OAuth.Messages; - using DotNetOpenAuth.OpenId.Extensions.OAuth; - -#if SAMPLESONLY - /// <summary> - /// A token manager that only retains tokens in memory. - /// Meant for SHORT TERM USE TOKENS ONLY. - /// </summary> - /// <remarks> - /// A likely application of this class is for "Sign In With Twitter", - /// where the user only signs in without providing any authorization to access - /// Twitter APIs except to authenticate, since that access token is only useful once. - /// </remarks> - internal class InMemoryTokenManager : IConsumerTokenManager, IOpenIdOAuthTokenManager { - private Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>(); - - /// <summary> - /// Initializes a new instance of the <see cref="InMemoryTokenManager"/> class. - /// </summary> - /// <param name="consumerKey">The consumer key.</param> - /// <param name="consumerSecret">The consumer secret.</param> - public InMemoryTokenManager(string consumerKey, string consumerSecret) { - if (string.IsNullOrEmpty(consumerKey)) { - throw new ArgumentNullException("consumerKey"); - } - - this.ConsumerKey = consumerKey; - this.ConsumerSecret = consumerSecret; - } - - /// <summary> - /// Gets the consumer key. - /// </summary> - /// <value>The consumer key.</value> - public string ConsumerKey { get; private set; } - - /// <summary> - /// Gets the consumer secret. - /// </summary> - /// <value>The consumer secret.</value> - public string ConsumerSecret { get; private set; } - - #region ITokenManager Members - - /// <summary> - /// Gets the Token Secret given a request or access token. - /// </summary> - /// <param name="token">The request or access token.</param> - /// <returns> - /// The secret associated with the given token. - /// </returns> - /// <exception cref="ArgumentException">Thrown if the secret cannot be found for the given token.</exception> - public string GetTokenSecret(string token) { - return this.tokensAndSecrets[token]; - } - - /// <summary> - /// Stores a newly generated unauthorized request token, secret, and optional - /// application-specific parameters for later recall. - /// </summary> - /// <param name="request">The request message that resulted in the generation of a new unauthorized request token.</param> - /// <param name="response">The response message that includes the unauthorized request token.</param> - /// <exception cref="ArgumentException">Thrown if the consumer key is not registered, or a required parameter was not found in the parameters collection.</exception> - /// <remarks> - /// Request tokens stored by this method SHOULD NOT associate any user account with this token. - /// It usually opens up security holes in your application to do so. Instead, you associate a user - /// account with access tokens (not request tokens) in the <see cref="ExpireRequestTokenAndStoreNewAccessToken"/> - /// method. - /// </remarks> - public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) { - this.tokensAndSecrets[response.Token] = response.TokenSecret; - } - - /// <summary> - /// Deletes a request token and its associated secret and stores a new access token and secret. - /// </summary> - /// <param name="consumerKey">The Consumer that is exchanging its request token for an access token.</param> - /// <param name="requestToken">The Consumer's request token that should be deleted/expired.</param> - /// <param name="accessToken">The new access token that is being issued to the Consumer.</param> - /// <param name="accessTokenSecret">The secret associated with the newly issued access token.</param> - /// <remarks> - /// <para> - /// Any scope of granted privileges associated with the request token from the - /// original call to <see cref="StoreNewRequestToken"/> should be carried over - /// to the new Access Token. - /// </para> - /// <para> - /// To associate a user account with the new access token, - /// <see cref="System.Web.HttpContext.User">HttpContext.Current.User</see> may be - /// useful in an ASP.NET web application within the implementation of this method. - /// Alternatively you may store the access token here without associating with a user account, - /// and wait until <see cref="WebConsumer.ProcessUserAuthorization()"/> or - /// <see cref="DesktopConsumer.ProcessUserAuthorization(string, string)"/> return the access - /// token to associate the access token with a user account at that point. - /// </para> - /// </remarks> - public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) { - this.tokensAndSecrets.Remove(requestToken); - this.tokensAndSecrets[accessToken] = accessTokenSecret; - } - - /// <summary> - /// Classifies a token as a request token or an access token. - /// </summary> - /// <param name="token">The token to classify.</param> - /// <returns>Request or Access token, or invalid if the token is not recognized.</returns> - public TokenType GetTokenType(string token) { - throw new NotImplementedException(); - } - - #endregion - - #region IOpenIdOAuthTokenManager Members - - /// <summary> - /// Stores a new request token obtained over an OpenID request. - /// </summary> - /// <param name="consumerKey">The consumer key.</param> - /// <param name="authorization">The authorization message carrying the request token and authorized access scope.</param> - /// <remarks> - /// <para>The token secret is the empty string.</para> - /// <para>Tokens stored by this method should be short-lived to mitigate - /// possible security threats. Their lifetime should be sufficient for the - /// relying party to receive the positive authentication assertion and immediately - /// send a follow-up request for the access token.</para> - /// </remarks> - public void StoreOpenIdAuthorizedRequestToken(string consumerKey, AuthorizationApprovedResponse authorization) { - this.tokensAndSecrets[authorization.RequestToken] = string.Empty; - } - - #endregion - } -#else -#error The InMemoryTokenManager class is only for samples as it forgets all tokens whenever the application restarts! You should implement IConsumerTokenManager in your own app that stores tokens in a persistent store (like a SQL database). -#endif -}
\ No newline at end of file diff --git a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs index e665d96..7bbfaa1 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs @@ -35,22 +35,18 @@ namespace DotNetOpenAuth.ApplicationBlock { /// 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. @@ -68,22 +64,33 @@ namespace DotNetOpenAuth.ApplicationBlock { 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. - /// </summary> - private static WebConsumer signInConsumer; + private class HostFactories : IHostFactories { + private static readonly IHostFactories underlyingFactories = new DefaultOAuthHostFactories(); - /// <summary> - /// The lock acquired to initialize the <see cref="signInConsumer"/> field. - /// </summary> - private static object signInConsumerInitLock = new object(); + public HttpMessageHandler CreateHttpMessageHandler() { + return new WebRequestHandler(); + } - /// <summary> - /// Initializes static members of the <see cref="TwitterConsumer"/> class. - /// </summary> - static TwitterConsumer() { - // Twitter can't handle the Expect 100 Continue HTTP header. - ServicePointManager.FindServicePoint(GetFavoritesEndpoint).Expect100Continue = false; + public HttpClient CreateHttpClient(HttpMessageHandler handler = null) { + var client = underlyingFactories.CreateHttpClient(handler); + + // Twitter can't handle the Expect 100 Continue HTTP header. + client.DefaultRequestHeaders.ExpectContinue = false; + return client; + } + } + + 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 HostFactories(), + }; + } else { + throw new InvalidOperationException("No Twitter OAuth consumer key and secret could be found in web.config AppSettings."); + } } /// <summary> @@ -96,45 +103,7 @@ namespace DotNetOpenAuth.ApplicationBlock { } } - /// <summary> - /// Gets the consumer to use for the Sign in to Twitter feature. - /// </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); - } - } - } - - return signInConsumer; - } - } - - 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."); - } - } - - return tokenManager; - } - } - - public static async Task<JArray> GetUpdatesAsync( - ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) { + public static async Task<JArray> GetUpdatesAsync(Consumer twitter, AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) { using (var httpClient = twitter.CreateHttpClient(accessToken)) { using (var response = await httpClient.GetAsync(GetFriendTimelineStatusEndpoint, cancellationToken)) { response.EnsureSuccessStatusCode(); @@ -145,7 +114,7 @@ namespace DotNetOpenAuth.ApplicationBlock { } } - public static async Task<XDocument> GetFavorites(ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) { + public static async Task<XDocument> GetFavorites(Consumer twitter, AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) { using (var httpClient = twitter.CreateHttpClient(accessToken)) { using (HttpResponseMessage response = await httpClient.GetAsync(GetFavoritesEndpoint, cancellationToken)) { response.EnsureSuccessStatusCode(); @@ -154,7 +123,7 @@ namespace DotNetOpenAuth.ApplicationBlock { } } - public static async Task<XDocument> UpdateProfileBackgroundImageAsync(ConsumerBase twitter, string accessToken, string image, bool tile, CancellationToken cancellationToken) { + public static async Task<XDocument> UpdateProfileBackgroundImageAsync(Consumer twitter, 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); @@ -172,12 +141,12 @@ namespace DotNetOpenAuth.ApplicationBlock { } } - public static Task<XDocument> UpdateProfileImageAsync(ConsumerBase twitter, string accessToken, string pathToImage, CancellationToken cancellationToken = default(CancellationToken)) { + public static Task<XDocument> UpdateProfileImageAsync(Consumer twitter, AccessToken accessToken, string pathToImage, CancellationToken cancellationToken = default(CancellationToken)) { string contentType = "image/" + Path.GetExtension(pathToImage).Substring(1).ToLowerInvariant(); return UpdateProfileImageAsync(twitter, accessToken, File.OpenRead(pathToImage), contentType, cancellationToken); } - public static async Task<XDocument> UpdateProfileImageAsync(ConsumerBase twitter, string accessToken, Stream image, string contentType, CancellationToken cancellationToken = default(CancellationToken)) { + public static async Task<XDocument> UpdateProfileImageAsync(Consumer twitter, 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); @@ -193,7 +162,7 @@ namespace DotNetOpenAuth.ApplicationBlock { } } - public static async Task<XDocument> VerifyCredentialsAsync(ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) { + public static async Task<XDocument> VerifyCredentialsAsync(Consumer twitter, AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) { using (var httpClient = twitter.CreateHttpClient(accessToken)) { using (var response = await httpClient.GetAsync(VerifyCredentialsEndpoint, cancellationToken)) { response.EnsureSuccessStatusCode(); @@ -204,7 +173,7 @@ namespace DotNetOpenAuth.ApplicationBlock { } } - public static async Task<string> GetUsername(ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) { + public static async Task<string> GetUsername(Consumer twitter, AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) { XDocument xml = await VerifyCredentialsAsync(twitter, accessToken, cancellationToken); XPathNavigator nav = xml.CreateNavigator(); return nav.SelectSingleNode("/user/screen_name").Value; @@ -223,14 +192,17 @@ namespace DotNetOpenAuth.ApplicationBlock { /// <c>return StartSignInWithTwitter().<see cref="MessagingUtilities.AsActionResult">AsActionResult()</see></c> /// to actually perform the redirect. /// </remarks> - public static async Task<HttpResponseMessage> StartSignInWithTwitterAsync(bool forceNewLogin = false, CancellationToken cancellationToken = default(CancellationToken)) { + 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 request = await TwitterSignIn.PrepareRequestUserAuthorizationAsync(callback, null, redirectParameters, cancellationToken); - return await TwitterSignIn.Channel.PrepareResponseAsync(request, cancellationToken); + + var consumer = CreateConsumer(); + consumer.ServiceProvider = SignInWithTwitterServiceDescription; + Uri redirectUrl = await consumer.RequestUserAuthorizationAsync(callback, cancellationToken: cancellationToken); + return redirectUrl; } /// <summary> @@ -241,19 +213,16 @@ namespace DotNetOpenAuth.ApplicationBlock { /// <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(CancellationToken cancellationToken = default(CancellationToken)) { - var response = await TwitterSignIn.ProcessUserAuthorizationAsync(cancellationToken: cancellationToken); + public static async Task<Tuple<string, int>> TryFinishSignInWithTwitterAsync(Uri completeUrl, CancellationToken cancellationToken = default(CancellationToken)) { + var consumer = CreateConsumer(); + consumer.ServiceProvider = SignInWithTwitterServiceDescription; + var response = await consumer.ProcessUserAuthorizationAsync(completeUrl, cancellationToken: cancellationToken); if (response == null) { return null; } string screenName = response.ExtraData["screen_name"]; int userId = int.Parse(response.ExtraData["user_id"]); - - // 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 Tuple.Create(screenName, userId); } } diff --git a/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs index cedcbf7..4bcb112 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs @@ -4,10 +4,10 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.ApplicationBlock -{ +namespace DotNetOpenAuth.ApplicationBlock { using System; using System.Collections.Generic; + using System.Configuration; using System.Linq; using System.Net; using System.Text; @@ -18,47 +18,35 @@ namespace DotNetOpenAuth.ApplicationBlock using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; - public static class YammerConsumer - { + public static class YammerConsumer { /// <summary> /// The Consumer to use for accessing Google data APIs. /// </summary> - public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription { - RequestTokenEndpoint = new MessageReceivingEndpoint("https://www.yammer.com/oauth/request_token", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest), - UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://www.yammer.com/oauth/authorize", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest), - AccessTokenEndpoint = new MessageReceivingEndpoint("https://www.yammer.com/oauth/access_token", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest), - TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new PlaintextSigningBindingElement() }, - ProtocolVersion = ProtocolVersion.V10, - }; + public static readonly ServiceProviderDescription ServiceDescription = + new ServiceProviderDescription( + "https://www.yammer.com/oauth/request_token", + "https://www.yammer.com/oauth/authorize", + "https://www.yammer.com/oauth/access_token"); - public static DesktopConsumer CreateConsumer(IConsumerTokenManager tokenManager) { - return new DesktopConsumer(ServiceDescription, tokenManager); - } - - public static Task<Tuple<Uri, string>> PrepareRequestAuthorizationAsync(DesktopConsumer consumer, CancellationToken cancellationToken = default(CancellationToken)) { - if (consumer == null) { - throw new ArgumentNullException("consumer"); + /// <summary> + /// Gets a value indicating whether the Twitter consumer key and secret are set in the web.config file. + /// </summary> + public static bool IsConsumerConfigured { + get { + return !string.IsNullOrEmpty(ConfigurationManager.AppSettings["yammerConsumerKey"]) && + !string.IsNullOrEmpty(ConfigurationManager.AppSettings["yammerConsumerSecret"]); } - - return consumer.RequestUserAuthorizationAsync(null, null, cancellationToken); } - public static async Task<AuthorizedTokenResponse> CompleteAuthorizationAsync(DesktopConsumer consumer, string requestToken, string userCode, CancellationToken cancellationToken = default(CancellationToken)) { - // Because Yammer has a proprietary callback_token parameter, and it's passed - // with the message that specifically bans extra arguments being passed, we have - // to cheat by adding the data to the URL itself here. - var customServiceDescription = new ServiceProviderDescription { - RequestTokenEndpoint = ServiceDescription.RequestTokenEndpoint, - UserAuthorizationEndpoint = ServiceDescription.UserAuthorizationEndpoint, - AccessTokenEndpoint = new MessageReceivingEndpoint(ServiceDescription.AccessTokenEndpoint.Location.AbsoluteUri + "?oauth_verifier=" + Uri.EscapeDataString(userCode), HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest), - TamperProtectionElements = ServiceDescription.TamperProtectionElements, - ProtocolVersion = ProtocolVersion.V10, - }; - - // To use a custom service description we also must create a new WebConsumer. - var customConsumer = new DesktopConsumer(customServiceDescription, consumer.TokenManager); - var response = await customConsumer.ProcessUserAuthorizationAsync(requestToken, userCode, cancellationToken); - return response; + public static Consumer CreateConsumer(bool forWeb = true) { + string consumerKey = ConfigurationManager.AppSettings["yammerConsumerKey"]; + string consumerSecret = ConfigurationManager.AppSettings["yammerConsumerSecret"]; + if (IsConsumerConfigured) { + ITemporaryCredentialStorage storage = forWeb ? (ITemporaryCredentialStorage)new CookieTemporaryCredentialStorage() : new MemoryTemporaryCredentialStorage(); + return new Consumer(consumerKey, consumerSecret, ServiceDescription, storage); + } else { + throw new InvalidOperationException("No Yammer OAuth consumer key and secret could be found in web.config AppSettings."); + } } } } |