diff options
Diffstat (limited to 'src/DotNetOpenAuth.AspNet/Clients')
17 files changed, 219 insertions, 790 deletions
diff --git a/src/DotNetOpenAuth.AspNet/Clients/DictionaryExtensions.cs b/src/DotNetOpenAuth.AspNet/Clients/DictionaryExtensions.cs index f441c07..a84fdcf 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/DictionaryExtensions.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/DictionaryExtensions.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.AspNet.Clients { using System; using System.Collections.Generic; + using System.Collections.Specialized; using System.Xml.Linq; /// <summary> @@ -25,8 +26,8 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <param name="elementName"> /// Name of the element. /// </param> - public static void AddDataIfNotEmpty( - this Dictionary<string, string> dictionary, XDocument document, string elementName) { + internal static void AddDataIfNotEmpty( + this NameValueCollection dictionary, XDocument document, string elementName) { var element = document.Root.Element(elementName); if (element != null) { dictionary.AddItemIfNotEmpty(elementName, element.Value); @@ -45,7 +46,7 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <param name="value"> /// The value. /// </param> - public static void AddItemIfNotEmpty(this IDictionary<string, string> dictionary, string key, string value) { + internal static void AddItemIfNotEmpty(this NameValueCollection dictionary, string key, string value) { if (key == null) { throw new ArgumentNullException("key"); } diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/AuthenticationOnlyCookieOAuthTokenManager.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/AuthenticationOnlyCookieOAuthTokenManager.cs deleted file mode 100644 index efc382f..0000000 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/AuthenticationOnlyCookieOAuthTokenManager.cs +++ /dev/null @@ -1,127 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="AuthenticationOnlyCookieOAuthTokenManager.cs" company="Microsoft"> -// Copyright (c) Microsoft. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.AspNet.Clients { - using System; - using System.Text; - using System.Web; - using System.Web.Security; - - /// <summary> - /// Stores OAuth tokens in the current request's cookie - /// </summary> - public class AuthenticationOnlyCookieOAuthTokenManager : IOAuthTokenManager { - /// <summary> - /// Key used for token cookie - /// </summary> - protected const string TokenCookieKey = "OAuthTokenSecret"; - - /// <summary> - /// Primary request context. - /// </summary> - private readonly HttpContextBase primaryContext; - - /// <summary> - /// Initializes a new instance of the <see cref="AuthenticationOnlyCookieOAuthTokenManager"/> class. - /// </summary> - public AuthenticationOnlyCookieOAuthTokenManager() { - } - - /// <summary> - /// Initializes a new instance of the <see cref="AuthenticationOnlyCookieOAuthTokenManager"/> class. - /// </summary> - /// <param name="context">The current request context.</param> - public AuthenticationOnlyCookieOAuthTokenManager(HttpContextBase context) { - this.primaryContext = context; - } - - /// <summary> - /// Gets the effective HttpContext object to use. - /// </summary> - protected HttpContextBase Context { - get { - return this.primaryContext ?? new HttpContextWrapper(HttpContext.Current); - } - } - - /// <summary> - /// Gets the token secret from the specified token. - /// </summary> - /// <param name="token">The token.</param> - /// <returns> - /// The token's secret - /// </returns> - public virtual string GetTokenSecret(string token) { - HttpCookie cookie = this.Context.Request.Cookies[TokenCookieKey]; - if (cookie == null || string.IsNullOrEmpty(cookie.Values[token])) { - return null; - } - - string secret = DecodeAndUnprotectToken(token, cookie.Values[token]); - return secret; - } - - /// <summary> - /// Replaces the request token with access token. - /// </summary> - /// <param name="requestToken">The request token.</param> - /// <param name="accessToken">The access token.</param> - /// <param name="accessTokenSecret">The access token secret.</param> - public virtual void ReplaceRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret) { - var cookie = new HttpCookie(TokenCookieKey) { - Value = string.Empty, - Expires = DateTime.UtcNow.AddDays(-5) - }; - this.Context.Response.Cookies.Set(cookie); - } - - /// <summary> - /// Stores the request token together with its secret. - /// </summary> - /// <param name="requestToken">The request token.</param> - /// <param name="requestTokenSecret">The request token secret.</param> - public virtual void StoreRequestToken(string requestToken, string requestTokenSecret) { - var cookie = new HttpCookie(TokenCookieKey) { - HttpOnly = true - }; - - if (FormsAuthentication.RequireSSL) { - cookie.Secure = true; - } - - var encryptedToken = ProtectAndEncodeToken(requestToken, requestTokenSecret); - cookie.Values[requestToken] = encryptedToken; - - this.Context.Response.Cookies.Set(cookie); - } - - /// <summary> - /// Protect and url-encode the specified token secret. - /// </summary> - /// <param name="token">The token to be used as a key.</param> - /// <param name="tokenSecret">The token secret to be protected</param> - /// <returns>The encrypted and protected string.</returns> - protected static string ProtectAndEncodeToken(string token, string tokenSecret) - { - byte[] cookieBytes = Encoding.UTF8.GetBytes(tokenSecret); - var secretBytes = MachineKeyUtil.Protect(cookieBytes, TokenCookieKey, "Token:" + token); - return HttpServerUtility.UrlTokenEncode(secretBytes); - } - - /// <summary> - /// Url-decode and unprotect the specified encrypted token string. - /// </summary> - /// <param name="token">The token to be used as a key.</param> - /// <param name="encryptedToken">The encrypted token to be decrypted</param> - /// <returns>The original token secret</returns> - protected static string DecodeAndUnprotectToken(string token, string encryptedToken) - { - byte[] cookieBytes = HttpServerUtility.UrlTokenDecode(encryptedToken); - byte[] clearBytes = MachineKeyUtil.Unprotect(cookieBytes, TokenCookieKey, "Token:" + token); - return Encoding.UTF8.GetString(clearBytes); - } - } -}
\ No newline at end of file diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/CookieOAuthTokenManager.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/CookieOAuthTokenManager.cs deleted file mode 100644 index 398ee85..0000000 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/CookieOAuthTokenManager.cs +++ /dev/null @@ -1,79 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="CookieOAuthTokenManager.cs" company="Microsoft"> -// Copyright (c) Microsoft. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.AspNet.Clients { - using System.Web; - using System.Web.Security; - - /// <summary> - /// Stores OAuth tokens in the current request's cookie. - /// </summary> - /// <remarks> - /// This class is different from the <see cref="AuthenticationOnlyCookieOAuthTokenManager"/> in that - /// it also stores the access token after the authentication has succeeded. - /// </remarks> - public class CookieOAuthTokenManager : AuthenticationOnlyCookieOAuthTokenManager { - /// <summary> - /// Initializes a new instance of the <see cref="CookieOAuthTokenManager"/> class. - /// </summary> - public CookieOAuthTokenManager() { - } - - /// <summary> - /// Initializes a new instance of the <see cref="CookieOAuthTokenManager"/> class. - /// </summary> - /// <param name="context">The current request context.</param> - public CookieOAuthTokenManager(HttpContextBase context) - : base(context) { - } - - /// <summary> - /// Gets the token secret from the specified token. - /// </summary> - /// <param name="token">The token.</param> - /// <returns> - /// The token's secret - /// </returns> - public override string GetTokenSecret(string token) { - string secret = base.GetTokenSecret(token); - if (secret != null) { - return secret; - } - - // The base class checks for cookies in the Request object. - // Here we check in the Response object as well because we - // may have set it earlier in the request life cycle. - HttpCookie cookie = this.Context.Response.Cookies[TokenCookieKey]; - if (cookie == null || string.IsNullOrEmpty(cookie.Values[token])) { - return null; - } - - secret = DecodeAndUnprotectToken(token, cookie.Values[token]); - return secret; - } - - /// <summary> - /// Replaces the request token with access token. - /// </summary> - /// <param name="requestToken">The request token.</param> - /// <param name="accessToken">The access token.</param> - /// <param name="accessTokenSecret">The access token secret.</param> - public override void ReplaceRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret) { - var cookie = new HttpCookie(TokenCookieKey) { - HttpOnly = true - }; - - if (FormsAuthentication.RequireSSL) { - cookie.Secure = true; - } - - var encryptedToken = ProtectAndEncodeToken(accessToken, accessTokenSecret); - cookie.Values[accessToken] = encryptedToken; - - this.Context.Response.Cookies.Set(cookie); - } - } -}
\ No newline at end of file diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/DotNetOpenAuthWebConsumer.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/DotNetOpenAuthWebConsumer.cs index e216906..1b6318f 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/DotNetOpenAuthWebConsumer.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth/DotNetOpenAuthWebConsumer.cs @@ -8,6 +8,10 @@ namespace DotNetOpenAuth.AspNet.Clients { using System; using System.Collections.Generic; using System.Net; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + using System.Web; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; @@ -17,13 +21,13 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <summary> /// The dot net open auth web consumer. /// </summary> - public class DotNetOpenAuthWebConsumer : IOAuthWebWorker, IDisposable { + public class DotNetOpenAuthWebConsumer : IOAuthWebWorker { #region Constants and Fields /// <summary> /// The _web consumer. /// </summary> - private readonly WebConsumer webConsumer; + private readonly Consumer webConsumer; #endregion @@ -38,75 +42,65 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <param name="tokenManager"> /// The token manager. /// </param> - public DotNetOpenAuthWebConsumer(ServiceProviderDescription serviceDescription, IConsumerTokenManager tokenManager) { + public DotNetOpenAuthWebConsumer(ServiceProviderDescription serviceDescription, string consumerKey, string consumerSecret) { Requires.NotNull(serviceDescription, "serviceDescription"); - Requires.NotNull(tokenManager, "tokenManager"); - this.webConsumer = new WebConsumer(serviceDescription, tokenManager); + this.webConsumer = new Consumer { + ServiceProvider = serviceDescription, + ConsumerKey = consumerKey, + ConsumerSecret = consumerSecret, + TemporaryCredentialStorage = new CookieTemporaryCredentialStorage(), + }; } #endregion - #region Public Methods and Operators - /// <summary> - /// The prepare authorized request. + /// Gets the DotNetOpenAuth <see cref="WebConsumer"/> instance that can be used to make OAuth 1.0 authorized HTTP requests. /// </summary> - /// <param name="profileEndpoint"> - /// The profile endpoint. - /// </param> - /// <param name="accessToken"> - /// The access token. - /// </param> - /// <returns>An HTTP request.</returns> - public HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint profileEndpoint, string accessToken) { - return this.webConsumer.PrepareAuthorizedRequest(profileEndpoint, accessToken); + public Consumer Consumer { + get { return this.webConsumer; } } + #region Public Methods and Operators + /// <summary> - /// The process user authorization. + /// Creates an HTTP message handler that authorizes outgoing web requests. /// </summary> - /// <returns>The response message.</returns> - public AuthorizedTokenResponse ProcessUserAuthorization() { - return this.webConsumer.ProcessUserAuthorization(); + /// <param name="accessToken">The access token.</param> + public HttpMessageHandler CreateMessageHandler(AccessToken accessToken) { + Requires.NotNullOrEmpty(accessToken.Token, "accessToken"); + + return this.Consumer.CreateMessageHandler(accessToken); } /// <summary> - /// The request authentication. + /// The process user authorization. /// </summary> - /// <param name="callback"> - /// The callback. - /// </param> - public void RequestAuthentication(Uri callback) { - var redirectParameters = new Dictionary<string, string>(); - UserAuthorizationRequest request = this.webConsumer.PrepareRequestUserAuthorization( - callback, null, redirectParameters); - this.webConsumer.Channel.PrepareResponse(request).Send(); - } - - #endregion + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The response message. + /// </returns> + public Task<AccessTokenResponse> ProcessUserAuthorizationAsync(HttpContextBase context = null, CancellationToken cancellationToken = default(CancellationToken)) { + if (context == null) { + context = new HttpContextWrapper(HttpContext.Current); + } - #region IDisposable members + return this.webConsumer.ProcessUserAuthorizationAsync(context.Request.Url, cancellationToken: cancellationToken); + } /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// The request authentication. /// </summary> - /// <filterpriority>2</filterpriority> - public void Dispose() { - this.Dispose(true); - GC.SuppressFinalize(this); + /// <param name="callback">The callback.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The response message. + /// </returns> + public Task<Uri> RequestAuthenticationAsync(Uri callback, CancellationToken cancellationToken = default(CancellationToken)) { + return this.webConsumer.RequestUserAuthorizationAsync(callback, cancellationToken: cancellationToken); } #endregion - - /// <summary> - /// Releases unmanaged and - optionally - managed resources - /// </summary> - /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected virtual void Dispose(bool disposing) { - if (disposing) { - this.webConsumer.Dispose(); - } - } } } diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/IOAuthTokenManager.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/IOAuthTokenManager.cs deleted file mode 100644 index 92f1c22..0000000 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/IOAuthTokenManager.cs +++ /dev/null @@ -1,38 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="IOAuthTokenManager.cs" company="Microsoft"> -// Copyright (c) Microsoft. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.AspNet.Clients { - /// <summary> - /// A token manager for use by a web site in its role as a consumer of - /// an individual ServiceProvider. - /// </summary> - /// <remarks> - /// This interface is used by clients of the DotNetOpenAuth.AspNet classes. - /// </remarks> - public interface IOAuthTokenManager { - /// <summary> - /// Gets the token secret from the specified token. - /// </summary> - /// <param name="token">The token.</param> - /// <returns>The token's secret</returns> - string GetTokenSecret(string token); - - /// <summary> - /// Stores the request token together with its secret. - /// </summary> - /// <param name="requestToken">The request token.</param> - /// <param name="requestTokenSecret">The request token secret.</param> - void StoreRequestToken(string requestToken, string requestTokenSecret); - - /// <summary> - /// Replaces the request token with access token. - /// </summary> - /// <param name="requestToken">The request token.</param> - /// <param name="accessToken">The access token.</param> - /// <param name="accessTokenSecret">The access token secret.</param> - void ReplaceRequestTokenWithAccessToken(string requestToken, string accessToken, string accessTokenSecret); - } -}
\ No newline at end of file diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/IOAuthWebWorker.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/IOAuthWebWorker.cs index a054a1c..e3ee3e8 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/IOAuthWebWorker.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth/IOAuthWebWorker.cs @@ -7,41 +7,39 @@ namespace DotNetOpenAuth.AspNet.Clients { using System; using System.Net; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + using System.Web; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.Messages; /// <summary> - /// The io auth web worker. + /// The interface implemented by all OAuth web authentication modules in this assembly. /// </summary> public interface IOAuthWebWorker { - #region Public Methods and Operators - /// <summary> - /// The prepare authorized request. + /// Creates an HTTP message handler that authorizes outgoing web requests. /// </summary> - /// <param name="profileEndpoint"> - /// The profile endpoint. - /// </param> - /// <param name="accessToken"> - /// The access token. - /// </param> - /// <returns>An HTTP request.</returns> - HttpWebRequest PrepareAuthorizedRequest(MessageReceivingEndpoint profileEndpoint, string accessToken); + /// <param name="accessToken">The access token.</param> + HttpMessageHandler CreateMessageHandler(AccessToken accessToken); /// <summary> /// The process user authorization. /// </summary> - /// <returns>The response message.</returns> - AuthorizedTokenResponse ProcessUserAuthorization(); + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The access token, if obtained; otherwise <c>null</c>. + /// </returns> + Task<AccessTokenResponse> ProcessUserAuthorizationAsync(HttpContextBase context = null, CancellationToken cancellationToken = default(CancellationToken)); /// <summary> /// The request authentication. /// </summary> - /// <param name="callback"> - /// The callback. - /// </param> - void RequestAuthentication(Uri callback); - - #endregion + /// <param name="callback">The callback.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>The URL to redirect the user agent to.</returns> + Task<Uri> RequestAuthenticationAsync(Uri callback, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/InMemoryOAuthTokenManager.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/InMemoryOAuthTokenManager.cs deleted file mode 100644 index a7b641c..0000000 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/InMemoryOAuthTokenManager.cs +++ /dev/null @@ -1,158 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="InMemoryOAuthTokenManager.cs" company="Microsoft"> -// Copyright (c) Microsoft. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.AspNet.Clients { - using System; - using System.Collections.Generic; - using DotNetOpenAuth.OAuth; - using DotNetOpenAuth.OAuth.ChannelElements; - using DotNetOpenAuth.OAuth.Messages; - using Validation; - - /// <summary> - /// An implementation of IOAuthTokenManager which stores keys in memory. - /// </summary> - public sealed class InMemoryOAuthTokenManager : IConsumerTokenManager { - #region Constants and Fields - - /// <summary> - /// The _tokens and secrets. - /// </summary> - private readonly Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>(); - - #endregion - - #region Constructors and Destructors - - /// <summary> - /// Initializes a new instance of the <see cref="InMemoryOAuthTokenManager"/> class. - /// </summary> - /// <param name="consumerKey"> - /// The consumer key. - /// </param> - /// <param name="consumerSecret"> - /// The consumer secret. - /// </param> - public InMemoryOAuthTokenManager(string consumerKey, string consumerSecret) { - Requires.NotNull(consumerKey, "consumerKey"); - Requires.NotNull(consumerSecret, "consumerSecret"); - - this.ConsumerKey = consumerKey; - this.ConsumerSecret = consumerSecret; - } - - #endregion - - #region Public Properties - - /// <summary> - /// Gets the consumer key. - /// </summary> - public string ConsumerKey { get; private set; } - - /// <summary> - /// Gets the consumer secret. - /// </summary> - public string ConsumerSecret { get; private set; } - - #endregion - - #region Public Methods and Operators - - /// <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> - /// 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> - /// 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(); - } - - /// <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; - } - - #endregion - } -} diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/LinkedInClient.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/LinkedInClient.cs index 3c157f3..daf3441 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/LinkedInClient.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth/LinkedInClient.cs @@ -10,11 +10,15 @@ namespace DotNetOpenAuth.AspNet.Clients { using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; using System.Xml.Linq; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; + using System.Collections.Specialized; /// <summary> /// Represents LinkedIn authentication client. @@ -25,21 +29,10 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <summary> /// Describes the OAuth service provider endpoints for LinkedIn. /// </summary> - public static readonly ServiceProviderDescription LinkedInServiceDescription = new ServiceProviderDescription { - RequestTokenEndpoint = - new MessageReceivingEndpoint( - "https://api.linkedin.com/uas/oauth/requestToken", - HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), - UserAuthorizationEndpoint = - new MessageReceivingEndpoint( - "https://www.linkedin.com/uas/oauth/authenticate", - HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), - AccessTokenEndpoint = - new MessageReceivingEndpoint( - "https://api.linkedin.com/uas/oauth/accessToken", - HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), - TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() }, - }; + public static readonly ServiceProviderDescription LinkedInServiceDescription = new ServiceProviderDescription( + "https://api.linkedin.com/uas/oauth/requestToken", + "https://www.linkedin.com/uas/oauth/authenticate", + "https://api.linkedin.com/uas/oauth/accessToken"); #endregion @@ -48,28 +41,10 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <summary> /// Initializes a new instance of the <see cref="LinkedInClient"/> class. /// </summary> - /// <remarks> - /// Tokens exchanged during the OAuth handshake are stored in cookies. - /// </remarks> - /// <param name="consumerKey"> - /// The LinkedIn app's consumer key. - /// </param> - /// <param name="consumerSecret"> - /// The LinkedIn app's consumer secret. - /// </param> - [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", - Justification = "We can't dispose the object because we still need it through the app lifetime.")] - public LinkedInClient(string consumerKey, string consumerSecret) - : this(consumerKey, consumerSecret, new CookieOAuthTokenManager()) { } - - /// <summary> - /// Initializes a new instance of the <see cref="LinkedInClient"/> class. - /// </summary> /// <param name="consumerKey">The consumer key.</param> /// <param name="consumerSecret">The consumer secret.</param> - /// <param name="tokenManager">The token manager.</param> - public LinkedInClient(string consumerKey, string consumerSecret, IOAuthTokenManager tokenManager) - : base("linkedIn", LinkedInServiceDescription, new SimpleConsumerTokenManager(consumerKey, consumerSecret, tokenManager)) { + public LinkedInClient(string consumerKey, string consumerSecret) + : base("linkedIn", LinkedInServiceDescription, consumerKey, consumerSecret) { } #endregion @@ -79,46 +54,48 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <summary> /// Check if authentication succeeded after user is redirected back from the service provider. /// </summary> - /// <param name="response"> - /// The response token returned from service provider - /// </param> + /// <param name="response">The response token returned from service provider</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> - /// Authentication result. + /// Authentication result. /// </returns> [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We don't care if the request fails.")] - protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response) { + protected override async Task<AuthenticationResult> VerifyAuthenticationCoreAsync(AccessTokenResponse response, CancellationToken cancellationToken = default(CancellationToken)) { // See here for Field Selectors API http://developer.linkedin.com/docs/DOC-1014 const string ProfileRequestUrl = "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,industry,summary)"; - string accessToken = response.AccessToken; - - var profileEndpoint = new MessageReceivingEndpoint(ProfileRequestUrl, HttpDeliveryMethods.GetRequest); - HttpWebRequest request = this.WebWorker.PrepareAuthorizedRequest(profileEndpoint, accessToken); - + var accessToken = response.AccessToken; + var authorizingHandler = this.WebWorker.CreateMessageHandler(accessToken); try { - using (WebResponse profileResponse = request.GetResponse()) { - using (Stream responseStream = profileResponse.GetResponseStream()) { - XDocument document = LoadXDocumentFromStream(responseStream); - string userId = document.Root.Element("id").Value; - - string firstName = document.Root.Element("first-name").Value; - string lastName = document.Root.Element("last-name").Value; - string userName = firstName + " " + lastName; - - var extraData = new Dictionary<string, string>(); - extraData.Add("accesstoken", accessToken); - extraData.Add("name", userName); - extraData.AddDataIfNotEmpty(document, "headline"); - extraData.AddDataIfNotEmpty(document, "summary"); - extraData.AddDataIfNotEmpty(document, "industry"); - - return new AuthenticationResult( - isSuccessful: true, provider: this.ProviderName, providerUserId: userId, userName: userName, extraData: extraData); + using (var httpClient = new HttpClient(authorizingHandler)) { + using (HttpResponseMessage profileResponse = await httpClient.GetAsync(ProfileRequestUrl, cancellationToken)) { + using (Stream responseStream = await profileResponse.Content.ReadAsStreamAsync()) { + XDocument document = LoadXDocumentFromStream(responseStream); + string userId = document.Root.Element("id").Value; + + string firstName = document.Root.Element("first-name").Value; + string lastName = document.Root.Element("last-name").Value; + string userName = firstName + " " + lastName; + + var extraData = new NameValueCollection(); + extraData.Add("accesstoken", accessToken.Token); + extraData.Add("accesstokensecret", accessToken.Secret); + extraData.Add("name", userName); + extraData.AddDataIfNotEmpty(document, "headline"); + extraData.AddDataIfNotEmpty(document, "summary"); + extraData.AddDataIfNotEmpty(document, "industry"); + + return new AuthenticationResult( + isSuccessful: true, + provider: this.ProviderName, + providerUserId: userId, + userName: userName, + extraData: extraData); + } } } - } - catch (Exception exception) { + } catch (Exception exception) { return new AuthenticationResult(exception); } } diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/OAuthClient.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/OAuthClient.cs index a0afeca..1841ef3 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/OAuthClient.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth/OAuthClient.cs @@ -9,6 +9,8 @@ namespace DotNetOpenAuth.AspNet.Clients { using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; + using System.Threading; + using System.Threading.Tasks; using System.Web; using System.Xml; using System.Xml.Linq; @@ -17,6 +19,7 @@ namespace DotNetOpenAuth.AspNet.Clients { using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; using Validation; + using System.Collections.Specialized; /// <summary> /// Represents base class for OAuth 1.0 clients @@ -31,34 +34,14 @@ namespace DotNetOpenAuth.AspNet.Clients { /// Name of the provider. /// </param> /// <param name="serviceDescription"> - /// The service description. - /// </param> - /// <param name="consumerKey"> - /// The consumer key. - /// </param> - /// <param name="consumerSecret"> - /// The consumer secret. - /// </param> - protected OAuthClient( - string providerName, ServiceProviderDescription serviceDescription, string consumerKey, string consumerSecret) - : this(providerName, serviceDescription, new InMemoryOAuthTokenManager(consumerKey, consumerSecret)) { } - - /// <summary> - /// Initializes a new instance of the <see cref="OAuthClient"/> class. - /// </summary> - /// <param name="providerName"> - /// Name of the provider. - /// </param> - /// <param name="serviceDescription"> /// The service Description. /// </param> /// <param name="tokenManager"> /// The token Manager. /// </param> - [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "I don't know how to ensure this rule is followed given this API")] protected OAuthClient( - string providerName, ServiceProviderDescription serviceDescription, IConsumerTokenManager tokenManager) - : this(providerName, new DotNetOpenAuthWebConsumer(serviceDescription, tokenManager)) { + string providerName, ServiceProviderDescription serviceDescription, string consumerKey, string consumerSecret) + : this(providerName, new DotNetOpenAuthWebConsumer(serviceDescription, consumerKey, consumerSecret)) { } /// <summary> @@ -103,42 +86,40 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <summary> /// Attempts to authenticate users by forwarding them to an external website, and upon succcess or failure, redirect users back to the specified url. /// </summary> - /// <param name="context"> - /// The context. - /// </param> - /// <param name="returnUrl"> - /// The return url after users have completed authenticating against external website. - /// </param> - public virtual void RequestAuthentication(HttpContextBase context, Uri returnUrl) { + /// <param name="context">The context.</param> + /// <param name="returnUrl">The return url after users have completed authenticating against external website.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// A task that completes with the asynchronous operation. + /// </returns> + public virtual Task RequestAuthenticationAsync(HttpContextBase context, Uri returnUrl, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(returnUrl, "returnUrl"); Requires.NotNull(context, "context"); Uri callback = returnUrl.StripQueryArgumentsWithPrefix("oauth_"); - this.WebWorker.RequestAuthentication(callback); + return this.WebWorker.RequestAuthenticationAsync(callback, cancellationToken); } /// <summary> /// Check if authentication succeeded after user is redirected back from the service provider. /// </summary> - /// <param name="context"> - /// The context. - /// </param> + /// <param name="context">The context.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> - /// An instance of <see cref="AuthenticationResult"/> containing authentication result. + /// An instance of <see cref="AuthenticationResult" /> containing authentication result. /// </returns> - public virtual AuthenticationResult VerifyAuthentication(HttpContextBase context) { - AuthorizedTokenResponse response = this.WebWorker.ProcessUserAuthorization(); + public virtual async Task<AuthenticationResult> VerifyAuthenticationAsync(HttpContextBase context, CancellationToken cancellationToken = default(CancellationToken)) { + AccessTokenResponse response = await this.WebWorker.ProcessUserAuthorizationAsync(context, cancellationToken); if (response == null) { return AuthenticationResult.Failed; } - AuthenticationResult result = this.VerifyAuthenticationCore(response); + AuthenticationResult result = await this.VerifyAuthenticationCoreAsync(response, cancellationToken); if (result.IsSuccessful && result.ExtraData != null) { // add the access token to the user data dictionary just in case page developers want to use it - var wrapExtraData = result.ExtraData.IsReadOnly - ? new Dictionary<string, string>(result.ExtraData) - : result.ExtraData; - wrapExtraData["accesstoken"] = response.AccessToken; + var wrapExtraData = new NameValueCollection(result.ExtraData); + wrapExtraData["accesstoken"] = response.AccessToken.Token; + wrapExtraData["accesstokensecret"] = response.AccessToken.Secret; AuthenticationResult wrapResult = new AuthenticationResult( result.IsSuccessful, @@ -174,12 +155,13 @@ namespace DotNetOpenAuth.AspNet.Clients { /// Check if authentication succeeded after user is redirected back from the service provider. /// </summary> /// <param name="response"> - /// The response token returned from service provider + /// The access token returned from service provider /// </param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// Authentication result /// </returns> - protected abstract AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response); + protected abstract Task<AuthenticationResult> VerifyAuthenticationCoreAsync(AccessTokenResponse response, CancellationToken cancellationToken); #endregion } } diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/SimpleConsumerTokenManager.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/SimpleConsumerTokenManager.cs deleted file mode 100644 index 899204c..0000000 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/SimpleConsumerTokenManager.cs +++ /dev/null @@ -1,104 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="SimpleConsumerTokenManager.cs" company="Microsoft"> -// Copyright (c) Microsoft. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.AspNet.Clients { - using System; - using DotNetOpenAuth.OAuth.ChannelElements; - using Validation; - - /// <summary> - /// Simple wrapper around IConsumerTokenManager - /// </summary> - public class SimpleConsumerTokenManager : IConsumerTokenManager { - /// <summary> - /// Store the token manager. - /// </summary> - private readonly IOAuthTokenManager tokenManager; - - /// <summary> - /// Initializes a new instance of the <see cref="SimpleConsumerTokenManager"/> class. - /// </summary> - /// <param name="consumerKey">The consumer key.</param> - /// <param name="consumerSecret">The consumer secret.</param> - /// <param name="tokenManager">The OAuth token manager.</param> - public SimpleConsumerTokenManager(string consumerKey, string consumerSecret, IOAuthTokenManager tokenManager) { - Requires.NotNullOrEmpty(consumerKey, "consumerKey"); - Requires.NotNullOrEmpty(consumerSecret, "consumerSecret"); - Requires.NotNull(tokenManager, "oAuthTokenManager"); - - this.ConsumerKey = consumerKey; - this.ConsumerSecret = consumerSecret; - this.tokenManager = tokenManager; - } - - /// <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; - } - - /// <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.tokenManager.GetTokenSecret(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> - public void StoreNewRequestToken(DotNetOpenAuth.OAuth.Messages.UnauthorizedTokenRequest request, DotNetOpenAuth.OAuth.Messages.ITokenSecretContainingMessage response) { - this.tokenManager.StoreRequestToken(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> - public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) { - this.tokenManager.ReplaceRequestTokenWithAccessToken(requestToken, 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 NotSupportedException(); - } - } -}
\ No newline at end of file diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth/TwitterClient.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth/TwitterClient.cs index 886917a..0c17ed3 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth/TwitterClient.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth/TwitterClient.cs @@ -10,11 +10,15 @@ namespace DotNetOpenAuth.AspNet.Clients { using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; using System.Xml.Linq; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; + using System.Collections.Specialized; /// <summary> /// Represents a Twitter client @@ -25,51 +29,23 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <summary> /// The description of Twitter's OAuth protocol URIs for use with their "Sign in with Twitter" feature. /// </summary> - public static readonly ServiceProviderDescription TwitterServiceDescription = new ServiceProviderDescription { - RequestTokenEndpoint = - new MessageReceivingEndpoint( - "https://api.twitter.com/oauth/request_token", - HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), - UserAuthorizationEndpoint = - new MessageReceivingEndpoint( - "https://api.twitter.com/oauth/authenticate", - HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), - AccessTokenEndpoint = - new MessageReceivingEndpoint( - "https://api.twitter.com/oauth/access_token", - HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), - TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() }, - }; + public static readonly ServiceProviderDescription TwitterServiceDescription = + new ServiceProviderDescription( + "https://api.twitter.com/oauth/request_token", + "https://api.twitter.com/oauth/authenticate", + "https://api.twitter.com/oauth/access_token"); #endregion #region Constructors and Destructors /// <summary> - /// Initializes a new instance of the <see cref="TwitterClient"/> class with the specified consumer key and consumer secret. - /// </summary> - /// <remarks> - /// Tokens exchanged during the OAuth handshake are stored in cookies. - /// </remarks> - /// <param name="consumerKey"> - /// The consumer key. - /// </param> - /// <param name="consumerSecret"> - /// The consumer secret. - /// </param> - [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", - Justification = "We can't dispose the object because we still need it through the app lifetime.")] - public TwitterClient(string consumerKey, string consumerSecret) - : this(consumerKey, consumerSecret, new AuthenticationOnlyCookieOAuthTokenManager()) { } - - /// <summary> /// Initializes a new instance of the <see cref="TwitterClient"/> class. /// </summary> /// <param name="consumerKey">The consumer key.</param> /// <param name="consumerSecret">The consumer secret.</param> - /// <param name="tokenManager">The token manager.</param> - public TwitterClient(string consumerKey, string consumerSecret, IOAuthTokenManager tokenManager) - : base("twitter", TwitterServiceDescription, new SimpleConsumerTokenManager(consumerKey, consumerSecret, tokenManager)) { + public TwitterClient(string consumerKey, string consumerSecret) + : base("twitter", TwitterServiceDescription, consumerKey, consumerSecret) { } #endregion @@ -79,34 +55,34 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <summary> /// Check if authentication succeeded after user is redirected back from the service provider. /// </summary> - /// <param name="response"> - /// The response token returned from service provider - /// </param> + /// <param name="response">The response token returned from service provider</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> - /// Authentication result + /// Authentication result /// </returns> [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We don't care if the request for additional data fails.")] - protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response) { - string accessToken = response.AccessToken; + protected override async Task<AuthenticationResult> VerifyAuthenticationCoreAsync(AccessTokenResponse response, CancellationToken cancellationToken) { string userId = response.ExtraData["user_id"]; string userName = response.ExtraData["screen_name"]; var profileRequestUrl = new Uri("https://api.twitter.com/1/users/show.xml?user_id=" + MessagingUtilities.EscapeUriDataStringRfc3986(userId)); - var profileEndpoint = new MessageReceivingEndpoint(profileRequestUrl, HttpDeliveryMethods.GetRequest); - HttpWebRequest request = this.WebWorker.PrepareAuthorizedRequest(profileEndpoint, accessToken); + var authorizingHandler = this.WebWorker.CreateMessageHandler(response.AccessToken); - var extraData = new Dictionary<string, string>(); - extraData.Add("accesstoken", accessToken); + var extraData = new NameValueCollection(); + extraData.Add("accesstoken", response.AccessToken.Token); + extraData.Add("accesstokensecret", response.AccessToken.Secret); try { - using (WebResponse profileResponse = request.GetResponse()) { - using (Stream responseStream = profileResponse.GetResponseStream()) { - XDocument document = LoadXDocumentFromStream(responseStream); - extraData.AddDataIfNotEmpty(document, "name"); - extraData.AddDataIfNotEmpty(document, "location"); - extraData.AddDataIfNotEmpty(document, "description"); - extraData.AddDataIfNotEmpty(document, "url"); + using (var httpClient = new HttpClient(authorizingHandler)) { + using (HttpResponseMessage profileResponse = await httpClient.GetAsync(profileRequestUrl, cancellationToken)) { + using (Stream responseStream = await profileResponse.Content.ReadAsStreamAsync()) { + XDocument document = LoadXDocumentFromStream(responseStream); + extraData.AddDataIfNotEmpty(document, "name"); + extraData.AddDataIfNotEmpty(document, "location"); + extraData.AddDataIfNotEmpty(document, "description"); + extraData.AddDataIfNotEmpty(document, "url"); + } } } } diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs index d20e452..c06c1dc 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs @@ -12,6 +12,7 @@ namespace DotNetOpenAuth.AspNet.Clients { using System.Web; using DotNetOpenAuth.Messaging; using Validation; + using System.Collections.Specialized; /// <summary> /// The facebook client. @@ -92,7 +93,7 @@ namespace DotNetOpenAuth.AspNet.Clients { /// The access token. /// </param> /// <returns>A dictionary of profile data.</returns> - protected override IDictionary<string, string> GetUserData(string accessToken) { + protected override NameValueCollection GetUserData(string accessToken) { FacebookGraphData graphData; var request = WebRequest.Create( @@ -104,7 +105,7 @@ namespace DotNetOpenAuth.AspNet.Clients { } // this dictionary must contains - var userData = new Dictionary<string, string>(); + var userData = new NameValueCollection(); userData.AddItemIfNotEmpty("id", graphData.Id); userData.AddItemIfNotEmpty("username", graphData.Email); userData.AddItemIfNotEmpty("name", graphData.Name); diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth2/MicrosoftClient.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth2/MicrosoftClient.cs index 3e5f71f..b9c4941 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth2/MicrosoftClient.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth2/MicrosoftClient.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.AspNet.Clients { using System.Net; using DotNetOpenAuth.Messaging; using Validation; + using System.Collections.Specialized; /// <summary> /// The Microsoft account client. @@ -110,7 +111,7 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <returns> /// A dictionary contains key-value pairs of user data /// </returns> - protected override IDictionary<string, string> GetUserData(string accessToken) { + protected override NameValueCollection GetUserData(string accessToken) { MicrosoftClientUserData graph; var request = WebRequest.Create( @@ -121,7 +122,7 @@ namespace DotNetOpenAuth.AspNet.Clients { } } - var userData = new Dictionary<string, string>(); + var userData = new NameValueCollection(); userData.AddItemIfNotEmpty("id", graph.Id); userData.AddItemIfNotEmpty("username", graph.Name); userData.AddItemIfNotEmpty("name", graph.Name); diff --git a/src/DotNetOpenAuth.AspNet/Clients/OAuth2/OAuth2Client.cs b/src/DotNetOpenAuth.AspNet/Clients/OAuth2/OAuth2Client.cs index 014f459..8e6b5f3 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/OAuth2/OAuth2Client.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/OAuth2/OAuth2Client.cs @@ -8,8 +8,14 @@ namespace DotNetOpenAuth.AspNet.Clients { using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; + using System.Threading; + using System.Threading.Tasks; using System.Web; + + using DotNetOpenAuth.Messaging; + using Validation; + using System.Collections.Specialized; /// <summary> /// Represents the base class for OAuth 2.0 clients @@ -57,30 +63,31 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <summary> /// Attempts to authenticate users by forwarding them to an external website, and upon succcess or failure, redirect users back to the specified url. /// </summary> - /// <param name="context"> - /// The context. - /// </param> - /// <param name="returnUrl"> - /// The return url after users have completed authenticating against external website. - /// </param> - public virtual void RequestAuthentication(HttpContextBase context, Uri returnUrl) { + /// <param name="context">The context.</param> + /// <param name="returnUrl">The return url after users have completed authenticating against external website.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// A task that completes with the asynchronous operation. + /// </returns> + public virtual Task RequestAuthenticationAsync(HttpContextBase context, Uri returnUrl, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(context, "context"); Requires.NotNull(returnUrl, "returnUrl"); string redirectUrl = this.GetServiceLoginUrl(returnUrl).AbsoluteUri; context.Response.Redirect(redirectUrl, endResponse: true); + return MessagingUtilities.CompletedTask; } /// <summary> /// Check if authentication succeeded after user is redirected back from the service provider. /// </summary> - /// <param name="context"> - /// The context. - /// </param> + /// <param name="context">The context.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> - /// An instance of <see cref="AuthenticationResult"/> containing authentication result. + /// An instance of <see cref="AuthenticationResult" /> containing authentication result. /// </returns> - public AuthenticationResult VerifyAuthentication(HttpContextBase context) { + /// <exception cref="System.InvalidOperationException">Always thrown.</exception> + public Task<AuthenticationResult> VerifyAuthenticationAsync(HttpContextBase context, CancellationToken cancellationToken = default(CancellationToken)) { throw new InvalidOperationException(WebResources.OAuthRequireReturnUrl); } @@ -89,10 +96,11 @@ namespace DotNetOpenAuth.AspNet.Clients { /// </summary> /// <param name="context">The context.</param> /// <param name="returnPageUrl">The return URL which should match the value passed to RequestAuthentication() method.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> - /// An instance of <see cref="AuthenticationResult"/> containing authentication result. + /// An instance of <see cref="AuthenticationResult" /> containing authentication result. /// </returns> - public virtual AuthenticationResult VerifyAuthentication(HttpContextBase context, Uri returnPageUrl) { + public virtual async Task<AuthenticationResult> VerifyAuthenticationAsync(HttpContextBase context, Uri returnPageUrl, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(context, "context"); string code = context.Request.QueryString["code"]; @@ -105,19 +113,15 @@ namespace DotNetOpenAuth.AspNet.Clients { return AuthenticationResult.Failed; } - IDictionary<string, string> userData = this.GetUserData(accessToken); + var userData = this.GetUserData(accessToken); if (userData == null) { return AuthenticationResult.Failed; } - string id = userData["id"]; - string name; - // Some oAuth providers do not return value for the 'username' attribute. // In that case, try the 'name' attribute. If it's still unavailable, fall back to 'id' - if (!userData.TryGetValue("username", out name) && !userData.TryGetValue("name", out name)) { - name = id; - } + string id = userData["id"]; + string name = userData["username"] ?? userData["name"] ?? id; // add the access token to the user data dictionary just in case page developers want to use it userData["accesstoken"] = accessToken; @@ -152,7 +156,7 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <returns> /// A dictionary contains key-value pairs of user data /// </returns> - protected abstract IDictionary<string, string> GetUserData(string accessToken); + protected abstract NameValueCollection GetUserData(string accessToken); /// <summary> /// Queries the access token from the specified authorization code. diff --git a/src/DotNetOpenAuth.AspNet/Clients/OpenID/GoogleOpenIdClient.cs b/src/DotNetOpenAuth.AspNet/Clients/OpenID/GoogleOpenIdClient.cs index 6b4061a..2a68bd8 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/OpenID/GoogleOpenIdClient.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/OpenID/GoogleOpenIdClient.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.AspNet.Clients { using System.Collections.Generic; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; using DotNetOpenAuth.OpenId.RelyingParty; + using System.Collections.Specialized; /// <summary> /// Represents Google OpenID client. @@ -32,10 +33,10 @@ namespace DotNetOpenAuth.AspNet.Clients { /// The response message. /// </param> /// <returns>A dictionary of profile data; or null if no data is available.</returns> - protected override Dictionary<string, string> GetExtraData(IAuthenticationResponse response) { + protected override NameValueCollection GetExtraData(IAuthenticationResponse response) { FetchResponse fetchResponse = response.GetExtension<FetchResponse>(); if (fetchResponse != null) { - var extraData = new Dictionary<string, string>(); + var extraData = new NameValueCollection(); extraData.AddItemIfNotEmpty("email", fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.Email)); extraData.AddItemIfNotEmpty("country", fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.HomeAddress.Country)); extraData.AddItemIfNotEmpty("firstName", fetchResponse.GetAttributeValue(WellKnownAttributes.Name.First)); diff --git a/src/DotNetOpenAuth.AspNet/Clients/OpenID/OpenIDClient.cs b/src/DotNetOpenAuth.AspNet/Clients/OpenID/OpenIDClient.cs index a41b504..901741b 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/OpenID/OpenIDClient.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/OpenID/OpenIDClient.cs @@ -8,11 +8,14 @@ namespace DotNetOpenAuth.AspNet.Clients { using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; + using System.Threading; + using System.Threading.Tasks; using System.Web; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.RelyingParty; using Validation; + using System.Collections.Specialized; /// <summary> /// Base classes for OpenID clients. @@ -79,51 +82,47 @@ namespace DotNetOpenAuth.AspNet.Clients { /// <summary> /// Attempts to authenticate users by forwarding them to an external website, and upon succcess or failure, redirect users back to the specified url. /// </summary> - /// <param name="context"> - /// The context of the current request. - /// </param> - /// <param name="returnUrl"> - /// The return url after users have completed authenticating against external website. - /// </param> + /// <param name="context">The context of the current request.</param> + /// <param name="returnUrl">The return url after users have completed authenticating against external website.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// A task that completes with the asynchronous operation. + /// </returns> [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "We don't have a Uri object handy.")] - public virtual void RequestAuthentication(HttpContextBase context, Uri returnUrl) { + public virtual async Task RequestAuthenticationAsync(HttpContextBase context, Uri returnUrl, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(returnUrl, "returnUrl"); var realm = new Realm(returnUrl.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped)); - IAuthenticationRequest request = RelyingParty.CreateRequest(this.providerIdentifier, realm, returnUrl); + IAuthenticationRequest request = await RelyingParty.CreateRequestAsync(this.providerIdentifier, realm, returnUrl, cancellationToken); // give subclasses a chance to modify request message, e.g. add extension attributes, etc. this.OnBeforeSendingAuthenticationRequest(request); - request.RedirectToProvider(); + await request.RedirectToProviderAsync(context); } /// <summary> /// Check if authentication succeeded after user is redirected back from the service provider. /// </summary> - /// <param name="context"> - /// The context of the current request. - /// </param> + /// <param name="context">The context of the current request.</param> + /// <param name="cancellationToken">The cancellation token.</param> /// <returns> - /// An instance of <see cref="AuthenticationResult"/> containing authentication result. + /// An instance of <see cref="AuthenticationResult" /> containing authentication result. /// </returns> - public virtual AuthenticationResult VerifyAuthentication(HttpContextBase context) { - IAuthenticationResponse response = RelyingParty.GetResponse(); + /// <exception cref="System.InvalidOperationException">Thrown if no OpenID response was found in the incoming HTTP request.</exception> + public virtual async Task<AuthenticationResult> VerifyAuthenticationAsync(HttpContextBase context, CancellationToken cancellationToken = default(CancellationToken)) { + IAuthenticationResponse response = await RelyingParty.GetResponseAsync(context.Request, cancellationToken); if (response == null) { throw new InvalidOperationException(WebResources.OpenIDFailedToGetResponse); } if (response.Status == AuthenticationStatus.Authenticated) { string id = response.ClaimedIdentifier; - string username; - - Dictionary<string, string> extraData = this.GetExtraData(response) ?? new Dictionary<string, string>(); + var extraData = this.GetExtraData(response) ?? new NameValueCollection(); // try to look up username from the 'username' or 'email' property. If not found, fall back to 'friendly id' - if (!extraData.TryGetValue("username", out username) && !extraData.TryGetValue("email", out username)) { - username = response.FriendlyIdentifierForDisplay; - } + string username = extraData["username"] ?? extraData["email"] ?? response.FriendlyIdentifierForDisplay; return new AuthenticationResult(true, this.ProviderName, id, username, extraData); } @@ -142,7 +141,7 @@ namespace DotNetOpenAuth.AspNet.Clients { /// The response message. /// </param> /// <returns>Always null.</returns> - protected virtual Dictionary<string, string> GetExtraData(IAuthenticationResponse response) { + protected virtual NameValueCollection GetExtraData(IAuthenticationResponse response) { return null; } diff --git a/src/DotNetOpenAuth.AspNet/Clients/OpenID/YahooOpenIdClient.cs b/src/DotNetOpenAuth.AspNet/Clients/OpenID/YahooOpenIdClient.cs index bd420fc..d282d4f 100644 --- a/src/DotNetOpenAuth.AspNet/Clients/OpenID/YahooOpenIdClient.cs +++ b/src/DotNetOpenAuth.AspNet/Clients/OpenID/YahooOpenIdClient.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.AspNet.Clients { using System.Collections.Generic; using DotNetOpenAuth.OpenId.Extensions.AttributeExchange; using DotNetOpenAuth.OpenId.RelyingParty; + using System.Collections.Specialized; /// <summary> /// The yahoo open id client. @@ -32,10 +33,10 @@ namespace DotNetOpenAuth.AspNet.Clients { /// The response message. /// </param> /// <returns>A dictionary of profile data; or null if no data is available.</returns> - protected override Dictionary<string, string> GetExtraData(IAuthenticationResponse response) { + protected override NameValueCollection GetExtraData(IAuthenticationResponse response) { FetchResponse fetchResponse = response.GetExtension<FetchResponse>(); if (fetchResponse != null) { - var extraData = new Dictionary<string, string>(); + var extraData = new NameValueCollection(); extraData.AddItemIfNotEmpty("email", fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.Email)); extraData.AddItemIfNotEmpty("fullName", fetchResponse.GetAttributeValue(WellKnownAttributes.Name.FullName)); |