diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2012-12-31 22:54:20 -0800 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2012-12-31 22:54:20 -0800 |
commit | 30cdda15c5e8b6db0d7260697c0a13c06943afec (patch) | |
tree | 4b3ea104a96a617502bc11193ad2a99c59d917d7 /src/DotNetOpenAuth.OpenId/OpenId | |
parent | 90b6aa8ba9d15e0254eccf05b73b24f334128654 (diff) | |
download | DotNetOpenAuth-30cdda15c5e8b6db0d7260697c0a13c06943afec.zip DotNetOpenAuth-30cdda15c5e8b6db0d7260697c0a13c06943afec.tar.gz DotNetOpenAuth-30cdda15c5e8b6db0d7260697c0a13c06943afec.tar.bz2 |
DNOA.OpenId.RP now builds.
Diffstat (limited to 'src/DotNetOpenAuth.OpenId/OpenId')
11 files changed, 158 insertions, 79 deletions
diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs index ff8a766..c448e2f 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/BackwardCompatibilityBindingElement.cs @@ -6,6 +6,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OpenId.Messages; @@ -16,6 +18,11 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// are required to send back with positive assertions. /// </summary> internal class BackwardCompatibilityBindingElement : IChannelBindingElement { + private static readonly Task<MessageProtections?> NullTask = Task.FromResult<MessageProtections?>(null); + + private static readonly Task<MessageProtections?> NoneTask = + Task.FromResult<MessageProtections?>(MessageProtections.None); + /// <summary> /// The "dnoa.op_endpoint" callback parameter that stores the Provider Endpoint URL /// to tack onto the return_to URI. @@ -59,7 +66,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { SignedResponseRequest request = message as SignedResponseRequest; if (request != null && request.Version.Major < 2) { request.AddReturnToArguments(ProviderEndpointParameterName, request.Recipient.AbsoluteUri); @@ -69,10 +76,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { request.AddReturnToArguments(ClaimedIdentifierParameterName, authRequest.ClaimedIdentifier); } - return MessageProtections.None; + return NoneTask; } - return null; + return NullTask; } /// <summary> @@ -92,7 +99,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { IndirectSignedResponse response = message as IndirectSignedResponse; if (response != null && response.Version.Major < 2) { // GetReturnToArgument may return parameters that are not signed, @@ -118,10 +125,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { } } - return MessageProtections.None; + return NoneTask; } - return null; + return NullTask; } #endregion diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ExtensionsBindingElement.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ExtensionsBindingElement.cs index f24c8b4..2a5946a 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ExtensionsBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ExtensionsBindingElement.cs @@ -10,6 +10,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OpenId.Extensions; @@ -21,6 +23,11 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// their carrying OpenID messages. /// </summary> internal class ExtensionsBindingElement : IChannelBindingElement { + private static readonly Task<MessageProtections?> NullTask = Task.FromResult<MessageProtections?>(null); + + private static readonly Task<MessageProtections?> NoneTask = + Task.FromResult<MessageProtections?>(MessageProtections.None); + /// <summary> /// False if unsigned extensions should be dropped. Must always be true on Providers, since RPs never sign extensions. /// </summary> @@ -77,7 +84,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification = "It doesn't look too bad to me. :)")] - public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var extendableMessage = message as IProtocolMessageWithExtensions; if (extendableMessage != null) { Protocol protocol = Protocol.Lookup(message.Version); @@ -120,10 +127,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { // Add the extension parameters to the base message for transmission. baseMessageDictionary.AddExtraParameters(extensionManager.GetArgumentsToSend(includeOpenIdPrefix)); - return MessageProtections.None; + return NoneTask; } - return null; + return NullTask; } /// <summary> @@ -143,7 +150,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var extendableMessage = message as IProtocolMessageWithExtensions; if (extendableMessage != null) { // First add the extensions that are signed by the Provider. @@ -164,10 +171,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { } } - return MessageProtections.None; + return NoneTask; } - return null; + return NullTask; } #endregion diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs index eb4ca65..221994a 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/OpenIdChannel.cs @@ -16,6 +16,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Net.Http; using System.Net.Http.Headers; using System.Text; + using System.Threading; using System.Threading.Tasks; using DotNetOpenAuth.Configuration; @@ -51,7 +52,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <param name="messageTypeProvider">A class prepared to analyze incoming messages and indicate what concrete /// message types can deserialize from it.</param> /// <param name="bindingElements">The binding elements to use in sending and receiving messages.</param> - protected OpenIdChannel(IMessageFactory messageTypeProvider, IChannelBindingElement[] bindingElements, IHostFactories hostFactories) + protected OpenIdChannel(IMessageFactory messageTypeProvider, IChannelBindingElement[] bindingElements, IHostFactories hostFactories = null) : base(messageTypeProvider, bindingElements, hostFactories ?? new DefaultOpenIdHostFactories()) { Requires.NotNull(messageTypeProvider, "messageTypeProvider"); @@ -84,18 +85,18 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Thrown when the message is somehow invalid, except for check_authentication messages. /// This can be due to tampering, replay attack or expiration, among other things. /// </exception> - protected override void ProcessIncomingMessage(IProtocolMessage message) { + protected override async Task ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var checkAuthRequest = message as CheckAuthenticationRequest; if (checkAuthRequest != null) { IndirectSignedResponse originalResponse = new IndirectSignedResponse(checkAuthRequest, this); try { - base.ProcessIncomingMessage(originalResponse); + await base.ProcessIncomingMessageAsync(originalResponse, cancellationToken); checkAuthRequest.IsValid = true; } catch (ProtocolException) { checkAuthRequest.IsValid = false; } } else { - base.ProcessIncomingMessage(message); + await base.ProcessIncomingMessageAsync(message, cancellationToken); } // Convert an OpenID indirect error message, which we never expect diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs index 726c01f..c55704d 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/ReturnToSignatureBindingElement.cs @@ -9,6 +9,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Collections.Generic; using System.Collections.Specialized; using System.Security.Cryptography; + using System.Threading; + using System.Threading.Tasks; using System.Web; using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; @@ -31,6 +33,11 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// anything except a particular message part.</para> /// </remarks> internal class ReturnToSignatureBindingElement : IChannelBindingElement { + private static readonly Task<MessageProtections?> NullTask = Task.FromResult<MessageProtections?>(null); + + private static readonly Task<MessageProtections?> NoneTask = + Task.FromResult<MessageProtections?>(MessageProtections.None); + /// <summary> /// The name of the callback parameter we'll tack onto the return_to value /// to store our signature on the return_to parameter. @@ -98,7 +105,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { SignedResponseRequest request = message as SignedResponseRequest; if (request != null && request.ReturnTo != null && request.SignReturnTo) { var cryptoKeyPair = this.cryptoKeyStore.GetCurrentKey(SecretUri.AbsoluteUri, OpenIdElement.Configuration.MaxAuthenticationTime); @@ -107,10 +114,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { request.AddReturnToArguments(ReturnToSignatureParameterName, signature); // We return none because we are not signing the entire message (only a part). - return MessageProtections.None; + return NoneTask; } - return null; + return NullTask; } /// <summary> @@ -130,7 +137,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { IndirectSignedResponse response = message as IndirectSignedResponse; if (response != null) { @@ -150,11 +157,11 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { Logger.Bindings.WarnFormat("The return_to signature failed verification."); } - return MessageProtections.None; + return NoneTask; } } - return null; + return NullTask; } #endregion diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SigningBindingElement.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SigningBindingElement.cs index 584b0e9..83d45a1 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SigningBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SigningBindingElement.cs @@ -11,6 +11,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Globalization; using System.Linq; using System.Net.Security; + using System.Threading; + using System.Threading.Tasks; using System.Web; using DotNetOpenAuth.Loggers; using DotNetOpenAuth.Messaging; @@ -23,6 +25,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Signs and verifies authentication assertions. /// </summary> internal abstract class SigningBindingElement : IChannelBindingElement { + private static readonly Task<MessageProtections?> NullTask = Task.FromResult<MessageProtections?>(null); + #region IChannelBindingElement Properties /// <summary> @@ -57,8 +61,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// The protections (if any) that this binding element applied to the message. /// Null if this binding element did not even apply to this binding element. /// </returns> - public virtual MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { - return null; + public virtual Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { + return NullTask; } /// <summary> @@ -74,7 +78,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Thrown when the binding element rules indicate that this message is invalid and should /// NOT be processed. /// </exception> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public async Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var signedMessage = message as ITamperResistantOpenIdMessage; if (signedMessage != null) { Logger.Bindings.DebugFormat("Verifying incoming {0} message signature of: {1}", message.GetType().Name, signedMessage.Signature); @@ -92,7 +96,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { } else { ErrorUtilities.VerifyInternal(this.Channel != null, "Cannot verify private association signature because we don't have a channel."); - protectionsApplied = this.VerifySignatureByUnrecognizedHandle(message, signedMessage, protectionsApplied); + protectionsApplied = await this.VerifySignatureByUnrecognizedHandleAsync(message, signedMessage, protectionsApplied, cancellationToken); } return protectionsApplied; @@ -108,7 +112,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <param name="signedMessage">The signed message.</param> /// <param name="protectionsApplied">The protections applied.</param> /// <returns>The applied protections.</returns> - protected abstract MessageProtections VerifySignatureByUnrecognizedHandle(IProtocolMessage message, ITamperResistantOpenIdMessage signedMessage, MessageProtections protectionsApplied); + protected abstract Task<MessageProtections> VerifySignatureByUnrecognizedHandleAsync(IProtocolMessage message, ITamperResistantOpenIdMessage signedMessage, MessageProtections protectionsApplied, CancellationToken cancellationToken); #endregion diff --git a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SkipSecurityBindingElement.cs b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SkipSecurityBindingElement.cs index d162cf6..900a422 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SkipSecurityBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/ChannelElements/SkipSecurityBindingElement.cs @@ -10,12 +10,16 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Diagnostics; using System.Linq; using System.Text; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; /// <summary> /// Spoofs security checks on incoming OpenID messages. /// </summary> internal class SkipSecurityBindingElement : IChannelBindingElement { + private static readonly Task<MessageProtections?> NullTask = Task.FromResult<MessageProtections?>(null); + #region IChannelBindingElement Members /// <summary> @@ -50,7 +54,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { Debug.Fail("SkipSecurityBindingElement.ProcessOutgoingMessage should never be called."); return null; } @@ -72,14 +76,14 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public Task<MessageProtections?> ProcessIncomingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { var signedMessage = message as ITamperResistantOpenIdMessage; if (signedMessage != null) { Logger.Bindings.DebugFormat("Skipped security checks of incoming {0} message for preview purposes.", message.GetType().Name); - return this.Protection; + return Task.FromResult<MessageProtections?>(this.Protection); } - return null; + return NullTask; } #endregion diff --git a/src/DotNetOpenAuth.OpenId/OpenId/IOpenIdHost.cs b/src/DotNetOpenAuth.OpenId/OpenId/IOpenIdHost.cs index 0c5bf80..cf52fef 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/IOpenIdHost.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/IOpenIdHost.cs @@ -22,7 +22,7 @@ namespace DotNetOpenAuth.OpenId { SecuritySettings SecuritySettings { get; } /// <summary> - /// Gets the web request handler. + /// Gets the factory for various dependencies. /// </summary> IHostFactories HostFactories { get; } } diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Messages/NegativeAssertionResponse.cs b/src/DotNetOpenAuth.OpenId/OpenId/Messages/NegativeAssertionResponse.cs index d67e9fe..1bb52b9 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/Messages/NegativeAssertionResponse.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/Messages/NegativeAssertionResponse.cs @@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId.Messages { using System.Collections.Generic; using System.Linq; using System.Text; + using System.Threading; using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using Validation; @@ -22,9 +23,19 @@ namespace DotNetOpenAuth.OpenId.Messages { /// <summary> /// Initializes a new instance of the <see cref="NegativeAssertionResponse"/> class. /// </summary> - /// <param name="request">The request that the relying party sent.</param> - internal NegativeAssertionResponse(CheckIdRequest request) - : this(request, null) { + /// <param name="request">The request.</param> + private NegativeAssertionResponse(SignedResponseRequest request) + : base(request, GetMode(request)) { + } + + /// <summary> + /// Initializes a new instance of the <see cref="NegativeAssertionResponse"/> class. + /// </summary> + /// <param name="version">The version.</param> + /// <param name="relyingPartyReturnTo">The relying party return to.</param> + /// <param name="mode">The value of the openid.mode parameter.</param> + internal NegativeAssertionResponse(Version version, Uri relyingPartyReturnTo, string mode) + : base(version, relyingPartyReturnTo, mode) { } /// <summary> @@ -32,24 +43,17 @@ namespace DotNetOpenAuth.OpenId.Messages { /// </summary> /// <param name="request">The request that the relying party sent.</param> /// <param name="channel">The channel to use to simulate construction of the user_setup_url, if applicable. May be null, but the user_setup_url will not be constructed.</param> - internal NegativeAssertionResponse(SignedResponseRequest request, Channel channel) - : base(request, GetMode(request)) { + internal static async Task<NegativeAssertionResponse> CreateAsync(SignedResponseRequest request, CancellationToken cancellationToken, Channel channel = null) { + var result = new NegativeAssertionResponse(request); + // If appropriate, and when we're provided with a channel to do it, // go ahead and construct the user_setup_url - if (this.Version.Major < 2 && request.Immediate && channel != null) { + if (result.Version.Major < 2 && request.Immediate && channel != null) { // All requests are CheckIdRequests in OpenID 1.x, so this cast should be safe. - this.UserSetupUrl = ConstructUserSetupUrl((CheckIdRequest)request, channel); + result.UserSetupUrl = await ConstructUserSetupUrlAsync((CheckIdRequest)request, channel, cancellationToken); } - } - /// <summary> - /// Initializes a new instance of the <see cref="NegativeAssertionResponse"/> class. - /// </summary> - /// <param name="version">The version.</param> - /// <param name="relyingPartyReturnTo">The relying party return to.</param> - /// <param name="mode">The value of the openid.mode parameter.</param> - internal NegativeAssertionResponse(Version version, Uri relyingPartyReturnTo, string mode) - : base(version, relyingPartyReturnTo, mode) { + return result; } /// <summary> @@ -114,7 +118,7 @@ namespace DotNetOpenAuth.OpenId.Messages { /// <param name="immediateRequest">The immediate request.</param> /// <param name="channel">The channel to use to simulate construction of the message.</param> /// <returns>The value to use for the user_setup_url parameter.</returns> - private static Uri ConstructUserSetupUrl(CheckIdRequest immediateRequest, Channel channel) { + private static async Task<Uri> ConstructUserSetupUrlAsync(CheckIdRequest immediateRequest, Channel channel, CancellationToken cancellationToken) { Requires.NotNull(immediateRequest, "immediateRequest"); Requires.NotNull(channel, "channel"); ErrorUtilities.VerifyInternal(immediateRequest.Immediate, "Only immediate requests should be sent here."); @@ -124,7 +128,7 @@ namespace DotNetOpenAuth.OpenId.Messages { setupRequest.ReturnTo = immediateRequest.ReturnTo; setupRequest.Realm = immediateRequest.Realm; setupRequest.AssociationHandle = immediateRequest.AssociationHandle; - var response = channel.PrepareResponse(setupRequest); + var response = await channel.PrepareResponseAsync(setupRequest, cancellationToken); return response.GetDirectUriRequest(); } diff --git a/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs b/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs index f0ed946..f8d542d 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/OpenIdUtilities.cs @@ -187,24 +187,36 @@ namespace DotNetOpenAuth.OpenId { internal static HttpClient CreateHttpClient(this IHostFactories hostFactories, bool requireSsl, RequestCachePolicy cachePolicy = null) { Requires.NotNull(hostFactories, "hostFactories"); - var handler = hostFactories.CreateHttpMessageHandler(); - var webRequestHandler = handler as WebRequestHandler; - var untrustedHandler = handler as UntrustedWebRequestHandler; - if (webRequestHandler != null) { - if (cachePolicy != null) { - webRequestHandler.CachePolicy = cachePolicy; - } - } else if (untrustedHandler != null) { - if (cachePolicy != null) { - untrustedHandler.CachePolicy = cachePolicy; - } + var rootHandler = hostFactories.CreateHttpMessageHandler(); + var handler = rootHandler; + do { + var webRequestHandler = handler as WebRequestHandler; + var untrustedHandler = handler as UntrustedWebRequestHandler; + var delegatingHandler = handler as DelegatingHandler; + if (webRequestHandler != null) { + if (cachePolicy != null) { + webRequestHandler.CachePolicy = cachePolicy; + } - untrustedHandler.IsSslRequired = requireSsl; - } else { - Logger.Http.DebugFormat("Unable to set cache policy on unsupported {0}.", handler.GetType().FullName); + break; + } else if (untrustedHandler != null) { + if (cachePolicy != null) { + untrustedHandler.CachePolicy = cachePolicy; + } + + untrustedHandler.IsSslRequired = requireSsl; + break; + } else if (delegatingHandler != null) { + handler = delegatingHandler.InnerHandler; + } else { + Logger.Http.DebugFormat("Unable to set cache policy on unsupported {0}.", handler.GetType().FullName); + break; + } } + while (true); + - return hostFactories.CreateHttpClient(handler); + return hostFactories.CreateHttpClient(rootHandler); } internal static Uri GetDirectUriRequest(this HttpResponseMessage response) { diff --git a/src/DotNetOpenAuth.OpenId/OpenId/RelyingParty/IAuthenticationRequest.cs b/src/DotNetOpenAuth.OpenId/OpenId/RelyingParty/IAuthenticationRequest.cs index 35a92e1..34a2595 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/RelyingParty/IAuthenticationRequest.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/RelyingParty/IAuthenticationRequest.cs @@ -10,6 +10,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System.Linq; using System.Net.Http; using System.Text; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Messages; @@ -25,12 +27,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { AuthenticationRequestMode Mode { get; set; } /// <summary> - /// Gets the HTTP response the relying party should send to the user agent - /// to redirect it to the OpenID Provider to start the OpenID authentication process. - /// </summary> - HttpResponseMessage RedirectingResponse { get; } - - /// <summary> /// Gets the URL that the user agent will return to after authentication /// completes or fails at the Provider. /// </summary> @@ -174,12 +170,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { void AddExtension(IOpenIdMessageExtension extension); /// <summary> - /// Redirects the user agent to the provider for authentication. - /// Execution of the current page terminates after this call. + /// Gets the HTTP response the relying party should send to the user agent + /// to redirect it to the OpenID Provider to start the OpenID authentication process. /// </summary> - /// <remarks> - /// This method requires an ASP.NET HttpContext. - /// </remarks> - void RedirectToProvider(); + Task<HttpResponseMessage> GetRedirectingResponseAsync(CancellationToken cancellationToken); } } diff --git a/src/DotNetOpenAuth.OpenId/OpenId/UntrustedWebRequestHandler.cs b/src/DotNetOpenAuth.OpenId/OpenId/UntrustedWebRequestHandler.cs index c61ac7f..25d4bb6 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/UntrustedWebRequestHandler.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/UntrustedWebRequestHandler.cs @@ -207,6 +207,31 @@ namespace DotNetOpenAuth.OpenId { return client; } + /// <summary> + /// Determines whether an exception was thrown because of the remote HTTP server returning HTTP 417 Expectation Failed. + /// </summary> + /// <param name="ex">The caught exception.</param> + /// <returns> + /// <c>true</c> if the failure was originally caused by a 417 Exceptation Failed error; otherwise, <c>false</c>. + /// </returns> + internal static bool IsExceptionFrom417ExpectationFailed(Exception ex) { + while (ex != null) { + WebException webEx = ex as WebException; + if (webEx != null) { + HttpWebResponse response = webEx.Response as HttpWebResponse; + if (response != null) { + if (response.StatusCode == HttpStatusCode.ExpectationFailed) { + return true; + } + } + } + + ex = ex.InnerException; + } + + return false; + } + protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { this.EnsureAllowableRequestUri(request.RequestUri); @@ -226,9 +251,24 @@ namespace DotNetOpenAuth.OpenId { ErrorUtilities.VerifyProtocol(request.Method != HttpMethod.Post, MessagingStrings.UntrustedRedirectsOnPOSTNotSupported); Uri redirectUri = new Uri(request.RequestUri, response.Headers.Location); request = request.Clone(redirectUri); - } else { - return response; + continue; } + + if (response.StatusCode == HttpStatusCode.ExpectationFailed) { + // Some OpenID servers doesn't understand the Expect header and send 417 error back. + // If this server just failed from that, alter the ServicePoint for this server + // so that we don't send that header again next time (whenever that is). + // "Expect: 100-Continue" HTTP header. (see Google Code Issue 72) + // We don't want to blindly set all ServicePoints to not use the Expect header + // as that would be a security hole allowing any visitor to a web site change + // the web site's global behavior when calling that host. + // TODO: verify that this still works in DNOA 5.0 + var servicePoint = ServicePointManager.FindServicePoint(request.RequestUri); + Logger.Http.InfoFormat("HTTP POST to {0} resulted in 417 Expectation Failed. Changing ServicePoint to not use Expect: Continue next time.", request.RequestUri); + servicePoint.Expect100Continue = false; + } + + return response; } throw ErrorUtilities.ThrowProtocol(MessagingStrings.TooManyRedirects, originalRequestUri); |