diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2013-03-04 13:54:33 -0800 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2013-03-04 13:54:33 -0800 |
commit | 36bbbea5002c889558a67c380e46dff668251b25 (patch) | |
tree | 87721c7f13cfd27b75f7132b71549688b55eb14a /src | |
parent | fd85211bfc50805d740392bfc6770df7c6f4c7d0 (diff) | |
download | DotNetOpenAuth-36bbbea5002c889558a67c380e46dff668251b25.zip DotNetOpenAuth-36bbbea5002c889558a67c380e46dff668251b25.tar.gz DotNetOpenAuth-36bbbea5002c889558a67c380e46dff668251b25.tar.bz2 |
Switched Channel to receiving messages via HttpRequestMessage as well.
Diffstat (limited to 'src')
26 files changed, 328 insertions, 311 deletions
diff --git a/src/DotNetOpenAuth.Core/Messaging/Channel.cs b/src/DotNetOpenAuth.Core/Messaging/Channel.cs index e36bb94..8564e38 100644 --- a/src/DotNetOpenAuth.Core/Messaging/Channel.cs +++ b/src/DotNetOpenAuth.Core/Messaging/Channel.cs @@ -325,21 +325,6 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Gets the protocol message embedded in the given HTTP request, if present. /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns> - /// The deserialized message, if one is found. Null otherwise. - /// </returns> - /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current" /> is null.</exception> - /// <remarks> - /// Requires an HttpContext.Current context. - /// </remarks> - public Task<IDirectedProtocolMessage> ReadFromRequestAsync(CancellationToken cancellationToken) { - return this.ReadFromRequestAsync(this.GetRequestFromContext(), cancellationToken); - } - - /// <summary> - /// Gets the protocol message embedded in the given HTTP request, if present. - /// </summary> /// <typeparam name="TRequest">The expected type of the message to be received.</typeparam> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="httpRequest">The request to search for an embedded message.</param> @@ -348,9 +333,9 @@ namespace DotNetOpenAuth.Messaging { /// </returns> /// <exception cref="InvalidOperationException">Thrown when <see cref="HttpContext.Current" /> is null.</exception> /// <exception cref="ProtocolException">Thrown when a request message of an unexpected type is received.</exception> - public async Task<TRequest> TryReadFromRequestAsync<TRequest>(CancellationToken cancellationToken, HttpRequestBase httpRequest = null) + public async Task<TRequest> TryReadFromRequestAsync<TRequest>(HttpRequestMessage httpRequest, CancellationToken cancellationToken) where TRequest : class, IProtocolMessage { - httpRequest = httpRequest ?? this.GetRequestFromContext(); + Requires.NotNull(httpRequest, "httpRequest"); IProtocolMessage untypedRequest = await this.ReadFromRequestAsync(httpRequest, cancellationToken); if (untypedRequest == null) { @@ -373,10 +358,11 @@ namespace DotNetOpenAuth.Messaging { /// </returns> /// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception> [SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")] - public async Task<TRequest> ReadFromRequestAsync<TRequest>(CancellationToken cancellationToken, HttpRequestBase httpRequest = null) + public async Task<TRequest> ReadFromRequestAsync<TRequest>(HttpRequestMessage httpRequest, CancellationToken cancellationToken) where TRequest : class, IProtocolMessage { - httpRequest = httpRequest ?? this.GetRequestFromContext(); - TRequest request = await this.TryReadFromRequestAsync<TRequest>(cancellationToken, httpRequest); + Requires.NotNull(httpRequest, "httpRequest"); + + TRequest request = await this.TryReadFromRequestAsync<TRequest>(httpRequest, cancellationToken); ErrorUtilities.VerifyProtocol(request != null, MessagingStrings.ExpectedMessageNotReceived, typeof(TRequest)); return request; } @@ -389,20 +375,20 @@ namespace DotNetOpenAuth.Messaging { /// <returns> /// The deserialized message, if one is found. Null otherwise. /// </returns> - public async Task<IDirectedProtocolMessage> ReadFromRequestAsync(HttpRequestBase httpRequest, CancellationToken cancellationToken) { + public async Task<IDirectedProtocolMessage> ReadFromRequestAsync(HttpRequestMessage httpRequest, CancellationToken cancellationToken) { Requires.NotNull(httpRequest, "httpRequest"); - if (Logger.Channel.IsInfoEnabled && httpRequest.GetPublicFacingUrl() != null) { - Logger.Channel.InfoFormat("Scanning incoming request for messages: {0}", httpRequest.GetPublicFacingUrl().AbsoluteUri); + if (Logger.Channel.IsInfoEnabled && httpRequest.RequestUri != null) { + Logger.Channel.InfoFormat("Scanning incoming request for messages: {0}", httpRequest.RequestUri.AbsoluteUri); } - IDirectedProtocolMessage requestMessage = this.ReadFromRequestCore(httpRequest, cancellationToken); + IDirectedProtocolMessage requestMessage = await this.ReadFromRequestCoreAsync(httpRequest, cancellationToken); if (requestMessage != null) { Logger.Channel.DebugFormat("Incoming request received: {0}", requestMessage.GetType().Name); var directRequest = requestMessage as IHttpDirectRequest; if (directRequest != null) { - foreach (string header in httpRequest.Headers) { - directRequest.Headers[header] = httpRequest.Headers[header]; + foreach (var header in httpRequest.Headers) { + directRequest.Headers.Add(header.Key, header.Value); } } @@ -539,6 +525,21 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Parses the URL encoded form content. + /// </summary> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>A sequence of key=value pairs found in the request's entity; or an empty sequence if none are found.</returns> + protected internal static async Task<IEnumerable<KeyValuePair<string, string>>> ParseUrlEncodedFormContentAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + if (request.Content != null && request.Content.Headers.ContentType != null + && request.Content.Headers.ContentType.MediaType.Equals(HttpFormUrlEncoded)) { + return HttpUtility.ParseQueryString(await request.Content.ReadAsStringAsync()).AsKeyValuePairs(); + } + + return Enumerable.Empty<KeyValuePair<string, string>>(); + } + + /// <summary> /// Gets the HTTP context for the current HTTP request. /// </summary> /// <returns>An HttpContextBase instance.</returns> @@ -671,8 +672,8 @@ namespace DotNetOpenAuth.Messaging { var webRequest = this.CreateHttpRequest(request); var directRequest = request as IHttpDirectRequest; if (directRequest != null) { - foreach (string header in directRequest.Headers) { - webRequest.Headers.Add(header, directRequest.Headers[header]); + foreach (var header in directRequest.Headers) { + webRequest.Headers.Add(header.Key, header.Value); } } @@ -741,16 +742,18 @@ namespace DotNetOpenAuth.Messaging { /// <param name="request">The request to search for an embedded message.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The deserialized message, if one is found. Null otherwise.</returns> - protected virtual IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request, CancellationToken cancellationToken) { + protected virtual async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Requires.NotNull(request, "request"); - Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.GetPublicFacingUrl().AbsoluteUri); + Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.Method, request.RequestUri.AbsoluteUri); + + var fields = new Dictionary<string, string>(); // Search Form data first, and if nothing is there search the QueryString - Assumes.True(request.Form != null && request.GetQueryStringBeforeRewriting() != null); - var fields = request.Form.ToDictionary(); - if (fields.Count == 0 && request.HttpMethod != "POST") { // OpenID 2.0 section 4.1.2 - fields = request.GetQueryStringBeforeRewriting().ToDictionary(); + fields.AddRange(await ParseUrlEncodedFormContentAsync(request, cancellationToken)); + + if (fields.Count == 0 && request.Method.Method != "POST") { // OpenID 2.0 section 4.1.2 + fields.AddRange(HttpUtility.ParseQueryString(request.RequestUri.Query).AsKeyValuePairs()); } MessageReceivingEndpoint recipient; diff --git a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs index 7b26869..226102d 100644 --- a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs +++ b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs @@ -15,6 +15,6 @@ namespace DotNetOpenAuth.Messaging { /// Gets the HTTP headers of the request. /// </summary> /// <value>May be an empty collection, but must not be <c>null</c>.</value> - WebHeaderCollection Headers { get; } + System.Net.Http.Headers.HttpRequestHeaders Headers { get; } } } diff --git a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs index 38abe4a..25a56f4 100644 --- a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs @@ -360,7 +360,15 @@ namespace DotNetOpenAuth.Messaging { // HttpRequest.Url gives us the internal URL in a cloud environment, // So we use a variable that (at least from what I can tell) gives us // the public URL: - if (serverVariables["HTTP_HOST"] != null) { + string httpHost; + try { + httpHost = serverVariables["HTTP_HOST"]; + } catch (NullReferenceException) { + // The VS dev web server can throw this. :( + httpHost = null; + } + + if (httpHost != null) { ErrorUtilities.VerifySupported(request.Url.Scheme == Uri.UriSchemeHttps || request.Url.Scheme == Uri.UriSchemeHttp, "Only HTTP and HTTPS are supported protocols."); string scheme = serverVariables["HTTP_X_FORWARDED_PROTO"] ?? request.Url.Scheme; Uri hostAndPort = new Uri(scheme + Uri.SchemeDelimiter + serverVariables["HTTP_HOST"]); @@ -412,21 +420,45 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Wraps an instance of <see cref="HttpRequestBase"/> as an <see cref="HttpRequestMessage"/> instance. + /// </summary> + /// <param name="request">The request.</param> + /// <returns>An instance of <see cref="HttpRequestMessage"/></returns> + public static HttpRequestMessage AsHttpRequestMessage(this HttpRequestBase request) { + Requires.NotNull(request, "request"); + + Uri publicFacingUrl = request.GetPublicFacingUrl(); + var httpRequest = new HttpRequestMessage(new HttpMethod(request.HttpMethod), publicFacingUrl); + + if (request.Form != null) { + // Avoid a request message that will try to read the request stream twice for already parsed data. + httpRequest.Content = new FormUrlEncodedContent(request.Form.AsKeyValuePairs()); + } else if (request.InputStream != null) { + httpRequest.Content = new StreamContent(request.InputStream); + } + + request.ApplyHeaders(httpRequest); + + return httpRequest; + } + + /// <summary> /// Sends a response message to the HTTP client. /// </summary> /// <param name="response">The response message.</param> - /// <param name="responseContext">The response context to send the response with.</param> + /// <param name="context">The HTTP context to send the response with.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// A task that completes with the asynchronous operation. /// </returns> - public static async Task SendAsync(this HttpResponseMessage response, HttpResponseBase responseContext = null, CancellationToken cancellationToken = default(CancellationToken)) { + public static async Task SendAsync(this HttpResponseMessage response, HttpContextBase context = null, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(response, "response"); - if (responseContext == null) { + if (context == null) { ErrorUtilities.VerifyHttpContext(); - responseContext = new HttpResponseWrapper(HttpContext.Current.Response); + context = new HttpContextWrapper(HttpContext.Current); } + var responseContext = context.Response; responseContext.StatusCode = (int)response.StatusCode; responseContext.StatusDescription = response.ReasonPhrase; foreach (var header in response.Headers) { @@ -438,16 +470,11 @@ namespace DotNetOpenAuth.Messaging { if (response.Content != null) { await response.Content.CopyToAsync(responseContext.OutputStream).ConfigureAwait(false); } - } - /// <summary> - /// Sends a response message to the HTTP client. - /// </summary> - /// <param name="response">The response message.</param> - /// <param name="responseContext">The response context to send the response with.</param> - /// <param name="cancellationToken">The cancellation token.</param> - public static void Send(this HttpResponseMessage response, HttpResponseBase responseContext = null, CancellationToken cancellationToken = default(CancellationToken)) { - SendAsync(response, responseContext, cancellationToken).GetAwaiter().GetResult(); + // This prevents a hosting ASP.NET web forms page from rendering HTML after a control + // has taken control of the response stream. + context.ApplicationInstance.CompleteRequest(); + context.Response.End(); } /// <summary> @@ -587,24 +614,15 @@ namespace DotNetOpenAuth.Messaging { /// <param name="scheme">The scheme. Must not be null or empty.</param> /// <param name="authorizationHeader">The authorization header. May be null or empty.</param> /// <returns>A sequence of key=value pairs discovered in the header. Never null, but may be empty.</returns> - internal static IEnumerable<KeyValuePair<string, string>> ParseAuthorizationHeader(string scheme, string authorizationHeader) { + internal static IEnumerable<KeyValuePair<string, string>> ParseAuthorizationHeader(string scheme, AuthenticationHeaderValue authorizationHeader) { Requires.NotNullOrEmpty(scheme, "scheme"); - string prefix = scheme + " "; - if (authorizationHeader != null) { - // The authorization header may have multiple sections. Look for the appropriate one. - string[] authorizationSections = new string[] { authorizationHeader }; // what is the right delimiter, if any? - foreach (string authorization in authorizationSections) { - string trimmedAuth = authorization.Trim(); - if (trimmedAuth.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) { // RFC 2617 says this is case INsensitive - string data = trimmedAuth.Substring(prefix.Length); - return from element in data.Split(CommaArray) - let parts = element.Trim().Split(EqualsArray, 2) - let key = Uri.UnescapeDataString(parts[0]) - let value = Uri.UnescapeDataString(parts[1].Trim(QuoteArray)) - select new KeyValuePair<string, string>(key, value); - } - } + if (authorizationHeader != null && authorizationHeader.Scheme.Equals(scheme, StringComparison.OrdinalIgnoreCase)) { // RFC 2617 says this is case INsensitive + return from element in authorizationHeader.Parameter.Split(CommaArray) + let parts = element.Trim().Split(EqualsArray, 2) + let key = Uri.UnescapeDataString(parts[0]) + let value = Uri.UnescapeDataString(parts[1].Trim(QuoteArray)) + select new KeyValuePair<string, string>(key, value); } return Enumerable.Empty<KeyValuePair<string, string>>(); @@ -1184,80 +1202,45 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Clones an <see cref="HttpWebRequest"/> in order to send it again. - /// </summary> - /// <param name="request">The request to clone.</param> - /// <returns>The newly created instance.</returns> - internal static HttpWebRequest Clone(this HttpWebRequest request) { - Requires.NotNull(request, "request"); - Requires.That(request.RequestUri != null, "request", "request.RequestUri cannot be null."); - return Clone(request, request.RequestUri); - } - - /// <summary> - /// Clones an <see cref="HttpWebRequest"/> in order to send it again. + /// Clones an <see cref="HttpWebRequest" /> in order to send it again. /// </summary> - /// <param name="request">The request to clone.</param> - /// <param name="newRequestUri">The new recipient of the request.</param> - /// <returns>The newly created instance.</returns> - internal static HttpWebRequest Clone(this HttpWebRequest request, Uri newRequestUri) { + /// <param name="request">The request with headers to clone.</param> + /// <param name="message">The message to set headers on.</param> + internal static void ApplyHeaders(this HttpRequestBase request, HttpRequestMessage message) { Requires.NotNull(request, "request"); - Requires.NotNull(newRequestUri, "newRequestUri"); - - var newRequest = (HttpWebRequest)WebRequest.Create(newRequestUri); + Requires.NotNull(message, "message"); - // First copy headers. Only set those that are explicitly set on the original request, + // First copy headers. Only set those that are explicitly set on the original requestHeaders, // because some properties (like IfModifiedSince) activate special behavior when set, // even when set to their "original" values. foreach (string headerName in request.Headers) { switch (headerName) { - case "Accept": newRequest.Accept = request.Accept; break; + case "Accept": message.Headers.Accept.AddRange(request.AcceptTypes.Select(at => new MediaTypeWithQualityHeaderValue(at))); break; case "Connection": break; // Keep-Alive controls this - case "Content-Length": newRequest.ContentLength = request.ContentLength; break; - case "Content-Type": newRequest.ContentType = request.ContentType; break; - case "Expect": newRequest.Expect = request.Expect; break; + case "Content-Length": message.Content.Headers.ContentLength = request.ContentLength; break; + case "Content-Type": message.Content.Headers.ContentType = new MediaTypeHeaderValue(request.ContentType); break; + case "Expect": message.Headers.Expect.Add(new NameValueWithParametersHeaderValue(request.Headers[headerName])); break; case "Host": break; // implicitly copied as part of the RequestUri - case "If-Modified-Since": newRequest.IfModifiedSince = request.IfModifiedSince; break; - case "Keep-Alive": newRequest.KeepAlive = request.KeepAlive; break; - case "Proxy-Connection": break; // no property equivalent? - case "Referer": newRequest.Referer = request.Referer; break; - case "Transfer-Encoding": newRequest.TransferEncoding = request.TransferEncoding; break; - case "User-Agent": newRequest.UserAgent = request.UserAgent; break; - default: newRequest.Headers[headerName] = request.Headers[headerName]; break; - } - } - - newRequest.AllowAutoRedirect = request.AllowAutoRedirect; - newRequest.AllowWriteStreamBuffering = request.AllowWriteStreamBuffering; - newRequest.AuthenticationLevel = request.AuthenticationLevel; - newRequest.AutomaticDecompression = request.AutomaticDecompression; - newRequest.CachePolicy = request.CachePolicy; - newRequest.ClientCertificates = request.ClientCertificates; - newRequest.ConnectionGroupName = request.ConnectionGroupName; - newRequest.ContinueDelegate = request.ContinueDelegate; - newRequest.CookieContainer = request.CookieContainer; - newRequest.Credentials = request.Credentials; - newRequest.ImpersonationLevel = request.ImpersonationLevel; - newRequest.MaximumAutomaticRedirections = request.MaximumAutomaticRedirections; - newRequest.MaximumResponseHeadersLength = request.MaximumResponseHeadersLength; - newRequest.MediaType = request.MediaType; - newRequest.Method = request.Method; - newRequest.Pipelined = request.Pipelined; - newRequest.PreAuthenticate = request.PreAuthenticate; - newRequest.ProtocolVersion = request.ProtocolVersion; - newRequest.ReadWriteTimeout = request.ReadWriteTimeout; - newRequest.SendChunked = request.SendChunked; - newRequest.Timeout = request.Timeout; - newRequest.UseDefaultCredentials = request.UseDefaultCredentials; + case "If-Modified-Since": break; // we don't care + case "Keep-Alive": message.Headers.Connection.Add(request.Headers[headerName]); break; + case "Proxy-Connection": break; // we don't care + case "Referer": message.Headers.Referrer = request.UrlReferrer; break; + case "Transfer-Encoding": message.Headers.TransferEncoding.Add(new TransferCodingHeaderValue(request.Headers[headerName])); break; + case "User-Agent": message.Headers.UserAgent.Add(new ProductInfoHeaderValue(request.UserAgent)); break; + default: + HttpHeaders headers = headerName.StartsWith("Content-", StringComparison.Ordinal) && message.Content != null + ? (HttpHeaders)message.Content.Headers + : message.Headers; + var values = request.Headers.GetValues(headerName); + if (values.Length == 1) { + headers.Add(headerName, values[0]); + } else { + headers.Add(headerName, values); + } - try { - newRequest.Proxy = request.Proxy; - newRequest.UnsafeAuthenticatedConnectionSharing = request.UnsafeAuthenticatedConnectionSharing; - } catch (SecurityException) { - Logger.Messaging.Warn("Unable to clone some HttpWebRequest properties due to partial trust."); + break; + } } - - return newRequest; } /// <summary> @@ -1508,8 +1491,8 @@ namespace DotNetOpenAuth.Messaging { /// <param name="request">The request to get recipient information from.</param> /// <returns>The recipient.</returns> /// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception> - internal static MessageReceivingEndpoint GetRecipient(this HttpRequestBase request) { - return new MessageReceivingEndpoint(request.GetPublicFacingUrl(), GetHttpDeliveryMethod(request.HttpMethod)); + internal static MessageReceivingEndpoint GetRecipient(this HttpRequestMessage request) { + return new MessageReceivingEndpoint(request.RequestUri, GetHttpDeliveryMethod(request.Method.Method)); } /// <summary> @@ -2111,7 +2094,8 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="context">The context in which the result is executed. The context information includes the controller, HTTP content, request context, and route data.</param> public override void ExecuteResult(ControllerContext context) { - this.response.Send(context.HttpContext.Response); + // TODO: fix this to be asynchronous. + this.response.SendAsync(context.HttpContext).GetAwaiter().GetResult(); } } } diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs index 5e6cfb3..3f677b8 100644 --- a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs +++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuth/ServiceProvider.cs @@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OAuth { using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; + using System.Net.Http; using System.Security.Principal; using System.ServiceModel.Channels; using System.Threading; @@ -214,7 +215,23 @@ namespace DotNetOpenAuth.OAuth { /// Requires HttpContext.Current. /// </remarks> public Task<IDirectedProtocolMessage> ReadRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - return this.Channel.ReadFromRequestAsync(request ?? this.Channel.GetRequestFromContext(), cancellationToken); + return this.ReadRequestAsync((request ?? this.Channel.GetRequestFromContext()).AsHttpRequestMessage(), cancellationToken); + } + + /// <summary> + /// Reads any incoming OAuth message. + /// </summary> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The deserialized message. + /// </returns> + /// <remarks> + /// Requires HttpContext.Current. + /// </remarks> + public Task<IDirectedProtocolMessage> ReadRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); + return this.Channel.ReadFromRequestAsync(request, cancellationToken); } /// <summary> @@ -226,8 +243,24 @@ namespace DotNetOpenAuth.OAuth { /// The incoming request, or null if no OAuth message was attached. /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> - public async Task<UnauthorizedTokenRequest> ReadTokenRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - var message = await this.Channel.TryReadFromRequestAsync<UnauthorizedTokenRequest>(cancellationToken, request); + public Task<UnauthorizedTokenRequest> ReadTokenRequestAsync( + HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + return this.ReadTokenRequestAsync((request ?? this.channel.GetRequestFromContext()).AsHttpRequestMessage(), cancellationToken); + } + + /// <summary> + /// Reads a request for an unauthorized token from the incoming HTTP request. + /// </summary> + /// <param name="request">The HTTP request to read from.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns> + /// The incoming request, or null if no OAuth message was attached. + /// </returns> + /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> + public async Task<UnauthorizedTokenRequest> ReadTokenRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); + + var message = await this.Channel.TryReadFromRequestAsync<UnauthorizedTokenRequest>(request, cancellationToken); if (message != null) { ErrorUtilities.VerifyProtocol(message.Version >= Protocol.Lookup(this.SecuritySettings.MinimumRequiredOAuthVersion).Version, OAuthStrings.MinimumConsumerVersionRequirementNotMet, this.SecuritySettings.MinimumRequiredOAuthVersion, message.Version); } @@ -261,8 +294,9 @@ namespace DotNetOpenAuth.OAuth { /// The incoming request, or null if no OAuth message was attached. /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> - public Task<UserAuthorizationRequest> ReadAuthorizationRequestAsync(HttpRequestBase request, CancellationToken cancellationToken = default(CancellationToken)) { - return this.Channel.TryReadFromRequestAsync<UserAuthorizationRequest>(cancellationToken, request); + public Task<UserAuthorizationRequest> ReadAuthorizationRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + request = request ?? this.channel.GetRequestFromContext(); + return this.Channel.TryReadFromRequestAsync<UserAuthorizationRequest>(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> @@ -337,7 +371,8 @@ namespace DotNetOpenAuth.OAuth { /// </returns> /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> public Task<AuthorizedTokenRequest> ReadAccessTokenRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - return this.Channel.TryReadFromRequestAsync<AuthorizedTokenRequest>(cancellationToken, request); + request = request ?? this.Channel.GetRequestFromContext(); + return this.Channel.TryReadFromRequestAsync<AuthorizedTokenRequest>(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> @@ -391,9 +426,8 @@ namespace DotNetOpenAuth.OAuth { /// </remarks> /// <exception cref="ProtocolException">Thrown if an unexpected message is attached to the request.</exception> public async Task<AccessProtectedResourceRequest> ReadProtectedResourceAuthorizationAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - Requires.NotNull(request, "request"); - - var accessMessage = await this.Channel.TryReadFromRequestAsync<AccessProtectedResourceRequest>(cancellationToken, request); + request = request ?? this.Channel.GetRequestFromContext(); + var accessMessage = await this.Channel.TryReadFromRequestAsync<AccessProtectedResourceRequest>(request.AsHttpRequestMessage(), cancellationToken); if (accessMessage != null) { if (this.TokenManager.GetTokenType(accessMessage.AccessToken) != TokenType.AccessToken) { throw new ProtocolException( diff --git a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuthReporting.cs b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuthReporting.cs index 8171e1a..5b00c1a 100644 --- a/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuthReporting.cs +++ b/src/DotNetOpenAuth.OAuth.ServiceProvider/OAuthReporting.cs @@ -45,7 +45,7 @@ namespace DotNetOpenAuth { builder.Append(nonceStore.GetType().Name); } builder.Append(" "); - builder.Append(service.UserAuthorizationEndpoint.Location); + builder.Append(service.UserAuthorizationEndpoint != null ? service.UserAuthorizationEndpoint.Location.AbsoluteUri : string.Empty); Reporting.ObservedFeatures.Add(builder.ToString()); Reporting.Touch(); } diff --git a/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs index 708c9c9..0925402 100644 --- a/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -118,28 +118,23 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <param name="request">The HTTP request to search.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The deserialized message, if one is found. Null otherwise.</returns> - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request, CancellationToken cancellationToken) { + protected override async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // First search the Authorization header. - string authorization = request.Headers[HttpRequestHeaders.Authorization]; + var authorization = request.Headers.Authorization; var fields = MessagingUtilities.ParseAuthorizationHeader(Protocol.AuthorizationHeaderScheme, authorization).ToDictionary(); fields.Remove("realm"); // ignore the realm parameter, since we don't use it, and it must be omitted from signature base string. // Scrape the entity - if (!string.IsNullOrEmpty(request.Headers[HttpRequestHeaders.ContentType])) { - var contentType = new ContentType(request.Headers[HttpRequestHeaders.ContentType]); - if (string.Equals(contentType.MediaType, HttpFormUrlEncoded, StringComparison.Ordinal)) { - foreach (string key in request.Form) { - if (key != null) { - fields.Add(key, request.Form[key]); - } else { - Logger.OAuth.WarnFormat("Ignoring query string parameter '{0}' since it isn't a standard name=value parameter.", request.Form[key]); - } - } + foreach (var pair in await ParseUrlEncodedFormContentAsync(request, cancellationToken)) { + if (pair.Key != null) { + fields.Add(pair.Key, pair.Value); + } else { + Logger.OAuth.WarnFormat("Ignoring query string parameter '{0}' since it isn't a standard name=value parameter.", pair.Value); } } // Scrape the query string - var qs = request.GetQueryStringBeforeRewriting(); + var qs = HttpUtility.ParseQueryString(request.RequestUri.Query); foreach (string key in qs) { if (key != null) { fields.Add(key, qs[key]); @@ -162,8 +157,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { // Add receiving HTTP transport information required for signature generation. var signedMessage = message as ITamperResistantOAuthMessage; if (signedMessage != null) { - signedMessage.Recipient = request.GetPublicFacingUrl(); - signedMessage.HttpMethod = new HttpMethod(request.HttpMethod); + signedMessage.Recipient = request.RequestUri; + signedMessage.HttpMethod = request.Method; } return message; diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs index cd603eb..421a09e 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs @@ -93,11 +93,8 @@ namespace DotNetOpenAuth.OAuth2 { /// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception> [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "unauthorizedclient", Justification = "Protocol required.")] public async Task<EndUserAuthorizationRequest> ReadAuthorizationRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - if (request == null) { - request = this.Channel.GetRequestFromContext(); - } - - var message = await this.Channel.TryReadFromRequestAsync<EndUserAuthorizationRequest>(cancellationToken, request); + request = request ?? this.Channel.GetRequestFromContext(); + var message = await this.Channel.TryReadFromRequestAsync<EndUserAuthorizationRequest>(request.AsHttpRequestMessage(), cancellationToken); if (message != null) { if (message.ResponseType == EndUserAuthorizationResponseType.AuthorizationCode) { // Clients with no secrets can only request implicit grant types. @@ -142,14 +139,12 @@ namespace DotNetOpenAuth.OAuth2 { /// The HTTP response to send to the client. /// </returns> public async Task<HttpResponseMessage> HandleTokenRequestAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { - if (request == null) { - request = this.Channel.GetRequestFromContext(); - } + request = request ?? this.Channel.GetRequestFromContext(); AccessTokenRequestBase requestMessage; IProtocolMessage responseMessage; try { - requestMessage = await this.Channel.TryReadFromRequestAsync<AccessTokenRequestBase>(cancellationToken, request); + requestMessage = await this.Channel.TryReadFromRequestAsync<AccessTokenRequestBase>(request.AsHttpRequestMessage(), cancellationToken); if (requestMessage != null) { var accessTokenResult = this.AuthorizationServerServices.CreateAccessToken(requestMessage); ErrorUtilities.VerifyHost(accessTokenResult != null, "IAuthorizationServerHost.CreateAccessToken must not return null."); diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs index 2a7f414..3fcbf67 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs @@ -96,9 +96,11 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// <returns> /// The deserialized message, if one is found. Null otherwise. /// </returns> - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request, CancellationToken cancellationToken) { - if (!string.IsNullOrEmpty(request.Url.Fragment)) { - var fields = HttpUtility.ParseQueryString(request.Url.Fragment.Substring(1)).ToDictionary(); + protected override async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + Requires.NotNull(request, "request"); + + if (!string.IsNullOrEmpty(request.RequestUri.Fragment)) { + var fields = HttpUtility.ParseQueryString(request.RequestUri.Fragment.Substring(1)).ToDictionary(); MessageReceivingEndpoint recipient; try { @@ -111,7 +113,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { return (IDirectedProtocolMessage)this.Receive(fields, recipient); } - return base.ReadFromRequestCore(request, cancellationToken); + return await base.ReadFromRequestCoreAsync(request, cancellationToken); } /// <summary> diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs index 65f3e1d..ae1a823 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs @@ -17,6 +17,8 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.Messages; + using Validation; + /// <summary> /// The messaging channel used by OAuth 2.0 Clients. /// </summary> @@ -112,18 +114,20 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// <returns> /// The deserialized message, if one is found. Null otherwise. /// </returns> - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request, CancellationToken cancellationToken) { - Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.GetPublicFacingUrl().AbsoluteUri); + protected override async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + Requires.NotNull(request, "request"); + + Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.Method, request.RequestUri.AbsoluteUri); - var fields = request.GetQueryStringBeforeRewriting().ToDictionary(); + var fields = HttpUtility.ParseQueryString(request.RequestUri.Query).ToDictionary(); // Also read parameters from the fragment, if it's available. // Typically the fragment is not available because the browser doesn't send it to a web server // but this request may have been fabricated by an installed desktop app, in which case // the fragment is available. - string fragment = request.Url.Fragment; + string fragment = request.RequestUri.Fragment; if (!string.IsNullOrEmpty(fragment)) { - foreach (var pair in HttpUtility.ParseQueryString(fragment.Substring(1)).ToDictionary()) { + foreach (var pair in HttpUtility.ParseQueryString(fragment.Substring(1)).AsKeyValuePairs()) { fields.Add(pair.Key, pair.Value); } } diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs index ffd896e..d56b308 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth2 { using System; using System.Collections.Generic; using System.Linq; + using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -127,7 +128,7 @@ namespace DotNetOpenAuth.OAuth2 { authorizationState = new AuthorizationState(); } - var carrier = new HttpRequestInfo("GET", actualRedirectUrl); + var carrier = new HttpRequestMessage(HttpMethod.Get, actualRedirectUrl); IDirectedProtocolMessage response = await this.Channel.ReadFromRequestAsync(carrier, cancellationToken); if (response == null) { return null; diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs index 0476521..c8eb69d 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs @@ -135,13 +135,10 @@ namespace DotNetOpenAuth.OAuth2 { RequiresEx.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), Strings.RequiredPropertyNotYetPreset, "ClientIdentifier"); RequiresEx.ValidState(this.ClientCredentialApplicator != null, Strings.RequiredPropertyNotYetPreset, "ClientCredentialApplicator"); - if (request == null) { - request = this.Channel.GetRequestFromContext(); - } - - var response = await this.Channel.TryReadFromRequestAsync<IMessageWithClientState>(cancellationToken, request); + request = request ?? this.Channel.GetRequestFromContext(); + var response = await this.Channel.TryReadFromRequestAsync<IMessageWithClientState>(request.AsHttpRequestMessage(), cancellationToken); if (response != null) { - Uri callback = MessagingUtilities.StripMessagePartsFromQueryString(request.GetPublicFacingUrl(), this.Channel.MessageDescriptions.Get(response)); + Uri callback = request.GetPublicFacingUrl().StripMessagePartsFromQueryString(this.Channel.MessageDescriptions.Get(response)); IAuthorizationState authorizationState; if (this.AuthorizationTracker != null) { authorizationState = this.AuthorizationTracker.GetAuthorizationState(callback, response.ClientState); diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj index bfbacff..d0f7bcf 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj @@ -79,6 +79,8 @@ <None Include="packages.config" /> </ItemGroup> <ItemGroup> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Net.Http.WebRequest" /> <Reference Include="Validation, Version=2.0.0.0, Culture=neutral, PublicKeyToken=2fc06f0d701809a7, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\Validation.2.0.2.13022\lib\portable-windows8+net40+sl5+windowsphone8\Validation.dll</HintPath> diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs index 96eecbb..cda9a1f 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs @@ -7,6 +7,8 @@ namespace DotNetOpenAuth.OAuth2.Messages { using System; using System.Net; + using System.Net.Http; + using DotNetOpenAuth.Messaging; /// <summary> @@ -16,7 +18,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// <summary> /// The backing for the <see cref="Headers"/> property. /// </summary> - private readonly WebHeaderCollection headers = new WebHeaderCollection(); + private readonly System.Net.Http.Headers.HttpRequestHeaders headers = new HttpRequestMessage().Headers; /// <summary> /// Initializes a new instance of the <see cref="AuthenticatedClientRequestBase"/> class. @@ -51,7 +53,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// Gets the HTTP headers of the request. /// </summary> /// <value>May be an empty collection, but must not be <c>null</c>.</value> - public WebHeaderCollection Headers { + public System.Net.Http.Headers.HttpRequestHeaders Headers { get { return this.headers; } } } diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/packages.config b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/packages.config index e3309bc..d32d62f 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/packages.config +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/packages.config @@ -1,4 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <packages> + <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" /> <package id="Validation" version="2.0.2.13022" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs index 363b8e0..c645753 100644 --- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs @@ -55,10 +55,12 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// <returns> /// The deserialized message, if one is found. Null otherwise. /// </returns> - protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestBase request, CancellationToken cancellationToken) { + protected override async Task<IDirectedProtocolMessage> ReadFromRequestCoreAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + Requires.NotNull(request, "request"); + var fields = new Dictionary<string, string>(); string accessToken; - if ((accessToken = SearchForBearerAccessTokenInRequest(request)) != null) { + if ((accessToken = await SearchForBearerAccessTokenInRequestAsync(request, cancellationToken)) != null) { fields[Protocol.token_type] = Protocol.AccessTokenTypes.Bearer; fields[Protocol.access_token] = accessToken; } @@ -129,27 +131,24 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// </summary> /// <param name="request">The request.</param> /// <returns>The bearer access token, if one exists. Otherwise <c>null</c>.</returns> - private static string SearchForBearerAccessTokenInRequest(HttpRequestBase request) { + private static async Task<string> SearchForBearerAccessTokenInRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Requires.NotNull(request, "request"); // First search the authorization header. - string authorizationHeader = request.Headers[HttpRequestHeaders.Authorization]; - if (!string.IsNullOrEmpty(authorizationHeader) && authorizationHeader.StartsWith(Protocol.BearerHttpAuthorizationSchemeWithTrailingSpace, StringComparison.OrdinalIgnoreCase)) { - return authorizationHeader.Substring(Protocol.BearerHttpAuthorizationSchemeWithTrailingSpace.Length); + var authorizationHeader = request.Headers.Authorization; + if (authorizationHeader != null && string.Equals(authorizationHeader.Scheme, Protocol.BearerHttpAuthorizationScheme, StringComparison.OrdinalIgnoreCase)) { + return authorizationHeader.Parameter; } // Failing that, scan the entity - if (!string.IsNullOrEmpty(request.Headers[HttpRequestHeaders.ContentType])) { - var contentType = new ContentType(request.Headers[HttpRequestHeaders.ContentType]); - if (string.Equals(contentType.MediaType, HttpFormUrlEncoded, StringComparison.Ordinal)) { - if (request.Form[Protocol.BearerTokenEncodedUrlParameterName] != null) { - return request.Form[Protocol.BearerTokenEncodedUrlParameterName]; - } + foreach (var pair in await ParseUrlEncodedFormContentAsync(request, cancellationToken)) { + if (string.Equals(pair.Key, Protocol.BearerTokenEncodedUrlParameterName, StringComparison.Ordinal)) { + return pair.Value; } } // Finally, check the least desirable location: the query string - var unrewrittenQuery = request.GetQueryStringBeforeRewriting(); + var unrewrittenQuery = HttpUtility.ParseQueryString(request.RequestUri.Query); if (!string.IsNullOrEmpty(unrewrittenQuery[Protocol.BearerTokenEncodedUrlParameterName])) { return unrewrittenQuery[Protocol.BearerTokenEncodedUrlParameterName]; } diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs index 88ce451..e990e0b 100644 --- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs @@ -88,17 +88,34 @@ namespace DotNetOpenAuth.OAuth2 { /// </returns> /// <exception cref="ProtocolFaultResponseException">Thrown when the client is not authorized. This exception should be caught and the /// <see cref="ProtocolFaultResponseException.ErrorResponseMessage" /> message should be returned to the client.</exception> - public virtual async Task<AccessToken> GetAccessTokenAsync(HttpRequestBase httpRequestInfo = null, CancellationToken cancellationToken = default(CancellationToken), params string[] requiredScopes) { + public virtual Task<AccessToken> GetAccessTokenAsync(HttpRequestBase httpRequestInfo = null, CancellationToken cancellationToken = default(CancellationToken), params string[] requiredScopes) { + Requires.NotNull(requiredScopes, "requiredScopes"); + RequiresEx.ValidState(this.ScopeSatisfiedCheck != null, Strings.RequiredPropertyNotYetPreset); + + httpRequestInfo = httpRequestInfo ?? this.Channel.GetRequestFromContext(); + return this.GetAccessTokenAsync(httpRequestInfo.AsHttpRequestMessage(), cancellationToken, requiredScopes); + } + + /// <summary> + /// Discovers what access the client should have considering the access token in the current request. + /// </summary> + /// <param name="request">The HTTP request message.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <param name="requiredScopes">The set of scopes required to approve this request.</param> + /// <returns> + /// The access token describing the authorization the client has. Never <c>null</c>. + /// </returns> + /// <exception cref="ProtocolFaultResponseException">Thrown when the client is not authorized. This exception should be caught and the + /// <see cref="ProtocolFaultResponseException.ErrorResponseMessage" /> message should be returned to the client.</exception> + public virtual async Task<AccessToken> GetAccessTokenAsync(HttpRequestMessage requestMessage, CancellationToken cancellationToken = default(CancellationToken), params string[] requiredScopes) { + Requires.NotNull(requestMessage, "requestMessage"); Requires.NotNull(requiredScopes, "requiredScopes"); RequiresEx.ValidState(this.ScopeSatisfiedCheck != null, Strings.RequiredPropertyNotYetPreset); - if (httpRequestInfo == null) { - httpRequestInfo = this.Channel.GetRequestFromContext(); - } AccessToken accessToken; AccessProtectedResourceRequest request = null; try { - request = await this.Channel.TryReadFromRequestAsync<AccessProtectedResourceRequest>(cancellationToken, httpRequestInfo); + request = await this.Channel.TryReadFromRequestAsync<AccessProtectedResourceRequest>(requestMessage, cancellationToken); if (request != null) { accessToken = this.AccessTokenAnalyzer.DeserializeAccessToken(request, request.AccessToken); ErrorUtilities.VerifyHost(accessToken != null, "IAccessTokenAnalyzer.DeserializeAccessToken returned a null reslut."); @@ -133,22 +150,6 @@ namespace DotNetOpenAuth.OAuth2 { /// <summary> /// Discovers what access the client should have considering the access token in the current request. /// </summary> - /// <param name="request">The HTTP request message.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="requiredScopes">The set of scopes required to approve this request.</param> - /// <returns> - /// The access token describing the authorization the client has. Never <c>null</c>. - /// </returns> - /// <exception cref="ProtocolFaultResponseException">Thrown when the client is not authorized. This exception should be caught and the - /// <see cref="ProtocolFaultResponseException.ErrorResponseMessage" /> message should be returned to the client.</exception> - public virtual Task<AccessToken> GetAccessTokenAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken), params string[] requiredScopes) { - Requires.NotNull(request, "request"); - return this.GetAccessTokenAsync(new HttpRequestInfo(request), cancellationToken, requiredScopes); - } - - /// <summary> - /// Discovers what access the client should have considering the access token in the current request. - /// </summary> /// <param name="httpRequestInfo">The HTTP request info.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <param name="requiredScopes">The set of scopes required to approve this request.</param> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs index e51eb24..aeba8d7 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs +++ b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs @@ -180,12 +180,12 @@ namespace DotNetOpenAuth.OAuth2 { /// </summary> /// <param name="headers">The incoming web headers.</param> /// <returns>The network credentials; or <c>null</c> if none could be discovered in the request.</returns> - internal static NetworkCredential ParseHttpBasicAuth(WebHeaderCollection headers) { + internal static NetworkCredential ParseHttpBasicAuth(System.Net.Http.Headers.HttpRequestHeaders headers) { Requires.NotNull(headers, "headers"); - string authorizationHeader = headers[HttpRequestHeaders.Authorization]; - if (authorizationHeader != null && authorizationHeader.StartsWith(HttpBasicAuthScheme, StringComparison.Ordinal)) { - string base64 = authorizationHeader.Substring(HttpBasicAuthScheme.Length); + var authorizationHeader = headers.Authorization; + if (authorizationHeader != null && string.Equals(authorizationHeader.Scheme, HttpBasicAuthScheme, StringComparison.Ordinal)) { + string base64 = authorizationHeader.Parameter; byte[] bits = Convert.FromBase64String(base64); string usernameColonPassword = HttpBasicEncoding.GetString(bits); string[] usernameAndPassword = usernameColonPassword.Split(ColonSeparator, 2); diff --git a/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs index c5c3540..fb1aee3 100644 --- a/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs +++ b/src/DotNetOpenAuth.OpenId.Provider.UI/OpenId/Provider/ProviderEndpoint.cs @@ -46,12 +46,14 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <summary> /// Backing field for the <see cref="Provider"/> property. /// </summary> - private static OpenIdProvider provider; + private Lazy<OpenIdProvider> provider; /// <summary> - /// The lock that must be obtained when initializing the provider field. + /// Initializes a new instance of the <see cref="ProviderEndpoint"/> class. /// </summary> - private static object providerInitializerLock = new object(); + public ProviderEndpoint() { + this.provider = new Lazy<OpenIdProvider>(this.CreateProvider); + } /// <summary> /// Fired when an incoming OpenID request is an authentication challenge @@ -70,22 +72,14 @@ namespace DotNetOpenAuth.OpenId.Provider { /// Gets or sets the <see cref="OpenIdProvider"/> instance to use for all instances of this control. /// </summary> /// <value>The default value is an <see cref="OpenIdProvider"/> instance initialized according to the web.config file.</value> - public static OpenIdProvider Provider { + public OpenIdProvider Provider { get { - if (provider == null) { - lock (providerInitializerLock) { - if (provider == null) { - provider = CreateProvider(); - } - } - } - - return provider; + return this.provider.Value; } set { Requires.NotNull(value, "value"); - provider = value; + this.provider = new Lazy<OpenIdProvider>(() => value, LazyThreadSafetyMode.PublicationOnly); } } @@ -181,10 +175,10 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <returns> /// The response message. /// </returns> - public static Task<HttpResponseMessage> PrepareResponseAsync(CancellationToken cancellationToken = default(CancellationToken)) { + public Task<HttpResponseMessage> PrepareResponseAsync(CancellationToken cancellationToken = default(CancellationToken)) { var pendingRequest = PendingRequest; PendingRequest = null; - return Provider.PrepareResponseAsync(pendingRequest, cancellationToken); + return this.Provider.PrepareResponseAsync(pendingRequest, cancellationToken); } /// <summary> @@ -196,46 +190,45 @@ namespace DotNetOpenAuth.OpenId.Provider { protected override void OnLoad(EventArgs e) { base.OnLoad(e); - Task.Run( - async delegate { - // There is the unusual scenario that this control is hosted by - // an ASP.NET web page that has other UI on it to that the user - // might see, including controls that cause a postback to occur. - // We definitely want to ignore postbacks, since any openid messages - // they contain will be old. - if (this.Enabled && !this.Page.IsPostBack) { - // Use the explicitly given state store on this control if there is one. - // Then try the configuration file specified one. Finally, use the default - // in-memory one that's built into OpenIdProvider. - // determine what incoming message was received - IRequest request = await Provider.GetRequestAsync(new HttpRequestWrapper(this.Context.Request), CancellationToken.None); - if (request != null) { - PendingRequest = null; + this.Page.RegisterAsyncTask(new PageAsyncTask(async cancellationToken => { + // There is the unusual scenario that this control is hosted by + // an ASP.NET web page that has other UI on it to that the user + // might see, including controls that cause a postback to occur. + // We definitely want to ignore postbacks, since any openid messages + // they contain will be old. + if (this.Enabled && !this.Page.IsPostBack) { + // Use the explicitly given state store on this control if there is one. + // Then try the configuration file specified one. Finally, use the default + // in-memory one that's built into OpenIdProvider. + // determine what incoming message was received + IRequest request = await Provider.GetRequestAsync(new HttpRequestWrapper(this.Context.Request), cancellationToken); + if (request != null) { + PendingRequest = null; - // process the incoming message appropriately and send the response - IAuthenticationRequest idrequest; - IAnonymousRequest anonRequest; - if ((idrequest = request as IAuthenticationRequest) != null) { - PendingAuthenticationRequest = idrequest; - this.OnAuthenticationChallenge(idrequest); - } else if ((anonRequest = request as IAnonymousRequest) != null) { - PendingAnonymousRequest = anonRequest; - if (!this.OnAnonymousRequest(anonRequest)) { - // This is a feature not supported by the OP, so - // go ahead and set disapproved so we can send a response. - Logger.OpenId.Warn( - "An incoming anonymous OpenID request message was detected, but the ProviderEndpoint.AnonymousRequest event is not handled, so returning cancellation message to relying party."); - anonRequest.IsApproved = false; - } - } - if (request.IsResponseReady) { - PendingAuthenticationRequest = null; - var response = await Provider.PrepareResponseAsync(request, CancellationToken.None); - response.Send(new HttpContextWrapper(this.Context).Response); + // process the incoming message appropriately and send the response + IAuthenticationRequest idrequest; + IAnonymousRequest anonRequest; + if ((idrequest = request as IAuthenticationRequest) != null) { + PendingAuthenticationRequest = idrequest; + this.OnAuthenticationChallenge(idrequest); + } else if ((anonRequest = request as IAnonymousRequest) != null) { + PendingAnonymousRequest = anonRequest; + if (!this.OnAnonymousRequest(anonRequest)) { + // This is a feature not supported by the OP, so + // go ahead and set disapproved so we can send a response. + Logger.OpenId.Warn( + "An incoming anonymous OpenID request message was detected, but the ProviderEndpoint.AnonymousRequest event is not handled, so returning cancellation message to relying party."); + anonRequest.IsApproved = false; } } + if (request.IsResponseReady) { + PendingAuthenticationRequest = null; + var response = await Provider.PrepareResponseAsync(request, cancellationToken); + await response.SendAsync(new HttpContextWrapper(this.Context), cancellationToken); + } } - }); + } + })); } /// <summary> @@ -268,8 +261,8 @@ namespace DotNetOpenAuth.OpenId.Provider { /// Creates the default OpenIdProvider to use. /// </summary> /// <returns>The new instance of OpenIdProvider.</returns> - private static OpenIdProvider CreateProvider() { - return new OpenIdProvider(OpenIdElement.Configuration.Provider.ApplicationStore.CreateInstance(OpenIdProvider.GetHttpApplicationStore(), null)); + private OpenIdProvider CreateProvider() { + return new OpenIdProvider(OpenIdElement.Configuration.Provider.ApplicationStore.CreateInstance(OpenIdProvider.GetHttpApplicationStore(new HttpContextWrapper(this.Context)), null)); } } } diff --git a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/HostProcessedRequest.cs b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/HostProcessedRequest.cs index 7671194..5f3bba2 100644 --- a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/HostProcessedRequest.cs +++ b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/HostProcessedRequest.cs @@ -31,6 +31,8 @@ namespace DotNetOpenAuth.OpenId.Provider { /// </summary> private RelyingPartyDiscoveryResult? realmDiscoveryResult; + private IHostFactories hostFactories; + /// <summary> /// Initializes a new instance of the <see cref="HostProcessedRequest"/> class. /// </summary> @@ -40,6 +42,7 @@ namespace DotNetOpenAuth.OpenId.Provider { : base(request, provider.SecuritySettings) { Requires.NotNull(provider, "provider"); + this.hostFactories = provider.Channel.HostFactories; this.negativeResponse = new Lazy<Task<NegativeAssertionResponse>>(() => NegativeAssertionResponse.CreateAsync(request, CancellationToken.None, provider.Channel)); Reporting.RecordEventOccurrence(this, request.Realm); } @@ -139,8 +142,8 @@ namespace DotNetOpenAuth.OpenId.Provider { /// Result of realm discovery. /// </returns> private async Task<RelyingPartyDiscoveryResult> IsReturnUrlDiscoverableCoreAsync(IHostFactories hostFactories, CancellationToken cancellationToken) { - Requires.NotNull(hostFactories, "hostFactories"); ErrorUtilities.VerifyInternal(this.Realm != null, "Realm should have been read or derived by now."); + hostFactories = hostFactories ?? this.hostFactories; try { if (this.SecuritySettings.RequireSsl && this.Realm.Scheme != Uri.UriSchemeHttps) { diff --git a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs index e71c291..a571e31 100644 --- a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs +++ b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs @@ -214,14 +214,15 @@ namespace DotNetOpenAuth.OpenId.Provider { /// on its own user database and policies.</para> /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para> /// </remarks> - public Task<IRequest> GetRequestAsync(CancellationToken cancellationToken) { - return this.GetRequestAsync(this.Channel.GetRequestFromContext(), cancellationToken); + public Task<IRequest> GetRequestAsync(HttpRequestBase request, CancellationToken cancellationToken) { + request = request ?? this.Channel.GetRequestFromContext(); + return this.GetRequestAsync(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> /// Gets the incoming OpenID request if there is one, or null if none was detected. /// </summary> - /// <param name="httpRequestInfo">The incoming HTTP request to extract the message from.</param> + /// <param name="request">The incoming HTTP request to extract the message from.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// The request that the hosting Provider should process and then transmit the response for. @@ -234,17 +235,17 @@ namespace DotNetOpenAuth.OpenId.Provider { /// be authentication requests where the Provider site has to make decisions based /// on its own user database and policies. /// </remarks> - public async Task<IRequest> GetRequestAsync(HttpRequestBase httpRequestInfo, CancellationToken cancellationToken) { - Requires.NotNull(httpRequestInfo, "httpRequestInfo"); + public async Task<IRequest> GetRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "request"); IDirectedProtocolMessage incomingMessage = null; try { - incomingMessage = await this.Channel.ReadFromRequestAsync(httpRequestInfo, cancellationToken); + incomingMessage = await this.Channel.ReadFromRequestAsync(request, cancellationToken); if (incomingMessage == null) { // If the incoming request does not resemble an OpenID message at all, // it's probably a user who just navigated to this URL, and we should // just return null so the host can display a message to the user. - if (httpRequestInfo.HttpMethod == "GET" && !httpRequestInfo.GetPublicFacingUrl().QueryStringContainPrefixedParameters(Protocol.Default.openid.Prefix)) { + if (request.Method == HttpMethod.Get && !request.RequestUri.QueryStringContainPrefixedParameters(Protocol.Default.openid.Prefix)) { return null; } @@ -292,7 +293,7 @@ namespace DotNetOpenAuth.OpenId.Provider { throw ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessageReceivedOfMany); } catch (ProtocolException ex) { - IRequest errorResponse = this.GetErrorResponse(ex, httpRequestInfo, incomingMessage); + IRequest errorResponse = this.GetErrorResponse(ex, request, incomingMessage); if (errorResponse == null) { throw; } @@ -450,16 +451,16 @@ namespace DotNetOpenAuth.OpenId.Provider { /// <returns> /// Either the <see cref="IRequest"/> to return to the host site or null to indicate no response could be reasonably created and that the caller should rethrow the exception. /// </returns> - private IRequest GetErrorResponse(ProtocolException ex, HttpRequestBase httpRequestInfo, IDirectedProtocolMessage incomingMessage) { + private IRequest GetErrorResponse(ProtocolException ex, HttpRequestMessage request, IDirectedProtocolMessage incomingMessage) { Requires.NotNull(ex, "ex"); - Requires.NotNull(httpRequestInfo, "httpRequestInfo"); + Requires.NotNull(request, "request"); Logger.OpenId.Error("An exception was generated while processing an incoming OpenID request.", ex); IErrorMessage errorMessage; // We must create the appropriate error message type (direct vs. indirect) // based on what we see in the request. - string returnTo = httpRequestInfo.QueryString[Protocol.Default.openid.return_to]; + string returnTo = HttpUtility.ParseQueryString(request.RequestUri.Query)[Protocol.Default.openid.return_to]; if (returnTo != null) { // An indirect request message from the RP // We need to return an indirect response error message so the RP can consume it. @@ -470,7 +471,7 @@ namespace DotNetOpenAuth.OpenId.Provider { } else { errorMessage = new IndirectErrorResponse(Protocol.Default.Version, new Uri(returnTo)); } - } else if (httpRequestInfo.HttpMethod == "POST") { + } else if (request.Method == HttpMethod.Post) { // A direct request message from the RP // We need to return a direct response error message so the RP can consume it. // Consistent with OpenID 2.0 section 5.1.2.2. diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs b/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs index defd15c..066b5a3 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs @@ -442,11 +442,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { }; HttpResponseMessage response = await this.RelyingParty.ProcessResponseFromPopupAsync( - new HttpRequestWrapper(this.Context.Request), + new HttpRequestWrapper(this.Context.Request).AsHttpRequestMessage(), callback, cancellationToken); - response.Send(); + await response.SendAsync(new HttpContextWrapper(this.Context), cancellationToken); } /// <summary> diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs b/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs index dc8ba54..77a5b44 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty.UI/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs @@ -677,7 +677,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// A task that completes with the asynchronous operation. /// </returns> protected virtual Task ScriptClosingPopupOrIFrameAsync(CancellationToken cancellationToken) { - return this.RelyingParty.ProcessResponseFromPopupAsync(cancellationToken); + return this.RelyingParty.ProcessResponseFromPopupAsync(new HttpRequestWrapper(this.Context.Request).AsHttpRequestMessage(), cancellationToken); } /// <summary> diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs index 6a1ef83..33bec3b 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs @@ -520,9 +520,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <remarks> /// Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context. /// </remarks> - public Task<IAuthenticationResponse> GetResponseAsync(CancellationToken cancellationToken = default(CancellationToken)) { - RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); - return this.GetResponseAsync(this.Channel.GetRequestFromContext(), cancellationToken); + public Task<IAuthenticationResponse> GetResponseAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + request = request ?? this.channel.GetRequestFromContext(); + return this.GetResponseAsync(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> @@ -533,10 +533,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <returns> /// The processed authentication response if there is any; <c>null</c> otherwise. /// </returns> - public async Task<IAuthenticationResponse> GetResponseAsync(HttpRequestBase httpRequestInfo, CancellationToken cancellationToken = default(CancellationToken)) { - Requires.NotNull(httpRequestInfo, "httpRequestInfo"); + public async Task<IAuthenticationResponse> GetResponseAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { + Requires.NotNull(request, "httpRequestInfo"); try { - var message = await this.Channel.ReadFromRequestAsync(httpRequestInfo, cancellationToken); + var message = await this.Channel.ReadFromRequestAsync(request, cancellationToken); PositiveAssertionResponse positiveAssertion; NegativeAssertionResponse negativeAssertion; IndirectSignedResponse positiveExtensionOnly; @@ -572,6 +572,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <summary> /// Processes the response received in a popup window or iframe to an AJAX-directed OpenID authentication. /// </summary> + /// <param name="request">The request.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// The HTTP response to send to this HTTP request. @@ -579,10 +580,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <remarks> /// Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context. /// </remarks> - public Task<HttpResponseMessage> ProcessResponseFromPopupAsync(CancellationToken cancellationToken) { - RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); - - return this.ProcessResponseFromPopupAsync(this.Channel.GetRequestFromContext(), cancellationToken); + public Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestBase request = null, CancellationToken cancellationToken = default(CancellationToken)) { + request = request ?? this.Channel.GetRequestFromContext(); + return this.ProcessResponseFromPopupAsync(request.AsHttpRequestMessage(), cancellationToken); } /// <summary> @@ -593,9 +593,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <returns> /// The HTTP response to send to this HTTP request. /// </returns> - public Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestBase request, CancellationToken cancellationToken) { + public Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestMessage request, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(request, "request"); - return this.ProcessResponseFromPopupAsync(request, null, cancellationToken); } @@ -680,11 +679,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// The HTTP response to send to this HTTP request. /// </returns> [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "OpenID", Justification = "real word"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "iframe", Justification = "Code contracts")] - internal async Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestBase request, Action<AuthenticationStatus> callback, CancellationToken cancellationToken) { + internal async Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestMessage request, Action<AuthenticationStatus> callback, CancellationToken cancellationToken) { Requires.NotNull(request, "request"); string extensionsJson = null; - var authResponse = await this.NonVerifyingRelyingParty.GetResponseAsync(cancellationToken); + var authResponse = await this.NonVerifyingRelyingParty.GetResponseAsync(request, cancellationToken); ErrorUtilities.VerifyProtocol(authResponse != null, OpenIdStrings.PopupRedirectMissingResponse); // Give the caller a chance to notify the hosting page and fill up the clientScriptExtensions collection. @@ -692,7 +691,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { callback(authResponse.Status); } - Logger.OpenId.DebugFormat("Popup or iframe callback from OP: {0}", request.Url); + Logger.OpenId.DebugFormat("Popup or iframe callback from OP: {0}", request.RequestUri); Logger.Controls.DebugFormat( "An authentication response was found in a popup window or iframe using a non-verifying RP with status: {0}", authResponse.Status); @@ -714,13 +713,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } string payload = "document.URL"; - if (request.HttpMethod == "POST") { + if (request.Method == HttpMethod.Post) { // Promote all form variables to the query string, but since it won't be passed // to any server (this is a javascript window-to-window transfer) the length of // it can be arbitrarily long, whereas it was POSTed here probably because it // was too long for HTTP transit. - UriBuilder payloadUri = new UriBuilder(request.Url); - payloadUri.AppendQueryArgs(request.Form.ToDictionary()); + UriBuilder payloadUri = new UriBuilder(request.RequestUri); + payloadUri.AppendQueryArgs(await Channel.ParseUrlEncodedFormContentAsync(request, cancellationToken)); payload = MessagingUtilities.GetSafeJavascriptValue(payloadUri.Uri.AbsoluteUri); } diff --git a/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs b/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs index e111655..5073897 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs @@ -97,7 +97,7 @@ namespace DotNetOpenAuth.OpenId { context = context ?? new HttpContextWrapper(HttpContext.Current); var response = await authenticationRequest.GetRedirectingResponseAsync(cancellationToken); - await response.SendAsync(context.Response); + await response.SendAsync(context, cancellationToken); } /// <summary> diff --git a/src/DotNetOpenAuth.TestWeb/OpenIdProviderEndpoint.ashx b/src/DotNetOpenAuth.TestWeb/OpenIdProviderEndpoint.ashx index 7225a01..70dc9af 100644 --- a/src/DotNetOpenAuth.TestWeb/OpenIdProviderEndpoint.ashx +++ b/src/DotNetOpenAuth.TestWeb/OpenIdProviderEndpoint.ashx @@ -61,7 +61,7 @@ public class OpenIdProviderEndpoint : IHttpAsyncHandler { } var response = await provider.PrepareResponseAsync(request, context.Response.ClientDisconnectedToken); - await response.SendAsync(new HttpResponseWrapper(context.Response), context.Response.ClientDisconnectedToken); + await response.SendAsync(new HttpContextWrapper(context), context.Response.ClientDisconnectedToken); } } }
\ No newline at end of file diff --git a/src/packages/repositories.config b/src/packages/repositories.config index fe99184..0d28c5f 100644 --- a/src/packages/repositories.config +++ b/src/packages/repositories.config @@ -18,6 +18,7 @@ <repository path="..\..\samples\OpenIdRelyingPartyWebForms\packages.config" /> <repository path="..\..\samples\OpenIdRelyingPartyWebFormsVB\packages.config" /> <repository path="..\..\samples\OpenIdWebRingSsoProvider\packages.config" /> + <repository path="..\..\samples\OpenIdWebRingSsoRelyingParty\packages.config" /> <repository path="..\DotNetOpenAuth.AspNet.Test\packages.config" /> <repository path="..\DotNetOpenAuth.AspNet\packages.config" /> <repository path="..\DotNetOpenAuth.Core.UI\packages.config" /> |