summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2013-02-25 21:26:04 -0800
committerAndrew Arnott <andrewarnott@gmail.com>2013-02-25 21:26:04 -0800
commit38a1162c5cbaea035e655dc9accd92f9de5019ed (patch)
tree489ba7dfa106d5b0a8878ac386f2d2130bdf6b21
parent10fc3ad3a7feda0cb5ab64aabe2e26bbce94595a (diff)
downloadDotNetOpenAuth-38a1162c5cbaea035e655dc9accd92f9de5019ed.zip
DotNetOpenAuth-38a1162c5cbaea035e655dc9accd92f9de5019ed.tar.gz
DotNetOpenAuth-38a1162c5cbaea035e655dc9accd92f9de5019ed.tar.bz2
OAuth 1.0 Consumers are now *much* simpler, entirely avoiding channels.
Build breaks in other projects, however.
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj7
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs82
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs147
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs131
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs60
-rw-r--r--samples/OAuthConsumer/Web.config4
-rw-r--r--src/DotNetOpenAuth.AspNet/Clients/OAuth/SimpleConsumerTokenManager.cs104
-rw-r--r--src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj2
-rw-r--r--src/DotNetOpenAuth.AspNet/WebResources.Designer.cs11
-rw-r--r--src/DotNetOpenAuth.AspNet/WebResources.resx3
-rw-r--r--src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj1
-rw-r--r--src/DotNetOpenAuth.Core/MachineKeyUtil.cs (renamed from src/DotNetOpenAuth.AspNet/MachineKeyUtil.cs)4
-rw-r--r--src/DotNetOpenAuth.Core/Strings.Designer.cs11
-rw-r--r--src/DotNetOpenAuth.Core/Strings.resx3
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/DotNetOpenAuth.OAuth.Consumer.csproj13
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/AccessToken.cs39
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/AccessTokenResponse.cs19
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/IConsumerTokenManager.cs25
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerChannel.cs67
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs108
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/RsaSha1ConsumerSigningBindingElement.cs76
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/Consumer.cs336
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs258
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/CookieTemporaryCredentialStorage.cs130
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/DesktopConsumer.cs79
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/ITemporaryCredentialStorage.cs40
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/MemoryTemporaryCredentialStorage.cs65
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/OAuth1HttpMessageHandlerBase.cs42
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/OAuth1RsaSha1HttpMessageHandler.cs15
-rw-r--r--src/DotNetOpenAuth.OAuth.Consumer/OAuth/WebConsumer.cs87
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/DotNetOpenAuth.OAuth.ServiceProvider.csproj1
-rw-r--r--src/DotNetOpenAuth.OAuth.ServiceProvider/OAuthReporting.cs (renamed from src/DotNetOpenAuth.OAuth/OAuthReporting.cs)4
-rw-r--r--src/DotNetOpenAuth.OAuth/DotNetOpenAuth.OAuth.csproj4
-rw-r--r--src/DotNetOpenAuth.OAuth/OAuth/Messages/MessageBaseSimple.cs10
-rw-r--r--src/DotNetOpenAuth.OAuth/OAuth/Protocol.cs25
-rw-r--r--src/DotNetOpenAuth.OAuth/OAuth/ServiceProviderDescription.cs105
-rw-r--r--src/DotNetOpenAuth.OpenIdOAuth/OAuth/WebConsumerOpenIdRelyingParty.cs41
37 files changed, 902 insertions, 1257 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
index 9f74693..19f26b5 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
+++ b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
@@ -101,9 +101,6 @@
<Compile Include="GoogleConsumer.cs" />
<Compile Include="HttpAsyncHandlerBase.cs" />
<Compile Include="InMemoryClientAuthorizationTracker.cs" />
- <Compile Include="InMemoryTokenManager.cs">
- <SubType>Code</SubType>
- </Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TwitterConsumer.cs" />
<Compile Include="Util.cs" />
@@ -138,6 +135,10 @@
<Project>{60426312-6AE5-4835-8667-37EDEA670222}</Project>
<Name>DotNetOpenAuth.Core</Name>
</ProjectReference>
+ <ProjectReference Include="..\..\src\DotNetOpenAuth.OAuth.Common\DotNetOpenAuth.OAuth.Common.csproj">
+ <Project>{115217c5-22cd-415c-a292-0dd0238cdd89}</Project>
+ <Name>DotNetOpenAuth.OAuth.Common</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\src\DotNetOpenAuth.OAuth.Consumer\DotNetOpenAuth.OAuth.Consumer.csproj">
<Project>{B202E40D-4663-4A2B-ACDA-865F88FF7CAA}</Project>
<Name>DotNetOpenAuth.OAuth.Consumer</Name>
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs
index cd3c5fe..2302837 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs
@@ -4,8 +4,7 @@
// </copyright>
//-----------------------------------------------------------------------
-namespace DotNetOpenAuth.ApplicationBlock
-{
+namespace DotNetOpenAuth.ApplicationBlock {
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -29,17 +28,15 @@ namespace DotNetOpenAuth.ApplicationBlock
/// <summary>
/// A consumer capable of communicating with Google Data APIs.
/// </summary>
- public static class GoogleConsumer
- {
+ public class GoogleConsumer : Consumer {
/// <summary>
/// The Consumer to use for accessing Google data APIs.
/// </summary>
- public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription {
- RequestTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
- UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthAuthorizeToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
- AccessTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetAccessToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
- TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
- };
+ public static readonly ServiceProviderDescription ServiceDescription =
+ new ServiceProviderDescription(
+ "https://www.google.com/accounts/OAuthGetRequestToken",
+ "https://www.google.com/accounts/OAuthAuthorizeToken",
+ "https://www.google.com/accounts/OAuthGetAccessToken");
/// <summary>
/// A mapping between Google's applications and their URI scope values.
@@ -72,8 +69,7 @@ namespace DotNetOpenAuth.ApplicationBlock
/// The many specific authorization scopes Google offers.
/// </summary>
[Flags]
- public enum Applications : long
- {
+ public enum Applications : long {
/// <summary>
/// The Gmail address book.
/// </summary>
@@ -155,23 +151,7 @@ namespace DotNetOpenAuth.ApplicationBlock
Maps = 0x8000,
}
- /// <summary>
- /// The service description to use for accessing Google data APIs using an X509 certificate.
- /// </summary>
- /// <param name="signingCertificate">The signing certificate.</param>
- /// <returns>A service description that can be used to create an instance of
- /// <see cref="DesktopConsumer"/> or <see cref="WebConsumer"/>. </returns>
- public static ServiceProviderDescription CreateRsaSha1ServiceDescription(X509Certificate2 signingCertificate) {
- if (signingCertificate == null) {
- throw new ArgumentNullException("signingCertificate");
- }
-
- return new ServiceProviderDescription {
- RequestTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
- UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthAuthorizeToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
- AccessTokenEndpoint = new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetAccessToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
- TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new RsaSha1ConsumerSigningBindingElement(signingCertificate) },
- };
+ public GoogleConsumer() {
}
/// <summary>
@@ -184,40 +164,12 @@ namespace DotNetOpenAuth.ApplicationBlock
/// A task that completes with the asynchronous operation.
/// </returns>
/// <exception cref="System.ArgumentNullException">consumer</exception>
- public static async Task RequestAuthorizationAsync(WebConsumer consumer, Applications requestedAccessScope, CancellationToken cancellationToken = default(CancellationToken)) {
- if (consumer == null) {
- throw new ArgumentNullException("consumer");
- }
-
+ public Task<Uri> RequestUserAuthorizationAsync(Applications requestedAccessScope, CancellationToken cancellationToken = default(CancellationToken)) {
var extraParameters = new Dictionary<string, string> {
{ "scope", GetScopeUri(requestedAccessScope) },
};
Uri callback = Util.GetCallbackUrlFromContext();
- var request = await consumer.PrepareRequestUserAuthorizationAsync(callback, extraParameters, null, cancellationToken);
- var redirectingResponse = await consumer.Channel.PrepareResponseAsync(request, cancellationToken);
- redirectingResponse.Send();
- }
-
- /// <summary>
- /// Requests authorization from Google to access data from a set of Google applications.
- /// </summary>
- /// <param name="consumer">The Google consumer previously constructed using <see cref="CreateWebConsumer" /> or <see cref="CreateDesktopConsumer" />.</param>
- /// <param name="requestedAccessScope">The requested access scope.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>
- /// The URI to redirect to and the request token.
- /// </returns>
- /// <exception cref="System.ArgumentNullException">consumer</exception>
- public static Task<Tuple<Uri, string>> RequestAuthorizationAsync(DesktopConsumer consumer, Applications requestedAccessScope, CancellationToken cancellationToken = default(CancellationToken)) {
- if (consumer == null) {
- throw new ArgumentNullException("consumer");
- }
-
- var extraParameters = new Dictionary<string, string> {
- { "scope", GetScopeUri(requestedAccessScope) },
- };
-
- return consumer.RequestUserAuthorizationAsync(extraParameters, null, cancellationToken);
+ return this.RequestUserAuthorizationAsync(callback, extraParameters, cancellationToken);
}
/// <summary>
@@ -232,14 +184,10 @@ namespace DotNetOpenAuth.ApplicationBlock
/// An XML document returned by Google.
/// </returns>
/// <exception cref="System.ArgumentNullException">consumer</exception>
- public static async Task<XDocument> GetContactsAsync(ConsumerBase consumer, string accessToken, int maxResults = 25, int startIndex = 1, CancellationToken cancellationToken = default(CancellationToken)) {
- if (consumer == null) {
- throw new ArgumentNullException("consumer");
- }
-
+ public async Task<XDocument> GetContactsAsync(AccessToken accessToken, int maxResults = 25, int startIndex = 1, CancellationToken cancellationToken = default(CancellationToken)) {
// Enable gzip compression. Google only compresses the response for recognized user agent headers. - Mike Lim
var handler = new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip };
- using (var httpClient = consumer.CreateHttpClient(accessToken, handler)) {
+ using (var httpClient = this.CreateHttpClient(accessToken, handler)) {
var request = new HttpRequestMessage(HttpMethod.Get, GetContactsEndpoint);
request.Content = new FormUrlEncodedContent(
new Dictionary<string, string>() {
@@ -255,7 +203,7 @@ namespace DotNetOpenAuth.ApplicationBlock
}
}
- public static async Task PostBlogEntryAsync(ConsumerBase consumer, string accessToken, string blogUrl, string title, XElement body, CancellationToken cancellationToken) {
+ public async Task PostBlogEntryAsync(AccessToken accessToken, string blogUrl, string title, XElement body, CancellationToken cancellationToken) {
string feedUrl;
var getBlogHome = WebRequest.Create(blogUrl);
using (var blogHomeResponse = getBlogHome.GetResponse()) {
@@ -285,7 +233,7 @@ namespace DotNetOpenAuth.ApplicationBlock
var request = new HttpRequestMessage(HttpMethod.Post, feedUrl);
request.Content = new StreamContent(ms);
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/atom+xml");
- using (var httpClient = consumer.CreateHttpClient(accessToken)) {
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
using (var response = await httpClient.SendAsync(request, cancellationToken)) {
if (response.StatusCode == HttpStatusCode.Created) {
// Success
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs b/samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs
deleted file mode 100644
index 35f6c08..0000000
--- a/samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs
+++ /dev/null
@@ -1,147 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="InMemoryTokenManager.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.ApplicationBlock {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics;
- using DotNetOpenAuth.OAuth.ChannelElements;
- using DotNetOpenAuth.OAuth.Messages;
- using DotNetOpenAuth.OpenId.Extensions.OAuth;
-
-#if SAMPLESONLY
- /// <summary>
- /// A token manager that only retains tokens in memory.
- /// Meant for SHORT TERM USE TOKENS ONLY.
- /// </summary>
- /// <remarks>
- /// A likely application of this class is for "Sign In With Twitter",
- /// where the user only signs in without providing any authorization to access
- /// Twitter APIs except to authenticate, since that access token is only useful once.
- /// </remarks>
- internal class InMemoryTokenManager : IConsumerTokenManager, IOpenIdOAuthTokenManager {
- private Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>();
-
- /// <summary>
- /// Initializes a new instance of the <see cref="InMemoryTokenManager"/> class.
- /// </summary>
- /// <param name="consumerKey">The consumer key.</param>
- /// <param name="consumerSecret">The consumer secret.</param>
- public InMemoryTokenManager(string consumerKey, string consumerSecret) {
- if (string.IsNullOrEmpty(consumerKey)) {
- throw new ArgumentNullException("consumerKey");
- }
-
- this.ConsumerKey = consumerKey;
- this.ConsumerSecret = consumerSecret;
- }
-
- /// <summary>
- /// Gets the consumer key.
- /// </summary>
- /// <value>The consumer key.</value>
- public string ConsumerKey { get; private set; }
-
- /// <summary>
- /// Gets the consumer secret.
- /// </summary>
- /// <value>The consumer secret.</value>
- public string ConsumerSecret { get; private set; }
-
- #region ITokenManager Members
-
- /// <summary>
- /// Gets the Token Secret given a request or access token.
- /// </summary>
- /// <param name="token">The request or access token.</param>
- /// <returns>
- /// The secret associated with the given token.
- /// </returns>
- /// <exception cref="ArgumentException">Thrown if the secret cannot be found for the given token.</exception>
- public string GetTokenSecret(string token) {
- return this.tokensAndSecrets[token];
- }
-
- /// <summary>
- /// Stores a newly generated unauthorized request token, secret, and optional
- /// application-specific parameters for later recall.
- /// </summary>
- /// <param name="request">The request message that resulted in the generation of a new unauthorized request token.</param>
- /// <param name="response">The response message that includes the unauthorized request token.</param>
- /// <exception cref="ArgumentException">Thrown if the consumer key is not registered, or a required parameter was not found in the parameters collection.</exception>
- /// <remarks>
- /// Request tokens stored by this method SHOULD NOT associate any user account with this token.
- /// It usually opens up security holes in your application to do so. Instead, you associate a user
- /// account with access tokens (not request tokens) in the <see cref="ExpireRequestTokenAndStoreNewAccessToken"/>
- /// method.
- /// </remarks>
- public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) {
- this.tokensAndSecrets[response.Token] = response.TokenSecret;
- }
-
- /// <summary>
- /// Deletes a request token and its associated secret and stores a new access token and secret.
- /// </summary>
- /// <param name="consumerKey">The Consumer that is exchanging its request token for an access token.</param>
- /// <param name="requestToken">The Consumer's request token that should be deleted/expired.</param>
- /// <param name="accessToken">The new access token that is being issued to the Consumer.</param>
- /// <param name="accessTokenSecret">The secret associated with the newly issued access token.</param>
- /// <remarks>
- /// <para>
- /// Any scope of granted privileges associated with the request token from the
- /// original call to <see cref="StoreNewRequestToken"/> should be carried over
- /// to the new Access Token.
- /// </para>
- /// <para>
- /// To associate a user account with the new access token,
- /// <see cref="System.Web.HttpContext.User">HttpContext.Current.User</see> may be
- /// useful in an ASP.NET web application within the implementation of this method.
- /// Alternatively you may store the access token here without associating with a user account,
- /// and wait until <see cref="WebConsumer.ProcessUserAuthorization()"/> or
- /// <see cref="DesktopConsumer.ProcessUserAuthorization(string, string)"/> return the access
- /// token to associate the access token with a user account at that point.
- /// </para>
- /// </remarks>
- public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret) {
- this.tokensAndSecrets.Remove(requestToken);
- this.tokensAndSecrets[accessToken] = accessTokenSecret;
- }
-
- /// <summary>
- /// Classifies a token as a request token or an access token.
- /// </summary>
- /// <param name="token">The token to classify.</param>
- /// <returns>Request or Access token, or invalid if the token is not recognized.</returns>
- public TokenType GetTokenType(string token) {
- throw new NotImplementedException();
- }
-
- #endregion
-
- #region IOpenIdOAuthTokenManager Members
-
- /// <summary>
- /// Stores a new request token obtained over an OpenID request.
- /// </summary>
- /// <param name="consumerKey">The consumer key.</param>
- /// <param name="authorization">The authorization message carrying the request token and authorized access scope.</param>
- /// <remarks>
- /// <para>The token secret is the empty string.</para>
- /// <para>Tokens stored by this method should be short-lived to mitigate
- /// possible security threats. Their lifetime should be sufficient for the
- /// relying party to receive the positive authentication assertion and immediately
- /// send a follow-up request for the access token.</para>
- /// </remarks>
- public void StoreOpenIdAuthorizedRequestToken(string consumerKey, AuthorizationApprovedResponse authorization) {
- this.tokensAndSecrets[authorization.RequestToken] = string.Empty;
- }
-
- #endregion
- }
-#else
-#error The InMemoryTokenManager class is only for samples as it forgets all tokens whenever the application restarts! You should implement IConsumerTokenManager in your own app that stores tokens in a persistent store (like a SQL database).
-#endif
-} \ No newline at end of file
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
index e665d96..7bbfaa1 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
@@ -35,22 +35,18 @@ namespace DotNetOpenAuth.ApplicationBlock {
/// The description of Twitter's OAuth protocol URIs for use with actually reading/writing
/// a user's private Twitter data.
/// </summary>
- public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription {
- RequestTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- UserAuthorizationEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/authorize", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- AccessTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
- };
+ public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription(
+ "https://api.twitter.com/oauth/request_token",
+ "https://api.twitter.com/oauth/authorize",
+ "https://api.twitter.com/oauth/access_token");
/// <summary>
/// The description of Twitter's OAuth protocol URIs for use with their "Sign in with Twitter" feature.
/// </summary>
- public static readonly ServiceProviderDescription SignInWithTwitterServiceDescription = new ServiceProviderDescription {
- RequestTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- UserAuthorizationEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/authenticate", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- AccessTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
- };
+ public static readonly ServiceProviderDescription SignInWithTwitterServiceDescription = new ServiceProviderDescription(
+ "https://api.twitter.com/oauth/request_token",
+ "https://api.twitter.com/oauth/authenticate",
+ "https://api.twitter.com/oauth/access_token");
/// <summary>
/// The URI to get a user's favorites.
@@ -68,22 +64,33 @@ namespace DotNetOpenAuth.ApplicationBlock {
private static readonly Uri VerifyCredentialsEndpoint = new Uri("http://api.twitter.com/1/account/verify_credentials.xml");
- /// <summary>
- /// The consumer used for the Sign in to Twitter feature.
- /// </summary>
- private static WebConsumer signInConsumer;
+ private class HostFactories : IHostFactories {
+ private static readonly IHostFactories underlyingFactories = new DefaultOAuthHostFactories();
- /// <summary>
- /// The lock acquired to initialize the <see cref="signInConsumer"/> field.
- /// </summary>
- private static object signInConsumerInitLock = new object();
+ public HttpMessageHandler CreateHttpMessageHandler() {
+ return new WebRequestHandler();
+ }
- /// <summary>
- /// Initializes static members of the <see cref="TwitterConsumer"/> class.
- /// </summary>
- static TwitterConsumer() {
- // Twitter can't handle the Expect 100 Continue HTTP header.
- ServicePointManager.FindServicePoint(GetFavoritesEndpoint).Expect100Continue = false;
+ public HttpClient CreateHttpClient(HttpMessageHandler handler = null) {
+ var client = underlyingFactories.CreateHttpClient(handler);
+
+ // Twitter can't handle the Expect 100 Continue HTTP header.
+ client.DefaultRequestHeaders.ExpectContinue = false;
+ return client;
+ }
+ }
+
+ public static Consumer CreateConsumer(bool forWeb = true) {
+ string consumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"];
+ string consumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"];
+ if (IsTwitterConsumerConfigured) {
+ ITemporaryCredentialStorage storage = forWeb ? (ITemporaryCredentialStorage)new CookieTemporaryCredentialStorage() : new MemoryTemporaryCredentialStorage();
+ return new Consumer(consumerKey, consumerSecret, ServiceDescription, storage) {
+ HostFactories = new HostFactories(),
+ };
+ } else {
+ throw new InvalidOperationException("No Twitter OAuth consumer key and secret could be found in web.config AppSettings.");
+ }
}
/// <summary>
@@ -96,45 +103,7 @@ namespace DotNetOpenAuth.ApplicationBlock {
}
}
- /// <summary>
- /// Gets the consumer to use for the Sign in to Twitter feature.
- /// </summary>
- /// <value>The twitter sign in.</value>
- private static WebConsumer TwitterSignIn {
- get {
- if (signInConsumer == null) {
- lock (signInConsumerInitLock) {
- if (signInConsumer == null) {
- signInConsumer = new WebConsumer(SignInWithTwitterServiceDescription, ShortTermUserSessionTokenManager);
- }
- }
- }
-
- return signInConsumer;
- }
- }
-
- private static InMemoryTokenManager ShortTermUserSessionTokenManager {
- get {
- var store = HttpContext.Current.Session;
- var tokenManager = (InMemoryTokenManager)store["TwitterShortTermUserSessionTokenManager"];
- if (tokenManager == null) {
- string consumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"];
- string consumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"];
- if (IsTwitterConsumerConfigured) {
- tokenManager = new InMemoryTokenManager(consumerKey, consumerSecret);
- store["TwitterShortTermUserSessionTokenManager"] = tokenManager;
- } else {
- throw new InvalidOperationException("No Twitter OAuth consumer key and secret could be found in web.config AppSettings.");
- }
- }
-
- return tokenManager;
- }
- }
-
- public static async Task<JArray> GetUpdatesAsync(
- ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ public static async Task<JArray> GetUpdatesAsync(Consumer twitter, AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
using (var httpClient = twitter.CreateHttpClient(accessToken)) {
using (var response = await httpClient.GetAsync(GetFriendTimelineStatusEndpoint, cancellationToken)) {
response.EnsureSuccessStatusCode();
@@ -145,7 +114,7 @@ namespace DotNetOpenAuth.ApplicationBlock {
}
}
- public static async Task<XDocument> GetFavorites(ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ public static async Task<XDocument> GetFavorites(Consumer twitter, AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
using (var httpClient = twitter.CreateHttpClient(accessToken)) {
using (HttpResponseMessage response = await httpClient.GetAsync(GetFavoritesEndpoint, cancellationToken)) {
response.EnsureSuccessStatusCode();
@@ -154,7 +123,7 @@ namespace DotNetOpenAuth.ApplicationBlock {
}
}
- public static async Task<XDocument> UpdateProfileBackgroundImageAsync(ConsumerBase twitter, string accessToken, string image, bool tile, CancellationToken cancellationToken) {
+ public static async Task<XDocument> UpdateProfileBackgroundImageAsync(Consumer twitter, AccessToken accessToken, string image, bool tile, CancellationToken cancellationToken) {
var imageAttachment = new StreamContent(File.OpenRead(image));
imageAttachment.Headers.ContentType = new MediaTypeHeaderValue("image/" + Path.GetExtension(image).Substring(1).ToLowerInvariant());
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, UpdateProfileBackgroundImageEndpoint);
@@ -172,12 +141,12 @@ namespace DotNetOpenAuth.ApplicationBlock {
}
}
- public static Task<XDocument> UpdateProfileImageAsync(ConsumerBase twitter, string accessToken, string pathToImage, CancellationToken cancellationToken = default(CancellationToken)) {
+ public static Task<XDocument> UpdateProfileImageAsync(Consumer twitter, AccessToken accessToken, string pathToImage, CancellationToken cancellationToken = default(CancellationToken)) {
string contentType = "image/" + Path.GetExtension(pathToImage).Substring(1).ToLowerInvariant();
return UpdateProfileImageAsync(twitter, accessToken, File.OpenRead(pathToImage), contentType, cancellationToken);
}
- public static async Task<XDocument> UpdateProfileImageAsync(ConsumerBase twitter, string accessToken, Stream image, string contentType, CancellationToken cancellationToken = default(CancellationToken)) {
+ public static async Task<XDocument> UpdateProfileImageAsync(Consumer twitter, AccessToken accessToken, Stream image, string contentType, CancellationToken cancellationToken = default(CancellationToken)) {
var imageAttachment = new StreamContent(image);
imageAttachment.Headers.ContentType = new MediaTypeHeaderValue(contentType);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, UpdateProfileImageEndpoint);
@@ -193,7 +162,7 @@ namespace DotNetOpenAuth.ApplicationBlock {
}
}
- public static async Task<XDocument> VerifyCredentialsAsync(ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ public static async Task<XDocument> VerifyCredentialsAsync(Consumer twitter, AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
using (var httpClient = twitter.CreateHttpClient(accessToken)) {
using (var response = await httpClient.GetAsync(VerifyCredentialsEndpoint, cancellationToken)) {
response.EnsureSuccessStatusCode();
@@ -204,7 +173,7 @@ namespace DotNetOpenAuth.ApplicationBlock {
}
}
- public static async Task<string> GetUsername(ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ public static async Task<string> GetUsername(Consumer twitter, AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
XDocument xml = await VerifyCredentialsAsync(twitter, accessToken, cancellationToken);
XPathNavigator nav = xml.CreateNavigator();
return nav.SelectSingleNode("/user/screen_name").Value;
@@ -223,14 +192,17 @@ namespace DotNetOpenAuth.ApplicationBlock {
/// <c>return StartSignInWithTwitter().<see cref="MessagingUtilities.AsActionResult">AsActionResult()</see></c>
/// to actually perform the redirect.
/// </remarks>
- public static async Task<HttpResponseMessage> StartSignInWithTwitterAsync(bool forceNewLogin = false, CancellationToken cancellationToken = default(CancellationToken)) {
+ public static async Task<Uri> StartSignInWithTwitterAsync(bool forceNewLogin = false, CancellationToken cancellationToken = default(CancellationToken)) {
var redirectParameters = new Dictionary<string, string>();
if (forceNewLogin) {
redirectParameters["force_login"] = "true";
}
Uri callback = MessagingUtilities.GetRequestUrlFromContext().StripQueryArgumentsWithPrefix("oauth_");
- var request = await TwitterSignIn.PrepareRequestUserAuthorizationAsync(callback, null, redirectParameters, cancellationToken);
- return await TwitterSignIn.Channel.PrepareResponseAsync(request, cancellationToken);
+
+ var consumer = CreateConsumer();
+ consumer.ServiceProvider = SignInWithTwitterServiceDescription;
+ Uri redirectUrl = await consumer.RequestUserAuthorizationAsync(callback, cancellationToken: cancellationToken);
+ return redirectUrl;
}
/// <summary>
@@ -241,19 +213,16 @@ namespace DotNetOpenAuth.ApplicationBlock {
/// <returns>
/// A tuple with the screen name and Twitter unique user ID if successful; otherwise <c>null</c>.
/// </returns>
- public static async Task<Tuple<string, int>> TryFinishSignInWithTwitterAsync(CancellationToken cancellationToken = default(CancellationToken)) {
- var response = await TwitterSignIn.ProcessUserAuthorizationAsync(cancellationToken: cancellationToken);
+ public static async Task<Tuple<string, int>> TryFinishSignInWithTwitterAsync(Uri completeUrl, CancellationToken cancellationToken = default(CancellationToken)) {
+ var consumer = CreateConsumer();
+ consumer.ServiceProvider = SignInWithTwitterServiceDescription;
+ var response = await consumer.ProcessUserAuthorizationAsync(completeUrl, cancellationToken: cancellationToken);
if (response == null) {
return null;
}
string screenName = response.ExtraData["screen_name"];
int userId = int.Parse(response.ExtraData["user_id"]);
-
- // If we were going to make this LOOK like OpenID even though it isn't,
- // this seems like a reasonable, secure claimed id to allow the user to assume.
- ////OpenId.Identifier fake_claimed_id = string.Format(CultureInfo.InvariantCulture, "http://twitter.com/{0}#{1}", screenName, userId);
-
return Tuple.Create(screenName, userId);
}
}
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs
index cedcbf7..4bcb112 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs
@@ -4,10 +4,10 @@
// </copyright>
//-----------------------------------------------------------------------
-namespace DotNetOpenAuth.ApplicationBlock
-{
+namespace DotNetOpenAuth.ApplicationBlock {
using System;
using System.Collections.Generic;
+ using System.Configuration;
using System.Linq;
using System.Net;
using System.Text;
@@ -18,47 +18,35 @@ namespace DotNetOpenAuth.ApplicationBlock
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
- public static class YammerConsumer
- {
+ public static class YammerConsumer {
/// <summary>
/// The Consumer to use for accessing Google data APIs.
/// </summary>
- public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription {
- RequestTokenEndpoint = new MessageReceivingEndpoint("https://www.yammer.com/oauth/request_token", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest),
- UserAuthorizationEndpoint = new MessageReceivingEndpoint("https://www.yammer.com/oauth/authorize", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
- AccessTokenEndpoint = new MessageReceivingEndpoint("https://www.yammer.com/oauth/access_token", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest),
- TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new PlaintextSigningBindingElement() },
- ProtocolVersion = ProtocolVersion.V10,
- };
+ public static readonly ServiceProviderDescription ServiceDescription =
+ new ServiceProviderDescription(
+ "https://www.yammer.com/oauth/request_token",
+ "https://www.yammer.com/oauth/authorize",
+ "https://www.yammer.com/oauth/access_token");
- public static DesktopConsumer CreateConsumer(IConsumerTokenManager tokenManager) {
- return new DesktopConsumer(ServiceDescription, tokenManager);
- }
-
- public static Task<Tuple<Uri, string>> PrepareRequestAuthorizationAsync(DesktopConsumer consumer, CancellationToken cancellationToken = default(CancellationToken)) {
- if (consumer == null) {
- throw new ArgumentNullException("consumer");
+ /// <summary>
+ /// Gets a value indicating whether the Twitter consumer key and secret are set in the web.config file.
+ /// </summary>
+ public static bool IsConsumerConfigured {
+ get {
+ return !string.IsNullOrEmpty(ConfigurationManager.AppSettings["yammerConsumerKey"]) &&
+ !string.IsNullOrEmpty(ConfigurationManager.AppSettings["yammerConsumerSecret"]);
}
-
- return consumer.RequestUserAuthorizationAsync(null, null, cancellationToken);
}
- public static async Task<AuthorizedTokenResponse> CompleteAuthorizationAsync(DesktopConsumer consumer, string requestToken, string userCode, CancellationToken cancellationToken = default(CancellationToken)) {
- // Because Yammer has a proprietary callback_token parameter, and it's passed
- // with the message that specifically bans extra arguments being passed, we have
- // to cheat by adding the data to the URL itself here.
- var customServiceDescription = new ServiceProviderDescription {
- RequestTokenEndpoint = ServiceDescription.RequestTokenEndpoint,
- UserAuthorizationEndpoint = ServiceDescription.UserAuthorizationEndpoint,
- AccessTokenEndpoint = new MessageReceivingEndpoint(ServiceDescription.AccessTokenEndpoint.Location.AbsoluteUri + "?oauth_verifier=" + Uri.EscapeDataString(userCode), HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest),
- TamperProtectionElements = ServiceDescription.TamperProtectionElements,
- ProtocolVersion = ProtocolVersion.V10,
- };
-
- // To use a custom service description we also must create a new WebConsumer.
- var customConsumer = new DesktopConsumer(customServiceDescription, consumer.TokenManager);
- var response = await customConsumer.ProcessUserAuthorizationAsync(requestToken, userCode, cancellationToken);
- return response;
+ public static Consumer CreateConsumer(bool forWeb = true) {
+ string consumerKey = ConfigurationManager.AppSettings["yammerConsumerKey"];
+ string consumerSecret = ConfigurationManager.AppSettings["yammerConsumerSecret"];
+ if (IsConsumerConfigured) {
+ ITemporaryCredentialStorage storage = forWeb ? (ITemporaryCredentialStorage)new CookieTemporaryCredentialStorage() : new MemoryTemporaryCredentialStorage();
+ return new Consumer(consumerKey, consumerSecret, ServiceDescription, storage);
+ } else {
+ throw new InvalidOperationException("No Yammer OAuth consumer key and secret could be found in web.config AppSettings.");
+ }
}
}
}
diff --git a/samples/OAuthConsumer/Web.config b/samples/OAuthConsumer/Web.config
index 3580fe6..69dab78 100644
--- a/samples/OAuthConsumer/Web.config
+++ b/samples/OAuthConsumer/Web.config
@@ -37,8 +37,8 @@
<!-- Fill in your various consumer keys and secrets here to make the sample work. -->
<!-- You must get these values by signing up with each individual service provider. -->
<!-- Twitter sign-up: https://twitter.com/oauth_clients -->
- <add key="twitterConsumerKey" value="" />
- <add key="twitterConsumerSecret" value="" />
+ <add key="twitterConsumerKey" value="5ZxhT5fqIodtU8fa7mA0w" />
+ <add key="twitterConsumerSecret" value="pZxtR63tLeMc8sd4rOqnZQqQjmfLiUMEWMokYFIjKq4" />
<!-- Google sign-up: https://www.google.com/accounts/ManageDomains -->
<add key="googleConsumerKey" value="anonymous"/>
<add key="googleConsumerSecret" value="anonymous"/>
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/DotNetOpenAuth.AspNet.csproj b/src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj
index 7a9d49b..7c64203 100644
--- a/src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj
+++ b/src/DotNetOpenAuth.AspNet/DotNetOpenAuth.AspNet.csproj
@@ -55,7 +55,6 @@
</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" />
@@ -73,7 +72,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/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.Core/DotNetOpenAuth.Core.csproj b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
index 7877cf0..88513af 100644
--- a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
+++ b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
@@ -22,6 +22,7 @@
<Compile Include="Assumes.cs" />
<Compile Include="IHostFactories.cs" />
<Compile Include="IRequireHostFactories.cs" />
+ <Compile Include="MachineKeyUtil.cs" />
<Compile Include="Messaging\Base64WebEncoder.cs" />
<Compile Include="Messaging\Bindings\AsymmetricCryptoKeyStoreWrapper.cs" />
<Compile Include="Messaging\Bindings\CryptoKey.cs" />
diff --git a/src/DotNetOpenAuth.AspNet/MachineKeyUtil.cs b/src/DotNetOpenAuth.Core/MachineKeyUtil.cs
index eb2020b..eceb38c 100644
--- a/src/DotNetOpenAuth.AspNet/MachineKeyUtil.cs
+++ b/src/DotNetOpenAuth.Core/MachineKeyUtil.cs
@@ -4,7 +4,7 @@
// </copyright>
//-----------------------------------------------------------------------
-namespace DotNetOpenAuth.AspNet {
+namespace DotNetOpenAuth {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
@@ -183,7 +183,7 @@ namespace DotNetOpenAuth.AspNet {
}
// if we reached this point, some cryptographic operation failed
- throw new CryptographicException(WebResources.Generic_CryptoFailure);
+ throw new CryptographicException(Strings.Generic_CryptoFailure);
}
/// <summary>
diff --git a/src/DotNetOpenAuth.Core/Strings.Designer.cs b/src/DotNetOpenAuth.Core/Strings.Designer.cs
index 9eefd62..b96f68a 100644
--- a/src/DotNetOpenAuth.Core/Strings.Designer.cs
+++ b/src/DotNetOpenAuth.Core/Strings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:4.0.30319.18010
+// Runtime Version:4.0.30319.18033
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -97,6 +97,15 @@ namespace DotNetOpenAuth {
}
/// <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 The HostFactories property must be set first..
/// </summary>
internal static string HostFactoriesRequired {
diff --git a/src/DotNetOpenAuth.Core/Strings.resx b/src/DotNetOpenAuth.Core/Strings.resx
index 133fe6f..1c2aee2 100644
--- a/src/DotNetOpenAuth.Core/Strings.resx
+++ b/src/DotNetOpenAuth.Core/Strings.resx
@@ -144,4 +144,7 @@
<data name="HostFactoriesRequired" xml:space="preserve">
<value>The HostFactories property must be set first.</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>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/DotNetOpenAuth.OAuth.Consumer.csproj b/src/DotNetOpenAuth.OAuth.Consumer/DotNetOpenAuth.OAuth.Consumer.csproj
index 922b860..fe14abc 100644
--- a/src/DotNetOpenAuth.OAuth.Consumer/DotNetOpenAuth.OAuth.Consumer.csproj
+++ b/src/DotNetOpenAuth.OAuth.Consumer/DotNetOpenAuth.OAuth.Consumer.csproj
@@ -19,17 +19,16 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
- <Compile Include="OAuth\ChannelElements\IConsumerTokenManager.cs" />
- <Compile Include="OAuth\ChannelElements\OAuthConsumerChannel.cs" />
- <Compile Include="OAuth\ChannelElements\OAuthConsumerMessageFactory.cs" />
- <Compile Include="OAuth\ChannelElements\RsaSha1ConsumerSigningBindingElement.cs" />
- <Compile Include="OAuth\ConsumerBase.cs" />
- <Compile Include="OAuth\DesktopConsumer.cs" />
+ <Compile Include="OAuth\AccessToken.cs" />
+ <Compile Include="OAuth\AccessTokenResponse.cs" />
+ <Compile Include="OAuth\ITemporaryCredentialStorage.cs" />
+ <Compile Include="OAuth\Consumer.cs" />
+ <Compile Include="OAuth\CookieTemporaryCredentialStorage.cs" />
+ <Compile Include="OAuth\MemoryTemporaryCredentialStorage.cs" />
<Compile Include="OAuth\OAuth1HmacSha1HttpMessageHandler.cs" />
<Compile Include="OAuth\OAuth1HttpMessageHandlerBase.cs" />
<Compile Include="OAuth\OAuth1PlainTextMessageHandler.cs" />
<Compile Include="OAuth\OAuth1RsaSha1HttpMessageHandler.cs" />
- <Compile Include="OAuth\WebConsumer.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>
</SubType>
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/AccessToken.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/AccessToken.cs
new file mode 100644
index 0000000..20e3ec4
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/AccessToken.cs
@@ -0,0 +1,39 @@
+//-----------------------------------------------------------------------
+// <copyright file="AccessToken.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth {
+ /// <summary>
+ /// An OAuth 1.0 access token and secret.
+ /// </summary>
+ public struct AccessToken {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AccessToken"/> struct.
+ /// </summary>
+ /// <param name="token">The token.</param>
+ /// <param name="secret">The secret.</param>
+ public AccessToken(string token, string secret)
+ : this() {
+ this.Token = token;
+ this.Secret = secret;
+ }
+
+ /// <summary>
+ /// Gets or sets the token.
+ /// </summary>
+ /// <value>
+ /// The token.
+ /// </value>
+ public string Token { get; set; }
+
+ /// <summary>
+ /// Gets or sets the token secret.
+ /// </summary>
+ /// <value>
+ /// The secret.
+ /// </value>
+ public string Secret { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/AccessTokenResponse.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/AccessTokenResponse.cs
new file mode 100644
index 0000000..dd35400
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/AccessTokenResponse.cs
@@ -0,0 +1,19 @@
+namespace DotNetOpenAuth.OAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ public class AccessTokenResponse {
+ public AccessTokenResponse(string accessToken, string tokenSecret, NameValueCollection extraData) {
+ this.AccessToken = new AccessToken(accessToken, tokenSecret);
+ this.ExtraData = extraData;
+ }
+
+ public AccessToken AccessToken { get; set; }
+
+ public NameValueCollection ExtraData { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/IConsumerTokenManager.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/IConsumerTokenManager.cs
deleted file mode 100644
index 74ec3be..0000000
--- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/IConsumerTokenManager.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="IConsumerTokenManager.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth.ChannelElements {
- /// <summary>
- /// A token manager for use by a web site in its role as a consumer of
- /// an individual ServiceProvider.
- /// </summary>
- public interface IConsumerTokenManager : ITokenManager {
- /// <summary>
- /// Gets the consumer key.
- /// </summary>
- /// <value>The consumer key.</value>
- string ConsumerKey { get; }
-
- /// <summary>
- /// Gets the consumer secret.
- /// </summary>
- /// <value>The consumer secret.</value>
- string ConsumerSecret { get; }
- }
-}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerChannel.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerChannel.cs
deleted file mode 100644
index a10ff09..0000000
--- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerChannel.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="OAuthConsumerChannel.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth.ChannelElements {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Linq;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using Validation;
-
- /// <summary>
- /// The messaging channel for OAuth 1.0(a) Consumers.
- /// </summary>
- internal class OAuthConsumerChannel : OAuthChannel {
- /// <summary>
- /// Initializes a new instance of the <see cref="OAuthConsumerChannel" /> class.
- /// </summary>
- /// <param name="signingBindingElement">The binding element to use for signing.</param>
- /// <param name="store">The web application store to use for nonces.</param>
- /// <param name="tokenManager">The token manager instance to use.</param>
- /// <param name="securitySettings">The security settings.</param>
- /// <param name="messageFactory">The message factory.</param>
- /// <param name="hostFactories">The host factories.</param>
- [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentNullException>(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "securitySettings", Justification = "Code contracts")]
- internal OAuthConsumerChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, IConsumerTokenManager tokenManager, ConsumerSecuritySettings securitySettings, IMessageFactory messageFactory = null, IHostFactories hostFactories = null)
- : base(
- signingBindingElement,
- tokenManager,
- securitySettings,
- messageFactory ?? new OAuthConsumerMessageFactory(),
- InitializeBindingElements(signingBindingElement, store),
- hostFactories) {
- Requires.NotNull(tokenManager, "tokenManager");
- Requires.NotNull(securitySettings, "securitySettings");
- Requires.NotNull(signingBindingElement, "signingBindingElement");
- }
-
- /// <summary>
- /// Gets the consumer secret for a given consumer key.
- /// </summary>
- /// <param name="consumerKey">The consumer key.</param>
- /// <returns>The consumer secret.</returns>
- protected override string GetConsumerSecret(string consumerKey) {
- var consumerTokenManager = (IConsumerTokenManager)this.TokenManager;
- ErrorUtilities.VerifyInternal(consumerKey == consumerTokenManager.ConsumerKey, "The token manager consumer key and the consumer key set earlier do not match!");
- return consumerTokenManager.ConsumerSecret;
- }
-
- /// <summary>
- /// Initializes the binding elements for the OAuth channel.
- /// </summary>
- /// <param name="signingBindingElement">The signing binding element.</param>
- /// <param name="store">The nonce store.</param>
- /// <returns>
- /// An array of binding elements used to initialize the channel.
- /// </returns>
- private static new IChannelBindingElement[] InitializeBindingElements(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store) {
- return OAuthChannel.InitializeBindingElements(signingBindingElement, store).ToArray();
- }
- }
-}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs
deleted file mode 100644
index e79749f..0000000
--- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs
+++ /dev/null
@@ -1,108 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="OAuthConsumerMessageFactory.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth.ChannelElements {
- using System;
- using System.Collections.Generic;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OAuth.Messages;
-
- /// <summary>
- /// An OAuth-protocol specific implementation of the <see cref="IMessageFactory"/>
- /// interface.
- /// </summary>
- public class OAuthConsumerMessageFactory : IMessageFactory {
- /// <summary>
- /// Initializes a new instance of the <see cref="OAuthConsumerMessageFactory"/> class.
- /// </summary>
- protected internal OAuthConsumerMessageFactory() {
- }
-
- #region IMessageFactory Members
-
- /// <summary>
- /// Analyzes an incoming request message payload to discover what kind of
- /// message is embedded in it and returns the type, or null if no match is found.
- /// </summary>
- /// <param name="recipient">The intended or actual recipient of the request message.</param>
- /// <param name="fields">The name/value pairs that make up the message payload.</param>
- /// <returns>
- /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can
- /// deserialize to. Null if the request isn't recognized as a valid protocol message.
- /// </returns>
- /// <remarks>
- /// The request messages are:
- /// UserAuthorizationResponse
- /// </remarks>
- public virtual IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) {
- MessageBase message = null;
-
- if (fields.ContainsKey("oauth_token")) {
- Protocol protocol = fields.ContainsKey("oauth_verifier") ? Protocol.V10a : Protocol.V10;
- message = new UserAuthorizationResponse(recipient.Location, protocol.Version);
- }
-
- if (message != null) {
- message.SetAsIncoming();
- }
-
- return message;
- }
-
- /// <summary>
- /// Analyzes an incoming request message payload to discover what kind of
- /// message is embedded in it and returns the type, or null if no match is found.
- /// </summary>
- /// <param name="request">
- /// The message that was sent as a request that resulted in the response.
- /// Null on a Consumer site that is receiving an indirect message from the Service Provider.
- /// </param>
- /// <param name="fields">The name/value pairs that make up the message payload.</param>
- /// <returns>
- /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can
- /// deserialize to. Null if the request isn't recognized as a valid protocol message.
- /// </returns>
- /// <remarks>
- /// The response messages are:
- /// UnauthorizedTokenResponse
- /// AuthorizedTokenResponse
- /// </remarks>
- public virtual IDirectResponseProtocolMessage GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields) {
- MessageBase message = null;
-
- // All response messages have the oauth_token field.
- if (!fields.ContainsKey("oauth_token")) {
- return null;
- }
-
- // All direct message responses should have the oauth_token_secret field.
- if (!fields.ContainsKey("oauth_token_secret")) {
- Logger.OAuth.Error("An OAuth message was expected to contain an oauth_token_secret but didn't.");
- return null;
- }
-
- var unauthorizedTokenRequest = request as UnauthorizedTokenRequest;
- var authorizedTokenRequest = request as AuthorizedTokenRequest;
- if (unauthorizedTokenRequest != null) {
- Protocol protocol = fields.ContainsKey("oauth_callback_confirmed") ? Protocol.V10a : Protocol.V10;
- message = new UnauthorizedTokenResponse(unauthorizedTokenRequest, protocol.Version);
- } else if (authorizedTokenRequest != null) {
- message = new AuthorizedTokenResponse(authorizedTokenRequest);
- } else {
- Logger.OAuth.ErrorFormat("Unexpected response message given the request type {0}", request.GetType().Name);
- throw new ProtocolException(OAuthStrings.InvalidIncomingMessage);
- }
-
- if (message != null) {
- message.SetAsIncoming();
- }
-
- return message;
- }
-
- #endregion
- }
-}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/RsaSha1ConsumerSigningBindingElement.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/RsaSha1ConsumerSigningBindingElement.cs
deleted file mode 100644
index d492e33..0000000
--- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/RsaSha1ConsumerSigningBindingElement.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="RsaSha1ConsumerSigningBindingElement.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth.ChannelElements {
- using System;
- using System.Diagnostics.CodeAnalysis;
- using System.Security.Cryptography;
- using System.Security.Cryptography.X509Certificates;
- using System.Text;
- using DotNetOpenAuth.Messaging;
- using Validation;
-
- /// <summary>
- /// A binding element that signs outgoing messages and verifies the signature on incoming messages.
- /// </summary>
- [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Sha", Justification = "Acronym")]
- public class RsaSha1ConsumerSigningBindingElement : RsaSha1SigningBindingElement {
- /// <summary>
- /// Initializes a new instance of the <see cref="RsaSha1ConsumerSigningBindingElement"/> class.
- /// </summary>
- /// <param name="signingCertificate">The certificate used to sign outgoing messages.</param>
- public RsaSha1ConsumerSigningBindingElement(X509Certificate2 signingCertificate) {
- Requires.NotNull(signingCertificate, "signingCertificate");
-
- this.SigningCertificate = signingCertificate;
- }
-
- /// <summary>
- /// Gets or sets the certificate used to sign outgoing messages. Used only by Consumers.
- /// </summary>
- public X509Certificate2 SigningCertificate { get; set; }
-
- /// <summary>
- /// Determines whether the signature on some message is valid.
- /// </summary>
- /// <param name="message">The message to check the signature on.</param>
- /// <returns>
- /// <c>true</c> if the signature on the message is valid; otherwise, <c>false</c>.
- /// </returns>
- protected override bool IsSignatureValid(ITamperResistantOAuthMessage message) {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Calculates a signature for a given message.
- /// </summary>
- /// <param name="message">The message to sign.</param>
- /// <returns>The signature for the message.</returns>
- /// <remarks>
- /// This method signs the message per OAuth 1.0 section 9.3.
- /// </remarks>
- protected override string GetSignature(ITamperResistantOAuthMessage message) {
- ErrorUtilities.VerifyOperation(this.SigningCertificate != null, OAuthStrings.X509CertificateNotProvidedForSigning);
-
- string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message));
- byte[] data = Encoding.ASCII.GetBytes(signatureBaseString);
- var provider = (RSACryptoServiceProvider)this.SigningCertificate.PrivateKey;
- byte[] binarySignature = provider.SignData(data, "SHA1");
- string base64Signature = Convert.ToBase64String(binarySignature);
- return base64Signature;
- }
-
- /// <summary>
- /// Creates a new object that is a copy of the current instance.
- /// </summary>
- /// <returns>
- /// A new object that is a copy of this instance.
- /// </returns>
- protected override ITamperProtectionChannelBindingElement Clone() {
- return new RsaSha1ConsumerSigningBindingElement(this.SigningCertificate);
- }
- }
-}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/Consumer.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/Consumer.cs
new file mode 100644
index 0000000..578902c
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/Consumer.cs
@@ -0,0 +1,336 @@
+//-----------------------------------------------------------------------
+// <copyright file="Consumer.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Linq;
+ using System.Net;
+ using System.Net.Http;
+ using System.Security.Cryptography.X509Certificates;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using System.Web;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+ using DotNetOpenAuth.OAuth.Messages;
+ using Validation;
+
+ /// <summary>
+ /// Base class for <see cref="WebConsumer"/> and <see cref="DesktopConsumer"/> types.
+ /// </summary>
+ public class Consumer {
+ /// <summary>
+ /// The host factories.
+ /// </summary>
+ private IHostFactories hostFactories;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Consumer"/> class.
+ /// </summary>
+ public Consumer() {
+ this.HostFactories = new DefaultOAuthHostFactories();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Consumer"/> class.
+ /// </summary>
+ /// <param name="consumerKey">The consumer key.</param>
+ /// <param name="consumerSecret">The consumer secret.</param>
+ /// <param name="serviceProvider">The service provider.</param>
+ /// <param name="temporaryCredentialStorage">The temporary credential storage.</param>
+ public Consumer(
+ string consumerKey,
+ string consumerSecret,
+ ServiceProviderDescription serviceProvider,
+ ITemporaryCredentialStorage temporaryCredentialStorage) {
+ this.ConsumerKey = consumerKey;
+ this.ConsumerSecret = consumerSecret;
+ this.ServiceProvider = serviceProvider;
+ this.TemporaryCredentialStorage = temporaryCredentialStorage;
+ }
+
+ /// <summary>
+ /// Gets or sets the object with factories for host-customizable services.
+ /// </summary>
+ public IHostFactories HostFactories {
+ get {
+ return this.hostFactories;
+ }
+
+ set {
+ Requires.NotNull(value, "value");
+ this.hostFactories = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets the Consumer Key used to communicate with the Service Provider.
+ /// </summary>
+ public string ConsumerKey { get; set; }
+
+ /// <summary>
+ /// Gets or sets the consumer secret.
+ /// </summary>
+ /// <value>
+ /// The consumer secret.
+ /// </value>
+ public string ConsumerSecret { get; set; }
+
+ /// <summary>
+ /// Gets or sets the consumer certificate.
+ /// </summary>
+ /// <value>
+ /// The consumer certificate.
+ /// </value>
+ /// <remarks>
+ /// If set, this causes all outgoing messages to be signed with the certificate instead of the consumer secret.
+ /// </remarks>
+ public X509Certificate2 ConsumerCertificate { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Service Provider that will be accessed.
+ /// </summary>
+ public ServiceProviderDescription ServiceProvider { get; set; }
+
+ /// <summary>
+ /// Gets the persistence store for tokens and secrets.
+ /// </summary>
+ public ITemporaryCredentialStorage TemporaryCredentialStorage { get; set; }
+
+ /// <summary>
+ /// Obtains an access token for a new account at the Service Provider via 2-legged OAuth.
+ /// </summary>
+ /// <param name="requestParameters">Any applicable parameters to include in the query string of the token request.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>The access token.</returns>
+ public async Task<AccessTokenResponse> RequestNewClientAccountAsync(IEnumerable<KeyValuePair<string, string>> requestParameters = null, CancellationToken cancellationToken = default(CancellationToken)) {
+ Verify.Operation(this.ConsumerKey != null, Strings.RequiredPropertyNotYetPreset, "ConsumerKey");
+ Verify.Operation(this.ServiceProvider != null, Strings.RequiredPropertyNotYetPreset, "ServiceProvider");
+
+ using (var handler = this.CreateMessageHandler()) {
+ using (var client = this.CreateHttpClient(handler)) {
+ string identifier, secret;
+
+ var requestUri = new UriBuilder(this.ServiceProvider.TemporaryCredentialsRequestEndpoint);
+ requestUri.AppendQueryArgument(Protocol.CallbackParameter, "oob");
+ requestUri.AppendQueryArgs(requestParameters);
+ var request = new HttpRequestMessage(this.ServiceProvider.TemporaryCredentialsRequestEndpointMethod, requestUri.Uri);
+ using (var response = await client.SendAsync(request, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Parse the response and ensure that it meets the requirements of the OAuth 1.0 spec.
+ string content = await response.Content.ReadAsStringAsync();
+ var responseData = HttpUtility.ParseQueryString(content);
+ identifier = responseData[Protocol.TokenParameter];
+ secret = responseData[Protocol.TokenSecretParameter];
+ ErrorUtilities.VerifyProtocol(!string.IsNullOrEmpty(identifier), MessagingStrings.RequiredParametersMissing, typeof(UnauthorizedTokenResponse).Name, Protocol.TokenParameter);
+ ErrorUtilities.VerifyProtocol(secret != null, MessagingStrings.RequiredParametersMissing, typeof(UnauthorizedTokenResponse).Name, Protocol.TokenSecretParameter);
+ }
+
+ // Immediately exchange the temporary credential for an access token.
+ handler.AccessToken = identifier;
+ handler.AccessTokenSecret = secret;
+ request = new HttpRequestMessage(this.ServiceProvider.TokenRequestEndpointMethod, this.ServiceProvider.TokenRequestEndpoint);
+ using (var response = await client.SendAsync(request, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+
+ // Parse the response and ensure that it meets the requirements of the OAuth 1.0 spec.
+ string content = await response.Content.ReadAsStringAsync();
+ var responseData = HttpUtility.ParseQueryString(content);
+ string accessToken = responseData[Protocol.TokenParameter];
+ string tokenSecret = responseData[Protocol.TokenSecretParameter];
+ ErrorUtilities.VerifyProtocol(!string.IsNullOrEmpty(accessToken), MessagingStrings.RequiredParametersMissing, typeof(AuthorizedTokenResponse).Name, Protocol.TokenParameter);
+ ErrorUtilities.VerifyProtocol(tokenSecret != null, MessagingStrings.RequiredParametersMissing, typeof(AuthorizedTokenResponse).Name, Protocol.TokenSecretParameter);
+
+ responseData.Remove(Protocol.TokenParameter);
+ responseData.Remove(Protocol.TokenSecretParameter);
+ return new AccessTokenResponse(accessToken, tokenSecret, responseData);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Prepares an OAuth message that begins an authorization request that will
+ /// redirect the user to the Service Provider to provide that authorization.
+ /// </summary>
+ /// <param name="callback">The absolute URI that the Service Provider should redirect the
+ /// User Agent to upon successful authorization, or <c>null</c> to signify an out of band return.</param>
+ /// <param name="requestParameters">Extra parameters to add to the request token message. Optional.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The URL to direct the user agent to for user authorization.
+ /// </returns>
+ public async Task<Uri> RequestUserAuthorizationAsync(Uri callback = null, IEnumerable<KeyValuePair<string, string>> requestParameters = null, CancellationToken cancellationToken = default(CancellationToken)) {
+ Requires.NotNull(callback, "callback");
+ Verify.Operation(this.ConsumerKey != null, Strings.RequiredPropertyNotYetPreset, "ConsumerKey");
+ Verify.Operation(this.TemporaryCredentialStorage != null, Strings.RequiredPropertyNotYetPreset, "TemporaryCredentialStorage");
+ Verify.Operation(this.ServiceProvider != null, Strings.RequiredPropertyNotYetPreset, "ServiceProvider");
+
+ // Obtain temporary credentials before the redirect.
+ using (var client = this.CreateHttpClient(new AccessToken())) {
+ var requestUri = new UriBuilder(this.ServiceProvider.TemporaryCredentialsRequestEndpoint);
+ requestUri.AppendQueryArgument(Protocol.CallbackParameter, callback != null ? callback.AbsoluteUri : "oob");
+ requestUri.AppendQueryArgs(requestParameters);
+ var request = new HttpRequestMessage(this.ServiceProvider.TemporaryCredentialsRequestEndpointMethod, requestUri.Uri);
+ using (var response = await client.SendAsync(request, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Parse the response and ensure that it meets the requirements of the OAuth 1.0 spec.
+ string content = await response.Content.ReadAsStringAsync();
+ var responseData = HttpUtility.ParseQueryString(content);
+ ErrorUtilities.VerifyProtocol(string.Equals(responseData[Protocol.CallbackConfirmedParameter], "true", StringComparison.Ordinal), MessagingStrings.RequiredParametersMissing, typeof(UnauthorizedTokenResponse).Name, Protocol.CallbackConfirmedParameter);
+ string identifier = responseData[Protocol.TokenParameter];
+ string secret = responseData[Protocol.TokenSecretParameter];
+ ErrorUtilities.VerifyProtocol(!string.IsNullOrEmpty(identifier), MessagingStrings.RequiredParametersMissing, typeof(UnauthorizedTokenResponse).Name, Protocol.TokenParameter);
+ ErrorUtilities.VerifyProtocol(secret != null, MessagingStrings.RequiredParametersMissing, typeof(UnauthorizedTokenResponse).Name, Protocol.TokenSecretParameter);
+
+ // Save the temporary credential we received so that after user authorization
+ // we can use it to obtain the access token.
+ cancellationToken.ThrowIfCancellationRequested();
+ this.TemporaryCredentialStorage.SaveTemporaryCredential(identifier, secret);
+
+ // Construct the user authorization URL so our caller can direct a browser to it.
+ var authorizationEndpoint = new UriBuilder(this.ServiceProvider.ResourceOwnerAuthorizationEndpoint);
+ authorizationEndpoint.AppendQueryArgument(Protocol.TokenParameter, identifier);
+ return authorizationEndpoint.Uri;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Obtains an access token after a successful user authorization.
+ /// </summary>
+ /// <param name="authorizationCompleteUri">The URI used to redirect back to the consumer that contains a message from the service provider.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The access token assigned by the Service Provider, or <c>null</c> if no response was detected in the specified URL.
+ /// </returns>
+ public Task<AccessTokenResponse> ProcessUserAuthorizationAsync(Uri authorizationCompleteUri, CancellationToken cancellationToken = default(CancellationToken)) {
+ Requires.NotNull(authorizationCompleteUri, "authorizationCompleteUri");
+ Verify.Operation(this.TemporaryCredentialStorage != null, Strings.RequiredPropertyNotYetPreset, "TemporaryCredentialStorage");
+
+ // Parse the response and verify that it meets spec requirements.
+ var queryString = HttpUtility.ParseQueryString(authorizationCompleteUri.Query);
+ string identifier = queryString[Protocol.TokenParameter];
+ string verifier = queryString[Protocol.VerifierParameter];
+
+ if (identifier == null) {
+ // We assume there is no response message here at all, and return null to indicate that.
+ return null;
+ }
+
+ ErrorUtilities.VerifyProtocol(!string.IsNullOrEmpty(identifier), MessagingStrings.RequiredNonEmptyParameterWasEmpty, typeof(UserAuthorizationResponse).Name, Protocol.TokenParameter);
+ ErrorUtilities.VerifyProtocol(!string.IsNullOrEmpty(verifier), MessagingStrings.RequiredNonEmptyParameterWasEmpty, typeof(UserAuthorizationResponse).Name, Protocol.VerifierParameter);
+
+ var temporaryCredential = this.TemporaryCredentialStorage.RetrieveTemporaryCredential();
+ Verify.Operation(string.Equals(temporaryCredential.Key, identifier, StringComparison.Ordinal), "Temporary credential identifiers do not match.");
+
+ return this.ProcessUserAuthorizationAsync(verifier, cancellationToken);
+ }
+
+ /// <summary>
+ /// Obtains an access token after a successful user authorization.
+ /// </summary>
+ /// <param name="verifier">The verifier.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The access token assigned by the Service Provider.
+ /// </returns>
+ public async Task<AccessTokenResponse> ProcessUserAuthorizationAsync(string verifier, CancellationToken cancellationToken = default(CancellationToken)) {
+ Requires.NotNull(verifier, "verifier");
+ Verify.Operation(this.ConsumerKey != null, Strings.RequiredPropertyNotYetPreset, "ConsumerKey");
+ Verify.Operation(this.TemporaryCredentialStorage != null, Strings.RequiredPropertyNotYetPreset, "TemporaryCredentialStorage");
+ Verify.Operation(this.ServiceProvider != null, Strings.RequiredPropertyNotYetPreset, "ServiceProvider");
+
+ var temporaryCredential = this.TemporaryCredentialStorage.RetrieveTemporaryCredential();
+
+ using (var client = this.CreateHttpClient(new AccessToken(temporaryCredential.Key, temporaryCredential.Value))) {
+ var requestUri = new UriBuilder(this.ServiceProvider.TokenRequestEndpoint);
+ requestUri.AppendQueryArgument(Protocol.VerifierParameter, verifier);
+ var request = new HttpRequestMessage(this.ServiceProvider.TokenRequestEndpointMethod, requestUri.Uri);
+ using (var response = await client.SendAsync(request, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+
+ // Parse the response and ensure that it meets the requirements of the OAuth 1.0 spec.
+ string content = await response.Content.ReadAsStringAsync();
+ var responseData = HttpUtility.ParseQueryString(content);
+ string accessToken = responseData[Protocol.TokenParameter];
+ string tokenSecret = responseData[Protocol.TokenSecretParameter];
+ ErrorUtilities.VerifyProtocol(!string.IsNullOrEmpty(accessToken), MessagingStrings.RequiredParametersMissing, typeof(AuthorizedTokenResponse).Name, Protocol.TokenParameter);
+ ErrorUtilities.VerifyProtocol(tokenSecret != null, MessagingStrings.RequiredParametersMissing, typeof(AuthorizedTokenResponse).Name, Protocol.TokenSecretParameter);
+
+ responseData.Remove(Protocol.TokenParameter);
+ responseData.Remove(Protocol.TokenSecretParameter);
+ return new AccessTokenResponse(accessToken, tokenSecret, responseData);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Creates a message handler that signs outbound requests with a previously obtained authorization.
+ /// </summary>
+ /// <param name="accessToken">The access token to authorize outbound HTTP requests with.</param>
+ /// <param name="innerHandler">The inner handler that actually sends the HTTP message on the network.</param>
+ /// <returns>
+ /// A message handler.
+ /// </returns>
+ /// <remarks>
+ /// Overrides of this method may allow various derived types of handlers to be returned,
+ /// enabling consumers that use RSA or other signing methods.
+ /// </remarks>
+ public virtual OAuth1HttpMessageHandlerBase CreateMessageHandler(AccessToken accessToken = default(AccessToken), HttpMessageHandler innerHandler = null) {
+ Verify.Operation(this.ConsumerKey != null, Strings.RequiredPropertyNotYetPreset, "ConsumerKey");
+
+ innerHandler = innerHandler ?? this.HostFactories.CreateHttpMessageHandler();
+ OAuth1HttpMessageHandlerBase handler;
+ if (this.ConsumerCertificate != null) {
+ handler = new OAuth1RsaSha1HttpMessageHandler(innerHandler) {
+ SigningCertificate = this.ConsumerCertificate,
+ };
+ } else {
+ handler = new OAuth1HmacSha1HttpMessageHandler(innerHandler);
+ }
+
+ handler.ConsumerKey = this.ConsumerKey;
+ handler.ConsumerSecret = this.ConsumerSecret;
+ handler.AccessToken = accessToken.Token;
+ handler.AccessTokenSecret = accessToken.Secret;
+
+ return handler;
+ }
+
+ /// <summary>
+ /// Creates the HTTP client.
+ /// </summary>
+ /// <param name="accessToken">The access token to authorize outbound HTTP requests with.</param>
+ /// <param name="innerHandler">The inner handler that actually sends the HTTP message on the network.</param>
+ /// <returns>The HttpClient to use.</returns>
+ public HttpClient CreateHttpClient(AccessToken accessToken, HttpMessageHandler innerHandler = null) {
+ var handler = this.CreateMessageHandler(accessToken, innerHandler);
+ var client = this.HostFactories.CreateHttpClient(handler);
+ return client;
+ }
+
+ /// <summary>
+ /// Creates the HTTP client.
+ /// </summary>
+ /// <param name="innerHandler">The inner handler that actually sends the HTTP message on the network.</param>
+ /// <returns>The HttpClient to use.</returns>
+ public HttpClient CreateHttpClient(OAuth1HttpMessageHandlerBase innerHandler) {
+ Requires.NotNull(innerHandler, "innerHandler");
+
+ var client = this.HostFactories.CreateHttpClient(innerHandler);
+ return client;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs
deleted file mode 100644
index 1bea2c5..0000000
--- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs
+++ /dev/null
@@ -1,258 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="ConsumerBase.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Linq;
- using System.Net;
- using System.Net.Http;
- using System.Threading;
- using System.Threading.Tasks;
- using DotNetOpenAuth.Configuration;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.Messaging.Bindings;
- using DotNetOpenAuth.OAuth.ChannelElements;
- using DotNetOpenAuth.OAuth.Messages;
- using Validation;
-
- /// <summary>
- /// Base class for <see cref="WebConsumer"/> and <see cref="DesktopConsumer"/> types.
- /// </summary>
- public class ConsumerBase : IDisposable {
- /// <summary>
- /// Initializes a new instance of the <see cref="ConsumerBase"/> class.
- /// </summary>
- /// <param name="serviceDescription">The endpoints and behavior of the Service Provider.</param>
- /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
- protected ConsumerBase(ServiceProviderDescription serviceDescription, IConsumerTokenManager tokenManager) {
- Requires.NotNull(serviceDescription, "serviceDescription");
- Requires.NotNull(tokenManager, "tokenManager");
-
- ITamperProtectionChannelBindingElement signingElement = serviceDescription.CreateTamperProtectionElement();
- INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge);
- this.SecuritySettings = OAuthElement.Configuration.Consumer.SecuritySettings.CreateSecuritySettings();
- this.OAuthChannel = new OAuthConsumerChannel(signingElement, store, tokenManager, this.SecuritySettings);
- this.ServiceProvider = serviceDescription;
-
- OAuthReporting.RecordFeatureAndDependencyUse(this, serviceDescription, tokenManager, null);
- }
-
- /// <summary>
- /// Gets the Consumer Key used to communicate with the Service Provider.
- /// </summary>
- public string ConsumerKey {
- get { return this.TokenManager.ConsumerKey; }
- }
-
- /// <summary>
- /// Gets the Service Provider that will be accessed.
- /// </summary>
- public ServiceProviderDescription ServiceProvider { get; private set; }
-
- /// <summary>
- /// Gets the persistence store for tokens and secrets.
- /// </summary>
- public IConsumerTokenManager TokenManager {
- get { return (IConsumerTokenManager)this.OAuthChannel.TokenManager; }
- }
-
- /// <summary>
- /// Gets the channel to use for sending/receiving messages.
- /// </summary>
- public Channel Channel {
- get { return this.OAuthChannel; }
- }
-
- /// <summary>
- /// Gets the security settings for this consumer.
- /// </summary>
- internal ConsumerSecuritySettings SecuritySettings { get; private set; }
-
- /// <summary>
- /// Gets or sets the channel to use for sending/receiving messages.
- /// </summary>
- internal OAuthChannel OAuthChannel { get; set; }
-
- /// <summary>
- /// Creates a message handler that signs outbound requests with a previously obtained authorization.
- /// </summary>
- /// <param name="accessToken">The access token to authorize outbound HTTP requests with.</param>
- /// <param name="innerHandler">The inner handler that actually sends the HTTP message on the network.</param>
- /// <returns>
- /// A message handler.
- /// </returns>
- public OAuth1HttpMessageHandlerBase CreateMessageHandler(string accessToken = null, HttpMessageHandler innerHandler = null) {
- return new OAuth1HmacSha1HttpMessageHandler() {
- ConsumerKey = this.ConsumerKey,
- ConsumerSecret = this.TokenManager.ConsumerSecret,
- AccessToken = accessToken,
- AccessTokenSecret = accessToken != null ? this.TokenManager.GetTokenSecret(accessToken) : null,
- InnerHandler = innerHandler ?? this.Channel.HostFactories.CreateHttpMessageHandler(),
- };
- }
-
- /// <summary>
- /// Creates the HTTP client.
- /// </summary>
- /// <param name="accessToken">The access token to authorize outbound HTTP requests with.</param>
- /// <param name="innerHandler">The inner handler that actually sends the HTTP message on the network.</param>
- /// <returns>The HttpClient to use.</returns>
- public HttpClient CreateHttpClient(string accessToken, HttpMessageHandler innerHandler = null) {
- Requires.NotNullOrEmpty(accessToken, "accessToken");
-
- var handler = this.CreateMessageHandler(accessToken, innerHandler);
- var client = this.Channel.HostFactories.CreateHttpClient(handler);
- return client;
- }
-
- /// <summary>
- /// Creates the HTTP client.
- /// </summary>
- /// <param name="innerHandler">The inner handler that actually sends the HTTP message on the network.</param>
- /// <returns>The HttpClient to use.</returns>
- public HttpClient CreateHttpClient(OAuth1HttpMessageHandlerBase innerHandler) {
- Requires.NotNull(innerHandler, "innerHandler");
-
- var client = this.Channel.HostFactories.CreateHttpClient(innerHandler);
- return client;
- }
-
- /// <summary>
- /// Obtains an access token for a new account at the Service Provider via 2-legged OAuth.
- /// </summary>
- /// <param name="requestParameters">Any applicable parameters to include in the query string of the token request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>The access token.</returns>
- /// <remarks>
- /// The token secret is stored in the <see cref="TokenManager"/>.
- /// </remarks>
- public async Task<string> RequestNewClientAccountAsync(IDictionary<string, string> requestParameters = null, CancellationToken cancellationToken = default(CancellationToken)) {
- // Obtain an unauthorized request token. Force use of OAuth 1.0 (not 1.0a) so that
- // we are not expected to provide an oauth_verifier which doesn't apply in 2-legged OAuth.
- var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint, Protocol.V10.Version) {
- ConsumerKey = this.ConsumerKey,
- };
- var tokenAccessor = this.Channel.MessageDescriptions.GetAccessor(token);
- tokenAccessor.AddExtraParameters(requestParameters);
- var requestTokenResponse = await this.Channel.RequestAsync<UnauthorizedTokenResponse>(token, cancellationToken);
- this.TokenManager.StoreNewRequestToken(token, requestTokenResponse);
-
- var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, Protocol.V10.Version) {
- RequestToken = requestTokenResponse.RequestToken,
- ConsumerKey = this.ConsumerKey,
- };
- var grantAccess = await this.Channel.RequestAsync<AuthorizedTokenResponse>(requestAccess, cancellationToken);
- this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, requestTokenResponse.RequestToken, grantAccess.AccessToken, grantAccess.TokenSecret);
- return grantAccess.AccessToken;
- }
-
- #region IDisposable Members
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose() {
- this.Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- #endregion
-
- /// <summary>
- /// Creates a web request prepared with OAuth authorization
- /// that may be further tailored by adding parameters by the caller.
- /// </summary>
- /// <param name="endpoint">The URL and method on the Service Provider to send the request to.</param>
- /// <param name="accessToken">The access token that permits access to the protected resource.</param>
- /// <returns>The initialized WebRequest object.</returns>
- protected internal AccessProtectedResourceRequest CreateAuthorizingMessage(MessageReceivingEndpoint endpoint, string accessToken) {
- Requires.NotNull(endpoint, "endpoint");
- Requires.NotNullOrEmpty(accessToken, "accessToken");
-
- AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint, this.ServiceProvider.Version) {
- AccessToken = accessToken,
- ConsumerKey = this.ConsumerKey,
- };
-
- return message;
- }
-
- /// <summary>
- /// Prepares an OAuth message that begins an authorization request that will
- /// redirect the user to the Service Provider to provide that authorization.
- /// </summary>
- /// <param name="callback">An optional Consumer URL that the Service Provider should redirect the
- /// User Agent to upon successful authorization.</param>
- /// <param name="requestParameters">Extra parameters to add to the request token message. Optional.</param>
- /// <param name="redirectParameters">Extra parameters to add to the redirect to Service Provider message. Optional.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>
- /// The pending user agent redirect based message to be sent as an HttpResponse.
- /// </returns>
- [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Justification = "Two results")]
- protected internal async Task<UserAuthorizationRequest> PrepareRequestUserAuthorizationAsync(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, CancellationToken cancellationToken = default(CancellationToken)) {
- // Obtain an unauthorized request token. Assume the OAuth version given in the service description.
- var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint, this.ServiceProvider.Version) {
- ConsumerKey = this.ConsumerKey,
- Callback = callback,
- };
- var tokenAccessor = this.Channel.MessageDescriptions.GetAccessor(token);
- tokenAccessor.AddExtraParameters(requestParameters);
- var requestTokenResponse = await this.Channel.RequestAsync<UnauthorizedTokenResponse>(token, cancellationToken);
- this.TokenManager.StoreNewRequestToken(token, requestTokenResponse);
-
- // Fine-tune our understanding of the SP's supported OAuth version if it's wrong.
- if (this.ServiceProvider.Version != requestTokenResponse.Version) {
- Logger.OAuth.WarnFormat("Expected OAuth service provider at endpoint {0} to use OAuth {1} but {2} was detected. Adjusting service description to new version.", this.ServiceProvider.RequestTokenEndpoint.Location, this.ServiceProvider.Version, requestTokenResponse.Version);
- this.ServiceProvider.ProtocolVersion = Protocol.Lookup(requestTokenResponse.Version).ProtocolVersion;
- }
-
- // Request user authorization. The OAuth version will automatically include
- // or drop the callback that we're setting here.
- ITokenContainingMessage assignedRequestToken = requestTokenResponse;
- var requestAuthorization = new UserAuthorizationRequest(this.ServiceProvider.UserAuthorizationEndpoint, assignedRequestToken.Token, requestTokenResponse.Version) {
- Callback = callback,
- };
- var requestAuthorizationAccessor = this.Channel.MessageDescriptions.GetAccessor(requestAuthorization);
- requestAuthorizationAccessor.AddExtraParameters(redirectParameters);
- return requestAuthorization;
- }
-
- /// <summary>
- /// Exchanges a given request token for access token.
- /// </summary>
- /// <param name="requestToken">The request token that the user has authorized.</param>
- /// <param name="verifier">The verifier code.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>
- /// The access token assigned by the Service Provider.
- /// </returns>
- protected async Task<AuthorizedTokenResponse> ProcessUserAuthorizationAsync(string requestToken, string verifier, CancellationToken cancellationToken = default(CancellationToken)) {
- Requires.NotNullOrEmpty(requestToken, "requestToken");
-
- var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, this.ServiceProvider.Version) {
- RequestToken = requestToken,
- VerificationCode = verifier,
- ConsumerKey = this.ConsumerKey,
- };
- var grantAccess = await this.Channel.RequestAsync<AuthorizedTokenResponse>(requestAccess, cancellationToken);
- this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, requestToken, grantAccess.AccessToken, grantAccess.TokenSecret);
- return grantAccess;
- }
-
- /// <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.Channel.Dispose();
- }
- }
- }
-}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/CookieTemporaryCredentialStorage.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/CookieTemporaryCredentialStorage.cs
new file mode 100644
index 0000000..25941e6
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/CookieTemporaryCredentialStorage.cs
@@ -0,0 +1,130 @@
+//-----------------------------------------------------------------------
+// <copyright file="CookieTemporaryCredentialStorage.cs" company="Microsoft">
+// Copyright (c) Microsoft. All rights reserved.
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+ using System.Web;
+ using System.Web.Security;
+ using Validation;
+
+ /// <summary>
+ /// Provides temporary credential storage by persisting them in a protected cookie on the
+ /// user agent (i.e. browser).
+ /// </summary>
+ public class CookieTemporaryCredentialStorage : ITemporaryCredentialStorage {
+ /// <summary>
+ /// Key used for token cookie
+ /// </summary>
+ protected const string TokenCookieKey = "DNOAOAuth1TempCredential";
+
+ /// <summary>
+ /// Primary request context.
+ /// </summary>
+ private readonly HttpContextBase httpContext;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CookieTemporaryCredentialsStorage"/> class
+ /// using <see cref="HttpContext.Current"/> as the source for the context to read and write cookies to.
+ /// </summary>
+ public CookieTemporaryCredentialStorage()
+ : this(new HttpContextWrapper(HttpContext.Current)) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CookieTemporaryCredentialsStorage"/> class.
+ /// </summary>
+ /// <param name="httpContext">The HTTP context from and to which to access cookies.</param>
+ public CookieTemporaryCredentialStorage(HttpContextBase httpContext) {
+ Requires.NotNull(httpContext, "httpContext");
+ this.httpContext = httpContext;
+ }
+
+ #region ITemporaryCredentialsStorage Members
+
+ /// <summary>
+ /// Saves the temporary credential.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <param name="secret">The secret.</param>
+ public void SaveTemporaryCredential(string identifier, string secret) {
+ var cookie = new HttpCookie(TokenCookieKey) {
+ HttpOnly = true
+ };
+
+ if (FormsAuthentication.RequireSSL) {
+ cookie.Secure = true;
+ }
+
+ var encryptedToken = ProtectAndEncodeToken(identifier, secret);
+ cookie.Values[identifier] = encryptedToken;
+
+ this.httpContext.Response.Cookies.Set(cookie);
+ }
+
+ /// <summary>
+ /// Obtains the temporary credential identifier and secret, if available.
+ /// </summary>
+ /// <returns>
+ /// An initialized key value pair if credentials are available; otherwise both key and value are <c>null</c>.
+ /// </returns>
+ /// <exception cref="System.NotImplementedException"></exception>
+ public KeyValuePair<string, string> RetrieveTemporaryCredential() {
+ HttpCookie cookie = this.httpContext.Request.Cookies[TokenCookieKey];
+ if (cookie == null || cookie.Values.Count == 0) {
+ return new KeyValuePair<string, string>();
+ }
+
+ string identifier = cookie.Values.GetKey(0);
+ string secret = DecodeAndUnprotectToken(identifier, cookie.Values[identifier]);
+ return new KeyValuePair<string, string>(identifier, secret);
+ }
+
+ /// <summary>
+ /// Clears the temporary credentials from storage.
+ /// </summary>
+ /// <remarks>
+ /// DotNetOpenAuth calls this when the credentials are no longer needed.
+ /// </remarks>
+ public void ClearTemporaryCredential() {
+ var cookie = new HttpCookie(TokenCookieKey) {
+ Value = string.Empty,
+ Expires = DateTime.UtcNow.AddDays(-5),
+ };
+ this.httpContext.Response.Cookies.Set(cookie);
+ }
+
+ #endregion
+
+ /// <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);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/DesktopConsumer.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/DesktopConsumer.cs
deleted file mode 100644
index a1bfd2d..0000000
--- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/DesktopConsumer.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="DesktopConsumer.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth {
- using System;
- using System.Collections.Generic;
- using System.Diagnostics.CodeAnalysis;
- using System.Threading;
- using System.Threading.Tasks;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OAuth;
- using DotNetOpenAuth.OAuth.ChannelElements;
- using DotNetOpenAuth.OAuth.Messages;
-
- /// <summary>
- /// Used by a desktop application to use OAuth to access the Service Provider on behalf of the User.
- /// </summary>
- /// <remarks>
- /// The methods on this class are thread-safe. Provided the properties are set and not changed
- /// afterward, a single instance of this class may be used by an entire desktop application safely.
- /// </remarks>
- public class DesktopConsumer : ConsumerBase {
- /// <summary>
- /// Initializes a new instance of the <see cref="DesktopConsumer"/> class.
- /// </summary>
- /// <param name="serviceDescription">The endpoints and behavior of the Service Provider.</param>
- /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
- public DesktopConsumer(ServiceProviderDescription serviceDescription, IConsumerTokenManager tokenManager)
- : base(serviceDescription, tokenManager) {
- }
-
- /// <summary>
- /// Begins an OAuth authorization request.
- /// </summary>
- /// <param name="requestParameters">Extra parameters to add to the request token message. Optional.</param>
- /// <param name="redirectParameters">Extra parameters to add to the redirect to Service Provider message. Optional.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>
- /// The URL to open a browser window to allow the user to provide authorization and the request token.
- /// </returns>
- [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "Two results")]
- public async Task<Tuple<Uri, string>> RequestUserAuthorizationAsync(IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, CancellationToken cancellationToken = default(CancellationToken)) {
- var message = await this.PrepareRequestUserAuthorizationAsync(null, requestParameters, redirectParameters, cancellationToken);
- var response = await this.Channel.PrepareResponseAsync(message, cancellationToken);
- return Tuple.Create(response.GetDirectUriRequest(), message.RequestToken);
- }
-
- /// <summary>
- /// Exchanges a given request token for access token.
- /// </summary>
- /// <param name="requestToken">The request token that the user has authorized.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>The access token assigned by the Service Provider.</returns>
- [Obsolete("Use the ProcessUserAuthorization method that takes a verifier parameter instead.")]
- public Task<AuthorizedTokenResponse> ProcessUserAuthorizationAsync(string requestToken, CancellationToken cancellationToken = default(CancellationToken)) {
- return this.ProcessUserAuthorizationAsync(requestToken, null, cancellationToken);
- }
-
- /// <summary>
- /// Exchanges a given request token for access token.
- /// </summary>
- /// <param name="requestToken">The request token that the user has authorized.</param>
- /// <param name="verifier">The verifier code typed in by the user. Must not be <c>Null</c> for OAuth 1.0a service providers and later.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>
- /// The access token assigned by the Service Provider.
- /// </returns>
- public new Task<AuthorizedTokenResponse> ProcessUserAuthorizationAsync(string requestToken, string verifier, CancellationToken cancellationToken = default(CancellationToken)) {
- if (this.ServiceProvider.Version >= Protocol.V10a.Version) {
- ErrorUtilities.VerifyNonZeroLength(verifier, "verifier");
- }
-
- return base.ProcessUserAuthorizationAsync(requestToken, verifier, cancellationToken);
- }
- }
-}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ITemporaryCredentialStorage.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ITemporaryCredentialStorage.cs
new file mode 100644
index 0000000..428749a
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ITemporaryCredentialStorage.cs
@@ -0,0 +1,40 @@
+//-----------------------------------------------------------------------
+// <copyright file="ITemporaryCredentialStorage.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth {
+ using System.Collections.Generic;
+
+ /// <summary>
+ /// A token manager for use by an OAuth Consumer to store a temporary credential
+ /// (previously known as "unauthorized request token and secret").
+ /// </summary>
+ /// <remarks>
+ /// The credentials stored here are obtained as described in:
+ /// http://tools.ietf.org/html/rfc5849#section-2.1
+ /// </remarks>
+ public interface ITemporaryCredentialStorage {
+ /// <summary>
+ /// Saves the specified temporary credential for later retrieval.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <param name="secret">The secret.</param>
+ void SaveTemporaryCredential(string identifier, string secret);
+
+ /// <summary>
+ /// Obtains a temporary credential secret, if available.
+ /// </summary>
+ /// <returns>The temporary credential identifier secret if available; otherwise a key value pair whose strings are left in their uninitialized <c>null</c> state.</returns>
+ KeyValuePair<string, string> RetrieveTemporaryCredential();
+
+ /// <summary>
+ /// Clears the temporary credentials from storage.
+ /// </summary>
+ /// <remarks>
+ /// DotNetOpenAuth calls this when the credentials are no longer needed.
+ /// </remarks>
+ void ClearTemporaryCredential();
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/MemoryTemporaryCredentialStorage.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/MemoryTemporaryCredentialStorage.cs
new file mode 100644
index 0000000..832084d
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/MemoryTemporaryCredentialStorage.cs
@@ -0,0 +1,65 @@
+//-----------------------------------------------------------------------
+// <copyright file="MemoryTemporaryCredentialStorage.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+
+ /// <summary>
+ /// Non-persistent memory storage for temporary credentials.
+ /// Useful for installed apps (not redirection based web apps).
+ /// </summary>
+ public class MemoryTemporaryCredentialStorage : ITemporaryCredentialStorage {
+ /// <summary>
+ /// The identifier.
+ /// </summary>
+ private string identifier;
+
+ /// <summary>
+ /// The secret.
+ /// </summary>
+ private string secret;
+
+ #region ITemporaryCredentialStorage Members
+
+ /// <summary>
+ /// Saves the specified temporary credential for later retrieval.
+ /// </summary>
+ /// <param name="identifier">The identifier.</param>
+ /// <param name="secret">The secret.</param>
+ public void SaveTemporaryCredential(string identifier, string secret) {
+ this.identifier = identifier;
+ this.secret = secret;
+ }
+
+ /// <summary>
+ /// Obtains a temporary credential secret, if available.
+ /// </summary>
+ /// <returns>
+ /// The temporary credential secret if available; otherwise <c>null</c>.
+ /// </returns>
+ public KeyValuePair<string, string> RetrieveTemporaryCredential() {
+ return new KeyValuePair<string, string>(this.identifier, this.secret);
+ }
+
+ /// <summary>
+ /// Clears the temporary credentials from storage.
+ /// </summary>
+ /// <param name="identifier">The identifier of the credentials to clear.</param>
+ /// <remarks>
+ /// DotNetOpenAuth calls this when the credentials are no longer needed.
+ /// </remarks>
+ public void ClearTemporaryCredential() {
+ this.identifier = null;
+ this.secret = null;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/OAuth1HttpMessageHandlerBase.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/OAuth1HttpMessageHandlerBase.cs
index 17d7b7a..aa462f3 100644
--- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/OAuth1HttpMessageHandlerBase.cs
+++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/OAuth1HttpMessageHandlerBase.cs
@@ -154,6 +154,10 @@ namespace DotNetOpenAuth.OAuth {
// Add parameters and signature to request.
switch (this.Location) {
case OAuthParametersLocation.AuthorizationHttpHeader:
+ // Some oauth parameters may have been put in the query string of the original message.
+ // We want to move any that we find into the authorization header.
+ oauthParameters.Add(ExtractOAuthParametersFromQueryString(request));
+
request.Headers.Authorization = new AuthenticationHeaderValue(Protocol.AuthorizationHeaderScheme, MessagingUtilities.AssembleAuthorizationHeader(oauthParameters.AsKeyValuePairs()));
break;
case OAuthParametersLocation.QueryString:
@@ -258,6 +262,38 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
+ /// Collects and removes all query string parameters beginning with "oauth_" from the specified request,
+ /// and returns them as a collection.
+ /// </summary>
+ /// <param name="request">The request whose query string should be searched for "oauth_" parameters.</param>
+ /// <returns>The collection of parameters that were removed from the query string.</returns>
+ private static NameValueCollection ExtractOAuthParametersFromQueryString(HttpRequestMessage request) {
+ Requires.NotNull(request, "request");
+
+ var extracted = new NameValueCollection();
+ if (!string.IsNullOrEmpty(request.RequestUri.Query)) {
+ var queryString = HttpUtility.ParseQueryString(request.RequestUri.Query);
+ foreach (var pair in queryString.AsKeyValuePairs()) {
+ if (pair.Key.StartsWith(Protocol.ParameterPrefix, StringComparison.Ordinal)) {
+ extracted.Add(pair.Key, pair.Value);
+ }
+ }
+
+ if (extracted.Count > 0) {
+ foreach (string key in extracted) {
+ queryString.Remove(key);
+ }
+
+ var modifiedRequestUri = new UriBuilder(request.RequestUri);
+ modifiedRequestUri.Query = MessagingUtilities.CreateQueryString(queryString.AsKeyValuePairs());
+ request.RequestUri = modifiedRequestUri.Uri;
+ }
+ }
+
+ return extracted;
+ }
+
+ /// <summary>
/// Constructs the "Signature Base String" as described in http://tools.ietf.org/html/rfc5849#section-3.4.1
/// </summary>
/// <param name="request">The HTTP request message.</param>
@@ -319,12 +355,6 @@ namespace DotNetOpenAuth.OAuth {
if (request.RequestUri.Query != null) {
// NameValueCollection does support non-unique keys, as long as you use it carefully.
nvc = HttpUtility.ParseQueryString(request.RequestUri.Query);
-
- // Remove any parameters beginning with "oauth_"
- var keysToRemove = nvc.Cast<string>().Where(k => k.StartsWith(Protocol.ParameterPrefix, StringComparison.Ordinal)).ToList();
- foreach (string key in keysToRemove) {
- nvc.Remove(key);
- }
} else {
nvc = new NameValueCollection(8);
}
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/OAuth1RsaSha1HttpMessageHandler.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/OAuth1RsaSha1HttpMessageHandler.cs
index fd2ad65..129ebc2 100644
--- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/OAuth1RsaSha1HttpMessageHandler.cs
+++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/OAuth1RsaSha1HttpMessageHandler.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth {
using System;
using System.Collections.Generic;
using System.Linq;
+ using System.Net.Http;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
@@ -20,6 +21,20 @@ namespace DotNetOpenAuth.OAuth {
/// </summary>
public class OAuth1RsaSha1HttpMessageHandler : OAuth1HttpMessageHandlerBase {
/// <summary>
+ /// Initializes a new instance of the <see cref="OAuth1RsaSha1HttpMessageHandler"/> class.
+ /// </summary>
+ public OAuth1RsaSha1HttpMessageHandler() {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuth1RsaSha1HttpMessageHandler"/> class.
+ /// </summary>
+ /// <param name="innerHandler">The inner handler which is responsible for processing the HTTP response messages.</param>
+ public OAuth1RsaSha1HttpMessageHandler(HttpMessageHandler innerHandler)
+ : base(innerHandler) {
+ }
+
+ /// <summary>
/// Gets or sets the certificate used to sign outgoing messages. Used only by Consumers.
/// </summary>
public X509Certificate2 SigningCertificate { get; set; }
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/WebConsumer.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/WebConsumer.cs
deleted file mode 100644
index 49a54a0..0000000
--- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/WebConsumer.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="WebConsumer.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth {
- using System;
- using System.Collections.Generic;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Web;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OAuth.ChannelElements;
- using DotNetOpenAuth.OAuth.Messages;
- using Validation;
-
- /// <summary>
- /// A website or application that uses OAuth to access the Service Provider on behalf of the User.
- /// </summary>
- /// <remarks>
- /// The methods on this class are thread-safe. Provided the properties are set and not changed
- /// afterward, a single instance of this class may be used by an entire web application safely.
- /// </remarks>
- public class WebConsumer : ConsumerBase {
- /// <summary>
- /// Initializes a new instance of the <see cref="WebConsumer"/> class.
- /// </summary>
- /// <param name="serviceDescription">The endpoints and behavior of the Service Provider.</param>
- /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
- public WebConsumer(ServiceProviderDescription serviceDescription, IConsumerTokenManager tokenManager)
- : base(serviceDescription, tokenManager) {
- }
-
- /// <summary>
- /// Begins an OAuth authorization request and redirects the user to the Service Provider
- /// to provide that authorization. Upon successful authorization, the user is redirected
- /// back to the current page.
- /// </summary>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- /// <remarks>
- /// Requires HttpContext.Current.
- /// </remarks>
- public Task<UserAuthorizationRequest> PrepareRequestUserAuthorizationAsync(CancellationToken cancellationToken = default(CancellationToken)) {
- Uri callback = this.Channel.GetRequestFromContext().GetPublicFacingUrl().StripQueryArgumentsWithPrefix(Protocol.ParameterPrefix);
- return this.PrepareRequestUserAuthorizationAsync(callback, null, null, cancellationToken);
- }
-
- /// <summary>
- /// Prepares an OAuth message that begins an authorization request that will
- /// redirect the user to the Service Provider to provide that authorization.
- /// </summary>
- /// <param name="callback">
- /// An optional Consumer URL that the Service Provider should redirect the
- /// User Agent to upon successful authorization.
- /// </param>
- /// <param name="requestParameters">Extra parameters to add to the request token message. Optional.</param>
- /// <param name="redirectParameters">Extra parameters to add to the redirect to Service Provider message. Optional.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
- public new Task<UserAuthorizationRequest> PrepareRequestUserAuthorizationAsync(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, CancellationToken cancellationToken = default(CancellationToken)) {
- return base.PrepareRequestUserAuthorizationAsync(callback, requestParameters, redirectParameters, cancellationToken);
- }
-
- /// <summary>
- /// Processes an incoming authorization-granted message from an SP and obtains an access token.
- /// </summary>
- /// <param name="request">The incoming HTTP request.</param>
- /// <param name="cancellationToken">The cancellation token.</param>
- /// <returns>
- /// The access token, or null if no incoming authorization message was recognized.
- /// </returns>
- public async Task<AuthorizedTokenResponse> ProcessUserAuthorizationAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) {
- request = request ?? this.Channel.GetRequestFromContext();
-
- var authorizationMessage = await this.Channel.TryReadFromRequestAsync<UserAuthorizationResponse>(cancellationToken, request);
- if (authorizationMessage != null) {
- string requestToken = authorizationMessage.RequestToken;
- string verifier = authorizationMessage.VerificationCode;
- return await this.ProcessUserAuthorizationAsync(requestToken, verifier, cancellationToken);
- } else {
- return null;
- }
- }
- }
-}
diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/DotNetOpenAuth.OAuth.ServiceProvider.csproj b/src/DotNetOpenAuth.OAuth.ServiceProvider/DotNetOpenAuth.OAuth.ServiceProvider.csproj
index 815a341..059f025 100644
--- a/src/DotNetOpenAuth.OAuth.ServiceProvider/DotNetOpenAuth.OAuth.ServiceProvider.csproj
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/DotNetOpenAuth.OAuth.ServiceProvider.csproj
@@ -19,6 +19,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
+ <Compile Include="OAuthReporting.cs" />
<Compile Include="OAuth\ChannelElements\IConsumerDescription.cs" />
<Compile Include="OAuth\ChannelElements\IServiceProviderAccessToken.cs" />
<Compile Include="OAuth\ChannelElements\IServiceProviderRequestToken.cs" />
diff --git a/src/DotNetOpenAuth.OAuth/OAuthReporting.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuthReporting.cs
index e2c0aab..aa3f934 100644
--- a/src/DotNetOpenAuth.OAuth/OAuthReporting.cs
+++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuthReporting.cs
@@ -45,9 +45,7 @@ namespace DotNetOpenAuth {
builder.Append(nonceStore.GetType().Name);
}
builder.Append(" ");
- builder.Append(service.Version);
- builder.Append(" ");
- builder.Append(service.UserAuthorizationEndpoint);
+ builder.Append(service.ResourceOwnerAuthorizationEndpoint);
Reporting.ObservedFeatures.Add(builder.ToString());
Reporting.Touch();
}
diff --git a/src/DotNetOpenAuth.OAuth/DotNetOpenAuth.OAuth.csproj b/src/DotNetOpenAuth.OAuth/DotNetOpenAuth.OAuth.csproj
index 58e09b0..8524a78 100644
--- a/src/DotNetOpenAuth.OAuth/DotNetOpenAuth.OAuth.csproj
+++ b/src/DotNetOpenAuth.OAuth/DotNetOpenAuth.OAuth.csproj
@@ -25,7 +25,6 @@
<Compile Include="Configuration\OAuthServiceProviderElement.cs" />
<Compile Include="Configuration\OAuthServiceProviderSecuritySettingsElement.cs" />
<Compile Include="Messaging\ITamperProtectionChannelBindingElement.cs" />
- <Compile Include="OAuthReporting.cs" />
<Compile Include="OAuth\ChannelElements\ITokenManager.cs" />
<Compile Include="OAuth\ChannelElements\OAuthHttpMethodBindingElement.cs" />
<Compile Include="OAuth\ChannelElements\PlaintextSigningBindingElement.cs" />
@@ -35,16 +34,17 @@
<Compile Include="OAuth\ChannelElements\UriOrOobEncoding.cs" />
<Compile Include="OAuth\ConsumerSecuritySettings.cs" />
<Compile Include="OAuth\Messages\ITokenSecretContainingMessage.cs" />
+ <Compile Include="OAuth\Messages\MessageBaseSimple.cs" />
<Compile Include="OAuth\OAuthStrings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>OAuthStrings.resx</DependentUpon>
</Compile>
<Compile Include="OAuth\SecuritySettings.cs" />
- <Compile Include="OAuth\ServiceProviderDescription.cs" />
<Compile Include="OAuth\Messages\ITokenContainingMessage.cs" />
<Compile Include="OAuth\Messages\SignedMessageBase.cs" />
<Compile Include="OAuth\ChannelElements\SigningBindingElementBase.cs" />
+ <Compile Include="OAuth\ServiceProviderDescription.cs" />
<Compile Include="OAuth\ServiceProviderSecuritySettings.cs" />
<Compile Include="OAuth\ChannelElements\ITamperResistantOAuthMessage.cs" />
<Compile Include="OAuth\Messages\MessageBase.cs" />
diff --git a/src/DotNetOpenAuth.OAuth/OAuth/Messages/MessageBaseSimple.cs b/src/DotNetOpenAuth.OAuth/OAuth/Messages/MessageBaseSimple.cs
new file mode 100644
index 0000000..23822d3
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth/OAuth/Messages/MessageBaseSimple.cs
@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DotNetOpenAuth.OAuth.Messages {
+ class MessageBaseSimple {
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth/OAuth/Protocol.cs b/src/DotNetOpenAuth.OAuth/OAuth/Protocol.cs
index 72f0ff4..049fd58 100644
--- a/src/DotNetOpenAuth.OAuth/OAuth/Protocol.cs
+++ b/src/DotNetOpenAuth.OAuth/OAuth/Protocol.cs
@@ -60,6 +60,31 @@ namespace DotNetOpenAuth.OAuth {
internal const string AuthorizationHeaderScheme = "OAuth";
/// <summary>
+ /// The name of the 'oauth_callback' parameter.
+ /// </summary>
+ internal const string CallbackParameter = "oauth_callback";
+
+ /// <summary>
+ /// The name of the 'oauth_callback_confirmed' parameter.
+ /// </summary>
+ internal const string CallbackConfirmedParameter = "oauth_callback_confirmed";
+
+ /// <summary>
+ /// The name of the 'oauth_token' parameter.
+ /// </summary>
+ internal const string TokenParameter = "oauth_token";
+
+ /// <summary>
+ /// The name of the 'oauth_token_secret' parameter.
+ /// </summary>
+ internal const string TokenSecretParameter = "oauth_token_secret";
+
+ /// <summary>
+ /// The name of the 'oauth_verifier' parameter.
+ /// </summary>
+ internal const string VerifierParameter = "oauth_verifier";
+
+ /// <summary>
/// Gets the <see cref="Protocol"/> instance with values initialized for V1.0 of the protocol.
/// </summary>
internal static readonly Protocol V10 = new Protocol {
diff --git a/src/DotNetOpenAuth.OAuth/OAuth/ServiceProviderDescription.cs b/src/DotNetOpenAuth.OAuth/OAuth/ServiceProviderDescription.cs
index 6dbe6ea..e6a2b32 100644
--- a/src/DotNetOpenAuth.OAuth/OAuth/ServiceProviderDescription.cs
+++ b/src/DotNetOpenAuth.OAuth/OAuth/ServiceProviderDescription.cs
@@ -1,101 +1,84 @@
//-----------------------------------------------------------------------
-// <copyright file="ServiceProviderDescription.cs" company="Outercurve Foundation">
-// Copyright (c) Outercurve Foundation. All rights reserved.
+// <copyright file="ServiceProviderDescription.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OAuth {
using System;
- using System.Diagnostics;
- using System.Diagnostics.CodeAnalysis;
+ using System.Collections.Generic;
using System.Linq;
- using DotNetOpenAuth.Messaging;
- using DotNetOpenAuth.OAuth.ChannelElements;
+ using System.Net.Http;
+ using System.Text;
+ using System.Threading.Tasks;
+ using Validation;
/// <summary>
- /// A description of the endpoints on a Service Provider.
+ /// Describes an OAuth 1.0 service provider.
/// </summary>
public class ServiceProviderDescription {
/// <summary>
- /// The field used to store the value of the <see cref="RequestTokenEndpoint"/> property.
- /// </summary>
- [DebuggerBrowsable(DebuggerBrowsableState.Never)]
- private MessageReceivingEndpoint requestTokenEndpoint;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="ServiceProviderDescription"/> class.
+ /// Initializes a new instance of the <see cref="ServiceProviderDescription" /> class.
/// </summary>
public ServiceProviderDescription() {
- this.ProtocolVersion = Protocol.Default.ProtocolVersion;
+ this.TemporaryCredentialsRequestEndpointMethod = HttpMethod.Post;
+ this.TokenRequestEndpointMethod = HttpMethod.Post;
}
/// <summary>
- /// Gets or sets the OAuth version supported by the Service Provider.
- /// </summary>
- public ProtocolVersion ProtocolVersion { get; set; }
-
- /// <summary>
- /// Gets or sets the URL used to obtain an unauthorized Request Token,
- /// described in Section 6.1 (Obtaining an Unauthorized Request Token).
+ /// Initializes a new instance of the <see cref="ServiceProviderDescription"/> class.
/// </summary>
- /// <remarks>
- /// The request URL query MUST NOT contain any OAuth Protocol Parameters.
- /// This is the URL that <see cref="OAuth.Messages.UnauthorizedTokenRequest"/> messages are directed to.
- /// </remarks>
- /// <exception cref="ArgumentException">Thrown if this property is set to a URI with OAuth protocol parameters.</exception>
- public MessageReceivingEndpoint RequestTokenEndpoint {
- get {
- return this.requestTokenEndpoint;
+ /// <param name="temporaryCredentialsRequestEndpoint">The temporary credentials request endpoint.</param>
+ /// <param name="resourceOwnerAuthorizationEndpoint">The resource owner authorization endpoint.</param>
+ /// <param name="tokenRequestEndpoint">The token request endpoint.</param>
+ public ServiceProviderDescription(
+ string temporaryCredentialsRequestEndpoint, string resourceOwnerAuthorizationEndpoint, string tokenRequestEndpoint) {
+ if (temporaryCredentialsRequestEndpoint != null) {
+ this.TemporaryCredentialsRequestEndpoint = new Uri(temporaryCredentialsRequestEndpoint, UriKind.Absolute);
}
- set {
- if (value != null && UriUtil.QueryStringContainPrefixedParameters(value.Location, OAuth.Protocol.ParameterPrefix)) {
- throw new ArgumentException(OAuthStrings.RequestUrlMustNotHaveOAuthParameters);
- }
+ if (resourceOwnerAuthorizationEndpoint != null) {
+ this.ResourceOwnerAuthorizationEndpoint = new Uri(resourceOwnerAuthorizationEndpoint, UriKind.Absolute);
+ }
- this.requestTokenEndpoint = value;
+ if (tokenRequestEndpoint != null) {
+ this.TokenRequestEndpoint = new Uri(tokenRequestEndpoint, UriKind.Absolute);
}
}
/// <summary>
- /// Gets or sets the URL used to obtain User authorization for Consumer access,
- /// described in Section 6.2 (Obtaining User Authorization).
+ /// Gets or sets the temporary credentials request endpoint.
/// </summary>
- /// <remarks>
- /// This is the URL that <see cref="OAuth.Messages.UserAuthorizationRequest"/> messages are
- /// indirectly (via the user agent) sent to.
- /// </remarks>
- public MessageReceivingEndpoint UserAuthorizationEndpoint { get; set; }
+ /// <value>
+ /// The temporary credentials request endpoint.
+ /// </value>
+ public Uri TemporaryCredentialsRequestEndpoint { get; set; }
/// <summary>
- /// Gets or sets the URL used to exchange the User-authorized Request Token
- /// for an Access Token, described in Section 6.3 (Obtaining an Access Token).
+ /// Gets or sets the HTTP method to use with the temporary credentials request endpoint.
/// </summary>
- /// <remarks>
- /// This is the URL that <see cref="OAuth.Messages.AuthorizedTokenRequest"/> messages are directed to.
- /// </remarks>
- public MessageReceivingEndpoint AccessTokenEndpoint { get; set; }
+ public HttpMethod TemporaryCredentialsRequestEndpointMethod { get; set; }
/// <summary>
- /// Gets or sets the signing policies that apply to this Service Provider.
+ /// Gets the resource owner authorization endpoint.
/// </summary>
- [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Type initializers require this format.")]
- public ITamperProtectionChannelBindingElement[] TamperProtectionElements { get; set; }
+ /// <value>
+ /// The resource owner authorization endpoint.
+ /// May be <c>null</c> for 2-legged OAuth.
+ /// </value>
+ public Uri ResourceOwnerAuthorizationEndpoint { get; set; }
/// <summary>
- /// Gets the OAuth version supported by the Service Provider.
+ /// Gets the token request endpoint.
/// </summary>
- internal Version Version {
- get { return Protocol.Lookup(this.ProtocolVersion).Version; }
- }
+ /// <value>
+ /// The token request endpoint.
+ /// </value>
+ public Uri TokenRequestEndpoint { get; set; }
/// <summary>
- /// Creates a signing element that includes all the signing elements this service provider supports.
+ /// Gets or sets the HTTP method to use with the token request endpoint.
/// </summary>
- /// <returns>The created signing element.</returns>
- internal ITamperProtectionChannelBindingElement CreateTamperProtectionElement() {
- RequiresEx.ValidState(this.TamperProtectionElements != null);
- return new SigningBindingElementChain(this.TamperProtectionElements.Select(el => (ITamperProtectionChannelBindingElement)el.Clone()).ToArray());
- }
+ public HttpMethod TokenRequestEndpointMethod { get; set; }
}
}
diff --git a/src/DotNetOpenAuth.OpenIdOAuth/OAuth/WebConsumerOpenIdRelyingParty.cs b/src/DotNetOpenAuth.OpenIdOAuth/OAuth/WebConsumerOpenIdRelyingParty.cs
index a19d505..dce51c2 100644
--- a/src/DotNetOpenAuth.OpenIdOAuth/OAuth/WebConsumerOpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth.OpenIdOAuth/OAuth/WebConsumerOpenIdRelyingParty.cs
@@ -8,9 +8,11 @@ namespace DotNetOpenAuth.OAuth {
using System;
using System.Collections.Generic;
using System.Linq;
+ using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+ using System.Web;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
@@ -26,14 +28,13 @@ namespace DotNetOpenAuth.OAuth {
/// The methods on this class are thread-safe. Provided the properties are set and not changed
/// afterward, a single instance of this class may be used by an entire web application safely.
/// </remarks>
- public class WebConsumerOpenIdRelyingParty : WebConsumer {
+ public class WebConsumerOpenIdRelyingParty : Consumer {
/// <summary>
/// Initializes a new instance of the <see cref="WebConsumerOpenIdRelyingParty"/> class.
/// </summary>
/// <param name="serviceDescription">The endpoints and behavior of the Service Provider.</param>
/// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
- public WebConsumerOpenIdRelyingParty(ServiceProviderDescription serviceDescription, IConsumerTokenManager tokenManager)
- : base(serviceDescription, tokenManager) {
+ public WebConsumerOpenIdRelyingParty() {
}
/// <summary>
@@ -64,11 +65,8 @@ namespace DotNetOpenAuth.OAuth {
/// The access token, if granted, is automatically stored in the <see cref="ConsumerBase.TokenManager" />.
/// The token manager instance must implement <see cref="IOpenIdOAuthTokenManager" />.
/// </remarks>
- public async Task<AuthorizedTokenResponse> ProcessUserAuthorizationAsync(IAuthenticationResponse openIdAuthenticationResponse, CancellationToken cancellationToken = default(CancellationToken)) {
+ public async Task<AccessTokenResponse> ProcessUserAuthorizationAsync(IAuthenticationResponse openIdAuthenticationResponse, CancellationToken cancellationToken = default(CancellationToken)) {
Requires.NotNull(openIdAuthenticationResponse, "openIdAuthenticationResponse");
- RequiresEx.ValidState(this.TokenManager is IOpenIdOAuthTokenManager);
- var openidTokenManager = this.TokenManager as IOpenIdOAuthTokenManager;
- ErrorUtilities.VerifyOperation(openidTokenManager != null, OAuthStrings.OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface, typeof(IOpenIdOAuthTokenManager).FullName);
// The OAuth extension is only expected in positive assertion responses.
if (openIdAuthenticationResponse.Status != AuthenticationStatus.Authenticated) {
@@ -81,21 +79,24 @@ namespace DotNetOpenAuth.OAuth {
return null;
}
- // Prepare a message to exchange the request token for an access token.
- // We are careful to use a v1.0 message version so that the oauth_verifier is not required.
- var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, Protocol.V10.Version) {
- RequestToken = positiveAuthorization.RequestToken,
- ConsumerKey = this.ConsumerKey,
- };
+ using (var client = this.CreateHttpClient(new AccessToken(positiveAuthorization.RequestToken, string.Empty))) {
+ var request = new HttpRequestMessage(this.ServiceProvider.TokenRequestEndpointMethod, this.ServiceProvider.TokenRequestEndpoint);
+ using (var response = await client.SendAsync(request, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
- // Retrieve the access token and store it in the token manager.
- openidTokenManager.StoreOpenIdAuthorizedRequestToken(this.ConsumerKey, positiveAuthorization);
- var grantAccess = await this.Channel.RequestAsync<AuthorizedTokenResponse>(requestAccess, cancellationToken);
- this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(this.ConsumerKey, positiveAuthorization.RequestToken, grantAccess.AccessToken, grantAccess.TokenSecret);
+ // Parse the response and ensure that it meets the requirements of the OAuth 1.0 spec.
+ string content = await response.Content.ReadAsStringAsync();
+ var responseData = HttpUtility.ParseQueryString(content);
+ string accessToken = responseData[Protocol.TokenParameter];
+ string tokenSecret = responseData[Protocol.TokenSecretParameter];
+ ErrorUtilities.VerifyProtocol(!string.IsNullOrEmpty(accessToken), MessagingStrings.RequiredParametersMissing, typeof(AuthorizedTokenResponse).Name, Protocol.TokenParameter);
+ ErrorUtilities.VerifyProtocol(tokenSecret != null, MessagingStrings.RequiredParametersMissing, typeof(AuthorizedTokenResponse).Name, Protocol.TokenSecretParameter);
- // Provide the caller with the access token so it may be associated with the user
- // that is logging in.
- return grantAccess;
+ responseData.Remove(Protocol.TokenParameter);
+ responseData.Remove(Protocol.TokenSecretParameter);
+ return new AccessTokenResponse(accessToken, tokenSecret, responseData);
+ }
+ }
}
}
}