summaryrefslogtreecommitdiffstats
path: root/src/DotNetOpenAuth.AspNet
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2013-03-26 11:19:06 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2013-03-26 11:19:06 -0700
commit3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb (patch)
treec15816c3d7f6e74334553f2ff98605ce1c22c538 /src/DotNetOpenAuth.AspNet
parent5e9014f36b2d53b8e419918675df636540ea24e2 (diff)
parente6f7409f4caceb7bc2a5b4ddbcb1a4097af340f2 (diff)
downloadDotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.zip
DotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.tar.gz
DotNetOpenAuth-3d37ff45cab6838d80b22e6b782a0b9b4c2f4aeb.tar.bz2
Move to HttpClient throughout library.
Diffstat (limited to 'src/DotNetOpenAuth.AspNet')
-rw-r--r--src/DotNetOpenAuth.AspNet/AuthenticationResult.cs16
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/DictionaryExtensions.cs7
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/AuthenticationOnlyCookieOAuthTokenManager.cs127
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/CookieOAuthTokenManager.cs79
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/DotNetOpenAuthWebConsumer.cs92
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/IOAuthTokenManager.cs38
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/IOAuthWebWorker.cs38
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/InMemoryOAuthTokenManager.cs158
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/LinkedInClient.cs107
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/OAuthClient.cs68
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/SimpleConsumerTokenManager.cs104
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/TwitterClient.cs80
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth2/FacebookClient.cs5
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth2/MicrosoftClient.cs5
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth2/OAuth2Client.cs48
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OpenID/GoogleOpenIdClient.cs5
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OpenID/OpenIDClient.cs43
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OpenID/YahooOpenIdClient.cs5
-rw-r--r--src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj16
-rw-r--r--src/DotNetOpenAuth.AspNet/IAuthenticationClient.cs23
-rw-r--r--src/DotNetOpenAuth.AspNet/MachineKeyUtil.cs354
-rw-r--r--src/DotNetOpenAuth.AspNet/OpenAuthSecurityManager.cs27
-rw-r--r--src/DotNetOpenAuth.AspNet/WebResources.Designer.cs11
-rw-r--r--src/DotNetOpenAuth.AspNet/WebResources.resx3
-rw-r--r--src/DotNetOpenAuth.AspNet/packages.config3
25 files changed, 260 insertions, 1202 deletions
diff --git a/src/DotNetOpenAuth.AspNet/AuthenticationResult.cs b/src/DotNetOpenAuth.AspNet/AuthenticationResult.cs
index 9e8492d..4493288 100644
--- a/src/DotNetOpenAuth.AspNet/AuthenticationResult.cs
+++ b/src/DotNetOpenAuth.AspNet/AuthenticationResult.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.AspNet {
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using DotNetOpenAuth.Messaging;
+ using System.Collections.Specialized;
/// <summary>
/// Represents the result of OAuth or OpenID authentication.
@@ -46,10 +47,8 @@ namespace DotNetOpenAuth.AspNet {
/// <param name="exception">The exception.</param>
/// <param name="provider">The provider name.</param>
public AuthenticationResult(Exception exception, string provider)
- : this(isSuccessful: false)
- {
- if (exception == null)
- {
+ : this(isSuccessful: false) {
+ if (exception == null) {
throw new ArgumentNullException("exception");
}
@@ -76,15 +75,12 @@ namespace DotNetOpenAuth.AspNet {
/// The extra data.
/// </param>
public AuthenticationResult(
- bool isSuccessful, string provider, string providerUserId, string userName, IDictionary<string, string> extraData) {
+ bool isSuccessful, string provider, string providerUserId, string userName, NameValueCollection extraData) {
this.IsSuccessful = isSuccessful;
this.Provider = provider;
this.ProviderUserId = providerUserId;
this.UserName = userName;
- if (extraData != null) {
- // wrap extraData in a read-only dictionary
- this.ExtraData = new ReadOnlyDictionary<string, string>(extraData);
- }
+ this.ExtraData = extraData ?? new NameValueCollection();
}
/// <summary>
@@ -95,7 +91,7 @@ namespace DotNetOpenAuth.AspNet {
/// <summary>
/// Gets the optional extra data that may be returned from the provider
/// </summary>
- public IDictionary<string, string> ExtraData { get; private set; }
+ public NameValueCollection ExtraData { get; private set; }
/// <summary>
/// Gets a value indicating whether the authentication step is successful.
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));
diff --git a/src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj b/src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj
index 16229c7..b3b52d9 100644
--- a/src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj
+++ b/src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj
@@ -32,6 +32,8 @@
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
@@ -39,21 +41,15 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
- <Reference Include="Validation">
- <HintPath>..\packages\Validation.2.0.1.12362\lib\portable-windows8+net40+sl5+windowsphone8\Validation.dll</HintPath>
- <Private>True</Private>
+ <Reference Include="Validation, Version=2.0.0.0, Culture=neutral, PublicKeyToken=2fc06f0d701809a7, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\packages\Validation.2.0.2.13022\lib\portable-windows8+net40+sl5+windowsphone8\Validation.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AuthenticationResult.cs" />
<Compile Include="Clients\DictionaryExtensions.cs" />
<Compile Include="Clients\OAuth2\WindowsLiveClient.cs" />
- <Compile Include="Clients\OAuth\AuthenticationOnlyCookieOAuthTokenManager.cs">
- <SubType>Code</SubType>
- </Compile>
- <Compile Include="Clients\OAuth\CookieOAuthTokenManager.cs" />
- <Compile Include="Clients\OAuth\IOAuthTokenManager.cs" />
- <Compile Include="Clients\OAuth\SimpleConsumerTokenManager.cs" />
<Compile Include="IAuthenticationClient.cs" />
<Compile Include="Clients\OAuth2\FacebookClient.cs" />
<Compile Include="Clients\OAuth2\FacebookGraphData.cs" />
@@ -63,7 +59,6 @@
<Compile Include="Clients\OAuth2\MicrosoftClient.cs" />
<Compile Include="Clients\OAuth2\MicrosoftClientUserData.cs" />
<Compile Include="Clients\OAuth\DotNetOpenAuthWebConsumer.cs" />
- <Compile Include="Clients\OAuth\InMemoryOAuthTokenManager.cs" />
<Compile Include="Clients\OAuth\IOAuthWebWorker.cs" />
<Compile Include="Clients\OAuth\LinkedInClient.cs" />
<Compile Include="Clients\OAuth\OAuthClient.cs" />
@@ -71,7 +66,6 @@
<Compile Include="Clients\OpenID\GoogleOpenIdClient.cs" />
<Compile Include="Clients\OpenID\OpenIdClient.cs" />
<Compile Include="Clients\OpenID\YahooOpenIdClient.cs" />
- <Compile Include="MachineKeyUtil.cs" />
<Compile Include="UriHelper.cs" />
<Compile Include="IOpenAuthDataProvider.cs" />
<Compile Include="OpenAuthAuthenticationTicketHelper.cs" />
diff --git a/src/DotNetOpenAuth.AspNet/IAuthenticationClient.cs b/src/DotNetOpenAuth.AspNet/IAuthenticationClient.cs
index 4d9acde..b13f0d1 100644
--- a/src/DotNetOpenAuth.AspNet/IAuthenticationClient.cs
+++ b/src/DotNetOpenAuth.AspNet/IAuthenticationClient.cs
@@ -6,6 +6,8 @@
namespace DotNetOpenAuth.AspNet {
using System;
+ using System.Threading;
+ using System.Threading.Tasks;
using System.Web;
/// <summary>
@@ -20,23 +22,20 @@ namespace DotNetOpenAuth.AspNet {
/// <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>
- void RequestAuthentication(HttpContextBase context, Uri returnUrl);
+ /// <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 async operation.</returns>
+ Task RequestAuthenticationAsync(HttpContextBase context, Uri returnUrl, CancellationToken cancellationToken = default(CancellationToken));
/// <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>
- AuthenticationResult VerifyAuthentication(HttpContextBase context);
+ Task<AuthenticationResult> VerifyAuthenticationAsync(HttpContextBase context, CancellationToken cancellationToken = default(CancellationToken));
}
}
diff --git a/src/DotNetOpenAuth.AspNet/MachineKeyUtil.cs b/src/DotNetOpenAuth.AspNet/MachineKeyUtil.cs
deleted file mode 100644
index eb2020b..0000000
--- a/src/DotNetOpenAuth.AspNet/MachineKeyUtil.cs
+++ /dev/null
@@ -1,354 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="MachineKeyUtil.cs" company="Microsoft">
-// Copyright (c) Microsoft. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.AspNet {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.IO;
- using System.Net;
- using System.Reflection;
- using System.Security.Cryptography;
- using System.Text;
- using System.Web;
- using System.Web.Security;
-
- /// <summary>
- /// Provides helpers that mimic the ASP.NET 4.5 MachineKey.Protect / Unprotect APIs,
- /// even when running on ASP.NET 4.0. Consumers are expected to follow the same
- /// conventions used by the MachineKey.Protect / Unprotect APIs (consult MSDN docs
- /// for how these are meant to be used). Additionally, since this helper class
- /// dynamically switches between the two based on whether the current application is
- /// .NET 4.0 or 4.5, consumers should never persist output from the Protect method
- /// since the implementation will change when upgrading 4.0 -> 4.5. This should be
- /// used for transient data only.
- /// </summary>
- internal static class MachineKeyUtil {
- /// <summary>
- /// MachineKey implementation depending on the target .NET framework version
- /// </summary>
- private static readonly IMachineKey MachineKeyImpl = GetMachineKeyImpl();
-
- /// <summary>
- /// ProtectUnprotect delegate.
- /// </summary>
- /// <param name="data">The data.</param>
- /// <param name="purposes">The purposes.</param>
- /// <returns>Result of either Protect or Unprotect methods.</returns>
- private delegate byte[] ProtectUnprotect(byte[] data, string[] purposes);
-
- /// <summary>
- /// Abstract the MachineKey implementation in .NET 4.0 and 4.5
- /// </summary>
- private interface IMachineKey {
- /// <summary>
- /// Protects the specified user data.
- /// </summary>
- /// <param name="userData">The user data.</param>
- /// <param name="purposes">The purposes.</param>
- /// <returns>The protected data.</returns>
- byte[] Protect(byte[] userData, string[] purposes);
-
- /// <summary>
- /// Unprotects the specified protected data.
- /// </summary>
- /// <param name="protectedData">The protected data.</param>
- /// <param name="purposes">The purposes.</param>
- /// <returns>The unprotected data.</returns>
- byte[] Unprotect(byte[] protectedData, string[] purposes);
- }
-
- /// <summary>
- /// Protects the specified user data.
- /// </summary>
- /// <param name="userData">The user data.</param>
- /// <param name="purposes">The purposes.</param>
- /// <returns>The encrypted data</returns>
- public static byte[] Protect(byte[] userData, params string[] purposes) {
- return MachineKeyImpl.Protect(userData, purposes);
- }
-
- /// <summary>
- /// Unprotects the specified protected data.
- /// </summary>
- /// <param name="protectedData">The protected data.</param>
- /// <param name="purposes">The purposes.</param>
- /// <returns>The unencrypted data</returns>
- public static byte[] Unprotect(byte[] protectedData, params string[] purposes) {
- return MachineKeyImpl.Unprotect(protectedData, purposes);
- }
-
- /// <summary>
- /// Gets the machine key implementation based on the runtime framework version.
- /// </summary>
- /// <returns>The machine key implementation</returns>
- private static IMachineKey GetMachineKeyImpl() {
- // Late bind to the MachineKey.Protect / Unprotect methods only if <httpRuntime targetFramework="4.5" />.
- // This helps ensure that round-tripping the payloads continues to work even if the application is
- // deployed to a mixed 4.0 / 4.5 farm environment.
- PropertyInfo targetFrameworkProperty = typeof(HttpRuntime).GetProperty("TargetFramework", typeof(Version));
- Version targetFramework = (targetFrameworkProperty != null) ? targetFrameworkProperty.GetValue(null, null) as Version : null;
- if (targetFramework != null && targetFramework >= new Version(4, 5)) {
- ProtectUnprotect protectThunk = (ProtectUnprotect)Delegate.CreateDelegate(typeof(ProtectUnprotect), typeof(MachineKey), "Protect", ignoreCase: false, throwOnBindFailure: false);
- ProtectUnprotect unprotectThunk = (ProtectUnprotect)Delegate.CreateDelegate(typeof(ProtectUnprotect), typeof(MachineKey), "Unprotect", ignoreCase: false, throwOnBindFailure: false);
- if (protectThunk != null && unprotectThunk != null) {
- return new MachineKey45(protectThunk, unprotectThunk); // ASP.NET 4.5
- }
- }
-
- return new MachineKey40(); // ASP.NET 4.0
- }
-
- /// <summary>
- /// On ASP.NET 4.0, we perform some transforms which mimic the behaviors of MachineKey.Protect
- /// and Unprotect.
- /// </summary>
- private sealed class MachineKey40 : IMachineKey {
- /// <summary>
- /// This is the magic header that identifies a MachineKey40 payload.
- /// It helps differentiate this from other encrypted payloads.</summary>
- private const uint MagicHeader = 0x8519140c;
-
- /// <summary>
- /// The SHA-256 factory to be used.
- /// </summary>
- private static readonly Func<SHA256> sha256Factory = GetSHA256Factory();
-
- /// <summary>
- /// Protects the specified user data.
- /// </summary>
- /// <param name="userData">The user data.</param>
- /// <param name="purposes">The purposes.</param>
- /// <returns>The protected data</returns>
- public byte[] Protect(byte[] userData, string[] purposes) {
- if (userData == null) {
- throw new ArgumentNullException("userData");
- }
-
- // dataWithHeader = {magic header} .. {purposes} .. {userData}
- byte[] dataWithHeader = new byte[checked(4 /* magic header */ + (256 / 8) /* purposes */ + userData.Length)];
- unchecked {
- dataWithHeader[0] = (byte)(MagicHeader >> 24);
- dataWithHeader[1] = (byte)(MagicHeader >> 16);
- dataWithHeader[2] = (byte)(MagicHeader >> 8);
- dataWithHeader[3] = (byte)MagicHeader;
- }
- byte[] purposeHash = ComputeSHA256(purposes);
- Buffer.BlockCopy(purposeHash, 0, dataWithHeader, 4, purposeHash.Length);
- Buffer.BlockCopy(userData, 0, dataWithHeader, 4 + (256 / 8), userData.Length);
-
- // encrypt + sign
- string hexValue = MachineKey.Encode(dataWithHeader, MachineKeyProtection.All);
-
- // convert hex -> binary
- byte[] binary = HexToBinary(hexValue);
- return binary;
- }
-
- /// <summary>
- /// Unprotects the specified protected data.
- /// </summary>
- /// <param name="protectedData">The protected data.</param>
- /// <param name="purposes">The purposes.</param>
- /// <returns>The unprotected data</returns>
- public byte[] Unprotect(byte[] protectedData, string[] purposes) {
- if (protectedData == null) {
- throw new ArgumentNullException("protectedData");
- }
-
- // convert binary -> hex and calculate what the purpose should read
- string hexEncodedData = BinaryToHex(protectedData);
- byte[] purposeHash = ComputeSHA256(purposes);
-
- try {
- // decrypt / verify signature
- byte[] dataWithHeader = MachineKey.Decode(hexEncodedData, MachineKeyProtection.All);
-
- // validate magic header and purpose string
- if (dataWithHeader != null
- && dataWithHeader.Length >= (4 + (256 / 8))
- && (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(dataWithHeader, 0)) == MagicHeader
- && AreByteArraysEqual(new ArraySegment<byte>(purposeHash), new ArraySegment<byte>(dataWithHeader, 4, 256 / 8))) {
- // validation succeeded
- byte[] userData = new byte[dataWithHeader.Length - 4 - (256 / 8)];
- Buffer.BlockCopy(dataWithHeader, 4 + (256 / 8), userData, 0, userData.Length);
- return userData;
- }
- }
- catch {
- // swallow since will be rethrown immediately below
- }
-
- // if we reached this point, some cryptographic operation failed
- throw new CryptographicException(WebResources.Generic_CryptoFailure);
- }
-
- /// <summary>
- /// Convert bytes to hex string.
- /// </summary>
- /// <param name="binary">The input array.</param>
- /// <returns>Hex string</returns>
- internal static string BinaryToHex(byte[] binary) {
- StringBuilder builder = new StringBuilder(checked(binary.Length * 2));
- for (int i = 0; i < binary.Length; i++) {
- byte b = binary[i];
- builder.Append(HexDigit(b >> 4));
- builder.Append(HexDigit(b & 0x0F));
- }
- string result = builder.ToString();
- return result;
- }
-
- /// <summary>
- /// This method is specially written to take the same amount of time
- /// regardless of where 'a' and 'b' differ. Please do not optimize it.</summary>
- /// <param name="a">first array.</param>
- /// <param name="b">second array.</param>
- /// <returns><c href="true" /> if equal, others <c href="false" /></returns>
- private static bool AreByteArraysEqual(ArraySegment<byte> a, ArraySegment<byte> b) {
- if (a.Count != b.Count) {
- return false;
- }
-
- bool areEqual = true;
- for (int i = 0; i < a.Count; i++) {
- areEqual &= a.Array[a.Offset + i] == b.Array[b.Offset + i];
- }
- return areEqual;
- }
-
- /// <summary>
- /// Computes a SHA256 hash over all of the input parameters.
- /// Each parameter is UTF8 encoded and preceded by a 7-bit encoded</summary>
- /// integer describing the encoded byte length of the string.
- /// <param name="parameters">The parameters.</param>
- /// <returns>The output hash</returns>
- [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification = "MemoryStream is resilient to double-Dispose")]
- private static byte[] ComputeSHA256(IList<string> parameters) {
- using (MemoryStream ms = new MemoryStream()) {
- using (BinaryWriter bw = new BinaryWriter(ms)) {
- if (parameters != null) {
- foreach (string parameter in parameters) {
- bw.Write(parameter); // also writes the length as a prefix; unambiguous
- }
- bw.Flush();
- }
-
- using (SHA256 sha256 = sha256Factory()) {
- byte[] retVal = sha256.ComputeHash(ms.GetBuffer(), 0, checked((int)ms.Length));
- return retVal;
- }
- }
- }
- }
-
- /// <summary>
- /// Gets the SHA-256 factory.
- /// </summary>
- /// <returns>SHA256 factory</returns>
- private static Func<SHA256> GetSHA256Factory() {
- // Note: ASP.NET 4.5 always prefers CNG, but the CNG algorithms are not that
- // performant on 4.0 and below. The following list is optimized for speed
- // given our scenarios.
- if (!CryptoConfig.AllowOnlyFipsAlgorithms) {
- // This provider is not FIPS-compliant, so we can't use it if FIPS compliance
- // is mandatory.
- return () => new SHA256Managed();
- }
-
- try {
- using (SHA256Cng sha256 = new SHA256Cng()) {
- return () => new SHA256Cng();
- }
- }
- catch (PlatformNotSupportedException) {
- // CNG not supported (perhaps because we're not on Windows Vista or above); move on
- }
-
- // If all else fails, fall back to CAPI.
- return () => new SHA256CryptoServiceProvider();
- }
-
- /// <summary>
- /// Convert to hex character
- /// </summary>
- /// <param name="value">The value to be converted.</param>
- /// <returns>Hex character</returns>
- private static char HexDigit(int value) {
- return (char)(value > 9 ? value + '7' : value + '0');
- }
-
- /// <summary>
- /// Convert hdex string to bytes.
- /// </summary>
- /// <param name="hex">Input hex string.</param>
- /// <returns>The bytes</returns>
- private static byte[] HexToBinary(string hex) {
- int size = hex.Length / 2;
- byte[] bytes = new byte[size];
- for (int idx = 0; idx < size; idx++) {
- bytes[idx] = (byte)((HexValue(hex[idx * 2]) << 4) + HexValue(hex[(idx * 2) + 1]));
- }
- return bytes;
- }
-
- /// <summary>
- /// Convert hex digit to byte.
- /// </summary>
- /// <param name="digit">The hex digit.</param>
- /// <returns>The byte</returns>
- private static int HexValue(char digit) {
- return digit > '9' ? digit - '7' : digit - '0';
- }
- }
-
- /// <summary>
- /// On ASP.NET 4.5, we can just delegate to MachineKey.Protect and MachineKey.Unprotect directly,
- /// which contain optimized code paths.
- /// </summary>
- private sealed class MachineKey45 : IMachineKey {
- /// <summary>
- /// Protect thunk
- /// </summary>
- private readonly ProtectUnprotect protectThunk;
-
- /// <summary>
- /// Unprotect thunk
- /// </summary>
- private readonly ProtectUnprotect unprotectThunk;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="MachineKey45"/> class.
- /// </summary>
- /// <param name="protectThunk">The protect thunk.</param>
- /// <param name="unprotectThunk">The unprotect thunk.</param>
- public MachineKey45(ProtectUnprotect protectThunk, ProtectUnprotect unprotectThunk) {
- this.protectThunk = protectThunk;
- this.unprotectThunk = unprotectThunk;
- }
-
- /// <summary>
- /// Protects the specified user data.
- /// </summary>
- /// <param name="userData">The user data.</param>
- /// <param name="purposes">The purposes.</param>
- /// <returns>The protected data</returns>
- public byte[] Protect(byte[] userData, string[] purposes) {
- return this.protectThunk(userData, purposes);
- }
-
- /// <summary>
- /// Unprotects the specified protected data.
- /// </summary>
- /// <param name="protectedData">The protected data.</param>
- /// <param name="purposes">The purposes.</param>
- /// <returns>The unprotected data</returns>
- public byte[] Unprotect(byte[] protectedData, string[] purposes) {
- return this.unprotectThunk(protectedData, purposes);
- }
- }
- }
-}
diff --git a/src/DotNetOpenAuth.AspNet/OpenAuthSecurityManager.cs b/src/DotNetOpenAuth.AspNet/OpenAuthSecurityManager.cs
index 6736205..7669072 100644
--- a/src/DotNetOpenAuth.AspNet/OpenAuthSecurityManager.cs
+++ b/src/DotNetOpenAuth.AspNet/OpenAuthSecurityManager.cs
@@ -8,6 +8,8 @@ namespace DotNetOpenAuth.AspNet {
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;
+ using System.Threading;
+ using System.Threading.Tasks;
using System.Web;
using System.Web.Security;
using DotNetOpenAuth.AspNet.Clients;
@@ -141,10 +143,12 @@ namespace DotNetOpenAuth.AspNet {
/// <summary>
/// Requests the specified provider to start the authentication by directing users to an external website
/// </summary>
- /// <param name="returnUrl">
- /// The return url after user is authenticated.
- /// </param>
- public void RequestAuthentication(string returnUrl) {
+ /// <param name="returnUrl">The return url after user is authenticated.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// A task that completes with the asynchronous operation.
+ /// </returns>
+ public async Task RequestAuthenticationAsync(string returnUrl, CancellationToken cancellationToken = default(CancellationToken)) {
// convert returnUrl to an absolute path
Uri uri;
if (!string.IsNullOrEmpty(returnUrl)) {
@@ -176,20 +180,21 @@ namespace DotNetOpenAuth.AspNet {
this.requestContext.Response.Cookies.Add(xsrfCookie);
// issue the redirect to the external auth provider
- this.authenticationProvider.RequestAuthentication(this.requestContext, uri);
+ await this.authenticationProvider.RequestAuthenticationAsync(this.requestContext, uri, cancellationToken);
}
/// <summary>
/// Checks if user is successfully authenticated when user is redirected back to this user.
/// </summary>
/// <param name="returnUrl">The return Url which must match exactly the Url passed into RequestAuthentication() earlier.</param>
- /// <remarks>
- /// This returnUrl parameter only applies to OAuth2 providers. For other providers, it ignores the returnUrl parameter.
- /// </remarks>
+ /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>
/// The result of the authentication.
/// </returns>
- public AuthenticationResult VerifyAuthentication(string returnUrl) {
+ /// <remarks>
+ /// This returnUrl parameter only applies to OAuth2 providers. For other providers, it ignores the returnUrl parameter.
+ /// </remarks>
+ public async Task<AuthenticationResult> VerifyAuthenticationAsync(string returnUrl, CancellationToken cancellationToken = default(CancellationToken)) {
// check for XSRF attack
string sessionId;
bool successful = this.ValidateRequestAgainstXsrfAttack(out sessionId);
@@ -223,7 +228,7 @@ namespace DotNetOpenAuth.AspNet {
uri = uri.AttachQueryStringParameter(SessionIdQueryStringName, sessionId);
try {
- AuthenticationResult result = oauth2Client.VerifyAuthentication(this.requestContext, uri);
+ AuthenticationResult result = await oauth2Client.VerifyAuthenticationAsync(this.requestContext, uri, cancellationToken);
if (!result.IsSuccessful) {
// if the result is a Failed result, creates a new Failed response which has providerName info.
result = new AuthenticationResult(
@@ -241,7 +246,7 @@ namespace DotNetOpenAuth.AspNet {
}
}
else {
- return this.authenticationProvider.VerifyAuthentication(this.requestContext);
+ return await this.authenticationProvider.VerifyAuthenticationAsync(this.requestContext, cancellationToken);
}
}
diff --git a/src/DotNetOpenAuth.AspNet/WebResources.Designer.cs b/src/DotNetOpenAuth.AspNet/WebResources.Designer.cs
index fd79a73..da1d1ca 100644
--- a/src/DotNetOpenAuth.AspNet/WebResources.Designer.cs
+++ b/src/DotNetOpenAuth.AspNet/WebResources.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:4.0.30319.544
+// Runtime Version:4.0.30319.18033
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -79,15 +79,6 @@ namespace DotNetOpenAuth.AspNet {
}
/// <summary>
- /// Looks up a localized string similar to The provided data could not be decrypted. If the current application is deployed in a web farm configuration, ensure that the &apos;decryptionKey&apos; and &apos;validationKey&apos; attributes are explicitly specified in the &lt;machineKey&gt; configuration section..
- /// </summary>
- internal static string Generic_CryptoFailure {
- get {
- return ResourceManager.GetString("Generic_CryptoFailure", resourceCulture);
- }
- }
-
- /// <summary>
/// Looks up a localized string similar to An OAuth data provider has already been registered for this application..
/// </summary>
internal static string OAuthDataProviderRegistered {
diff --git a/src/DotNetOpenAuth.AspNet/WebResources.resx b/src/DotNetOpenAuth.AspNet/WebResources.resx
index c1552e9..a491579 100644
--- a/src/DotNetOpenAuth.AspNet/WebResources.resx
+++ b/src/DotNetOpenAuth.AspNet/WebResources.resx
@@ -123,9 +123,6 @@
<data name="FailedToEncryptTicket" xml:space="preserve">
<value>Unable to encrypt the authentication ticket.</value>
</data>
- <data name="Generic_CryptoFailure" xml:space="preserve">
- <value>The provided data could not be decrypted. If the current application is deployed in a web farm configuration, ensure that the 'decryptionKey' and 'validationKey' attributes are explicitly specified in the &lt;machineKey&gt; configuration section.</value>
- </data>
<data name="OAuthDataProviderRegistered" xml:space="preserve">
<value>An OAuth data provider has already been registered for this application.</value>
</data>
diff --git a/src/DotNetOpenAuth.AspNet/packages.config b/src/DotNetOpenAuth.AspNet/packages.config
index 58890d8..d32d62f 100644
--- a/src/DotNetOpenAuth.AspNet/packages.config
+++ b/src/DotNetOpenAuth.AspNet/packages.config
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="Validation" version="2.0.1.12362" targetFramework="net45" />
+ <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
+ <package id="Validation" version="2.0.2.13022" targetFramework="net45" />
</packages> \ No newline at end of file