diff options
Diffstat (limited to 'src/DotNetOpenAuth.OpenId.RelyingParty')
12 files changed, 253 insertions, 202 deletions
diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/DotNetOpenAuth.OpenId.RelyingParty.csproj b/src/DotNetOpenAuth.OpenId.RelyingParty/DotNetOpenAuth.OpenId.RelyingParty.csproj index 43fd0ae..5e0eeca 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/DotNetOpenAuth.OpenId.RelyingParty.csproj +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/DotNetOpenAuth.OpenId.RelyingParty.csproj @@ -79,6 +79,8 @@ </ItemGroup> <ItemGroup> <Reference Include="System" /> + <Reference Include="System.Net.Http" /> + <Reference Include="System.Net.Http.WebRequest" /> <Reference Include="Validation"> <HintPath>..\packages\Validation.2.0.1.12362\lib\portable-windows8+net40+sl5+windowsphone8\Validation.dll</HintPath> <Private>True</Private> diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/RelyingPartySecurityOptions.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/RelyingPartySecurityOptions.cs index b9328dd..01aa16e 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/RelyingPartySecurityOptions.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/RelyingPartySecurityOptions.cs @@ -5,6 +5,8 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OpenId.ChannelElements { + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.RelyingParty; @@ -13,6 +15,11 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// Helps ensure compliance to some properties in the <see cref="RelyingPartySecuritySettings"/>. /// </summary> internal class RelyingPartySecurityOptions : IChannelBindingElement { + private static readonly Task<MessageProtections?> NullTask = Task.FromResult<MessageProtections?>(null); + + private static readonly Task<MessageProtections?> NoneTask = + Task.FromResult<MessageProtections?>(MessageProtections.None); + /// <summary> /// The security settings that are active on the relying party. /// </summary> @@ -58,8 +65,8 @@ 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) { - return null; + public Task<MessageProtections?> ProcessOutgoingMessageAsync(IProtocolMessage message, CancellationToken cancellationToken) { + return NullTask; } /// <summary> @@ -79,7 +86,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 positiveAssertion = message as PositiveAssertionResponse; if (positiveAssertion != null) { ErrorUtilities.VerifyProtocol( @@ -87,10 +94,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { positiveAssertion.LocalIdentifier == positiveAssertion.ClaimedIdentifier, OpenIdStrings.DelegatingIdentifiersNotAllowed); - return MessageProtections.None; + return NoneTask; } - return null; + return NullTask; } #endregion diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/RelyingPartySigningBindingElement.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/RelyingPartySigningBindingElement.cs index 3ec2eee..6a6dee2 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/RelyingPartySigningBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/RelyingPartySigningBindingElement.cs @@ -9,6 +9,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Collections.Generic; using System.Linq; using System.Text; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OpenId.Messages; @@ -78,12 +80,12 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// <returns> /// The applied protections. /// </returns> - protected override MessageProtections VerifySignatureByUnrecognizedHandle(IProtocolMessage message, ITamperResistantOpenIdMessage signedMessage, MessageProtections protectionsApplied) { + protected override async Task<MessageProtections> VerifySignatureByUnrecognizedHandleAsync(IProtocolMessage message, ITamperResistantOpenIdMessage signedMessage, MessageProtections protectionsApplied, CancellationToken cancellationToken) { // We did not recognize the association the provider used to sign the message. // Ask the provider to check the signature then. var indirectSignedResponse = (IndirectSignedResponse)signedMessage; var checkSignatureRequest = new CheckAuthenticationRequest(indirectSignedResponse, this.Channel); - var checkSignatureResponse = this.Channel.Request<CheckAuthenticationResponse>(checkSignatureRequest); + var checkSignatureResponse = await this.Channel.RequestAsync<CheckAuthenticationResponse>(checkSignatureRequest, cancellationToken); if (!checkSignatureResponse.IsValid) { Logger.Bindings.Error("Provider reports signature verification failed."); throw new InvalidSignatureException(message); diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/ReturnToNonceBindingElement.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/ReturnToNonceBindingElement.cs index c459487..d71a086 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/ReturnToNonceBindingElement.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/ChannelElements/ReturnToNonceBindingElement.cs @@ -9,6 +9,8 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { using System.Collections.Generic; using System.Linq; using System.Text; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OpenId.Messages; @@ -46,6 +48,11 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { /// only on the RP side and only on some messages.</para> /// </remarks> internal class ReturnToNonceBindingElement : IChannelBindingElement { + private static readonly Task<MessageProtections?> NullTask = Task.FromResult<MessageProtections?>(null); + + private static readonly Task<MessageProtections?> ReplayProtectionTask = + Task.FromResult<MessageProtections?>(MessageProtections.ReplayProtection); + /// <summary> /// The context within which return_to nonces must be unique -- they all go into the same bucket. /// </summary> @@ -136,17 +143,17 @@ 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) { // We only add a nonce to some auth requests. SignedResponseRequest request = message as SignedResponseRequest; if (this.UseRequestNonce(request)) { request.AddReturnToArguments(Protocol.ReturnToNonceParameter, CustomNonce.NewNonce().Serialize()); request.SignReturnTo = true; // a nonce without a signature is completely pointless - return MessageProtections.ReplayProtection; + return ReplayProtectionTask; } - return null; + return NullTask; } /// <summary> @@ -166,7 +173,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 (this.UseRequestNonce(response)) { if (!response.ReturnToParametersSignatureValidated) { @@ -190,10 +197,10 @@ namespace DotNetOpenAuth.OpenId.ChannelElements { throw new ReplayedMessageException(message); } - return MessageProtections.ReplayProtection; + return ReplayProtectionTask; } - return null; + return NullTask; } #endregion diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/HostMetaDiscoveryService.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/HostMetaDiscoveryService.cs index 1871f19..6a517ad 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/HostMetaDiscoveryService.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/HostMetaDiscoveryService.cs @@ -13,12 +13,17 @@ namespace DotNetOpenAuth.OpenId { using System.IO; using System.Linq; using System.Net; + using System.Net.Cache; + using System.Net.Http; + using System.Net.Http.Headers; using System.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; + using System.Threading; + using System.Threading.Tasks; using System.Xml; using System.Xml.XPath; using DotNetOpenAuth.Configuration; @@ -62,10 +67,15 @@ namespace DotNetOpenAuth.OpenId { /// <summary> /// Initializes a new instance of the <see cref="HostMetaDiscoveryService"/> class. /// </summary> - public HostMetaDiscoveryService() { + public HostMetaDiscoveryService(IHostFactories hostFactories) { + Requires.NotNull(hostFactories, "hostFactories"); + this.TrustedHostMetaProxies = new List<HostMetaProxy>(); + this.HostFactories = hostFactories; } + public IHostFactories HostFactories { get; private set; } + /// <summary> /// Gets the set of URI templates to use to contact host-meta hosting proxies /// for domain discovery. @@ -106,27 +116,25 @@ namespace DotNetOpenAuth.OpenId { /// <returns> /// A sequence of service endpoints yielded by discovery. Must not be null, but may be empty. /// </returns> - public IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, out bool abortDiscoveryChain) { - abortDiscoveryChain = false; - + public async Task<IdentifierDiscoveryServiceResult> DiscoverAsync(Identifier identifier, CancellationToken cancellationToken) { // Google Apps are always URIs -- not XRIs. var uriIdentifier = identifier as UriIdentifier; if (uriIdentifier == null) { - return Enumerable.Empty<IdentifierDiscoveryResult>(); + return new IdentifierDiscoveryServiceResult(Enumerable.Empty<IdentifierDiscoveryResult>()); } var results = new List<IdentifierDiscoveryResult>(); - string signingHost; - using (var response = GetXrdsResponse(uriIdentifier, requestHandler, out signingHost)) { - if (response != null) { + using (var response = await this.GetXrdsResponseAsync(uriIdentifier, cancellationToken)) { + if (response.Result != null) { try { var readerSettings = MessagingUtilities.CreateUntrustedXmlReaderSettings(); - var document = new XrdsDocument(XmlReader.Create(response.ResponseStream, readerSettings)); - ValidateXmlDSig(document, uriIdentifier, response, signingHost); + var responseStream = await response.Result.Content.ReadAsStreamAsync(); + var document = new XrdsDocument(XmlReader.Create(responseStream, readerSettings)); + await ValidateXmlDSigAsync(document, uriIdentifier, response.Result, response.SigningHost); var xrds = GetXrdElements(document, uriIdentifier.Uri.Host); // Look for claimed identifier template URIs for an additional XRDS document. - results.AddRange(GetExternalServices(xrds, uriIdentifier, requestHandler)); + results.AddRange(await this.GetExternalServicesAsync(xrds, uriIdentifier, cancellationToken)); // If we couldn't find any claimed identifiers, look for OP identifiers. // Normally this would be the opposite (OP Identifiers take precedence over @@ -136,15 +144,13 @@ namespace DotNetOpenAuth.OpenId { if (results.Count == 0) { results.AddRange(xrds.CreateServiceEndpoints(uriIdentifier, uriIdentifier)); } - - abortDiscoveryChain = true; } catch (XmlException ex) { - Logger.Yadis.ErrorFormat("Error while parsing XRDS document at {0} pointed to by host-meta: {1}", response.FinalUri, ex); + Logger.Yadis.ErrorFormat("Error while parsing XRDS document at {0} pointed to by host-meta: {1}", response.Result.RequestMessage.RequestUri, ex); } } } - return results; + return new IdentifierDiscoveryServiceResult(results, abortDiscoveryChain: true); } #endregion @@ -181,10 +187,9 @@ namespace DotNetOpenAuth.OpenId { /// <param name="identifier">The identifier under discovery.</param> /// <param name="requestHandler">The request handler.</param> /// <returns>The discovered services.</returns> - private static IEnumerable<IdentifierDiscoveryResult> GetExternalServices(IEnumerable<XrdElement> xrds, UriIdentifier identifier, IDirectWebRequestHandler requestHandler) { + private async Task<IEnumerable<IdentifierDiscoveryResult>> GetExternalServicesAsync(IEnumerable<XrdElement> xrds, UriIdentifier identifier, CancellationToken cancellationToken) { Requires.NotNull(xrds, "xrds"); Requires.NotNull(identifier, "identifier"); - Requires.NotNull(requestHandler, "requestHandler"); var results = new List<IdentifierDiscoveryResult>(); foreach (var serviceElement in GetDescribedByServices(xrds)) { @@ -194,10 +199,11 @@ namespace DotNetOpenAuth.OpenId { Uri externalLocation = new Uri(templateNode.Value.Trim().Replace("{%uri}", Uri.EscapeDataString(identifier.Uri.AbsoluteUri))); string nextAuthority = nextAuthorityNode != null ? nextAuthorityNode.Value.Trim() : identifier.Uri.Host; try { - using (var externalXrdsResponse = GetXrdsResponse(identifier, requestHandler, externalLocation)) { + using (var externalXrdsResponse = await this.GetXrdsResponseAsync(identifier, externalLocation, cancellationToken)) { var readerSettings = MessagingUtilities.CreateUntrustedXmlReaderSettings(); - XrdsDocument externalXrds = new XrdsDocument(XmlReader.Create(externalXrdsResponse.ResponseStream, readerSettings)); - ValidateXmlDSig(externalXrds, identifier, externalXrdsResponse, nextAuthority); + var responseStream = await externalXrdsResponse.Content.ReadAsStreamAsync(); + XrdsDocument externalXrds = new XrdsDocument(XmlReader.Create(responseStream, readerSettings)); + await ValidateXmlDSigAsync(externalXrds, identifier, externalXrdsResponse, nextAuthority); results.AddRange(GetXrdElements(externalXrds, identifier).CreateServiceEndpoints(identifier, identifier)); } } catch (ProtocolException ex) { @@ -220,7 +226,7 @@ namespace DotNetOpenAuth.OpenId { /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <exception cref="ProtocolException">Thrown if the XRDS document has an invalid or a missing signature.</exception> [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "XmlDSig", Justification = "xml")] - private static void ValidateXmlDSig(XrdsDocument document, UriIdentifier identifier, IncomingWebResponse response, string signingHost) { + private static async Task ValidateXmlDSigAsync(XrdsDocument document, UriIdentifier identifier, HttpResponseMessage response, string signingHost) { Requires.NotNull(document, "document"); Requires.NotNull(identifier, "identifier"); Requires.NotNull(response, "response"); @@ -246,11 +252,12 @@ namespace DotNetOpenAuth.OpenId { ErrorUtilities.VerifyProtocol(string.Equals(hostName, signingHost, StringComparison.OrdinalIgnoreCase), OpenIdStrings.MisdirectedSigningCertificate, hostName, signingHost); // Verify the signature itself - byte[] signature = Convert.FromBase64String(response.Headers["Signature"]); + byte[] signature = Convert.FromBase64String(response.Headers.GetValues("Signature").First()); var provider = (RSACryptoServiceProvider)certs.First().PublicKey.Key; - byte[] data = new byte[response.ResponseStream.Length]; - response.ResponseStream.Seek(0, SeekOrigin.Begin); - response.ResponseStream.Read(data, 0, data.Length); + var responseStream = await response.Content.ReadAsStreamAsync(); + byte[] data = new byte[responseStream.Length]; + responseStream.Seek(0, SeekOrigin.Begin); + await responseStream.ReadAsync(data, 0, data.Length); ErrorUtilities.VerifyProtocol(provider.VerifyData(data, "SHA1", signature), OpenIdStrings.InvalidDSig); } @@ -292,21 +299,28 @@ namespace DotNetOpenAuth.OpenId { /// A HTTP response carrying an XRDS document. /// </returns> /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception> - private static IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, Uri xrdsLocation) { + private async Task<HttpResponseMessage> GetXrdsResponseAsync(UriIdentifier identifier, Uri xrdsLocation, CancellationToken cancellationToken) { Requires.NotNull(identifier, "identifier"); - Requires.NotNull(requestHandler, "requestHandler"); Requires.NotNull(xrdsLocation, "xrdsLocation"); - var request = (HttpWebRequest)WebRequest.Create(xrdsLocation); - request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy; - request.Accept = ContentTypes.Xrds; - var options = identifier.IsDiscoverySecureEndToEnd ? DirectWebRequestOptions.RequireSsl : DirectWebRequestOptions.None; - var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan); - if (!string.Equals(response.ContentType.MediaType, ContentTypes.Xrds, StringComparison.Ordinal)) { - Logger.Yadis.WarnFormat("Host-meta pointed to XRDS at {0}, but Content-Type at that URL was unexpected value '{1}'.", xrdsLocation, response.ContentType); - } + using (var httpClient = this.HostFactories.CreateHttpClient(identifier.IsDiscoverySecureEndToEnd, Yadis.IdentifierDiscoveryCachePolicy)) { + var request = new HttpRequestMessage(HttpMethod.Get, xrdsLocation); + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(ContentTypes.Xrds)); + var response = await httpClient.SendAsync(request, cancellationToken); + try { + if (!string.Equals(response.Content.Headers.ContentType.MediaType, ContentTypes.Xrds, StringComparison.Ordinal)) { + Logger.Yadis.WarnFormat( + "Host-meta pointed to XRDS at {0}, but Content-Type at that URL was unexpected value '{1}'.", + xrdsLocation, + response.Content.Headers.ContentType); + } - return response; + return response; + } catch { + response.Dispose(); + throw; + } + } } /// <summary> @@ -358,17 +372,16 @@ namespace DotNetOpenAuth.OpenId { /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <returns>A HTTP response carrying an XRDS document, or <c>null</c> if one could not be obtained.</returns> /// <exception cref="ProtocolException">Thrown if the XRDS document could not be obtained.</exception> - private IncomingWebResponse GetXrdsResponse(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { + private async Task<ResultWithSigningHost<HttpResponseMessage>> GetXrdsResponseAsync(UriIdentifier identifier, CancellationToken cancellationToken) { Requires.NotNull(identifier, "identifier"); - Requires.NotNull(requestHandler, "requestHandler"); - Uri xrdsLocation = this.GetXrdsLocation(identifier, requestHandler, out signingHost); - if (xrdsLocation == null) { - return null; - } - var response = GetXrdsResponse(identifier, requestHandler, xrdsLocation); + var result = await this.GetXrdsLocationAsync(identifier, cancellationToken); + if (result.Result == null) { + return new ResultWithSigningHost<HttpResponseMessage>(); + } - return response; + var response = await this.GetXrdsResponseAsync(identifier, result.Result, cancellationToken); + return new ResultWithSigningHost<HttpResponseMessage>(response, result.SigningHost); } /// <summary> @@ -378,26 +391,26 @@ namespace DotNetOpenAuth.OpenId { /// <param name="requestHandler">The request handler.</param> /// <param name="signingHost">The host name on the certificate that should be used to verify the signature in the XRDS.</param> /// <returns>An absolute URI, or <c>null</c> if one could not be determined.</returns> - private Uri GetXrdsLocation(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { + private async Task<ResultWithSigningHost<Uri>> GetXrdsLocationAsync(UriIdentifier identifier, CancellationToken cancellationToken) { Requires.NotNull(identifier, "identifier"); - Requires.NotNull(requestHandler, "requestHandler"); - using (var hostMetaResponse = this.GetHostMeta(identifier, requestHandler, out signingHost)) { - if (hostMetaResponse == null) { - return null; + + using (var hostMetaResponse = await this.GetHostMetaAsync(identifier, cancellationToken)) { + if (hostMetaResponse.Result == null) { + return new ResultWithSigningHost<Uri>(); } - using (var sr = hostMetaResponse.GetResponseReader()) { - string line = sr.ReadLine(); + using (var sr = new StreamReader(await hostMetaResponse.Result.Content.ReadAsStreamAsync())) { + string line = await sr.ReadLineAsync(); Match m = HostMetaLink.Match(line); if (m.Success) { Uri location = new Uri(m.Groups["location"].Value); - Logger.Yadis.InfoFormat("Found link to XRDS at {0} in host-meta document {1}.", location, hostMetaResponse.FinalUri); - return location; + Logger.Yadis.InfoFormat("Found link to XRDS at {0} in host-meta document {1}.", location, hostMetaResponse.Result.RequestMessage.RequestUri); + return new ResultWithSigningHost<Uri>(location, hostMetaResponse.SigningHost); } } - Logger.Yadis.WarnFormat("Could not find link to XRDS in host-meta document: {0}", hostMetaResponse.FinalUri); - return null; + Logger.Yadis.WarnFormat("Could not find link to XRDS in host-meta document: {0}", hostMetaResponse.Result.RequestMessage.RequestUri); + return new ResultWithSigningHost<Uri>(); } } @@ -410,35 +423,29 @@ namespace DotNetOpenAuth.OpenId { /// <returns> /// The host-meta response, or <c>null</c> if no host-meta document could be obtained. /// </returns> - private IncomingWebResponse GetHostMeta(UriIdentifier identifier, IDirectWebRequestHandler requestHandler, out string signingHost) { + private async Task<ResultWithSigningHost<HttpResponseMessage>> GetHostMetaAsync(UriIdentifier identifier, CancellationToken cancellationToken) { Requires.NotNull(identifier, "identifier"); - Requires.NotNull(requestHandler, "requestHandler"); - foreach (var hostMetaProxy in this.GetHostMetaLocations(identifier)) { - var hostMetaLocation = hostMetaProxy.GetProxy(identifier); - var request = (HttpWebRequest)WebRequest.Create(hostMetaLocation); - request.CachePolicy = Yadis.IdentifierDiscoveryCachePolicy; - var options = DirectWebRequestOptions.AcceptAllHttpResponses; - if (identifier.IsDiscoverySecureEndToEnd) { - options |= DirectWebRequestOptions.RequireSsl; - } - var response = requestHandler.GetResponse(request, options).GetSnapshot(Yadis.MaximumResultToScan); - try { - if (response.Status == HttpStatusCode.OK) { - Logger.Yadis.InfoFormat("Found host-meta for {0} at: {1}", identifier.Uri.Host, hostMetaLocation); - signingHost = hostMetaProxy.GetSigningHost(identifier); - return response; - } else { - Logger.Yadis.InfoFormat("Could not obtain host-meta for {0} from {1}", identifier.Uri.Host, hostMetaLocation); + + using (var httpClient = this.HostFactories.CreateHttpClient(identifier.IsDiscoverySecureEndToEnd, Yadis.IdentifierDiscoveryCachePolicy)) { + foreach (var hostMetaProxy in this.GetHostMetaLocations(identifier)) { + var hostMetaLocation = hostMetaProxy.GetProxy(identifier); + var response = await httpClient.GetAsync(hostMetaLocation, cancellationToken); + try { + if (response.IsSuccessStatusCode) { + Logger.Yadis.InfoFormat("Found host-meta for {0} at: {1}", identifier.Uri.Host, hostMetaLocation); + return new ResultWithSigningHost<HttpResponseMessage>(response, hostMetaProxy.GetSigningHost(identifier)); + } else { + Logger.Yadis.InfoFormat("Could not obtain host-meta for {0} from {1}", identifier.Uri.Host, hostMetaLocation); + response.Dispose(); + } + } catch { response.Dispose(); + throw; } - } catch { - response.Dispose(); - throw; } } - signingHost = null; - return null; + return new ResultWithSigningHost<HttpResponseMessage>(); } /// <summary> @@ -546,5 +553,22 @@ namespace DotNetOpenAuth.OpenId { return this.ProxyFormat.GetHashCode(); } } + + private struct ResultWithSigningHost<T> : IDisposable { + internal ResultWithSigningHost(T result, string signingHost) + : this() { + this.Result = result; + this.SigningHost = signingHost; + } + + public T Result { get; private set; } + + public string SigningHost { get; private set; } + + public void Dispose() { + var disposable = this.Result as IDisposable; + disposable.DisposeIfNotNull(); + } + } } } diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/Interop/OpenIdRelyingPartyShim.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/Interop/OpenIdRelyingPartyShim.cs index eb37d86..9568c1d 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/Interop/OpenIdRelyingPartyShim.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/Interop/OpenIdRelyingPartyShim.cs @@ -11,6 +11,7 @@ namespace DotNetOpenAuth.OpenId.Interop { using System.IO; using System.Runtime.InteropServices; using System.Text; + using System.Threading; using System.Web; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration; @@ -68,8 +69,9 @@ namespace DotNetOpenAuth.OpenId.Interop { /// <exception cref="ProtocolException">Thrown if no OpenID endpoint could be found.</exception> [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "COM requires primitive types")] public string CreateRequest(string userSuppliedIdentifier, string realm, string returnToUrl) { - var request = relyingParty.CreateRequest(userSuppliedIdentifier, realm, new Uri(returnToUrl)); - return request.RedirectingResponse.GetDirectUriRequest(relyingParty.Channel).AbsoluteUri; + var request = relyingParty.CreateRequestAsync(userSuppliedIdentifier, realm, new Uri(returnToUrl)).Result; + var response = request.GetRedirectingResponseAsync(CancellationToken.None).Result; + return response.GetDirectUriRequest().AbsoluteUri; } /// <summary> @@ -91,7 +93,7 @@ namespace DotNetOpenAuth.OpenId.Interop { /// <exception cref="ProtocolException">Thrown if no OpenID endpoint could be found.</exception> [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "COM requires primitive types")] public string CreateRequestWithSimpleRegistration(string userSuppliedIdentifier, string realm, string returnToUrl, string optionalSreg, string requiredSreg) { - var request = relyingParty.CreateRequest(userSuppliedIdentifier, realm, new Uri(returnToUrl)); + var request = relyingParty.CreateRequestAsync(userSuppliedIdentifier, realm, new Uri(returnToUrl)).Result; ClaimsRequest sreg = new ClaimsRequest(); if (!string.IsNullOrEmpty(optionalSreg)) { @@ -101,7 +103,8 @@ namespace DotNetOpenAuth.OpenId.Interop { sreg.SetProfileRequestFromList(requiredSreg.Split(','), DemandLevel.Require); } request.AddExtension(sreg); - return request.RedirectingResponse.GetDirectUriRequest(relyingParty.Channel).AbsoluteUri; + var response = request.GetRedirectingResponseAsync(CancellationToken.None).Result; + return response.GetDirectUriRequest().AbsoluteUri; } /// <summary> @@ -120,7 +123,7 @@ namespace DotNetOpenAuth.OpenId.Interop { } HttpRequestBase requestInfo = new HttpRequestInfo(method, new Uri(url), form: formMap); - var response = relyingParty.GetResponse(requestInfo); + var response = relyingParty.GetResponseAsync(requestInfo, CancellationToken.None).Result; if (response != null) { return new AuthenticationResponseShim(response); } diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/AssociationManager.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/AssociationManager.cs index dfb307b..14566e1 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/AssociationManager.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/AssociationManager.cs @@ -11,6 +11,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System.Net; using System.Security; using System.Text; + using System.Threading; + using System.Threading.Tasks; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.ChannelElements; using DotNetOpenAuth.OpenId.Messages; @@ -131,8 +133,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="provider">The provider to get an association for.</param> /// <returns>The existing or new association; <c>null</c> if none existed and one could not be created.</returns> - internal Association GetOrCreateAssociation(IProviderEndpoint provider) { - return this.GetExistingAssociation(provider) ?? this.CreateNewAssociation(provider); + internal async Task<Association> GetOrCreateAssociationAsync(IProviderEndpoint provider, CancellationToken cancellationToken) { + return this.GetExistingAssociation(provider) ?? await this.CreateNewAssociationAsync(provider, cancellationToken); } /// <summary> @@ -148,7 +150,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// association store. /// Any new association is automatically added to the <see cref="associationStore"/>. /// </remarks> - private Association CreateNewAssociation(IProviderEndpoint provider) { + private async Task<Association> CreateNewAssociationAsync(IProviderEndpoint provider, CancellationToken cancellationToken) { Requires.NotNull(provider, "provider"); // If there is no association store, there is no point in creating an association. @@ -160,7 +162,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { var associateRequest = AssociateRequestRelyingParty.Create(this.securitySettings, provider); const int RenegotiateRetries = 1; - return this.CreateNewAssociation(provider, associateRequest, RenegotiateRetries); + return await this.CreateNewAssociationAsync(provider, associateRequest, RenegotiateRetries, cancellationToken); } catch (VerificationException ex) { // See Trac ticket #163. In partial trust host environments, the // Diffie-Hellman implementation we're using for HTTP OP endpoints @@ -182,7 +184,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// The newly created association, or null if no association can be created with /// the given Provider given the current security settings. /// </returns> - private Association CreateNewAssociation(IProviderEndpoint provider, AssociateRequest associateRequest, int retriesRemaining) { + private async Task<Association> CreateNewAssociationAsync(IProviderEndpoint provider, AssociateRequest associateRequest, int retriesRemaining, CancellationToken cancellationToken) { Requires.NotNull(provider, "provider"); if (associateRequest == null || retriesRemaining < 0) { @@ -191,8 +193,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { return null; } + Exception exception = null; try { - var associateResponse = this.channel.Request(associateRequest); + var associateResponse = await this.channel.RequestAsync(associateRequest, cancellationToken); var associateSuccessfulResponse = associateResponse as IAssociateSuccessfulResponseRelyingParty; var associateUnsuccessfulResponse = associateResponse as AssociateUnsuccessfulResponse; if (associateSuccessfulResponse != null) { @@ -224,23 +227,27 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { associateUnsuccessfulResponse.SessionType); associateRequest = AssociateRequestRelyingParty.Create(this.securitySettings, provider, associateUnsuccessfulResponse.AssociationType, associateUnsuccessfulResponse.SessionType); - return this.CreateNewAssociation(provider, associateRequest, retriesRemaining - 1); + return await this.CreateNewAssociationAsync(provider, associateRequest, retriesRemaining - 1, cancellationToken); } else { throw new ProtocolException(MessagingStrings.UnexpectedMessageReceivedOfMany); } } catch (ProtocolException ex) { - // If the association failed because the remote server can't handle Expect: 100 Continue headers, - // then our web request handler should have already accomodated for future calls. Go ahead and - // immediately make one of those future calls now to try to get the association to succeed. - if (StandardWebRequestHandler.IsExceptionFrom417ExpectationFailed(ex)) { - return this.CreateNewAssociation(provider, associateRequest, retriesRemaining - 1); - } + exception = ex; + } - // Since having associations with OPs is not totally critical, we'll log and eat - // the exception so that auth may continue in dumb mode. - Logger.OpenId.ErrorFormat("An error occurred while trying to create an association with {0}. {1}", provider.Uri, ex); - return null; + Assumes.NotNull(exception); + + // If the association failed because the remote server can't handle Expect: 100 Continue headers, + // then our web request handler should have already accomodated for future calls. Go ahead and + // immediately make one of those future calls now to try to get the association to succeed. + if (UntrustedWebRequestHandler.IsExceptionFrom417ExpectationFailed(exception)) { + return await this.CreateNewAssociationAsync(provider, associateRequest, retriesRemaining - 1, cancellationToken); } + + // Since having associations with OPs is not totally critical, we'll log and eat + // the exception so that auth may continue in dumb mode. + Logger.OpenId.ErrorFormat("An error occurred while trying to create an association with {0}. {1}", provider.Uri, exception); + return null; } } } diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/AuthenticationRequest.cs index 92af297..f9abc37 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/AuthenticationRequest.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/AuthenticationRequest.cs @@ -9,10 +9,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; + using System.Net.Http; + using System.ServiceModel.Channels; using System.Text; using System.Threading; + using System.Threading.Tasks; using System.Web; - using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.ChannelElements; @@ -95,14 +97,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// to redirect it to the OpenID Provider to start the OpenID authentication process. /// </summary> /// <value></value> - public OutgoingWebResponse RedirectingResponse { - get { - foreach (var behavior in this.RelyingParty.Behaviors) { - behavior.OnOutgoingAuthenticationRequest(this); - } - - return this.RelyingParty.Channel.PrepareResponse(this.CreateRequestMessage()); + public async Task<HttpResponseMessage> GetRedirectingResponseAsync(CancellationToken cancellationToken) { + foreach (var behavior in this.RelyingParty.Behaviors) { + behavior.OnOutgoingAuthenticationRequest(this); } + + var request = await this.CreateRequestMessageAsync(cancellationToken); + return await this.RelyingParty.Channel.PrepareResponseAsync(request, cancellationToken); } /// <summary> @@ -293,16 +294,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { this.extensions.Add(extension); } - /// <summary> - /// Redirects the user agent to the provider for authentication. - /// </summary> - /// <remarks> - /// This method requires an ASP.NET HttpContext. - /// </remarks> - public void RedirectToProvider() { - this.RedirectingResponse.Send(); - } - #endregion /// <summary> @@ -318,7 +309,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// A sequence of authentication requests, any of which constitutes a valid identity assertion on the Claimed Identifier. /// Never null, but may be empty. /// </returns> - internal static IEnumerable<AuthenticationRequest> Create(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, bool createNewAssociationsAsNeeded) { + internal static async Task<IEnumerable<AuthenticationRequest>> CreateAsync(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, bool createNewAssociationsAsNeeded, CancellationToken cancellationToken) { Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier"); Requires.NotNull(relyingParty, "relyingParty"); Requires.NotNull(realm, "realm"); @@ -360,7 +351,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // Perform discovery right now (not deferred). IEnumerable<IdentifierDiscoveryResult> serviceEndpoints; try { - var results = relyingParty.Discover(userSuppliedIdentifier).CacheGeneratedResults(); + var results = (await relyingParty.DiscoverAsync(userSuppliedIdentifier, cancellationToken)).CacheGeneratedResults(); // If any OP Identifier service elements were found, we must not proceed // to use any Claimed Identifier services, per OpenID 2.0 sections 7.3.2.2 and 11.2. @@ -381,7 +372,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { serviceEndpoints = relyingParty.SecuritySettings.FilterEndpoints(serviceEndpoints); // Call another method that defers request generation. - return CreateInternal(userSuppliedIdentifier, relyingParty, realm, returnToUrl, serviceEndpoints, createNewAssociationsAsNeeded); + return await CreateInternalAsync(userSuppliedIdentifier, relyingParty, realm, returnToUrl, serviceEndpoints, createNewAssociationsAsNeeded, cancellationToken); } /// <summary> @@ -401,9 +392,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// based on the properties in this instance. /// </summary> /// <returns>The message to send to the Provider.</returns> - internal SignedResponseRequest CreateRequestMessageTestHook() - { - return this.CreateRequestMessage(); + internal Task<SignedResponseRequest> CreateRequestMessageTestHookAsync(CancellationToken cancellationToken) { + return this.CreateRequestMessageAsync(cancellationToken); } /// <summary> @@ -423,18 +413,18 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// All data validation and cleansing steps must have ALREADY taken place /// before calling this method. /// </remarks> - private static IEnumerable<AuthenticationRequest> CreateInternal(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, IEnumerable<IdentifierDiscoveryResult> serviceEndpoints, bool createNewAssociationsAsNeeded) { - // DO NOT USE CODE CONTRACTS IN THIS METHOD, since it uses yield return - ErrorUtilities.VerifyArgumentNotNull(userSuppliedIdentifier, "userSuppliedIdentifier"); - ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty"); - ErrorUtilities.VerifyArgumentNotNull(realm, "realm"); - ErrorUtilities.VerifyArgumentNotNull(serviceEndpoints, "serviceEndpoints"); + private static async Task<IEnumerable<AuthenticationRequest>> CreateInternalAsync(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, IEnumerable<IdentifierDiscoveryResult> serviceEndpoints, bool createNewAssociationsAsNeeded, CancellationToken cancellationToken) { + Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier"); + Requires.NotNull(relyingParty, "relyingParty"); + Requires.NotNull(realm, "realm"); + Requires.NotNull(serviceEndpoints, "serviceEndpoints"); //// // If shared associations are required, then we had better have an association store. ErrorUtilities.VerifyOperation(!relyingParty.SecuritySettings.RequireAssociation || relyingParty.AssociationManager.HasAssociationStore, OpenIdStrings.AssociationStoreRequired); Logger.Yadis.InfoFormat("Performing discovery on user-supplied identifier: {0}", userSuppliedIdentifier); IEnumerable<IdentifierDiscoveryResult> endpoints = FilterAndSortEndpoints(serviceEndpoints, relyingParty); + var results = new List<AuthenticationRequest>(); // Maintain a list of endpoints that we could not form an association with. // We'll fallback to generating requests to these if the ones we CAN create @@ -450,7 +440,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // In some scenarios (like the AJAX control wanting ALL auth requests possible), // we don't want to create associations with every Provider. But we'll use // associations where they are already formed from previous authentications. - association = createNewAssociationsAsNeeded ? relyingParty.AssociationManager.GetOrCreateAssociation(endpoint) : relyingParty.AssociationManager.GetExistingAssociation(endpoint); + association = createNewAssociationsAsNeeded ? await relyingParty.AssociationManager.GetOrCreateAssociationAsync(endpoint, cancellationToken) : relyingParty.AssociationManager.GetExistingAssociation(endpoint); if (association == null && createNewAssociationsAsNeeded) { Logger.OpenId.WarnFormat("Failed to create association with {0}. Skipping to next endpoint.", endpoint.ProviderEndpoint); @@ -461,7 +451,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } } - yield return new AuthenticationRequest(endpoint, realm, returnToUrl, relyingParty); + results.Add(new AuthenticationRequest(endpoint, realm, returnToUrl, relyingParty)); } // Now that we've run out of endpoints that respond to association requests, @@ -481,10 +471,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // because we've already tried. Let's not have it waste time trying again. var authRequest = new AuthenticationRequest(endpoint, realm, returnToUrl, relyingParty); authRequest.associationPreference = AssociationPreference.IfAlreadyEstablished; - yield return authRequest; + results.Add(authRequest); } } } + + return results; } /// <summary> @@ -535,8 +527,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// based on the properties in this instance. /// </summary> /// <returns>The message to send to the Provider.</returns> - private SignedResponseRequest CreateRequestMessage() { - Association association = this.GetAssociation(); + private async Task<SignedResponseRequest> CreateRequestMessageAsync(CancellationToken cancellationToken) { + Association association = await this.GetAssociationAsync(cancellationToken); SignedResponseRequest request; if (!this.IsExtensionOnly) { @@ -566,11 +558,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// Gets the association to use for this authentication request. /// </summary> /// <returns>The association to use; <c>null</c> to use 'dumb mode'.</returns> - private Association GetAssociation() { + private async Task<Association> GetAssociationAsync(CancellationToken cancellationToken) { Association association = null; switch (this.associationPreference) { case AssociationPreference.IfPossible: - association = this.RelyingParty.AssociationManager.GetOrCreateAssociation(this.DiscoveryResult); + association = await this.RelyingParty.AssociationManager.GetOrCreateAssociationAsync(this.DiscoveryResult, cancellationToken); if (association == null) { // Avoid trying to create the association again if the redirecting response // is generated again. diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/Extensions/UIUtilities.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/Extensions/UIUtilities.cs index a5de08b..80bfe65 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/Extensions/UIUtilities.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/Extensions/UIUtilities.cs @@ -6,10 +6,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty.Extensions.UI { using System; - using System.Globalization; - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OpenId.RelyingParty; - using Validation; +using System.Globalization; +using System.Threading; +using System.Threading.Tasks; +using DotNetOpenAuth.Messaging; +using DotNetOpenAuth.OpenId.RelyingParty; +using Validation; /// <summary> /// Constants used in implementing support for the UI extension. @@ -23,12 +25,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty.Extensions.UI { /// <param name="request">The authentication request to place in the window.</param> /// <param name="windowName">The name to assign to the popup window.</param> /// <returns>A string starting with 'window.open' and forming just that one method call.</returns> - internal static string GetWindowPopupScript(OpenIdRelyingParty relyingParty, IAuthenticationRequest request, string windowName) { + internal static async Task<string> GetWindowPopupScriptAsync(OpenIdRelyingParty relyingParty, IAuthenticationRequest request, string windowName, CancellationToken cancellationToken) { Requires.NotNull(relyingParty, "relyingParty"); Requires.NotNull(request, "request"); Requires.NotNullOrEmpty(windowName, "windowName"); - Uri popupUrl = request.RedirectingResponse.GetDirectUriRequest(relyingParty.Channel); + var response = await request.GetRedirectingResponseAsync(cancellationToken); + Uri popupUrl = response.GetDirectUriRequest(); return string.Format( CultureInfo.InvariantCulture, diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs index 2177591..a55e042 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs @@ -14,8 +14,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System.Globalization; using System.Linq; using System.Net; + using System.Net.Http; + using System.Net.Http.Headers; using System.Net.Mime; using System.Text; + using System.Threading; + using System.Threading.Tasks; using System.Web; using DotNetOpenAuth.Configuration; using DotNetOpenAuth.Messaging; @@ -272,11 +276,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> - /// Gets the web request handler to use for discovery and the part of - /// authentication where direct messages are sent to an untrusted remote party. + /// Gets the factory for various dependencies. /// </summary> - IDirectWebRequestHandler IOpenIdHost.WebRequestHandler { - get { return this.Channel.WebRequestHandler; } + IHostFactories IOpenIdHost.HostFactories { + get { return this.channel.HostFactories; } } /// <summary> @@ -288,14 +291,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } /// <summary> - /// Gets the web request handler to use for discovery and the part of - /// authentication where direct messages are sent to an untrusted remote party. - /// </summary> - internal IDirectWebRequestHandler WebRequestHandler { - get { return this.Channel.WebRequestHandler; } - } - - /// <summary> /// Gets the association manager. /// </summary> internal AssociationManager AssociationManager { get; private set; } @@ -339,12 +334,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// an object to send to the user agent to initiate the authentication. /// </returns> /// <exception cref="ProtocolException">Thrown if no OpenID endpoint could be found.</exception> - public IAuthenticationRequest CreateRequest(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) { + public async Task<IAuthenticationRequest> CreateRequestAsync(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier"); Requires.NotNull(realm, "realm"); Requires.NotNull(returnToUrl, "returnToUrl"); try { - return this.CreateRequests(userSuppliedIdentifier, realm, returnToUrl).First(); + return (await this.CreateRequestsAsync(userSuppliedIdentifier, realm, returnToUrl)).First(); } catch (InvalidOperationException ex) { throw ErrorUtilities.Wrap(ex, OpenIdStrings.OpenIdEndpointNotFound); } @@ -371,11 +366,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </remarks> /// <exception cref="ProtocolException">Thrown if no OpenID endpoint could be found.</exception> /// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception> - public IAuthenticationRequest CreateRequest(Identifier userSuppliedIdentifier, Realm realm) { + public async Task<IAuthenticationRequest> CreateRequestAsync(Identifier userSuppliedIdentifier, Realm realm) { Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier"); Requires.NotNull(realm, "realm"); try { - var result = this.CreateRequests(userSuppliedIdentifier, realm).First(); + var result = (await this.CreateRequestsAsync(userSuppliedIdentifier, realm)).First(); Assumes.True(result != null); return result; } catch (InvalidOperationException ex) { @@ -399,10 +394,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </remarks> /// <exception cref="ProtocolException">Thrown if no OpenID endpoint could be found.</exception> /// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception> - public IAuthenticationRequest CreateRequest(Identifier userSuppliedIdentifier) { + public async Task<IAuthenticationRequest> CreateRequestAsync(Identifier userSuppliedIdentifier) { Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier"); try { - return this.CreateRequests(userSuppliedIdentifier).First(); + return (await this.CreateRequestsAsync(userSuppliedIdentifier)).First(); } catch (InvalidOperationException ex) { throw ErrorUtilities.Wrap(ex, OpenIdStrings.OpenIdEndpointNotFound); } @@ -435,12 +430,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <para>No exception is thrown if no OpenID endpoints were discovered. /// An empty enumerable is returned instead.</para> /// </remarks> - public virtual IEnumerable<IAuthenticationRequest> CreateRequests(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl) { + public virtual async Task<IEnumerable<IAuthenticationRequest>> CreateRequestsAsync(Identifier userSuppliedIdentifier, Realm realm, Uri returnToUrl, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier"); Requires.NotNull(realm, "realm"); Requires.NotNull(returnToUrl, "returnToUrl"); - return AuthenticationRequest.Create(userSuppliedIdentifier, this, realm, returnToUrl, true).Cast<IAuthenticationRequest>().CacheGeneratedResults(); + var requests = await AuthenticationRequest.CreateAsync(userSuppliedIdentifier, this, realm, returnToUrl, true, cancellationToken); + return requests.Cast<IAuthenticationRequest>().CacheGeneratedResults(); } /// <summary> @@ -468,7 +464,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para> /// </remarks> /// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception> - public IEnumerable<IAuthenticationRequest> CreateRequests(Identifier userSuppliedIdentifier, Realm realm) { + public async Task<IEnumerable<IAuthenticationRequest>> CreateRequestsAsync(Identifier userSuppliedIdentifier, Realm realm) { RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier"); Requires.NotNull(realm, "realm"); @@ -491,7 +487,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { } returnTo.AppendQueryArgs(returnToParams); - return this.CreateRequests(userSuppliedIdentifier, realm, returnTo.Uri); + return await this.CreateRequestsAsync(userSuppliedIdentifier, realm, returnTo.Uri); } /// <summary> @@ -514,11 +510,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para> /// </remarks> /// <exception cref="InvalidOperationException">Thrown if <see cref="HttpContext.Current">HttpContext.Current</see> == <c>null</c>.</exception> - public IEnumerable<IAuthenticationRequest> CreateRequests(Identifier userSuppliedIdentifier) { + public async Task<IEnumerable<IAuthenticationRequest>> CreateRequestsAsync(Identifier userSuppliedIdentifier) { Requires.NotNull(userSuppliedIdentifier, "userSuppliedIdentifier"); RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); - return this.CreateRequests(userSuppliedIdentifier, Realm.AutoDetect); + return await this.CreateRequestsAsync(userSuppliedIdentifier, Realm.AutoDetect); } /// <summary> @@ -528,9 +524,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <remarks> /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para> /// </remarks> - public IAuthenticationResponse GetResponse() { + public Task<IAuthenticationResponse> GetResponseAsync(CancellationToken cancellationToken) { RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); - return this.GetResponse(this.Channel.GetRequestFromContext()); + return this.GetResponseAsync(this.Channel.GetRequestFromContext(), cancellationToken); } /// <summary> @@ -538,10 +534,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="httpRequestInfo">The HTTP request that may be carrying an authentication response from the Provider.</param> /// <returns>The processed authentication response if there is any; <c>null</c> otherwise.</returns> - public IAuthenticationResponse GetResponse(HttpRequestBase httpRequestInfo) { + public async Task<IAuthenticationResponse> GetResponseAsync(HttpRequestBase httpRequestInfo, CancellationToken cancellationToken) { Requires.NotNull(httpRequestInfo, "httpRequestInfo"); try { - var message = this.Channel.ReadFromRequest(httpRequestInfo); + var message = await this.Channel.ReadFromRequestAsync(httpRequestInfo, cancellationToken); PositiveAssertionResponse positiveAssertion; NegativeAssertionResponse negativeAssertion; IndirectSignedResponse positiveExtensionOnly; @@ -554,7 +550,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { OpenIdStrings.PositiveAssertionFromNonQualifiedProvider, providerEndpoint.Uri); - var response = new PositiveAuthenticationResponse(positiveAssertion, this); + var response = await PositiveAuthenticationResponse.CreateAsync(positiveAssertion, this, cancellationToken); foreach (var behavior in this.Behaviors) { behavior.OnIncomingPositiveAssertion(response); } @@ -581,10 +577,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <remarks> /// <para>Requires an <see cref="HttpContext.Current">HttpContext.Current</see> context.</para> /// </remarks> - public OutgoingWebResponse ProcessResponseFromPopup() { + public Task<HttpResponseMessage> ProcessResponseFromPopupAsync(CancellationToken cancellationToken) { RequiresEx.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); - return this.ProcessResponseFromPopup(this.Channel.GetRequestFromContext()); + return this.ProcessResponseFromPopupAsync(this.Channel.GetRequestFromContext(), cancellationToken); } /// <summary> @@ -592,10 +588,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="request">The incoming HTTP request that is expected to carry an OpenID authentication response.</param> /// <returns>The HTTP response to send to this HTTP request.</returns> - public OutgoingWebResponse ProcessResponseFromPopup(HttpRequestBase request) { + public Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestBase request, CancellationToken cancellationToken) { Requires.NotNull(request, "request"); - return this.ProcessResponseFromPopup(request, null); + return this.ProcessResponseFromPopupAsync(request, null, cancellationToken); } /// <summary> @@ -678,11 +674,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 OutgoingWebResponse ProcessResponseFromPopup(HttpRequestBase request, Action<AuthenticationStatus> callback) { + internal async Task<HttpResponseMessage> ProcessResponseFromPopupAsync(HttpRequestBase request, Action<AuthenticationStatus> callback, CancellationToken cancellationToken) { Requires.NotNull(request, "request"); string extensionsJson = null; - var authResponse = this.NonVerifyingRelyingParty.GetResponse(); + var authResponse = await this.NonVerifyingRelyingParty.GetResponseAsync(cancellationToken); ErrorUtilities.VerifyProtocol(authResponse != null, OpenIdStrings.PopupRedirectMissingResponse); // Give the caller a chance to notify the hosting page and fill up the clientScriptExtensions collection. @@ -734,8 +730,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="identifier">The identifier to discover services for.</param> /// <returns>A non-null sequence of services discovered for the identifier.</returns> - internal IEnumerable<IdentifierDiscoveryResult> Discover(Identifier identifier) { - return this.discoveryServices.Discover(identifier); + internal Task<IEnumerable<IdentifierDiscoveryResult>> DiscoverAsync(Identifier identifier, CancellationToken cancellationToken) { + return this.discoveryServices.DiscoverAsync(identifier, cancellationToken); } /// <summary> @@ -795,7 +791,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// <param name="methodCall">The method to call on the parent window, including /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param> /// <returns>The entire HTTP response to send to the popup window or iframe to perform the invocation.</returns> - private static OutgoingWebResponse InvokeParentPageScript(string methodCall) { + private static HttpResponseMessage InvokeParentPageScript(string methodCall) { Requires.NotNullOrEmpty(methodCall, "methodCall"); Logger.OpenId.DebugFormat("Sending Javascript callback: {0}", methodCall); @@ -824,9 +820,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { builder.AppendLine("//]]>--></script>"); builder.AppendLine("</body></html>"); - var response = new OutgoingWebResponse(); - response.Body = builder.ToString(); - response.Headers.Add(HttpResponseHeader.ContentType, new ContentType("text/html").ToString()); + var response = new HttpResponseMessage(); + response.Content = new StringContent(builder.ToString()); + response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html"); return response; } diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/PositiveAuthenticationResponse.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/PositiveAuthenticationResponse.cs index 509eb60..f05abaa 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/PositiveAuthenticationResponse.cs +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/PositiveAuthenticationResponse.cs @@ -8,6 +8,8 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { using System; using System.Diagnostics; using System.Linq; + using System.Threading; + using System.Threading.Tasks; using System.Web; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId.Messages; @@ -24,7 +26,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// </summary> /// <param name="response">The positive assertion response that was just received by the Relying Party.</param> /// <param name="relyingParty">The relying party.</param> - internal PositiveAuthenticationResponse(PositiveAssertionResponse response, OpenIdRelyingParty relyingParty) + private PositiveAuthenticationResponse(PositiveAssertionResponse response, OpenIdRelyingParty relyingParty) : base(response) { Requires.NotNull(relyingParty, "relyingParty"); @@ -36,8 +38,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { null, null); - this.VerifyDiscoveryMatchesAssertion(relyingParty); - Logger.OpenId.InfoFormat("Received identity assertion for {0} via {1}.", this.Response.ClaimedIdentifier, this.Provider.Uri); } @@ -123,6 +123,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { get { return (PositiveAssertionResponse)base.Response; } } + internal static async Task<PositiveAuthenticationResponse> CreateAsync( + PositiveAssertionResponse response, OpenIdRelyingParty relyingParty, CancellationToken cancellationToken) { + var result = new PositiveAuthenticationResponse(response, relyingParty); + await result.VerifyDiscoveryMatchesAssertionAsync(relyingParty, cancellationToken); + return result; + } + /// <summary> /// Verifies that the positive assertion data matches the results of /// discovery on the Claimed Identifier. @@ -134,7 +141,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { /// This would be an indication of either a misconfigured Provider or /// an attempt by someone to spoof another user's identity with a rogue Provider. /// </exception> - private void VerifyDiscoveryMatchesAssertion(OpenIdRelyingParty relyingParty) { + private async Task VerifyDiscoveryMatchesAssertionAsync(OpenIdRelyingParty relyingParty, CancellationToken cancellationToken) { Logger.OpenId.Debug("Verifying assertion matches identifier discovery results..."); // Ensure that we abide by the RP's rules regarding RequireSsl for this discovery step. @@ -163,7 +170,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty { // is signed by the RP before it's considered reliable. In 1.x stateless mode, this RP // doesn't (and can't) sign its own return_to URL, so its cached discovery information // is merely a hint that must be verified by performing discovery again here. - var discoveryResults = relyingParty.Discover(claimedId); + var discoveryResults = await relyingParty.DiscoverAsync(claimedId, cancellationToken); ErrorUtilities.VerifyProtocol( discoveryResults.Contains(this.Endpoint), OpenIdStrings.IssuedAssertionFailsIdentifierDiscovery, diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/packages.config b/src/DotNetOpenAuth.OpenId.RelyingParty/packages.config index 58890d8..1d93cf5 100644 --- a/src/DotNetOpenAuth.OpenId.RelyingParty/packages.config +++ b/src/DotNetOpenAuth.OpenId.RelyingParty/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.1.12362" targetFramework="net45" /> </packages>
\ No newline at end of file |