diff options
Diffstat (limited to 'src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs')
-rw-r--r-- | src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs | 267 |
1 files changed, 163 insertions, 104 deletions
diff --git a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs index acb37fc..6fb0d7a 100644 --- a/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/AuthenticationTests.cs @@ -6,6 +6,9 @@ namespace DotNetOpenAuth.Test.OpenId { using System; + using System.Net.Http; + using System.Threading.Tasks; + using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OpenId; @@ -24,76 +27,98 @@ namespace DotNetOpenAuth.Test.OpenId { } [Test] - public void SharedAssociationPositive() { - this.ParameterizedAuthenticationTest(true, true, false); + public async Task SharedAssociationPositive() { + await this.ParameterizedAuthenticationTestAsync(true, true, false); } /// <summary> /// Verifies that a shared association protects against tampering. /// </summary> [Test] - public void SharedAssociationTampered() { - this.ParameterizedAuthenticationTest(true, true, true); + public async Task SharedAssociationTampered() { + await this.ParameterizedAuthenticationTestAsync(true, true, true); } [Test] - public void SharedAssociationNegative() { - this.ParameterizedAuthenticationTest(true, false, false); + public async Task SharedAssociationNegative() { + await this.ParameterizedAuthenticationTestAsync(true, false, false); } [Test] - public void PrivateAssociationPositive() { - this.ParameterizedAuthenticationTest(false, true, false); + public async Task PrivateAssociationPositive() { + await this.ParameterizedAuthenticationTestAsync(false, true, false); } /// <summary> /// Verifies that a private association protects against tampering. /// </summary> [Test] - public void PrivateAssociationTampered() { - this.ParameterizedAuthenticationTest(false, true, true); + public async Task PrivateAssociationTampered() { + await this.ParameterizedAuthenticationTestAsync(false, true, true); } [Test] - public void NoAssociationNegative() { - this.ParameterizedAuthenticationTest(false, false, false); + public async Task NoAssociationNegative() { + await this.ParameterizedAuthenticationTestAsync(false, false, false); } [Test] - public void UnsolicitedAssertion() { - this.MockResponder.RegisterMockRPDiscovery(); - OpenIdCoordinator coordinator = new OpenIdCoordinator( - rp => { - rp.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler; - IAuthenticationResponse response = rp.GetResponse(); - Assert.AreEqual(AuthenticationStatus.Authenticated, response.Status); - }, - op => { - op.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler; + public async Task UnsolicitedAssertion() { + var opStore = new StandardProviderApplicationStore(); + var coordinator = new CoordinatorBase( + async (hostFactories, ct) => { + var op = new OpenIdProvider(opStore); Identifier id = GetMockIdentifier(ProtocolVersion.V20); - op.SendUnsolicitedAssertion(OPUri, RPRealmUri, id, OPLocalIdentifiers[0]); - AutoProvider(op); // handle check_auth - }); - coordinator.Run(); + var assertion = await op.PrepareUnsolicitedAssertionAsync(OPUri, RPRealmUri, id, OPLocalIdentifiers[0], ct); + + using (var httpClient = hostFactories.CreateHttpClient()) { + using (var response = await httpClient.GetAsync(assertion.Headers.Location)) { + response.EnsureSuccessStatusCode(); + } + } + }, + CoordinatorBase.Handle(RPRealmUri).By(async (hostFactories, req, ct) => { + var rp = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore(), hostFactories); + IAuthenticationResponse response = await rp.GetResponseAsync(); + Assert.AreEqual(AuthenticationStatus.Authenticated, response.Status); + return new HttpResponseMessage(); + }), + CoordinatorBase.Handle(OPUri).By( + async (req, ct) => { + var op = new OpenIdProvider(opStore); + return await this.AutoProviderActionAsync(op, req, ct); + }), + MockHttpRequest.RegisterMockRPDiscovery(ssl: false)); + await coordinator.RunAsync(); } [Test] - public void UnsolicitedAssertionRejected() { - this.MockResponder.RegisterMockRPDiscovery(); - OpenIdCoordinator coordinator = new OpenIdCoordinator( - rp => { - rp.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler; + public async Task UnsolicitedAssertionRejected() { + var opStore = new StandardProviderApplicationStore(); + var coordinator = new CoordinatorBase( + async (hostFactories, ct) => { + var op = new OpenIdProvider(opStore); + Identifier id = GetMockIdentifier(ProtocolVersion.V20); + var assertion = await op.PrepareUnsolicitedAssertionAsync(OPUri, RPRealmUri, id, OPLocalIdentifiers[0], ct); + using (var httpClient = hostFactories.CreateHttpClient()) { + using (var response = await httpClient.GetAsync(assertion.Headers.Location, ct)) { + response.EnsureSuccessStatusCode(); + } + } + }, + CoordinatorBase.Handle(RPRealmUri).By(async (hostFactories, req, ct) => { + var rp = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore(), hostFactories); rp.SecuritySettings.RejectUnsolicitedAssertions = true; - IAuthenticationResponse response = rp.GetResponse(); + IAuthenticationResponse response = await rp.GetResponseAsync(req, ct); Assert.AreEqual(AuthenticationStatus.Failed, response.Status); - }, - op => { - op.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler; - Identifier id = GetMockIdentifier(ProtocolVersion.V20); - op.SendUnsolicitedAssertion(OPUri, RPRealmUri, id, OPLocalIdentifiers[0]); - AutoProvider(op); // handle check_auth - }); - coordinator.Run(); + return new HttpResponseMessage(); + }), + CoordinatorBase.Handle(OPUri).By(async (hostFactories, req, ct) => { + var op = new OpenIdProvider(opStore); + return await this.AutoProviderActionAsync(op, req, ct); + }), + MockHttpRequest.RegisterMockRPDiscovery(false)); + await coordinator.RunAsync(); } /// <summary> @@ -101,25 +126,35 @@ namespace DotNetOpenAuth.Test.OpenId { /// when the appropriate security setting is set. /// </summary> [Test] - public void UnsolicitedDelegatingIdentifierRejection() { - this.MockResponder.RegisterMockRPDiscovery(); - OpenIdCoordinator coordinator = new OpenIdCoordinator( - rp => { - rp.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler; + public async Task UnsolicitedDelegatingIdentifierRejection() { + var opStore = new StandardProviderApplicationStore(); + var coordinator = new CoordinatorBase( + async (hostFactories, ct) => { + var op = new OpenIdProvider(opStore); + Identifier id = GetMockIdentifier(ProtocolVersion.V20, false, true); + var assertion = await op.PrepareUnsolicitedAssertionAsync(OPUri, RPRealmUri, id, OPLocalIdentifiers[0], ct); + using (var httpClient = hostFactories.CreateHttpClient()) { + using (var response = await httpClient.GetAsync(assertion.Headers.Location, ct)) { + response.EnsureSuccessStatusCode(); + } + } + }, + CoordinatorBase.Handle(RPRealmUri).By(async (hostFactories, req, ct) => { + var rp = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore(), hostFactories); rp.SecuritySettings.RejectDelegatingIdentifiers = true; - IAuthenticationResponse response = rp.GetResponse(); + IAuthenticationResponse response = await rp.GetResponseAsync(req, ct); Assert.AreEqual(AuthenticationStatus.Failed, response.Status); - }, - op => { - op.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler; - Identifier id = GetMockIdentifier(ProtocolVersion.V20, false, true); - op.SendUnsolicitedAssertion(OPUri, RPRealmUri, id, OPLocalIdentifiers[0]); - AutoProvider(op); // handle check_auth - }); - coordinator.Run(); + return new HttpResponseMessage(); + }), + CoordinatorBase.Handle(OPUri).By(async (hostFactories, req, ct) => { + var op = new OpenIdProvider(opStore); + return await this.AutoProviderActionAsync(op, req, ct); + }), + MockHttpRequest.RegisterMockRPDiscovery(false)); + await coordinator.RunAsync(); } - private void ParameterizedAuthenticationTest(bool sharedAssociation, bool positive, bool tamper) { + private async Task ParameterizedAuthenticationTestAsync(bool sharedAssociation, bool positive, bool tamper) { foreach (Protocol protocol in Protocol.AllPracticalVersions) { foreach (bool statelessRP in new[] { false, true }) { if (sharedAssociation && statelessRP) { @@ -129,21 +164,26 @@ namespace DotNetOpenAuth.Test.OpenId { foreach (bool immediate in new[] { false, true }) { TestLogger.InfoFormat("Beginning authentication test scenario. OpenID: {0}, Shared: {1}, positive: {2}, tamper: {3}, stateless: {4}, immediate: {5}", protocol.Version, sharedAssociation, positive, tamper, statelessRP, immediate); - this.ParameterizedAuthenticationTest(protocol, statelessRP, sharedAssociation, positive, immediate, tamper); + await this.ParameterizedAuthenticationTestAsync(protocol, statelessRP, sharedAssociation, positive, immediate, tamper); } } } } - private void ParameterizedAuthenticationTest(Protocol protocol, bool statelessRP, bool sharedAssociation, bool positive, bool immediate, bool tamper) { + private async Task ParameterizedAuthenticationTestAsync(Protocol protocol, bool statelessRP, bool sharedAssociation, bool positive, bool immediate, bool tamper) { Requires.That(!statelessRP || !sharedAssociation, null, "The RP cannot be stateless while sharing an association with the OP."); Requires.That(positive || !tamper, null, "Cannot tamper with a negative response."); var securitySettings = new ProviderSecuritySettings(); var cryptoKeyStore = new MemoryCryptoKeyStore(); var associationStore = new ProviderAssociationHandleEncoder(cryptoKeyStore); Association association = sharedAssociation ? HmacShaAssociationProvider.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, associationStore, securitySettings) : null; - var coordinator = new OpenIdCoordinator( - rp => { + int opStep = 0; + var coordinator = new CoordinatorBase( + CoordinatorBase.RelyingPartyDriver(async (rp, ct) => { + if (statelessRP) { + rp = new OpenIdRelyingParty(null, rp.Channel.HostFactories); + } + var request = new CheckIdRequest(protocol.Version, OPUri, immediate ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup); if (association != null) { @@ -155,17 +195,25 @@ namespace DotNetOpenAuth.Test.OpenId { request.LocalIdentifier = "http://localid"; request.ReturnTo = RPUri; request.Realm = RPUri; - rp.Channel.Respond(request); + var redirectRequest = await rp.Channel.PrepareResponseAsync(request, ct); + Uri redirectResponse; + using (var httpClient = rp.Channel.HostFactories.CreateHttpClient()) { + using (var response = await httpClient.GetAsync(redirectRequest.Headers.Location)) { + redirectResponse = response.Headers.Location; + } + } + + var assertionMessage = new HttpRequestMessage(HttpMethod.Get, redirectResponse.AbsoluteUri); if (positive) { if (tamper) { try { - rp.Channel.ReadFromRequest<PositiveAssertionResponse>(); + await rp.Channel.ReadFromRequestAsync<PositiveAssertionResponse>(assertionMessage, ct); Assert.Fail("Expected exception {0} not thrown.", typeof(InvalidSignatureException).Name); } catch (InvalidSignatureException) { TestLogger.InfoFormat("Caught expected {0} exception after tampering with signed data.", typeof(InvalidSignatureException).Name); } } else { - var response = rp.Channel.ReadFromRequest<PositiveAssertionResponse>(); + var response = await rp.Channel.ReadFromRequestAsync<PositiveAssertionResponse>(assertionMessage, ct); Assert.IsNotNull(response); Assert.AreEqual(request.ClaimedIdentifier, response.ClaimedIdentifier); Assert.AreEqual(request.LocalIdentifier, response.LocalIdentifier); @@ -177,15 +225,16 @@ namespace DotNetOpenAuth.Test.OpenId { // When the OP notices the replay we get a generic InvalidSignatureException. // When the RP notices the replay we get a specific ReplayMessageException. try { - CoordinatingChannel channel = (CoordinatingChannel)rp.Channel; - await channel.ReplayAsync(response); + // TODO: fix this. + ////CoordinatingChannel channel = (CoordinatingChannel)rp.Channel; + ////await channel.ReplayAsync(response); Assert.Fail("Expected ProtocolException was not thrown."); } catch (ProtocolException ex) { Assert.IsTrue(ex is ReplayedMessageException || ex is InvalidSignatureException, "A {0} exception was thrown instead of the expected {1} or {2}.", ex.GetType(), typeof(ReplayedMessageException).Name, typeof(InvalidSignatureException).Name); } } } else { - var response = rp.Channel.ReadFromRequest<NegativeAssertionResponse>(); + var response = await rp.Channel.ReadFromRequestAsync<NegativeAssertionResponse>(assertionMessage, ct); Assert.IsNotNull(response); if (immediate) { // Only 1.1 was required to include user_setup_url @@ -196,54 +245,64 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.IsNull(response.UserSetupUrl); } } - }, - op => { + }), + CoordinatorBase.HandleProvider(async (op, req, ct) => { if (association != null) { var key = cryptoKeyStore.GetCurrentKey(ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, TimeSpan.FromSeconds(1)); op.CryptoKeyStore.StoreKey(ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, key.Key, key.Value); } - var request = op.Channel.ReadFromRequest<CheckIdRequest>(); - Assert.IsNotNull(request); - IProtocolMessage response; - if (positive) { - response = new PositiveAssertionResponse(request); - } else { - response = new NegativeAssertionResponse(request, op.Channel); - } - op.Channel.Respond(response); - - if (positive && (statelessRP || !sharedAssociation)) { - var checkauthRequest = op.Channel.ReadFromRequest<CheckAuthenticationRequest>(); - var checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest); - checkauthResponse.IsValid = checkauthRequest.IsValid; - op.Channel.Respond(checkauthResponse); - - if (!tamper) { - // Respond to the replay attack. - checkauthRequest = op.Channel.ReadFromRequest<CheckAuthenticationRequest>(); - checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest); - checkauthResponse.IsValid = checkauthRequest.IsValid; - op.Channel.Respond(checkauthResponse); - } + switch (++opStep) { + case 1: + var request = await op.Channel.ReadFromRequestAsync<CheckIdRequest>(req, ct); + Assert.IsNotNull(request); + IProtocolMessage response; + if (positive) { + response = new PositiveAssertionResponse(request); + } else { + response = new NegativeAssertionResponse(request.Version, request.ReturnTo, request.Mode); + } + + return await op.Channel.PrepareResponseAsync(response, ct); + case 2: + if (positive && (statelessRP || !sharedAssociation)) { + var checkauthRequest = await op.Channel.ReadFromRequestAsync<CheckAuthenticationRequest>(req, ct); + var checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest); + checkauthResponse.IsValid = checkauthRequest.IsValid; + return await op.Channel.PrepareResponseAsync(checkauthResponse, ct); + } + + throw Assumes.NotReachable(); + case 3: + if (positive && (statelessRP || !sharedAssociation)) { + if (!tamper) { + // Respond to the replay attack. + var checkauthRequest = await op.Channel.ReadFromRequestAsync<CheckAuthenticationRequest>(req, ct); + var checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest); + checkauthResponse.IsValid = checkauthRequest.IsValid; + return await op.Channel.PrepareResponseAsync(checkauthResponse, ct); + } + } + + throw Assumes.NotReachable(); + default: + throw Assumes.NotReachable(); } - }); + })); if (tamper) { - coordinator.IncomingMessageFilter = message => { - var assertion = message as PositiveAssertionResponse; - if (assertion != null) { - // Alter the Local Identifier between the Provider and the Relying Party. - // If the signature binding element does its job, this should cause the RP - // to throw. - assertion.LocalIdentifier = "http://victim"; - } - }; - } - if (statelessRP) { - coordinator.RelyingParty = new OpenIdRelyingParty(null); + // TODO: fix this. + ////coordinator.IncomingMessageFilter = message => { + //// var assertion = message as PositiveAssertionResponse; + //// if (assertion != null) { + //// // Alter the Local Identifier between the Provider and the Relying Party. + //// // If the signature binding element does its job, this should cause the RP + //// // to throw. + //// assertion.LocalIdentifier = "http://victim"; + //// } + ////}; } - coordinator.Run(); + await coordinator.RunAsync(); } } } |