summaryrefslogtreecommitdiffstats
path: root/samples/DotNetOpenAuth.ApplicationBlock
diff options
context:
space:
mode:
Diffstat (limited to 'samples/DotNetOpenAuth.ApplicationBlock')
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj18
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs162
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/HttpAsyncHandlerBase.cs60
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/InMemoryTokenManager.cs147
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs292
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/Util.cs176
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs58
-rw-r--r--samples/DotNetOpenAuth.ApplicationBlock/packages.config5
8 files changed, 335 insertions, 583 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
index 81160f2..19f26b5 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
+++ b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj
@@ -33,6 +33,7 @@
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\src\</SolutionDir>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -68,11 +69,16 @@
<DefineConstants>$(DefineConstants);SAMPLESONLY</DefineConstants>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="Newtonsoft.Json">
+ <HintPath>..\..\src\packages\Newtonsoft.Json.4.5.11\lib\net40\Newtonsoft.Json.dll</HintPath>
+ </Reference>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel.Web" />
<Reference Include="System.Web" />
@@ -93,10 +99,8 @@
<Compile Include="Facebook\FacebookGraph.cs" />
<Compile Include="CustomExtensions\UIRequestAtRelyingPartyFactory.cs" />
<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" />
@@ -131,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>
@@ -168,6 +176,10 @@
<Name>DotNetOpenAuth.OpenId</Name>
</ProjectReference>
</ItemGroup>
+ <ItemGroup>
+ <None Include="packages.config" />
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " />
+ <Import Project="$(SolutionDir)\.nuget\nuget.targets" />
</Project> \ No newline at end of file
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs
index 1bdb04d..a7c062e 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs
@@ -7,14 +7,20 @@
namespace DotNetOpenAuth.ApplicationBlock {
using System;
using System.Collections.Generic;
+ using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
+ using System.Net.Http;
+ using System.Net.Http.Headers;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using System.Web;
using System.Xml;
using System.Xml.Linq;
using DotNetOpenAuth.Messaging;
@@ -24,16 +30,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.
@@ -60,7 +65,19 @@ namespace DotNetOpenAuth.ApplicationBlock {
/// <summary>
/// The URI to get contacts once authorization is granted.
/// </summary>
- private static readonly MessageReceivingEndpoint GetContactsEndpoint = new MessageReceivingEndpoint("http://www.google.com/m8/feeds/contacts/default/full/", HttpDeliveryMethods.GetRequest);
+ private static readonly Uri GetContactsEndpoint = new Uri("http://www.google.com/m8/feeds/contacts/default/full/");
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="GoogleConsumer"/> class.
+ /// </summary>
+ public GoogleConsumer() {
+ this.ServiceProvider = ServiceDescription;
+ this.ConsumerKey = ConfigurationManager.AppSettings["googleConsumerKey"];
+ this.ConsumerSecret = ConfigurationManager.AppSettings["googleConsumerSecret"];
+ this.TemporaryCredentialStorage = HttpContext.Current != null
+ ? (ITemporaryCredentialStorage)new CookieTemporaryCredentialStorage()
+ : new MemoryTemporaryCredentialStorage();
+ }
/// <summary>
/// The many specific authorization scopes Google offers.
@@ -149,91 +166,60 @@ namespace DotNetOpenAuth.ApplicationBlock {
}
/// <summary>
- /// The service description to use for accessing Google data APIs using an X509 certificate.
+ /// Gets the scope URI in Google's format.
/// </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) },
- };
+ /// <param name="scope">The scope, which may include one or several Google applications.</param>
+ /// <returns>A space-delimited list of URIs for the requested Google applications.</returns>
+ public static string GetScopeUri(Applications scope) {
+ return string.Join(" ", Util.GetIndividualFlags(scope).Select(app => DataScopeUris[(Applications)app]).ToArray());
}
/// <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>
- public static void RequestAuthorization(WebConsumer consumer, Applications requestedAccessScope) {
- if (consumer == null) {
- throw new ArgumentNullException("consumer");
- }
-
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// A task that completes with the asynchronous operation.
+ /// </returns>
+ 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 = consumer.PrepareRequestUserAuthorization(callback, extraParameters, null);
- consumer.Channel.Send(request);
- }
-
- /// <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="requestToken">The unauthorized request token assigned by Google.</param>
- /// <returns>The request token</returns>
- public static Uri RequestAuthorization(DesktopConsumer consumer, Applications requestedAccessScope, out string requestToken) {
- if (consumer == null) {
- throw new ArgumentNullException("consumer");
- }
-
- var extraParameters = new Dictionary<string, string> {
- { "scope", GetScopeUri(requestedAccessScope) },
- };
-
- return consumer.RequestUserAuthorization(extraParameters, null, out requestToken);
+ return this.RequestUserAuthorizationAsync(callback, extraParameters, cancellationToken);
}
/// <summary>
/// Gets the Gmail address book's contents.
/// </summary>
- /// <param name="consumer">The Google consumer.</param>
/// <param name="accessToken">The access token previously retrieved.</param>
/// <param name="maxResults">The maximum number of entries to return. If you want to receive all of the contacts, rather than only the default maximum, you can specify a very large number here.</param>
/// <param name="startIndex">The 1-based index of the first result to be retrieved (for paging).</param>
- /// <returns>An XML document returned by Google.</returns>
- public static XDocument GetContacts(ConsumerBase consumer, string accessToken, int maxResults/* = 25*/, int startIndex/* = 1*/) {
- if (consumer == null) {
- throw new ArgumentNullException("consumer");
- }
-
- var extraData = new Dictionary<string, string>() {
- { "start-index", startIndex.ToString(CultureInfo.InvariantCulture) },
- { "max-results", maxResults.ToString(CultureInfo.InvariantCulture) },
- };
- var request = consumer.PrepareAuthorizedRequest(GetContactsEndpoint, accessToken, extraData);
-
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// An XML document returned by Google.
+ /// </returns>
+ 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
- request.AutomaticDecompression = DecompressionMethods.GZip;
- request.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16";
-
- var response = consumer.Channel.WebRequestHandler.GetResponse(request);
- string body = response.GetResponseReader().ReadToEnd();
- XDocument result = XDocument.Parse(body);
- return result;
+ var handler = new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip };
+ using (var httpClient = this.CreateHttpClient(accessToken, handler)) {
+ var request = new HttpRequestMessage(HttpMethod.Get, GetContactsEndpoint);
+ request.Content = new FormUrlEncodedContent(
+ new Dictionary<string, string>() {
+ { "start-index", startIndex.ToString(CultureInfo.InvariantCulture) },
+ { "max-results", maxResults.ToString(CultureInfo.InvariantCulture) },
+ });
+ request.Headers.UserAgent.Add(ProductInfoHeaderValue.Parse("Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16"));
+ using (var response = await httpClient.SendAsync(request, cancellationToken)) {
+ string body = await response.Content.ReadAsStringAsync();
+ XDocument result = XDocument.Parse(body);
+ return result;
+ }
+ }
}
- public static void PostBlogEntry(ConsumerBase consumer, string accessToken, string blogUrl, string title, XElement body) {
+ public async Task PostBlogEntryAsync(AccessToken accessToken, string blogUrl, string title, XElement body, CancellationToken cancellationToken = default(CancellationToken)) {
string feedUrl;
var getBlogHome = WebRequest.Create(blogUrl);
using (var blogHomeResponse = getBlogHome.GetResponse()) {
@@ -258,31 +244,21 @@ namespace DotNetOpenAuth.ApplicationBlock {
XmlWriter xw = XmlWriter.Create(ms, xws);
entry.WriteTo(xw);
xw.Flush();
-
- WebRequest request = consumer.PrepareAuthorizedRequest(new MessageReceivingEndpoint(feedUrl, HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), accessToken);
- request.ContentType = "application/atom+xml";
- request.Method = "POST";
- request.ContentLength = ms.Length;
ms.Seek(0, SeekOrigin.Begin);
- using (Stream requestStream = request.GetRequestStream()) {
- ms.CopyTo(requestStream);
- }
- using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) {
- if (response.StatusCode == HttpStatusCode.Created) {
- // Success
- } else {
- // Error!
+
+ var request = new HttpRequestMessage(HttpMethod.Post, feedUrl);
+ request.Content = new StreamContent(ms);
+ request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/atom+xml");
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
+ using (var response = await httpClient.SendAsync(request, cancellationToken)) {
+ if (response.StatusCode == HttpStatusCode.Created) {
+ // Success
+ } else {
+ // Error!
+ response.EnsureSuccessStatusCode(); // throw some meaningful exception.
+ }
}
}
}
-
- /// <summary>
- /// Gets the scope URI in Google's format.
- /// </summary>
- /// <param name="scope">The scope, which may include one or several Google applications.</param>
- /// <returns>A space-delimited list of URIs for the requested Google applications.</returns>
- public static string GetScopeUri(Applications scope) {
- return string.Join(" ", Util.GetIndividualFlags(scope).Select(app => DataScopeUris[(Applications)app]).ToArray());
- }
}
}
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/HttpAsyncHandlerBase.cs b/samples/DotNetOpenAuth.ApplicationBlock/HttpAsyncHandlerBase.cs
new file mode 100644
index 0000000..a72a9b1
--- /dev/null
+++ b/samples/DotNetOpenAuth.ApplicationBlock/HttpAsyncHandlerBase.cs
@@ -0,0 +1,60 @@
+//-----------------------------------------------------------------------
+// <copyright file="HttpAsyncHandlerBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.ApplicationBlock {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using System.Web;
+
+ public abstract class HttpAsyncHandlerBase : IHttpAsyncHandler {
+ public abstract bool IsReusable { get; }
+
+ public IAsyncResult BeginProcessRequest(HttpContext context, System.AsyncCallback cb, object extraData) {
+ return ToApm(this.ProcessRequestAsync(context), cb, extraData);
+ }
+
+ public void EndProcessRequest(IAsyncResult result) {
+ ((Task)result).Wait(); // rethrows exceptions
+ }
+
+ public void ProcessRequest(HttpContext context) {
+ this.ProcessRequestAsync(context).GetAwaiter().GetResult();
+ }
+
+ protected abstract Task ProcessRequestAsync(HttpContext context);
+
+ private static Task ToApm(Task task, AsyncCallback callback, object state) {
+ if (task == null) {
+ throw new ArgumentNullException("task");
+ }
+
+ var tcs = new TaskCompletionSource<object>(state);
+ task.ContinueWith(
+ t => {
+ if (t.IsFaulted) {
+ tcs.TrySetException(t.Exception.InnerExceptions);
+ } else if (t.IsCanceled) {
+ tcs.TrySetCanceled();
+ } else {
+ tcs.TrySetResult(null);
+ }
+
+ if (callback != null) {
+ callback(tcs.Task);
+ }
+ },
+ CancellationToken.None,
+ TaskContinuationOptions.None,
+ TaskScheduler.Default);
+
+ return tcs.Task;
+ }
+ }
+}
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 013e66b..16f1c92 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs
@@ -11,71 +11,80 @@ namespace DotNetOpenAuth.ApplicationBlock {
using System.Globalization;
using System.IO;
using System.Net;
+ using System.Net.Http;
+ using System.Net.Http.Headers;
+ using System.Runtime.Serialization.Json;
+ using System.Threading;
+ using System.Threading.Tasks;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
+ using Newtonsoft.Json.Linq;
+
/// <summary>
/// A consumer capable of communicating with Twitter.
/// </summary>
- public static class TwitterConsumer {
+ public class TwitterConsumer : Consumer {
/// <summary>
/// The description of Twitter's OAuth protocol URIs for use with actually reading/writing
/// a user's private Twitter data.
/// </summary>
- public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription {
- RequestTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- UserAuthorizationEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/authorize", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- AccessTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
- };
+ public static readonly ServiceProviderDescription ServiceDescription = new ServiceProviderDescription(
+ "https://api.twitter.com/oauth/request_token",
+ "https://api.twitter.com/oauth/authorize",
+ "https://api.twitter.com/oauth/access_token");
/// <summary>
/// The description of Twitter's OAuth protocol URIs for use with their "Sign in with Twitter" feature.
/// </summary>
- public static readonly ServiceProviderDescription SignInWithTwitterServiceDescription = new ServiceProviderDescription {
- RequestTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/request_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- UserAuthorizationEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/authenticate", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- AccessTokenEndpoint = new MessageReceivingEndpoint("http://twitter.com/oauth/access_token", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
- TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
- };
+ public static readonly ServiceProviderDescription SignInWithTwitterServiceDescription = new ServiceProviderDescription(
+ "https://api.twitter.com/oauth/request_token",
+ "https://api.twitter.com/oauth/authenticate",
+ "https://api.twitter.com/oauth/access_token");
/// <summary>
/// The URI to get a user's favorites.
/// </summary>
- private static readonly MessageReceivingEndpoint GetFavoritesEndpoint = new MessageReceivingEndpoint("http://twitter.com/favorites.xml", HttpDeliveryMethods.GetRequest);
+ private static readonly Uri GetFavoritesEndpoint = new Uri("http://twitter.com/favorites.xml");
/// <summary>
/// The URI to get the data on the user's home page.
/// </summary>
- private static readonly MessageReceivingEndpoint GetFriendTimelineStatusEndpoint = new MessageReceivingEndpoint("http://twitter.com/statuses/friends_timeline.xml", HttpDeliveryMethods.GetRequest);
+ private static readonly Uri GetFriendTimelineStatusEndpoint = new Uri("https://api.twitter.com/1.1/statuses/home_timeline.json");
- private static readonly MessageReceivingEndpoint UpdateProfileBackgroundImageEndpoint = new MessageReceivingEndpoint("http://twitter.com/account/update_profile_background_image.xml", HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);
+ private static readonly Uri UpdateProfileBackgroundImageEndpoint = new Uri("http://twitter.com/account/update_profile_background_image.xml");
- private static readonly MessageReceivingEndpoint UpdateProfileImageEndpoint = new MessageReceivingEndpoint("http://twitter.com/account/update_profile_image.xml", HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);
+ private static readonly Uri UpdateProfileImageEndpoint = new Uri("http://twitter.com/account/update_profile_image.xml");
- private static readonly MessageReceivingEndpoint VerifyCredentialsEndpoint = new MessageReceivingEndpoint("http://api.twitter.com/1/account/verify_credentials.xml", HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);
+ private static readonly Uri VerifyCredentialsEndpoint = new Uri("http://api.twitter.com/1/account/verify_credentials.xml");
/// <summary>
- /// The consumer used for the Sign in to Twitter feature.
+ /// Initializes a new instance of the <see cref="TwitterConsumer"/> class.
/// </summary>
- private static WebConsumer signInConsumer;
-
- /// <summary>
- /// The lock acquired to initialize the <see cref="signInConsumer"/> field.
- /// </summary>
- private static object signInConsumerInitLock = new object();
+ public TwitterConsumer() {
+ this.ServiceProvider = ServiceDescription;
+ this.ConsumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"];
+ this.ConsumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"];
+ this.TemporaryCredentialStorage = HttpContext.Current != null
+ ? (ITemporaryCredentialStorage)new CookieTemporaryCredentialStorage()
+ : new MemoryTemporaryCredentialStorage();
+ }
/// <summary>
- /// Initializes static members of the <see cref="TwitterConsumer"/> class.
+ /// Initializes a new instance of the <see cref="TwitterConsumer"/> class.
/// </summary>
- static TwitterConsumer() {
- // Twitter can't handle the Expect 100 Continue HTTP header.
- ServicePointManager.FindServicePoint(GetFavoritesEndpoint.Location).Expect100Continue = false;
+ /// <param name="consumerKey">The consumer key.</param>
+ /// <param name="consumerSecret">The consumer secret.</param>
+ public TwitterConsumer(string consumerKey, string consumerSecret)
+ : this() {
+ this.ConsumerKey = consumerKey;
+ this.ConsumerSecret = consumerSecret;
}
/// <summary>
@@ -88,137 +97,160 @@ namespace DotNetOpenAuth.ApplicationBlock {
}
}
+ public static Consumer CreateConsumer(bool forWeb = true) {
+ string consumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"];
+ string consumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"];
+ if (IsTwitterConsumerConfigured) {
+ ITemporaryCredentialStorage storage = forWeb ? (ITemporaryCredentialStorage)new CookieTemporaryCredentialStorage() : new MemoryTemporaryCredentialStorage();
+ return new Consumer(consumerKey, consumerSecret, ServiceDescription, storage) {
+ HostFactories = new TwitterHostFactories(),
+ };
+ } else {
+ throw new InvalidOperationException("No Twitter OAuth consumer key and secret could be found in web.config AppSettings.");
+ }
+ }
+
/// <summary>
- /// Gets the consumer to use for the Sign in to Twitter feature.
+ /// Prepares a redirect that will send the user to Twitter to sign in.
/// </summary>
- /// <value>The twitter sign in.</value>
- private static WebConsumer TwitterSignIn {
- get {
- if (signInConsumer == null) {
- lock (signInConsumerInitLock) {
- if (signInConsumer == null) {
- signInConsumer = new WebConsumer(SignInWithTwitterServiceDescription, ShortTermUserSessionTokenManager);
- }
- }
- }
+ /// <param name="forceNewLogin">if set to <c>true</c> the user will be required to re-enter their Twitter credentials even if already logged in to Twitter.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// The redirect message.
+ /// </returns>
+ public static async Task<Uri> StartSignInWithTwitterAsync(bool forceNewLogin = false, CancellationToken cancellationToken = default(CancellationToken)) {
+ var redirectParameters = new Dictionary<string, string>();
+ if (forceNewLogin) {
+ redirectParameters["force_login"] = "true";
+ }
+ Uri callback = MessagingUtilities.GetRequestUrlFromContext().StripQueryArgumentsWithPrefix("oauth_");
+
+ var consumer = CreateConsumer();
+ consumer.ServiceProvider = SignInWithTwitterServiceDescription;
+ Uri redirectUrl = await consumer.RequestUserAuthorizationAsync(callback, cancellationToken: cancellationToken);
+ return redirectUrl;
+ }
- return signInConsumer;
+ /// <summary>
+ /// Checks the incoming web request to see if it carries a Twitter authentication response,
+ /// and provides the user's Twitter screen name and unique id if available.
+ /// </summary>
+ /// <param name="completeUrl">The URL that came back from the service provider to complete the authorization.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>
+ /// A tuple with the screen name and Twitter unique user ID if successful; otherwise <c>null</c>.
+ /// </returns>
+ public static async Task<Tuple<string, int>> TryFinishSignInWithTwitterAsync(Uri completeUrl = null, CancellationToken cancellationToken = default(CancellationToken)) {
+ var consumer = CreateConsumer();
+ consumer.ServiceProvider = SignInWithTwitterServiceDescription;
+ var response = await consumer.ProcessUserAuthorizationAsync(completeUrl ?? HttpContext.Current.Request.Url, cancellationToken: cancellationToken);
+ if (response == null) {
+ return null;
}
+
+ string screenName = response.ExtraData["screen_name"];
+ int userId = int.Parse(response.ExtraData["user_id"]);
+ return Tuple.Create(screenName, userId);
}
- private static InMemoryTokenManager ShortTermUserSessionTokenManager {
- get {
- var store = HttpContext.Current.Session;
- var tokenManager = (InMemoryTokenManager)store["TwitterShortTermUserSessionTokenManager"];
- if (tokenManager == null) {
- string consumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"];
- string consumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"];
- if (IsTwitterConsumerConfigured) {
- tokenManager = new InMemoryTokenManager(consumerKey, consumerSecret);
- store["TwitterShortTermUserSessionTokenManager"] = tokenManager;
- } else {
- throw new InvalidOperationException("No Twitter OAuth consumer key and secret could be found in web.config AppSettings.");
- }
- }
+ public async Task<JArray> GetUpdatesAsync(AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ if (String.IsNullOrEmpty(accessToken.Token)) {
+ throw new ArgumentNullException("accessToken.Token");
+ }
- return tokenManager;
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
+ using (var response = await httpClient.GetAsync(GetFriendTimelineStatusEndpoint, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ string jsonString = await response.Content.ReadAsStringAsync();
+ var json = JArray.Parse(jsonString);
+ return json;
+ }
}
}
- public static XDocument GetUpdates(ConsumerBase twitter, string accessToken) {
- IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(GetFriendTimelineStatusEndpoint, accessToken);
- return XDocument.Load(XmlReader.Create(response.GetResponseReader()));
- }
+ public async Task<XDocument> GetFavorites(AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ if (String.IsNullOrEmpty(accessToken.Token)) {
+ throw new ArgumentNullException("accessToken.Token");
+ }
- public static XDocument GetFavorites(ConsumerBase twitter, string accessToken) {
- IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(GetFavoritesEndpoint, accessToken);
- return XDocument.Load(XmlReader.Create(response.GetResponseReader()));
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
+ using (HttpResponseMessage response = await httpClient.GetAsync(GetFavoritesEndpoint, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ return XDocument.Parse(await response.Content.ReadAsStringAsync());
+ }
+ }
}
- public static XDocument UpdateProfileBackgroundImage(ConsumerBase twitter, string accessToken, string image, bool tile) {
- var parts = new[] {
- MultipartPostPart.CreateFormFilePart("image", image, "image/" + Path.GetExtension(image).Substring(1).ToLowerInvariant()),
- MultipartPostPart.CreateFormPart("tile", tile.ToString().ToLowerInvariant()),
- };
- HttpWebRequest request = twitter.PrepareAuthorizedRequest(UpdateProfileBackgroundImageEndpoint, accessToken, parts);
- request.ServicePoint.Expect100Continue = false;
- IncomingWebResponse response = twitter.Channel.WebRequestHandler.GetResponse(request);
- string responseString = response.GetResponseReader().ReadToEnd();
- return XDocument.Parse(responseString);
+ public async Task<XDocument> UpdateProfileBackgroundImageAsync(AccessToken accessToken, string image, bool tile, CancellationToken cancellationToken) {
+ var imageAttachment = new StreamContent(File.OpenRead(image));
+ imageAttachment.Headers.ContentType = new MediaTypeHeaderValue("image/" + Path.GetExtension(image).Substring(1).ToLowerInvariant());
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, UpdateProfileBackgroundImageEndpoint);
+ var content = new MultipartFormDataContent();
+ content.Add(imageAttachment, "image");
+ content.Add(new StringContent(tile.ToString().ToLowerInvariant()), "tile");
+ request.Content = content;
+ request.Headers.ExpectContinue = false;
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
+ using (HttpResponseMessage response = await httpClient.SendAsync(request, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ string responseString = await response.Content.ReadAsStringAsync();
+ return XDocument.Parse(responseString);
+ }
+ }
}
- public static XDocument UpdateProfileImage(ConsumerBase twitter, string accessToken, string pathToImage) {
+ public Task<XDocument> UpdateProfileImageAsync(AccessToken accessToken, string pathToImage, CancellationToken cancellationToken = default(CancellationToken)) {
string contentType = "image/" + Path.GetExtension(pathToImage).Substring(1).ToLowerInvariant();
- return UpdateProfileImage(twitter, accessToken, File.OpenRead(pathToImage), contentType);
+ return this.UpdateProfileImageAsync(accessToken, File.OpenRead(pathToImage), contentType, cancellationToken);
}
- public static XDocument UpdateProfileImage(ConsumerBase twitter, string accessToken, Stream image, string contentType) {
- var parts = new[] {
- MultipartPostPart.CreateFormFilePart("image", "twitterPhoto", contentType, image),
- };
- HttpWebRequest request = twitter.PrepareAuthorizedRequest(UpdateProfileImageEndpoint, accessToken, parts);
- IncomingWebResponse response = twitter.Channel.WebRequestHandler.GetResponse(request);
- string responseString = response.GetResponseReader().ReadToEnd();
- return XDocument.Parse(responseString);
+ public async Task<XDocument> UpdateProfileImageAsync(AccessToken accessToken, Stream image, string contentType, CancellationToken cancellationToken = default(CancellationToken)) {
+ var imageAttachment = new StreamContent(image);
+ imageAttachment.Headers.ContentType = new MediaTypeHeaderValue(contentType);
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, UpdateProfileImageEndpoint);
+ var content = new MultipartFormDataContent();
+ content.Add(imageAttachment, "image", "twitterPhoto");
+ request.Content = content;
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
+ using (HttpResponseMessage response = await httpClient.SendAsync(request, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ string responseString = await response.Content.ReadAsStringAsync();
+ return XDocument.Parse(responseString);
+ }
+ }
}
- public static XDocument VerifyCredentials(ConsumerBase twitter, string accessToken) {
- IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(VerifyCredentialsEndpoint, accessToken);
- return XDocument.Load(XmlReader.Create(response.GetResponseReader()));
+ public async Task<XDocument> VerifyCredentialsAsync(AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ using (var httpClient = this.CreateHttpClient(accessToken)) {
+ using (var response = await httpClient.GetAsync(VerifyCredentialsEndpoint, cancellationToken)) {
+ response.EnsureSuccessStatusCode();
+ using (var stream = await response.Content.ReadAsStreamAsync()) {
+ return XDocument.Load(XmlReader.Create(stream));
+ }
+ }
+ }
}
- public static string GetUsername(ConsumerBase twitter, string accessToken) {
- XDocument xml = VerifyCredentials(twitter, accessToken);
+ public async Task<string> GetUsername(AccessToken accessToken, CancellationToken cancellationToken = default(CancellationToken)) {
+ XDocument xml = await this.VerifyCredentialsAsync(accessToken, cancellationToken);
XPathNavigator nav = xml.CreateNavigator();
return nav.SelectSingleNode("/user/screen_name").Value;
}
- /// <summary>
- /// Prepares a redirect that will send the user to Twitter to sign in.
- /// </summary>
- /// <param name="forceNewLogin">if set to <c>true</c> the user will be required to re-enter their Twitter credentials even if already logged in to Twitter.</param>
- /// <returns>The redirect message.</returns>
- /// <remarks>
- /// Call <see cref="OutgoingWebResponse.Send"/> or
- /// <c>return StartSignInWithTwitter().<see cref="MessagingUtilities.AsActionResult">AsActionResult()</see></c>
- /// to actually perform the redirect.
- /// </remarks>
- public static OutgoingWebResponse StartSignInWithTwitter(bool forceNewLogin) {
- var redirectParameters = new Dictionary<string, string>();
- if (forceNewLogin) {
- redirectParameters["force_login"] = "true";
- }
- Uri callback = MessagingUtilities.GetRequestUrlFromContext().StripQueryArgumentsWithPrefix("oauth_");
- var request = TwitterSignIn.PrepareRequestUserAuthorization(callback, null, redirectParameters);
- return TwitterSignIn.Channel.PrepareResponse(request);
- }
+ private class TwitterHostFactories : IHostFactories {
+ private static readonly IHostFactories underlyingFactories = new DefaultOAuthHostFactories();
- /// <summary>
- /// Checks the incoming web request to see if it carries a Twitter authentication response,
- /// and provides the user's Twitter screen name and unique id if available.
- /// </summary>
- /// <param name="screenName">The user's Twitter screen name.</param>
- /// <param name="userId">The user's Twitter unique user ID.</param>
- /// <returns>
- /// A value indicating whether Twitter authentication was successful;
- /// otherwise <c>false</c> to indicate that no Twitter response was present.
- /// </returns>
- public static bool TryFinishSignInWithTwitter(out string screenName, out int userId) {
- screenName = null;
- userId = 0;
- var response = TwitterSignIn.ProcessUserAuthorization();
- if (response == null) {
- return false;
+ public HttpMessageHandler CreateHttpMessageHandler() {
+ return new WebRequestHandler();
}
- screenName = response.ExtraData["screen_name"];
- userId = int.Parse(response.ExtraData["user_id"]);
+ public HttpClient CreateHttpClient(HttpMessageHandler handler = null) {
+ var client = underlyingFactories.CreateHttpClient(handler);
- // If we were going to make this LOOK like OpenID even though it isn't,
- // this seems like a reasonable, secure claimed id to allow the user to assume.
- OpenId.Identifier fake_claimed_id = string.Format(CultureInfo.InvariantCulture, "http://twitter.com/{0}#{1}", screenName, userId);
-
- return true;
+ // Twitter can't handle the Expect 100 Continue HTTP header.
+ client.DefaultRequestHeaders.ExpectContinue = false;
+ return client;
+ }
}
}
}
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs
index 0bec372..3e0795a 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/Util.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/Util.cs
@@ -13,33 +13,6 @@
internal static readonly Random NonCryptoRandomDataGenerator = new Random();
/// <summary>
- /// Sets the channel's outgoing HTTP requests to use default network credentials.
- /// </summary>
- /// <param name="channel">The channel to modify.</param>
- public static void UseDefaultNetworkCredentialsOnOutgoingHttpRequests(this Channel channel)
- {
- Debug.Assert(!(channel.WebRequestHandler is WrappingWebRequestHandler), "Wrapping an already wrapped web request handler. This is legal, but highly suspect of a bug as you don't want to wrap the same channel repeatedly to apply the same effect.");
- AddOutgoingHttpRequestTransform(channel, http => http.Credentials = CredentialCache.DefaultNetworkCredentials);
- }
-
- /// <summary>
- /// Adds some action to any outgoing HTTP request on this channel.
- /// </summary>
- /// <param name="channel">The channel's whose outgoing HTTP requests should be modified.</param>
- /// <param name="action">The action to perform on outgoing HTTP requests.</param>
- internal static void AddOutgoingHttpRequestTransform(this Channel channel, Action<HttpWebRequest> action) {
- if (channel == null) {
- throw new ArgumentNullException("channel");
- }
-
- if (action == null) {
- throw new ArgumentNullException("action");
- }
-
- channel.WebRequestHandler = new WrappingWebRequestHandler(channel.WebRequestHandler, action);
- }
-
- /// <summary>
/// Enumerates through the individual set bits in a flag enum.
/// </summary>
/// <param name="flags">The flags enum value.</param>
@@ -106,154 +79,5 @@
return totalCopiedBytes;
}
-
- /// <summary>
- /// Wraps some instance of a web request handler in order to perform some extra operation on all
- /// outgoing HTTP requests.
- /// </summary>
- private class WrappingWebRequestHandler : IDirectWebRequestHandler
- {
- /// <summary>
- /// The handler being wrapped.
- /// </summary>
- private readonly IDirectWebRequestHandler wrappedHandler;
-
- /// <summary>
- /// The action to perform on outgoing HTTP requests.
- /// </summary>
- private readonly Action<HttpWebRequest> action;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="WrappingWebRequestHandler"/> class.
- /// </summary>
- /// <param name="wrappedHandler">The HTTP handler to wrap.</param>
- /// <param name="action">The action to perform on outgoing HTTP requests.</param>
- internal WrappingWebRequestHandler(IDirectWebRequestHandler wrappedHandler, Action<HttpWebRequest> action)
- {
- if (wrappedHandler == null) {
- throw new ArgumentNullException("wrappedHandler");
- }
-
- if (action == null) {
- throw new ArgumentNullException("action");
- }
-
- this.wrappedHandler = wrappedHandler;
- this.action = action;
- }
-
- #region Implementation of IDirectWebRequestHandler
-
- /// <summary>
- /// Determines whether this instance can support the specified options.
- /// </summary>
- /// <param name="options">The set of options that might be given in a subsequent web request.</param>
- /// <returns>
- /// <c>true</c> if this instance can support the specified options; otherwise, <c>false</c>.
- /// </returns>
- public bool CanSupport(DirectWebRequestOptions options)
- {
- return this.wrappedHandler.CanSupport(options);
- }
-
- /// <summary>
- /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
- /// <returns>
- /// The stream the caller should write out the entity data to.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
- /// and any other appropriate properties <i>before</i> calling this method.
- /// Callers <i>must</i> close and dispose of the request stream when they are done
- /// writing to it to avoid taking up the connection too long and causing long waits on
- /// subsequent requests.</para>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch.</para>
- /// </remarks>
- public Stream GetRequestStream(HttpWebRequest request)
- {
- this.action(request);
- return this.wrappedHandler.GetRequestStream(request);
- }
-
- /// <summary>
- /// Prepares an <see cref="HttpWebRequest"/> that contains an POST entity for sending the entity.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> that should contain the entity.</param>
- /// <param name="options">The options to apply to this web request.</param>
- /// <returns>
- /// The stream the caller should write out the entity data to.
- /// </returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>The caller should have set the <see cref="HttpWebRequest.ContentLength"/>
- /// and any other appropriate properties <i>before</i> calling this method.
- /// Callers <i>must</i> close and dispose of the request stream when they are done
- /// writing to it to avoid taking up the connection too long and causing long waits on
- /// subsequent requests.</para>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch.</para>
- /// </remarks>
- public Stream GetRequestStream(HttpWebRequest request, DirectWebRequestOptions options)
- {
- this.action(request);
- return this.wrappedHandler.GetRequestStream(request, options);
- }
-
- /// <summary>
- /// Processes an <see cref="HttpWebRequest"/> and converts the
- /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
- /// <returns>An instance of <see cref="IncomingWebResponse"/> describing the response.</returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, should be Closed before throwing.</para>
- /// </remarks>
- public IncomingWebResponse GetResponse(HttpWebRequest request)
- {
- // If the request has an entity, the action would have already been processed in GetRequestStream.
- if (request.Method == "GET")
- {
- this.action(request);
- }
-
- return this.wrappedHandler.GetResponse(request);
- }
-
- /// <summary>
- /// Processes an <see cref="HttpWebRequest"/> and converts the
- /// <see cref="HttpWebResponse"/> to a <see cref="IncomingWebResponse"/> instance.
- /// </summary>
- /// <param name="request">The <see cref="HttpWebRequest"/> to handle.</param>
- /// <param name="options">The options to apply to this web request.</param>
- /// <returns>An instance of <see cref="IncomingWebResponse"/> describing the response.</returns>
- /// <exception cref="ProtocolException">Thrown for any network error.</exception>
- /// <remarks>
- /// <para>Implementations should catch <see cref="WebException"/> and wrap it in a
- /// <see cref="ProtocolException"/> to abstract away the transport and provide
- /// a single exception type for hosts to catch. The <see cref="WebException.Response"/>
- /// value, if set, should be Closed before throwing.</para>
- /// </remarks>
- public IncomingWebResponse GetResponse(HttpWebRequest request, DirectWebRequestOptions options)
- {
- // If the request has an entity, the action would have already been processed in GetRequestStream.
- if (request.Method == "GET") {
- this.action(request);
- }
-
- return this.wrappedHandler.GetResponse(request, options);
- }
-
- #endregion
- }
}
}
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs
index bbeb861..1dff5b6 100644
--- a/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs
+++ b/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs
@@ -7,55 +7,45 @@
namespace DotNetOpenAuth.ApplicationBlock {
using System;
using System.Collections.Generic;
+ using System.Configuration;
using System.Linq;
using System.Net;
using System.Text;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using System.Web;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
- public static class YammerConsumer {
+ public class YammerConsumer : Consumer {
/// <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 YammerConsumer() {
+ this.ServiceProvider = ServiceDescription;
+ this.ConsumerKey = ConfigurationManager.AppSettings["YammerConsumerKey"];
+ this.ConsumerSecret = ConfigurationManager.AppSettings["YammerConsumerSecret"];
+ this.TemporaryCredentialStorage = HttpContext.Current != null
+ ? (ITemporaryCredentialStorage)new CookieTemporaryCredentialStorage()
+ : new MemoryTemporaryCredentialStorage();
}
- public static Uri PrepareRequestAuthorization(DesktopConsumer consumer, out string requestToken) {
- 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"]);
}
-
- Uri authorizationUrl = consumer.RequestUserAuthorization(null, null, out requestToken);
- return authorizationUrl;
- }
-
- public static AuthorizedTokenResponse CompleteAuthorization(DesktopConsumer consumer, string requestToken, string userCode) {
- // 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 = customConsumer.ProcessUserAuthorization(requestToken, userCode);
- return response;
}
}
}
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/packages.config b/samples/DotNetOpenAuth.ApplicationBlock/packages.config
new file mode 100644
index 0000000..d7219c5
--- /dev/null
+++ b/samples/DotNetOpenAuth.ApplicationBlock/packages.config
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
+ <package id="Newtonsoft.Json" version="4.5.11" targetFramework="net45" />
+</packages> \ No newline at end of file