Renamed Channel.Send method to Channel.PrepareResponse and then added a new Send...
[dotnetoauth.git] / src / DotNetOpenAuth.Test / OpenId / AuthenticationTests.cs
blob18721b6cbf9db17a2ce48576631d4bdfc7ef77ac
1 //-----------------------------------------------------------------------
2 // <copyright file="AuthenticationTests.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
4 // </copyright>
5 //-----------------------------------------------------------------------
7 namespace DotNetOpenAuth.Test.OpenId {
8 using System;
9 using DotNetOpenAuth.Messaging;
10 using DotNetOpenAuth.Messaging.Bindings;
11 using DotNetOpenAuth.OpenId;
12 using DotNetOpenAuth.OpenId.Messages;
13 using DotNetOpenAuth.OpenId.Provider;
14 using DotNetOpenAuth.OpenId.RelyingParty;
15 using DotNetOpenAuth.Test.Mocks;
16 using Microsoft.VisualStudio.TestTools.UnitTesting;
18 [TestClass]
19 public class AuthenticationTests : OpenIdTestBase {
20 [TestInitialize]
21 public override void SetUp() {
22 base.SetUp();
25 [TestMethod]
26 public void SharedAssociationPositive() {
27 this.ParameterizedPositiveAuthenticationTest(true, true, false);
30 /// <summary>
31 /// Verifies that a shared association protects against tampering.
32 /// </summary>
33 [TestMethod]
34 public void SharedAssociationTampered() {
35 this.ParameterizedPositiveAuthenticationTest(true, true, true);
38 [TestMethod]
39 public void SharedAssociationNegative() {
40 this.ParameterizedPositiveAuthenticationTest(true, false, false);
43 [TestMethod]
44 public void PrivateAssociationPositive() {
45 this.ParameterizedPositiveAuthenticationTest(false, true, false);
48 /// <summary>
49 /// Verifies that a private association protects against tampering.
50 /// </summary>
51 [TestMethod]
52 public void PrivateAssociationTampered() {
53 this.ParameterizedPositiveAuthenticationTest(false, true, true);
56 [TestMethod]
57 public void NoAssociationNegative() {
58 this.ParameterizedPositiveAuthenticationTest(false, false, false);
61 private void ParameterizedPositiveAuthenticationTest(bool sharedAssociation, bool positive, bool tamper) {
62 foreach (Protocol protocol in Protocol.AllPracticalVersions) {
63 this.ParameterizedPositiveAuthenticationTest(protocol, sharedAssociation, positive, tamper);
67 private void ParameterizedPositiveAuthenticationTest(Protocol protocol, bool sharedAssociation, bool positive, bool tamper) {
68 ErrorUtilities.VerifyArgument(positive || !tamper, "Cannot tamper with a negative response.");
69 Uri userSetupUrl = protocol.Version.Major < 2 ? new Uri("http://usersetupurl") : null;
70 ProviderSecuritySettings securitySettings = new ProviderSecuritySettings();
71 Association association = sharedAssociation ? HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, securitySettings) : null;
72 var coordinator = new OpenIdCoordinator(
73 rp => {
74 var request = new CheckIdRequest(protocol.Version, ProviderUri, AuthenticationRequestMode.Immediate);
76 if (association != null) {
77 rp.AssociationStore.StoreAssociation(ProviderUri, association);
78 request.AssociationHandle = association.Handle;
81 request.ClaimedIdentifier = "http://claimedid";
82 request.LocalIdentifier = "http://localid";
83 request.ReturnTo = RPUri;
84 rp.Channel.Send(request);
85 if (positive) {
86 if (tamper) {
87 try {
88 rp.Channel.ReadFromRequest<PositiveAssertionResponse>();
89 Assert.Fail("Expected exception {0} not thrown.", typeof(InvalidSignatureException).Name);
90 } catch (InvalidSignatureException) {
91 TestLogger.InfoFormat("Caught expected {0} exception after tampering with signed data.", typeof(InvalidSignatureException).Name);
93 } else {
94 var response = rp.Channel.ReadFromRequest<PositiveAssertionResponse>();
95 Assert.IsNotNull(response);
96 Assert.AreEqual(request.ClaimedIdentifier, response.ClaimedIdentifier);
97 Assert.AreEqual(request.LocalIdentifier, response.LocalIdentifier);
98 Assert.AreEqual(request.ReturnTo, response.ReturnTo);
100 // Attempt to replay the message and verify that it fails.
101 // Because in various scenarios and protocol versions different components
102 // notice the replay, we can get one of two exceptions thrown.
103 // When the OP notices the replay we get a generic InvalidSignatureException.
104 // When the RP notices the replay we get a specific ReplayMessageException.
105 Type expectedExceptionType = sharedAssociation || protocol.Version.Major < 2 ? typeof(ReplayedMessageException) : typeof(InvalidSignatureException);
106 try {
107 CoordinatingChannel channel = (CoordinatingChannel)rp.Channel;
108 channel.Replay(response);
109 Assert.Fail("Expected exception {0} was not thrown.", expectedExceptionType.Name);
110 } catch (ProtocolException ex) {
111 Assert.IsInstanceOfType(ex, expectedExceptionType);
114 } else {
115 var response = rp.Channel.ReadFromRequest<NegativeAssertionResponse>();
116 Assert.IsNotNull(response);
117 Assert.AreEqual(userSetupUrl, response.UserSetupUrl);
120 op => {
121 if (association != null) {
122 op.AssociationStore.StoreAssociation(AssociationRelyingPartyType.Smart, association);
125 var request = op.Channel.ReadFromRequest<CheckIdRequest>();
126 Assert.IsNotNull(request);
127 IProtocolMessage response;
128 if (positive) {
129 response = new PositiveAssertionResponse(request);
130 } else {
131 response = new NegativeAssertionResponse(request) { UserSetupUrl = userSetupUrl };
133 op.Channel.Send(response);
135 if (positive && !sharedAssociation) {
136 var checkauthRequest = op.Channel.ReadFromRequest<CheckAuthenticationRequest>();
137 var checkauthResponse = new CheckAuthenticationResponse(checkauthRequest);
138 checkauthResponse.IsValid = checkauthRequest.IsValid;
139 op.Channel.Send(checkauthResponse);
141 if (!tamper) {
142 // Respond to the replay attack.
143 checkauthRequest = op.Channel.ReadFromRequest<CheckAuthenticationRequest>();
144 checkauthResponse = new CheckAuthenticationResponse(checkauthRequest);
145 checkauthResponse.IsValid = checkauthRequest.IsValid;
146 op.Channel.Send(checkauthResponse);
150 if (tamper) {
151 coordinator.IncomingMessageFilter = message => {
152 var assertion = message as PositiveAssertionResponse;
153 if (assertion != null) {
154 // Alter the Local Identifier between the Provider and the Relying Party.
155 // If the signature binding element does its job, this should cause the RP
156 // to throw.
157 assertion.LocalIdentifier = "http://victim";
161 coordinator.Run();