diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2013-01-23 07:49:02 -0800 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2013-01-23 07:49:02 -0800 |
commit | f04d5ca8396bdb0d09b0fea4e22d4e96c4fa8ad2 (patch) | |
tree | 93b2b354a85a6705914d8e12ecfce159a24bb563 | |
parent | 22df10e81fe404fb3f7beb47a2e50a96b79ca78b (diff) | |
download | DotNetOpenAuth-f04d5ca8396bdb0d09b0fea4e22d4e96c4fa8ad2.zip DotNetOpenAuth-f04d5ca8396bdb0d09b0fea4e22d4e96c4fa8ad2.tar.gz DotNetOpenAuth-f04d5ca8396bdb0d09b0fea4e22d4e96c4fa8ad2.tar.bz2 |
ApplicationBlock builds.
19 files changed, 230 insertions, 304 deletions
diff --git a/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj b/samples/DotNetOpenAuth.ApplicationBlock/DotNetOpenAuth.ApplicationBlock.csproj index 81160f2..9f37931 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> @@ -73,6 +74,8 @@ <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" /> @@ -168,6 +171,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..50c385e 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/GoogleConsumer.cs @@ -4,7 +4,8 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.ApplicationBlock { +namespace DotNetOpenAuth.ApplicationBlock +{ using System; using System.Collections.Generic; using System.Diagnostics; @@ -12,9 +13,13 @@ namespace DotNetOpenAuth.ApplicationBlock { 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.Xml; using System.Xml.Linq; using DotNetOpenAuth.Messaging; @@ -24,7 +29,8 @@ namespace DotNetOpenAuth.ApplicationBlock { /// <summary> /// A consumer capable of communicating with Google Data APIs. /// </summary> - public static class GoogleConsumer { + public static class GoogleConsumer + { /// <summary> /// The Consumer to use for accessing Google data APIs. /// </summary> @@ -66,7 +72,8 @@ 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> @@ -172,7 +179,7 @@ namespace DotNetOpenAuth.ApplicationBlock { /// </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) { + public static async Task RequestAuthorizationAsync(WebConsumer consumer, Applications requestedAccessScope, CancellationToken cancellationToken = default(CancellationToken)) { if (consumer == null) { throw new ArgumentNullException("consumer"); } @@ -181,8 +188,9 @@ namespace DotNetOpenAuth.ApplicationBlock { { "scope", GetScopeUri(requestedAccessScope) }, }; Uri callback = Util.GetCallbackUrlFromContext(); - var request = consumer.PrepareRequestUserAuthorization(callback, extraParameters, null); - consumer.Channel.Send(request); + var request = await consumer.PrepareRequestUserAuthorizationAsync(callback, extraParameters, null, cancellationToken); + var redirectingResponse = await consumer.Channel.PrepareResponseAsync(request, cancellationToken); + redirectingResponse.Send(); } /// <summary> @@ -191,8 +199,8 @@ namespace DotNetOpenAuth.ApplicationBlock { /// <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) { + /// <returns>The URI to redirect to and the request token.</returns> + public static Task<Tuple<Uri, string>> RequestAuthorization(DesktopConsumer consumer, Applications requestedAccessScope, CancellationToken cancellationToken = default(CancellationToken)) { if (consumer == null) { throw new ArgumentNullException("consumer"); } @@ -201,7 +209,7 @@ namespace DotNetOpenAuth.ApplicationBlock { { "scope", GetScopeUri(requestedAccessScope) }, }; - return consumer.RequestUserAuthorization(extraParameters, null, out requestToken); + return consumer.RequestUserAuthorizationAsync(extraParameters, null, cancellationToken); } /// <summary> @@ -212,7 +220,7 @@ namespace DotNetOpenAuth.ApplicationBlock { /// <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*/) { + 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"); } @@ -221,19 +229,22 @@ namespace DotNetOpenAuth.ApplicationBlock { { "start-index", startIndex.ToString(CultureInfo.InvariantCulture) }, { "max-results", maxResults.ToString(CultureInfo.InvariantCulture) }, }; - var request = consumer.PrepareAuthorizedRequest(GetContactsEndpoint, accessToken, extraData); + var request = await consumer.PrepareAuthorizedRequestAsync(GetContactsEndpoint, accessToken, extraData, 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 }; + 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 httpClient = consumer.Channel.HostFactories.CreateHttpClient(handler)) { + 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 static async Task PostBlogEntryAsync(ConsumerBase consumer, string accessToken, string blogUrl, string title, XElement body, CancellationToken cancellationToken) { string feedUrl; var getBlogHome = WebRequest.Create(blogUrl); using (var blogHomeResponse = getBlogHome.GetResponse()) { @@ -258,20 +269,20 @@ 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 = await consumer.PrepareAuthorizedRequestAsync(new MessageReceivingEndpoint(feedUrl, HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest), accessToken, cancellationToken); + request.Content = new StreamContent(ms); + request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/atom+xml"); + request.Method = HttpMethod.Post; + using (var httpClient = consumer.Channel.HostFactories.CreateHttpClient()) { + using (var response = await httpClient.SendAsync(request, cancellationToken)) { + if (response.StatusCode == HttpStatusCode.Created) { + // Success + } else { + // Error! + response.EnsureSuccessStatusCode(); // throw some meaningful exception. + } } } } diff --git a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs index 013e66b..9cbedb6 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/TwitterConsumer.cs @@ -11,11 +11,16 @@ namespace DotNetOpenAuth.ApplicationBlock { using System.Globalization; using System.IO; using System.Net; + using System.Net.Http; + using System.Net.Http.Headers; + 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; @@ -125,50 +130,78 @@ namespace DotNetOpenAuth.ApplicationBlock { } } - public static XDocument GetUpdates(ConsumerBase twitter, string accessToken) { - IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(GetFriendTimelineStatusEndpoint, accessToken); - return XDocument.Load(XmlReader.Create(response.GetResponseReader())); + public static async Task<XDocument> GetUpdatesAsync( + ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) { + var request = await twitter.PrepareAuthorizedRequestAsync(GetFriendTimelineStatusEndpoint, accessToken, cancellationToken); + using (var httpClient = twitter.Channel.HostFactories.CreateHttpClient()) { + using (var response = await httpClient.SendAsync(request)) { + using (var stream = await response.Content.ReadAsStreamAsync()) { + return XDocument.Load(XmlReader.Create(stream)); + } + } + } } - public static XDocument GetFavorites(ConsumerBase twitter, string accessToken) { - IncomingWebResponse response = twitter.PrepareAuthorizedRequestAndSend(GetFavoritesEndpoint, accessToken); - return XDocument.Load(XmlReader.Create(response.GetResponseReader())); + public static async Task<XDocument> GetFavorites(ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) { + var request = await twitter.PrepareAuthorizedRequestAsync(GetFavoritesEndpoint, accessToken, cancellationToken); + using (var httpClient = twitter.Channel.HostFactories.CreateHttpClient()) { + using (HttpResponseMessage response = await httpClient.SendAsync(request)) { + 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()), + public static async Task<XDocument> UpdateProfileBackgroundImageAsync(ConsumerBase twitter, string 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()); + var parts = new List<MultipartContentMember> { + new MultipartContentMember(imageAttachment , "image"), + new MultipartContentMember(new StringContent(tile.ToString().ToLowerInvariant()) , "tile"), }; - 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); + HttpRequestMessage request = await twitter.PrepareAuthorizedRequestAsync(UpdateProfileBackgroundImageEndpoint, accessToken, parts, cancellationToken); + request.Headers.ExpectContinue = false; + using (var httpClient = twitter.Channel.HostFactories.CreateHttpClient()) { + using (HttpResponseMessage response = await httpClient.SendAsync(request)) { + string responseString = await response.Content.ReadAsStringAsync(); + return XDocument.Parse(responseString); + } + } } - public static XDocument UpdateProfileImage(ConsumerBase twitter, string accessToken, string pathToImage) { + public static Task<XDocument> UpdateProfileImageAsync(ConsumerBase twitter, string 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 UpdateProfileImageAsync(twitter, 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), + public static async Task<XDocument> UpdateProfileImageAsync(ConsumerBase twitter, string accessToken, Stream image, string contentType, CancellationToken cancellationToken = default(CancellationToken)) { + var imageAttachment = new StreamContent(image); + imageAttachment.Headers.ContentType = new MediaTypeHeaderValue(contentType); + var parts = new List<MultipartContentMember> { + new MultipartContentMember(imageAttachment,"image", "twitterPhoto"), }; - HttpWebRequest request = twitter.PrepareAuthorizedRequest(UpdateProfileImageEndpoint, accessToken, parts); - IncomingWebResponse response = twitter.Channel.WebRequestHandler.GetResponse(request); - string responseString = response.GetResponseReader().ReadToEnd(); - return XDocument.Parse(responseString); + + HttpRequestMessage request = await twitter.PrepareAuthorizedRequestAsync(UpdateProfileImageEndpoint, accessToken, parts, cancellationToken); + using (var httpClient = twitter.Channel.HostFactories.CreateHttpClient()) { + using (HttpResponseMessage response = await httpClient.SendAsync(request)) { + 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 static async Task<XDocument> VerifyCredentialsAsync(ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) { + var request = await twitter.PrepareAuthorizedRequestAsync(VerifyCredentialsEndpoint, accessToken, cancellationToken); + using (var httpClient = twitter.Channel.HostFactories.CreateHttpClient()) { + using (var response = await httpClient.SendAsync(request)) { + 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 static async Task<string> GetUsername(ConsumerBase twitter, string accessToken, CancellationToken cancellationToken = default(CancellationToken)) { + XDocument xml = await VerifyCredentialsAsync(twitter, accessToken, cancellationToken); XPathNavigator nav = xml.CreateNavigator(); return nav.SelectSingleNode("/user/screen_name").Value; } @@ -183,42 +216,37 @@ namespace DotNetOpenAuth.ApplicationBlock { /// <c>return StartSignInWithTwitter().<see cref="MessagingUtilities.AsActionResult">AsActionResult()</see></c> /// to actually perform the redirect. /// </remarks> - public static OutgoingWebResponse StartSignInWithTwitter(bool forceNewLogin) { + public static async Task<HttpResponseMessage> StartSignInWithTwitterAsync(bool forceNewLogin, CancellationToken cancellationToken) { 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); + var request = await TwitterSignIn.PrepareRequestUserAuthorizationAsync(callback, null, redirectParameters, cancellationToken); + return await TwitterSignIn.Channel.PrepareResponseAsync(request, cancellationToken); } /// <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. + /// A tuple with the screen name and Twitter unique user ID if successful; otherwise <c>null</c>. /// </returns> - public static bool TryFinishSignInWithTwitter(out string screenName, out int userId) { - screenName = null; - userId = 0; - var response = TwitterSignIn.ProcessUserAuthorization(); + public static async Task<Tuple<string, int>> TryFinishSignInWithTwitterAsync(CancellationToken cancellationToken = default(CancellationToken)) { + var response = await TwitterSignIn.ProcessUserAuthorizationAsync(cancellationToken: cancellationToken); if (response == null) { - return false; + return null; } - screenName = response.ExtraData["screen_name"]; - userId = int.Parse(response.ExtraData["user_id"]); + 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); + ////OpenId.Identifier fake_claimed_id = string.Format(CultureInfo.InvariantCulture, "http://twitter.com/{0}#{1}", screenName, userId); - return true; + return Tuple.Create(screenName, userId); } } } 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..cedcbf7 100644 --- a/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs +++ b/samples/DotNetOpenAuth.ApplicationBlock/YammerConsumer.cs @@ -4,18 +4,22 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.ApplicationBlock { +namespace DotNetOpenAuth.ApplicationBlock +{ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth; 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> @@ -31,16 +35,15 @@ namespace DotNetOpenAuth.ApplicationBlock { return new DesktopConsumer(ServiceDescription, tokenManager); } - public static Uri PrepareRequestAuthorization(DesktopConsumer consumer, out string requestToken) { + public static Task<Tuple<Uri, string>> PrepareRequestAuthorizationAsync(DesktopConsumer consumer, CancellationToken cancellationToken = default(CancellationToken)) { if (consumer == null) { throw new ArgumentNullException("consumer"); } - Uri authorizationUrl = consumer.RequestUserAuthorization(null, null, out requestToken); - return authorizationUrl; + return consumer.RequestUserAuthorizationAsync(null, null, cancellationToken); } - public static AuthorizedTokenResponse CompleteAuthorization(DesktopConsumer consumer, string requestToken, string userCode) { + 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. @@ -54,7 +57,7 @@ namespace DotNetOpenAuth.ApplicationBlock { // 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); + var response = await customConsumer.ProcessUserAuthorizationAsync(requestToken, userCode, cancellationToken); return response; } } diff --git a/samples/DotNetOpenAuth.ApplicationBlock/packages.config b/samples/DotNetOpenAuth.ApplicationBlock/packages.config new file mode 100644 index 0000000..d8ffcb7 --- /dev/null +++ b/samples/DotNetOpenAuth.ApplicationBlock/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" /> +</packages>
\ No newline at end of file diff --git a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs index bd6de1b..7828b5c 100644 --- a/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs +++ b/samples/OpenIdProviderMvc/Controllers/OpenIdController.cs @@ -3,6 +3,7 @@ namespace OpenIdProviderMvc.Controllers { using System.Collections.Generic; using System.Linq; using System.Net; + using System.Threading.Tasks; using System.Web; using System.Web.Mvc; using System.Web.Mvc.Ajax; @@ -29,12 +30,13 @@ namespace OpenIdProviderMvc.Controllers { public IFormsAuthentication FormsAuth { get; private set; } [ValidateInput(false)] - public ActionResult Provider() { - IRequest request = OpenIdProvider.GetRequest(); + public async Task<ActionResult> Provider() { + IRequest request = await OpenIdProvider.GetRequestAsync(this.Request, this.Response.ClientDisconnectedToken); if (request != null) { // Some requests are automatically handled by DotNetOpenAuth. If this is one, go ahead and let it go. if (request.IsResponseReady) { - return OpenIdProvider.PrepareResponse(request).AsActionResult(); + var response = await OpenIdProvider.PrepareResponseAsync(request, this.Response.ClientDisconnectedToken); + return response.AsActionResult(); } // This is apparently one that the host (the web site itself) has to respond to. @@ -51,28 +53,28 @@ namespace OpenIdProviderMvc.Controllers { } } - return this.ProcessAuthRequest(); + return await this.ProcessAuthRequest(); } else { // No OpenID request was recognized. This may be a user that stumbled on the OP Endpoint. return this.View(); } } - public ActionResult ProcessAuthRequest() { + public async Task<ActionResult> ProcessAuthRequest() { if (ProviderEndpoint.PendingRequest == null) { return this.RedirectToAction("Index", "Home"); } // Try responding immediately if possible. - ActionResult response; - if (this.AutoRespondIfPossible(out response)) { + ActionResult response = await this.AutoRespondIfPossibleAsync(); + if (response != null) { return response; } // We can't respond immediately with a positive result. But if we still have to respond immediately... if (ProviderEndpoint.PendingRequest.Immediate) { // We can't stop to prompt the user -- we must just return a negative response. - return this.SendAssertion(); + return await this.SendAssertion(); } return this.RedirectToAction("AskUser"); @@ -83,15 +85,15 @@ namespace OpenIdProviderMvc.Controllers { /// </summary> /// <returns>The response for the user agent.</returns> [Authorize] - public ActionResult AskUser() { + public async Task<ActionResult> AskUser() { if (ProviderEndpoint.PendingRequest == null) { // Oops... precious little we can confirm without a pending OpenID request. return this.RedirectToAction("Index", "Home"); } // The user MAY have just logged in. Try again to respond automatically to the RP if appropriate. - ActionResult response; - if (this.AutoRespondIfPossible(out response)) { + ActionResult response = await this.AutoRespondIfPossibleAsync(); + if (response != null) { return response; } @@ -106,10 +108,9 @@ namespace OpenIdProviderMvc.Controllers { } [HttpPost, Authorize, ValidateAntiForgeryToken] - public ActionResult AskUserResponse(bool confirmed) { + public async Task<ActionResult> AskUserResponse(bool confirmed) { if (!ProviderEndpoint.PendingAuthenticationRequest.IsDirectedIdentity && - !this.UserControlsIdentifier(ProviderEndpoint.PendingAuthenticationRequest)) - { + !this.UserControlsIdentifier(ProviderEndpoint.PendingAuthenticationRequest)) { // The user shouldn't have gotten this far without controlling the identifier we'd send an assertion for. return new HttpStatusCodeResult((int)HttpStatusCode.BadRequest); } @@ -122,14 +123,14 @@ namespace OpenIdProviderMvc.Controllers { throw new InvalidOperationException("There's no pending authentication request!"); } - return this.SendAssertion(); + return await this.SendAssertion(); } /// <summary> /// Sends a positive or a negative assertion, based on how the pending request is currently marked. /// </summary> /// <returns>An MVC redirect result.</returns> - public ActionResult SendAssertion() { + public async Task<ActionResult> SendAssertion() { var pendingRequest = ProviderEndpoint.PendingRequest; var authReq = pendingRequest as IAuthenticationRequest; var anonReq = pendingRequest as IAnonymousRequest; @@ -188,17 +189,17 @@ namespace OpenIdProviderMvc.Controllers { } } - return OpenIdProvider.PrepareResponse(pendingRequest).AsActionResult(); + var response = await OpenIdProvider.PrepareResponseAsync(pendingRequest, this.Response.ClientDisconnectedToken); + return response.AsActionResult(); } /// <summary> /// Attempts to formulate an automatic response to the RP if the user's profile allows it. /// </summary> - /// <param name="response">Receives the ActionResult for the caller to return, or <c>null</c> if no automatic response can be made.</param> - /// <returns>A value indicating whether an automatic response is possible.</returns> - private bool AutoRespondIfPossible(out ActionResult response) { + /// <returns>The ActionResult for the caller to return, or <c>null</c> if no automatic response can be made.</returns> + private async Task<ActionResult> AutoRespondIfPossibleAsync() { // If the odds are good we can respond to this one immediately (without prompting the user)... - if (ProviderEndpoint.PendingRequest.IsReturnUrlDiscoverable(OpenIdProvider.Channel.WebRequestHandler) == RelyingPartyDiscoveryResult.Success + if (await ProviderEndpoint.PendingRequest.IsReturnUrlDiscoverableAsync(OpenIdProvider.Channel.HostFactories, this.Response.ClientDisconnectedToken) == RelyingPartyDiscoveryResult.Success && User.Identity.IsAuthenticated && this.HasUserAuthorizedAutoLogin(ProviderEndpoint.PendingRequest)) { // Is this is an identity authentication request? (as opposed to an anonymous request)... @@ -207,21 +208,18 @@ namespace OpenIdProviderMvc.Controllers { if (ProviderEndpoint.PendingAuthenticationRequest.IsDirectedIdentity || this.UserControlsIdentifier(ProviderEndpoint.PendingAuthenticationRequest)) { ProviderEndpoint.PendingAuthenticationRequest.IsAuthenticated = true; - response = this.SendAssertion(); - return true; + return await this.SendAssertion(); } } // If this is an anonymous request, we can respond to that too. if (ProviderEndpoint.PendingAnonymousRequest != null) { ProviderEndpoint.PendingAnonymousRequest.IsApproved = true; - response = this.SendAssertion(); - return true; + return await this.SendAssertion(); } } - response = null; - return false; + return null; } /// <summary> diff --git a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj index 45686cd..4c57961 100644 --- a/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj +++ b/samples/OpenIdProviderMvc/OpenIdProviderMvc.csproj @@ -9,6 +9,7 @@ <IISExpressAnonymousAuthentication /> <IISExpressWindowsAuthentication /> <IISExpressUseClassicPipelineMode /> + <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\src\</SolutionDir> </PropertyGroup> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> @@ -49,6 +50,8 @@ <Reference Include="System.Data" /> <Reference Include="System.Data.DataSetExtensions" /> <Reference Include="System.Drawing" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Net.Http.WebRequest" /> <Reference Include="System.Web.DynamicData" /> <Reference Include="System.Web.Entity" /> <Reference Include="System.ComponentModel.DataAnnotations"> @@ -143,6 +146,9 @@ <Name>DotNetOpenAuth.ApplicationBlock</Name> </ProjectReference> </ItemGroup> + <ItemGroup> + <Content Include="packages.config" /> + </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" /> <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" /> @@ -172,4 +178,5 @@ </VisualStudio> </ProjectExtensions> <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/OpenIdProviderMvc/packages.config b/samples/OpenIdProviderMvc/packages.config new file mode 100644 index 0000000..d8ffcb7 --- /dev/null +++ b/samples/OpenIdProviderMvc/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" /> +</packages>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj index ccba40f..353ecca 100644 --- a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj +++ b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj @@ -29,6 +29,7 @@ <Compile Include="Messaging\Bindings\ICryptoKeyStore.cs" /> <Compile Include="Messaging\Bindings\MemoryCryptoKeyStore.cs" /> <Compile Include="Messaging\BinaryDataBagFormatter.cs" /> + <Compile Include="Messaging\MultipartContentMember.cs" /> <Compile Include="Messaging\DataBagFormatterBase.cs" /> <Compile Include="Messaging\HmacAlgorithms.cs" /> <Compile Include="Messaging\HttpRequestHeaders.cs" /> diff --git a/src/DotNetOpenAuth.Core/Messaging/Channel.cs b/src/DotNetOpenAuth.Core/Messaging/Channel.cs index 7eadecd..36c91c7 100644 --- a/src/DotNetOpenAuth.Core/Messaging/Channel.cs +++ b/src/DotNetOpenAuth.Core/Messaging/Channel.cs @@ -554,6 +554,28 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Adds just the binary data part of a message to a multipart form content object. + /// </summary> + /// <param name="requestMessageWithBinaryData">The request message with binary data.</param> + /// <returns>The initialized HttpContent.</returns> + protected static MultipartFormDataContent InitializeMultipartFormDataContent(IMessageWithBinaryData requestMessageWithBinaryData) { + Requires.NotNull(requestMessageWithBinaryData, "requestMessageWithBinaryData"); + + var content = new MultipartFormDataContent(); + foreach (var part in requestMessageWithBinaryData.BinaryData) { + if (string.IsNullOrEmpty(part.Name)) { + content.Add(part.Content); + } else if (string.IsNullOrEmpty(part.FileName)) { + content.Add(part.Content, part.Name); + } else { + content.Add(part.Content, part.Name, part.FileName); + } + } + + return content; + } + + /// <summary> /// Checks whether a given HTTP method is expected to include an entity body in its request. /// </summary> /// <param name="httpMethod">The HTTP method.</param> @@ -1041,10 +1063,7 @@ namespace DotNetOpenAuth.Messaging { var requestMessageWithBinaryData = requestMessage as IMessageWithBinaryData; if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) { - var content = new MultipartFormDataContent(); - foreach (var part in requestMessageWithBinaryData.BinaryData) { - content.Add(part.Value, part.Key); - } + var content = InitializeMultipartFormDataContent(requestMessageWithBinaryData); // When sending multi-part, all data gets send as multi-part -- even the non-binary data. foreach (var field in fields) { diff --git a/src/DotNetOpenAuth.Core/Messaging/IMessageWithBinaryData.cs b/src/DotNetOpenAuth.Core/Messaging/IMessageWithBinaryData.cs index bcf4d8d..84a7760 100644 --- a/src/DotNetOpenAuth.Core/Messaging/IMessageWithBinaryData.cs +++ b/src/DotNetOpenAuth.Core/Messaging/IMessageWithBinaryData.cs @@ -20,7 +20,7 @@ namespace DotNetOpenAuth.Messaging { /// Gets the parts of the message that carry binary data. /// </summary> /// <value>A list of parts. Never null.</value> - IDictionary<string, HttpContent> BinaryData { get; } + IList<MultipartContentMember> BinaryData { get; } /// <summary> /// Gets a value indicating whether this message should be sent as multi-part POST. diff --git a/src/DotNetOpenAuth.Core/Messaging/MultipartContentMember.cs b/src/DotNetOpenAuth.Core/Messaging/MultipartContentMember.cs new file mode 100644 index 0000000..2aa33ae --- /dev/null +++ b/src/DotNetOpenAuth.Core/Messaging/MultipartContentMember.cs @@ -0,0 +1,23 @@ +namespace DotNetOpenAuth.Messaging { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + using System.Text; + using System.Threading.Tasks; + + public struct MultipartContentMember { + public MultipartContentMember(HttpContent content, string name = null, string fileName = null) + : this() { + this.Content = content; + this.Name = name; + this.FileName = fileName; + } + + public HttpContent Content { get; set; } + + public string Name { get; set; } + + public string FileName { get; set; } + } +} diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs index b34d4f8..80a1381 100644 --- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs +++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs @@ -148,15 +148,13 @@ namespace DotNetOpenAuth.OAuth { /// <param name="accessToken">The access token that permits access to the protected resource.</param> /// <param name="binaryData">Extra parameters to include in the message. Must not be null, but may be empty.</param> /// <returns>The initialized WebRequest object.</returns> - public Task<HttpRequestMessage> PrepareAuthorizedRequestAsync(MessageReceivingEndpoint endpoint, string accessToken, IDictionary<string, HttpContent> binaryData, CancellationToken cancellationToken = default(CancellationToken)) { + public Task<HttpRequestMessage> PrepareAuthorizedRequestAsync(MessageReceivingEndpoint endpoint, string accessToken, IEnumerable<MultipartContentMember> binaryData, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(endpoint, "endpoint"); Requires.NotNullOrEmpty(accessToken, "accessToken"); Requires.NotNull(binaryData, "binaryData"); AccessProtectedResourceRequest message = this.CreateAuthorizingMessage(endpoint, accessToken); - foreach (var part in binaryData) { - message.BinaryData.Add(part); - } + message.BinaryData.AddRange(binaryData); return this.OAuthChannel.InitializeRequestAsync(message, cancellationToken); } diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/DesktopConsumer.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/DesktopConsumer.cs index 4627d23..cd16c7e 100644 --- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/DesktopConsumer.cs +++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/DesktopConsumer.cs @@ -38,7 +38,7 @@ namespace DotNetOpenAuth.OAuth { /// <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="requestToken">The request token that must be exchanged for an access token after the user has provided authorization.</param> - /// <returns>The URL to open a browser window to allow the user to provide authorization.</returns> + /// <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); diff --git a/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs index f3c5897..e5dd258 100644 --- a/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -309,11 +309,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { if (requestMessageWithBinaryData != null && requestMessageWithBinaryData.SendAsMultipart) { // Include the binary data in the multipart entity, and any standard text extra message data. // The standard declared message parts are included in the authorization header. - var content = new MultipartFormDataContent(); + var content = InitializeMultipartFormDataContent(requestMessageWithBinaryData); httpRequest.Content = content; - foreach (var contentPart in requestMessageWithBinaryData.BinaryData) { - content.Add(contentPart.Value, contentPart.Key); - } foreach (var extraData in requestMessage.ExtraData) { content.Add(new StringContent(extraData.Value), extraData.Key); diff --git a/src/DotNetOpenAuth.OAuth/OAuth/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth.OAuth/OAuth/Messages/AccessProtectedResourceRequest.cs index 77b1733..9b698b9 100644 --- a/src/DotNetOpenAuth.OAuth/OAuth/Messages/AccessProtectedResourceRequest.cs +++ b/src/DotNetOpenAuth.OAuth/OAuth/Messages/AccessProtectedResourceRequest.cs @@ -20,7 +20,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// <summary> /// A store for the binary data that is carried in the message. /// </summary> - private Dictionary<string, HttpContent> binaryData = new Dictionary<string, HttpContent>(); + private List<MultipartContentMember> binaryData = new List<MultipartContentMember>(); /// <summary> /// Initializes a new instance of the <see cref="AccessProtectedResourceRequest"/> class. @@ -58,7 +58,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Gets the parts of the message that carry binary data. /// </summary> /// <value>A list of parts. Never null.</value> - public IDictionary<string, HttpContent> BinaryData { + public IList<MultipartContentMember> BinaryData { get { return this.binaryData; } } diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Provider/IHostProcessedRequest.cs b/src/DotNetOpenAuth.OpenId/OpenId/Provider/IHostProcessedRequest.cs index 8f7767b..0e40f0f 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/Provider/IHostProcessedRequest.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/Provider/IHostProcessedRequest.cs @@ -56,6 +56,6 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <para>Return URL verification is only attempted if this method is called.</para> /// <para>See OpenID Authentication 2.0 spec section 9.2.1.</para> /// </remarks> - Task<RelyingPartyDiscoveryResult> IsReturnUrlDiscoverableAsync(IHostFactories hostFactories, CancellationToken cancellationToken); + Task<RelyingPartyDiscoveryResult> IsReturnUrlDiscoverableAsync(IHostFactories hostFactories = null, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/src/packages/repositories.config b/src/packages/repositories.config index 1f57a14..6bb73e0 100644 --- a/src/packages/repositories.config +++ b/src/packages/repositories.config @@ -3,6 +3,7 @@ <repository path="..\..\projecttemplates\MvcRelyingParty\packages.config" /> <repository path="..\..\projecttemplates\RelyingPartyLogic\packages.config" /> <repository path="..\..\projecttemplates\WebFormsRelyingParty\packages.config" /> + <repository path="..\..\samples\DotNetOpenAuth.ApplicationBlock\packages.config" /> <repository path="..\..\samples\OAuthAuthorizationServer\packages.config" /> <repository path="..\..\samples\OAuthClient\packages.config" /> <repository path="..\..\samples\OAuthConsumer\packages.config" /> @@ -10,6 +11,7 @@ <repository path="..\..\samples\OAuthResourceServer\packages.config" /> <repository path="..\..\samples\OAuthServiceProvider\packages.config" /> <repository path="..\..\samples\OpenIdOfflineProvider\packages.config" /> + <repository path="..\..\samples\OpenIdProviderMvc\packages.config" /> <repository path="..\..\samples\OpenIdProviderWebForms\packages.config" /> <repository path="..\..\samples\OpenIdRelyingPartyMvc\packages.config" /> <repository path="..\..\samples\OpenIdRelyingPartyWebForms\packages.config" /> |