/// <summary>
        /// Analyzes an incoming request message payload to discover what kind of
        /// message is embedded in it and returns the type, or null if no match is found.
        /// </summary>
        /// <param name="recipient">The intended or actual recipient of the request message.</param>
        /// <param name="fields">The name/value pairs that make up the message payload.</param>
        /// <returns>
        /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can
        /// deserialize to.  Null if the request isn't recognized as a valid protocol message.
        /// </returns>
        public IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields)
        {
            ErrorUtilities.VerifyArgumentNotNull(recipient, "recipient");
            ErrorUtilities.VerifyArgumentNotNull(fields, "fields");

            RequestBase message = null;

            // Discern the OpenID version of the message.
            Protocol protocol = Protocol.V11;
            string ns;
            if (fields.TryGetValue(Protocol.V20.openid.ns, out ns)) {
                ErrorUtilities.VerifyProtocol(string.Equals(ns, Protocol.OpenId2Namespace, StringComparison.Ordinal), MessagingStrings.UnexpectedMessagePartValue, Protocol.V20.openid.ns, ns);
                protocol = Protocol.V20;
            }

            string mode;
            if (fields.TryGetValue(protocol.openid.mode, out mode)) {
                if (string.Equals(mode, protocol.Args.Mode.associate)) {
                    if (fields.ContainsKey(protocol.openid.dh_consumer_public)) {
                        message = new AssociateDiffieHellmanRequest(protocol.Version, recipient.Location);
                    } else {
                        message = new AssociateUnencryptedRequest(protocol.Version, recipient.Location);
                    }
                } else if (string.Equals(mode, protocol.Args.Mode.checkid_setup) ||
                    string.Equals(mode, protocol.Args.Mode.checkid_immediate)) {
                    AuthenticationRequestMode authMode = string.Equals(mode, protocol.Args.Mode.checkid_immediate) ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup;
                    if (fields.ContainsKey(protocol.openid.identity)) {
                        message = new CheckIdRequest(protocol.Version, recipient.Location, authMode);
                    } else {
                        ErrorUtilities.VerifyProtocol(!fields.ContainsKey(protocol.openid.claimed_id), OpenIdStrings.IdentityAndClaimedIdentifierMustBeBothPresentOrAbsent);
                        message = new SignedResponseRequest(protocol.Version, recipient.Location, authMode);
                    }
                } else if (string.Equals(mode, protocol.Args.Mode.cancel) ||
                    (string.Equals(mode, protocol.Args.Mode.setup_needed) && (protocol.Version.Major >= 2 || fields.ContainsKey(protocol.openid.user_setup_url)))) {
                    message = new NegativeAssertionResponse(protocol.Version, recipient.Location, mode);
                } else if (string.Equals(mode, protocol.Args.Mode.id_res)) {
                    if (fields.ContainsKey(protocol.openid.identity)) {
                        message = new PositiveAssertionResponse(protocol.Version, recipient.Location);
                    } else {
                        ErrorUtilities.VerifyProtocol(!fields.ContainsKey(protocol.openid.claimed_id), OpenIdStrings.IdentityAndClaimedIdentifierMustBeBothPresentOrAbsent);
                        message = new IndirectSignedResponse(protocol.Version, recipient.Location);
                    }
                } else if (string.Equals(mode, protocol.Args.Mode.check_authentication)) {
                    message = new CheckAuthenticationRequest(protocol.Version, recipient.Location);
                } else if (string.Equals(mode, protocol.Args.Mode.error)) {
                    message = new IndirectErrorResponse(protocol.Version, recipient.Location);
                } else {
                    ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessagePartValue, protocol.openid.mode, mode);
                }
            }

            if (message != null) {
                message.SetAsIncoming();
            }

            return message;
        }
		public override void SetUp() {
			base.SetUp();

			this.protocol = Protocol.V20;
			this.request = new CheckIdRequest(this.protocol.Version, OPUri, AuthenticationRequestMode.Setup);
			this.request.ReturnTo = RPUri;
			this.response = new PositiveAssertionResponse(this.request);

			this.unsolicited = new PositiveAssertionResponse(this.protocol.Version, RPUri);
		}
		/// <summary>
		/// Initializes a new instance of the <see cref="PositiveAuthenticationResponse"/> class
		/// </summary>
		/// <param name="response">The positive assertion response that was just received by the Relying Party.</param>
		/// <param name="relyingParty">The relying party.</param>
		private PositiveAuthenticationResponse(PositiveAssertionResponse response, OpenIdRelyingParty relyingParty)
			: base(response) {
			Requires.NotNull(relyingParty, "relyingParty");

			this.Endpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier(
				this.Response.ClaimedIdentifier,
				this.Response.GetReturnToArgument(AuthenticationRequest.UserSuppliedIdentifierParameterName),
				this.Response.LocalIdentifier,
				new ProviderEndpointDescription(this.Response.ProviderEndpoint, this.Response.Version),
				null,
				null);

			Logger.OpenId.InfoFormat("Received identity assertion for {0} via {1}.", this.Response.ClaimedIdentifier, this.Provider.Uri);
		}
		public void ExactPositiveAssertionPreservation() {
			var rp = CreateRelyingParty(true);

			// Initialize the positive assertion response with some data that is NOT in normalized form.
			var positiveAssertion = new PositiveAssertionResponse(Protocol.Default.Version, RPUri)
			{
				ClaimedIdentifier = "https://HOST:443/a",
				ProviderEndpoint = new Uri("https://anotherHOST:443/b"),
			};

			var checkAuth = new CheckAuthenticationRequest(positiveAssertion, rp.Channel);
			var actual = rp.Channel.MessageDescriptions.GetAccessor(checkAuth);
			Assert.AreEqual("https://HOST:443/a", actual["openid.claimed_id"]);
			Assert.AreEqual("https://anotherHOST:443/b", actual["openid.op_endpoint"]);
		}
        /// <summary>
        /// Initializes a new instance of the <see cref="PositiveAuthenticationResponse"/> class.
        /// </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)
            : base(response)
        {
            Contract.Requires(relyingParty != null);
            ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");

            this.Endpoint = ServiceEndpoint.CreateForClaimedIdentifier(
                this.Response.ClaimedIdentifier,
                this.Response.GetReturnToArgument(AuthenticationRequest.UserSuppliedIdentifierParameterName),
                this.Response.LocalIdentifier,
                new ProviderEndpointDescription(this.Response.ProviderEndpoint, this.Response.Version),
                null,
                null);

            this.VerifyDiscoveryMatchesAssertion(relyingParty);
        }
		/// <summary>
		/// Simulates an extension request and response.
		/// </summary>
		/// <param name="protocol">The protocol to use in the roundtripping.</param>
		/// <param name="requests">The extensions to add to the request message.</param>
		/// <param name="responses">The extensions to add to the response message.</param>
		/// <remarks>
		/// This method relies on the extension objects' Equals methods to verify
		/// accurate transport.  The Equals methods should be verified by separate tests.
		/// </remarks>
		internal static void Roundtrip(
			Protocol protocol,
			IEnumerable<IOpenIdMessageExtension> requests,
			IEnumerable<IOpenIdMessageExtension> responses) {
			var securitySettings = new ProviderSecuritySettings();
			var cryptoKeyStore = new MemoryCryptoKeyStore();
			var associationStore = new ProviderAssociationHandleEncoder(cryptoKeyStore);
			Association association = HmacShaAssociationProvider.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, associationStore, securitySettings);
			var coordinator = new OpenIdCoordinator(
				rp => {
					RegisterExtension(rp.Channel, Mocks.MockOpenIdExtension.Factory);
					var requestBase = new CheckIdRequest(protocol.Version, OpenIdTestBase.OPUri, AuthenticationRequestMode.Immediate);
					OpenIdTestBase.StoreAssociation(rp, OpenIdTestBase.OPUri, association);
					requestBase.AssociationHandle = association.Handle;
					requestBase.ClaimedIdentifier = "http://claimedid";
					requestBase.LocalIdentifier = "http://localid";
					requestBase.ReturnTo = OpenIdTestBase.RPUri;

					foreach (IOpenIdMessageExtension extension in requests) {
						requestBase.Extensions.Add(extension);
					}

					rp.Channel.Respond(requestBase);
					var response = rp.Channel.ReadFromRequest<PositiveAssertionResponse>();

					var receivedResponses = response.Extensions.Cast<IOpenIdMessageExtension>();
					CollectionAssert<IOpenIdMessageExtension>.AreEquivalentByEquality(responses.ToArray(), receivedResponses.ToArray());
				},
				op => {
					RegisterExtension(op.Channel, Mocks.MockOpenIdExtension.Factory);
					var key = cryptoKeyStore.GetCurrentKey(ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, TimeSpan.FromSeconds(1));
					op.CryptoKeyStore.StoreKey(ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, key.Key, key.Value);
					var request = op.Channel.ReadFromRequest<CheckIdRequest>();
					var response = new PositiveAssertionResponse(request);
					var receivedRequests = request.Extensions.Cast<IOpenIdMessageExtension>();
					CollectionAssert<IOpenIdMessageExtension>.AreEquivalentByEquality(requests.ToArray(), receivedRequests.ToArray());

					foreach (var extensionResponse in responses) {
						response.Extensions.Add(extensionResponse);
					}

					op.Channel.Respond(response);
				});
			coordinator.Run();
		}
		/// <summary>
		/// Initializes a new instance of the <see cref="AuthenticationRequest"/> class.
		/// </summary>
		/// <param name="provider">The provider that received the request.</param>
		/// <param name="request">The incoming authentication request message.</param>
		internal AuthenticationRequest(OpenIdProvider provider, CheckIdRequest request)
			: base(provider, request) {
			Requires.NotNull(provider, "provider");

			this.positiveResponse = new PositiveAssertionResponse(request);

			if (this.ClaimedIdentifier == Protocol.ClaimedIdentifierForOPIdentifier &&
				Protocol.ClaimedIdentifierForOPIdentifier != null) {
				// Force the hosting OP to deal with identifier_select by nulling out the two identifiers.
				this.IsDirectedIdentity = true;
				this.positiveResponse.ClaimedIdentifier = null;
				this.positiveResponse.LocalIdentifier = null;
			}

			// URL delegation is only detectable from 2.0 RPs, since openid.claimed_id isn't included from 1.0 RPs.
			// If the openid.claimed_id is present, and if it's different than the openid.identity argument, then
			// the RP has discovered a claimed identifier that has delegated authentication to this Provider.
			this.IsDelegatedIdentifier = this.ClaimedIdentifier != null && this.ClaimedIdentifier != this.LocalIdentifier;

			Reporting.RecordEventOccurrence("AuthenticationRequest.IsDelegatedIdentifier", this.IsDelegatedIdentifier.ToString());
		}
Beispiel #8
0
		/// <summary>
		/// Simulates an extension request and response.
		/// </summary>
		/// <param name="protocol">The protocol to use in the roundtripping.</param>
		/// <param name="requests">The extensions to add to the request message.</param>
		/// <param name="responses">The extensions to add to the response message.</param>
		/// <remarks>
		/// This method relies on the extension objects' Equals methods to verify
		/// accurate transport.  The Equals methods should be verified by separate tests.
		/// </remarks>
		internal async Task RoundtripAsync(
			Protocol protocol, IEnumerable<IOpenIdMessageExtension> requests, IEnumerable<IOpenIdMessageExtension> responses) {
			var securitySettings = new ProviderSecuritySettings();
			var cryptoKeyStore = new MemoryCryptoKeyStore();
			var associationStore = new ProviderAssociationHandleEncoder(cryptoKeyStore);
			Association association = HmacShaAssociationProvider.Create(
				protocol,
				protocol.Args.SignatureAlgorithm.Best,
				AssociationRelyingPartyType.Smart,
				associationStore,
				securitySettings);

			this.HandleProvider(
				async (op, req) => {
					ExtensionTestUtilities.RegisterExtension(op.Channel, Mocks.MockOpenIdExtension.Factory);
					var key = cryptoKeyStore.GetCurrentKey(
						ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, TimeSpan.FromSeconds(1));
					op.CryptoKeyStore.StoreKey(
						ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, key.Key, key.Value);
					var request = await op.Channel.ReadFromRequestAsync<CheckIdRequest>(req, CancellationToken.None);
					var response = new PositiveAssertionResponse(request);
					var receivedRequests = request.Extensions.Cast<IOpenIdMessageExtension>();
					CollectionAssert<IOpenIdMessageExtension>.AreEquivalentByEquality(requests.ToArray(), receivedRequests.ToArray());

					foreach (var extensionResponse in responses) {
						response.Extensions.Add(extensionResponse);
					}

					return await op.Channel.PrepareResponseAsync(response);
				});

			{
				var rp = this.CreateRelyingParty();
				ExtensionTestUtilities.RegisterExtension(rp.Channel, Mocks.MockOpenIdExtension.Factory);
				var requestBase = new CheckIdRequest(protocol.Version, OpenIdTestBase.OPUri, AuthenticationRequestMode.Immediate);
				OpenIdTestBase.StoreAssociation(rp, OpenIdTestBase.OPUri, association);
				requestBase.AssociationHandle = association.Handle;
				requestBase.ClaimedIdentifier = "http://claimedid";
				requestBase.LocalIdentifier = "http://localid";
				requestBase.ReturnTo = OpenIdTestBase.RPUri;

				foreach (IOpenIdMessageExtension extension in requests) {
					requestBase.Extensions.Add(extension);
				}

				var redirectingRequest = await rp.Channel.PrepareResponseAsync(requestBase);
				Uri redirectingResponseUri;
				this.HostFactories.AllowAutoRedirects = false;
				using (var httpClient = rp.Channel.HostFactories.CreateHttpClient()) {
					using (var redirectingResponse = await httpClient.GetAsync(redirectingRequest.Headers.Location)) {
						Assert.AreEqual(HttpStatusCode.Found, redirectingResponse.StatusCode);
						redirectingResponseUri = redirectingResponse.Headers.Location;
					}
				}

				var response =
					await
					rp.Channel.ReadFromRequestAsync<PositiveAssertionResponse>(
						new HttpRequestMessage(HttpMethod.Get, redirectingResponseUri), CancellationToken.None);
				var receivedResponses = response.Extensions.Cast<IOpenIdMessageExtension>();
				CollectionAssert<IOpenIdMessageExtension>.AreEquivalentByEquality(responses.ToArray(), receivedResponses.ToArray());
			}
		}
Beispiel #9
0
		/// <summary>
		/// Prepares an identity assertion on behalf of one of this Provider's
		/// members in order to redirect the user agent to a relying party
		/// web site and log him/her in immediately in one uninterrupted step.
		/// </summary>
		/// <param name="providerEndpoint">The absolute URL on the Provider site that receives OpenID messages.</param>
		/// <param name="relyingPartyRealm">The URL of the Relying Party web site.
		/// This will typically be the home page, but may be a longer URL if
		/// that Relying Party considers the scope of its realm to be more specific.
		/// The URL provided here must allow discovery of the Relying Party's
		/// XRDS document that advertises its OpenID RP endpoint.</param>
		/// <param name="claimedIdentifier">The Identifier you are asserting your member controls.</param>
		/// <param name="localIdentifier">The Identifier you know your user by internally.  This will typically
		/// be the same as <paramref name="claimedIdentifier"/>.</param>
		/// <param name="extensions">The extensions.</param>
		/// <returns>
		/// A <see cref="OutgoingWebResponse"/> object describing the HTTP response to send
		/// the user agent to allow the redirect with assertion to happen.
		/// </returns>
		public OutgoingWebResponse PrepareUnsolicitedAssertion(Uri providerEndpoint, Realm relyingPartyRealm, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions) {
			Contract.Requires<ArgumentNullException>(providerEndpoint != null);
			Contract.Requires<ArgumentException>(providerEndpoint.IsAbsoluteUri);
			Contract.Requires<ArgumentNullException>(relyingPartyRealm != null);
			Contract.Requires<ArgumentNullException>(claimedIdentifier != null);
			Contract.Requires<ArgumentNullException>(localIdentifier != null);
			Contract.Requires<InvalidOperationException>(this.Channel.WebRequestHandler != null);

			// Although the RP should do their due diligence to make sure that this OP
			// is authorized to send an assertion for the given claimed identifier,
			// do due diligence by performing our own discovery on the claimed identifier
			// and make sure that it is tied to this OP and OP local identifier.
			if (this.SecuritySettings.UnsolicitedAssertionVerification != ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.NeverVerify) {
				var serviceEndpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, localIdentifier, new ProviderEndpointDescription(providerEndpoint, Protocol.Default.Version), null, null);
				var discoveredEndpoints = this.RelyingParty.Discover(claimedIdentifier);
				if (!discoveredEndpoints.Contains(serviceEndpoint)) {
					Logger.OpenId.WarnFormat(
						"Failed to send unsolicited assertion for {0} because its discovered services did not include this endpoint: {1}{2}{1}Discovered endpoints: {1}{3}",
						claimedIdentifier,
						Environment.NewLine,
						serviceEndpoint,
						discoveredEndpoints.ToStringDeferred(true));

					// Only FAIL if the setting is set for it.
					if (this.securitySettings.UnsolicitedAssertionVerification == ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.RequireSuccess) {
						ErrorUtilities.ThrowProtocol(OpenIdStrings.UnsolicitedAssertionForUnrelatedClaimedIdentifier, claimedIdentifier);
					}
				}
			}

			Logger.OpenId.InfoFormat("Preparing unsolicited assertion for {0}", claimedIdentifier);
			RelyingPartyEndpointDescription returnToEndpoint = null;
			var returnToEndpoints = relyingPartyRealm.DiscoverReturnToEndpoints(this.WebRequestHandler, true);
			if (returnToEndpoints != null) {
				returnToEndpoint = returnToEndpoints.FirstOrDefault();
			}
			ErrorUtilities.VerifyProtocol(returnToEndpoint != null, OpenIdStrings.NoRelyingPartyEndpointDiscovered, relyingPartyRealm);

			var positiveAssertion = new PositiveAssertionResponse(returnToEndpoint) {
				ProviderEndpoint = providerEndpoint,
				ClaimedIdentifier = claimedIdentifier,
				LocalIdentifier = localIdentifier,
			};

			if (extensions != null) {
				foreach (IExtensionMessage extension in extensions) {
					positiveAssertion.Extensions.Add(extension);
				}
			}

			Reporting.RecordEventOccurrence(this, "PrepareUnsolicitedAssertion");
			return this.Channel.PrepareResponse(positiveAssertion);
		}
		private void ParameterizedAuthenticationTest(Protocol protocol, bool statelessRP, bool sharedAssociation, bool positive, bool immediate, bool tamper) {
			Contract.Requires<ArgumentException>(!statelessRP || !sharedAssociation, "The RP cannot be stateless while sharing an association with the OP.");
			Contract.Requires<ArgumentException>(positive || !tamper, "Cannot tamper with a negative response.");
			var securitySettings = new ProviderSecuritySettings();
			var cryptoKeyStore = new MemoryCryptoKeyStore();
			var associationStore = new ProviderAssociationHandleEncoder(cryptoKeyStore);
			Association association = sharedAssociation ? HmacShaAssociation.Create(protocol, protocol.Args.SignatureAlgorithm.Best, AssociationRelyingPartyType.Smart, associationStore, securitySettings) : null;
			var coordinator = new OpenIdCoordinator(
				rp => {
					var request = new CheckIdRequest(protocol.Version, OPUri, immediate ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup);

					if (association != null) {
						StoreAssociation(rp, OPUri, association);
						request.AssociationHandle = association.Handle;
					}

					request.ClaimedIdentifier = "http://claimedid";
					request.LocalIdentifier = "http://localid";
					request.ReturnTo = RPUri;
					request.Realm = RPUri;
					rp.Channel.Respond(request);
					if (positive) {
						if (tamper) {
							try {
								rp.Channel.ReadFromRequest<PositiveAssertionResponse>();
								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>();
							Assert.IsNotNull(response);
							Assert.AreEqual(request.ClaimedIdentifier, response.ClaimedIdentifier);
							Assert.AreEqual(request.LocalIdentifier, response.LocalIdentifier);
							Assert.AreEqual(request.ReturnTo, response.ReturnTo);

							// Attempt to replay the message and verify that it fails.
							// Because in various scenarios and protocol versions different components
							// notice the replay, we can get one of two exceptions thrown.
							// 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;
								channel.Replay(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>();
						Assert.IsNotNull(response);
						if (immediate) {
							// Only 1.1 was required to include user_setup_url
							if (protocol.Version.Major < 2) {
								Assert.IsNotNull(response.UserSetupUrl);
							}
						} else {
							Assert.IsNull(response.UserSetupUrl);
						}
					}
				},
				op => {
					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);
						}
					}
				});
			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);
			}

			coordinator.Run();
		}
		private PositiveAssertionResponse GetPositiveAssertion(bool dualIdentifier) {
			Protocol protocol = Protocol.Default;
			PositiveAssertionResponse assertion = new PositiveAssertionResponse(protocol.Version, this.returnTo);
			assertion.ClaimedIdentifier = dualIdentifier ? this.GetMockDualIdentifier() : this.GetMockIdentifier(protocol.ProtocolVersion, false);
			assertion.LocalIdentifier = OPLocalIdentifiers[0];
			assertion.ReturnTo = this.returnTo;
			assertion.ProviderEndpoint = OPUri;
			return assertion;
		}
		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;
			int opStep = 0;
			HandleProvider(
				async (op, req) => {
					if (association != null) {
						var key = cryptoKeyStore.GetCurrentKey(
							ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, TimeSpan.FromSeconds(1));
						op.CryptoKeyStore.StoreKey(
							ProviderAssociationHandleEncoder.AssociationHandleEncodingSecretBucket, key.Key, key.Value);
					}

					switch (++opStep) {
						case 1:
							var request = await op.Channel.ReadFromRequestAsync<CheckIdRequest>(req, CancellationToken.None);
							Assert.IsNotNull(request);
							IProtocolMessage response;
							if (positive) {
								response = new PositiveAssertionResponse(request);
							} else {
								response = await NegativeAssertionResponse.CreateAsync(request, CancellationToken.None, op.Channel);
							}

							return await op.Channel.PrepareResponseAsync(response);
						case 2:
							if (positive && (statelessRP || !sharedAssociation)) {
								var checkauthRequest =
									await op.Channel.ReadFromRequestAsync<CheckAuthenticationRequest>(req, CancellationToken.None);
								var checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest);
								checkauthResponse.IsValid = checkauthRequest.IsValid;
								return await op.Channel.PrepareResponseAsync(checkauthResponse);
							}

							throw Assumes.NotReachable();
						case 3:
							if (positive && (statelessRP || !sharedAssociation)) {
								if (!tamper) {
									// Respond to the replay attack.
									var checkauthRequest =
										await op.Channel.ReadFromRequestAsync<CheckAuthenticationRequest>(req, CancellationToken.None);
									var checkauthResponse = new CheckAuthenticationResponse(checkauthRequest.Version, checkauthRequest);
									checkauthResponse.IsValid = checkauthRequest.IsValid;
									return await op.Channel.PrepareResponseAsync(checkauthResponse);
								}
							}

							throw Assumes.NotReachable();
						default:
							throw Assumes.NotReachable();
					}
				});

			{
				var rp = this.CreateRelyingParty(statelessRP);
				if (tamper) {
					rp.Channel.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";
						}
					};
				}

				var request = new CheckIdRequest(
					protocol.Version, OPUri, immediate ? AuthenticationRequestMode.Immediate : AuthenticationRequestMode.Setup);

				if (association != null) {
					StoreAssociation(rp, OPUri, association);
					request.AssociationHandle = association.Handle;
				}

				request.ClaimedIdentifier = "http://claimedid";
				request.LocalIdentifier = "http://localid";
				request.ReturnTo = RPUri;
				request.Realm = RPUri;
				var redirectRequest = await rp.Channel.PrepareResponseAsync(request);
				Uri redirectResponse;
				this.HostFactories.AllowAutoRedirects = false;
				using (var httpClient = rp.Channel.HostFactories.CreateHttpClient()) {
					using (var response = await httpClient.GetAsync(redirectRequest.Headers.Location)) {
						Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Redirect));
						redirectResponse = response.Headers.Location;
					}
				}

				var assertionMessage = new HttpRequestMessage(HttpMethod.Get, redirectResponse);
				if (positive) {
					if (tamper) {
						try {
							await rp.Channel.ReadFromRequestAsync<PositiveAssertionResponse>(assertionMessage, CancellationToken.None);
							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 =
							await rp.Channel.ReadFromRequestAsync<PositiveAssertionResponse>(assertionMessage, CancellationToken.None);
						Assert.IsNotNull(response);
						Assert.AreEqual(request.ClaimedIdentifier, response.ClaimedIdentifier);
						Assert.AreEqual(request.LocalIdentifier, response.LocalIdentifier);
						Assert.AreEqual(request.ReturnTo, response.ReturnTo);

						// Attempt to replay the message and verify that it fails.
						// Because in various scenarios and protocol versions different components
						// notice the replay, we can get one of two exceptions thrown.
						// When the OP notices the replay we get a generic InvalidSignatureException.
						// When the RP notices the replay we get a specific ReplayMessageException.
						try {
							await rp.Channel.ReadFromRequestAsync<PositiveAssertionResponse>(assertionMessage, CancellationToken.None);
							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 =
						await rp.Channel.ReadFromRequestAsync<NegativeAssertionResponse>(assertionMessage, CancellationToken.None);
					Assert.IsNotNull(response);
					if (immediate) {
						// Only 1.1 was required to include user_setup_url
						if (protocol.Version.Major < 2) {
							Assert.IsNotNull(response.UserSetupUrl);
						}
					} else {
						Assert.IsNull(response.UserSetupUrl);
					}
				}
			}
		}
Beispiel #13
0
        /// <summary>
        /// Prepares an identity assertion on behalf of one of this Provider's
        /// members in order to redirect the user agent to a relying party
        /// web site and log him/her in immediately in one uninterrupted step.
        /// </summary>
        /// <param name="providerEndpoint">The absolute URL on the Provider site that receives OpenID messages.</param>
        /// <param name="relyingParty">The URL of the Relying Party web site.
        /// This will typically be the home page, but may be a longer URL if
        /// that Relying Party considers the scope of its realm to be more specific.
        /// The URL provided here must allow discovery of the Relying Party's
        /// XRDS document that advertises its OpenID RP endpoint.</param>
        /// <param name="claimedIdentifier">The Identifier you are asserting your member controls.</param>
        /// <param name="localIdentifier">The Identifier you know your user by internally.  This will typically
        /// be the same as <paramref name="claimedIdentifier"/>.</param>
        /// <param name="extensions">The extensions.</param>
        /// <returns>
        /// A <see cref="OutgoingWebResponse"/> object describing the HTTP response to send
        /// the user agent to allow the redirect with assertion to happen.
        /// </returns>
        public OutgoingWebResponse PrepareUnsolicitedAssertion(Uri providerEndpoint, Realm relyingParty, Identifier claimedIdentifier, Identifier localIdentifier, params IExtensionMessage[] extensions)
        {
            Contract.Requires(providerEndpoint != null);
            Contract.Requires(providerEndpoint.IsAbsoluteUri);
            Contract.Requires(relyingParty != null);
            Contract.Requires(claimedIdentifier != null);
            Contract.Requires(localIdentifier != null);
            ErrorUtilities.VerifyArgumentNotNull(providerEndpoint, "providerEndpoint");
            ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
            ErrorUtilities.VerifyArgumentNotNull(claimedIdentifier, "claimedIdentifier");
            ErrorUtilities.VerifyArgumentNotNull(localIdentifier, "localIdentifier");
            ErrorUtilities.VerifyArgumentNamed(providerEndpoint.IsAbsoluteUri, "providerEndpoint", OpenIdStrings.AbsoluteUriRequired);

            // Although the RP should do their due diligence to make sure that this OP
            // is authorized to send an assertion for the given claimed identifier,
            // do due diligence by performing our own discovery on the claimed identifier
            // and make sure that it is tied to this OP and OP local identifier.
            var serviceEndpoint = DotNetOpenAuth.OpenId.RelyingParty.ServiceEndpoint.CreateForClaimedIdentifier(claimedIdentifier, localIdentifier, new ProviderEndpointDescription(providerEndpoint, Protocol.Default.Version), null, null);
            var discoveredEndpoints = claimedIdentifier.Discover(this.WebRequestHandler);
            if (!discoveredEndpoints.Contains(serviceEndpoint)) {
                Logger.OpenId.DebugFormat(
                    "Failed to send unsolicited assertion for {0} because its discovered services did not include this endpoint: {1}{2}{1}Discovered endpoints: {1}{3}",
                    claimedIdentifier,
                    Environment.NewLine,
                    serviceEndpoint,
                    discoveredEndpoints.ToStringDeferred(true));
                ErrorUtilities.ThrowProtocol(OpenIdStrings.UnsolicitedAssertionForUnrelatedClaimedIdentifier, claimedIdentifier);
            }

            Logger.OpenId.InfoFormat("Preparing unsolicited assertion for {0}", claimedIdentifier);
            RelyingPartyEndpointDescription returnToEndpoint = null;
            var returnToEndpoints = relyingParty.Discover(this.WebRequestHandler, true);
            if (returnToEndpoints != null) {
                returnToEndpoint = returnToEndpoints.FirstOrDefault();
            }
            ErrorUtilities.VerifyProtocol(returnToEndpoint != null, OpenIdStrings.NoRelyingPartyEndpointDiscovered, relyingParty);

            var positiveAssertion = new PositiveAssertionResponse(returnToEndpoint) {
                ProviderEndpoint = providerEndpoint,
                ClaimedIdentifier = claimedIdentifier,
                LocalIdentifier = localIdentifier,
            };

            if (extensions != null) {
                foreach (IExtensionMessage extension in extensions) {
                    positiveAssertion.Extensions.Add(extension);
                }
            }

            return this.Channel.PrepareResponse(positiveAssertion);
        }
		/// <summary>
		/// Initializes a new instance of the <see cref="PositiveAuthenticationResponse"/> class
		/// after verifying that discovery on the identifier matches the asserted data.
		/// </summary>
		/// <param name="response">The response.</param>
		/// <param name="relyingParty">The relying party.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <returns>The newly initialized instance.</returns>
		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;
		}
Beispiel #15
0
		/// <summary>
		/// Prepares an identity assertion on behalf of one of this Provider's
		/// members in order to redirect the user agent to a relying party
		/// web site and log him/her in immediately in one uninterrupted step.
		/// </summary>
		/// <param name="providerEndpoint">The absolute URL on the Provider site that receives OpenID messages.</param>
		/// <param name="relyingPartyRealm">The URL of the Relying Party web site.
		/// This will typically be the home page, but may be a longer URL if
		/// that Relying Party considers the scope of its realm to be more specific.
		/// The URL provided here must allow discovery of the Relying Party's
		/// XRDS document that advertises its OpenID RP endpoint.</param>
		/// <param name="claimedIdentifier">The Identifier you are asserting your member controls.</param>
		/// <param name="localIdentifier">The Identifier you know your user by internally.  This will typically
		/// be the same as <paramref name="claimedIdentifier" />.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <param name="extensions">The extensions.</param>
		/// <returns>
		/// A <see cref="HttpResponseMessage" /> object describing the HTTP response to send
		/// the user agent to allow the redirect with assertion to happen.
		/// </returns>
		public async Task<HttpResponseMessage> PrepareUnsolicitedAssertionAsync(Uri providerEndpoint, Realm relyingPartyRealm, Identifier claimedIdentifier, Identifier localIdentifier, CancellationToken cancellationToken = default(CancellationToken), params IExtensionMessage[] extensions) {
			Requires.NotNull(providerEndpoint, "providerEndpoint");
			Requires.That(providerEndpoint.IsAbsoluteUri, "providerEndpoint", OpenIdStrings.AbsoluteUriRequired);
			Requires.NotNull(relyingPartyRealm, "relyingPartyRealm");
			Requires.NotNull(claimedIdentifier, "claimedIdentifier");
			Requires.NotNull(localIdentifier, "localIdentifier");
			RequiresEx.ValidState(this.Channel.HostFactories != null);

			// Although the RP should do their due diligence to make sure that this OP
			// is authorized to send an assertion for the given claimed identifier,
			// do due diligence by performing our own discovery on the claimed identifier
			// and make sure that it is tied to this OP and OP local identifier.
			if (this.SecuritySettings.UnsolicitedAssertionVerification != ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.NeverVerify) {
				var serviceEndpoint = IdentifierDiscoveryResult.CreateForClaimedIdentifier(claimedIdentifier, localIdentifier, new ProviderEndpointDescription(providerEndpoint, Protocol.Default.Version), null, null);
				var discoveredEndpoints = await this.discoveryServices.DiscoverAsync(claimedIdentifier, cancellationToken);
				if (!discoveredEndpoints.Contains(serviceEndpoint)) {
					Logger.OpenId.WarnFormat(
						"Failed to send unsolicited assertion for {0} because its discovered services did not include this endpoint: {1}{2}{1}Discovered endpoints: {1}{3}",
						claimedIdentifier,
						Environment.NewLine,
						serviceEndpoint,
						discoveredEndpoints.ToStringDeferred(true));

					// Only FAIL if the setting is set for it.
					if (this.securitySettings.UnsolicitedAssertionVerification == ProviderSecuritySettings.UnsolicitedAssertionVerificationLevel.RequireSuccess) {
						ErrorUtilities.ThrowProtocol(OpenIdStrings.UnsolicitedAssertionForUnrelatedClaimedIdentifier, claimedIdentifier);
					}
				}
			}

			Logger.OpenId.InfoFormat("Preparing unsolicited assertion for {0}", claimedIdentifier);
			RelyingPartyEndpointDescription returnToEndpoint = null;
			var returnToEndpoints = await relyingPartyRealm.DiscoverReturnToEndpointsAsync(this.Channel.HostFactories, true, cancellationToken);
			if (returnToEndpoints != null) {
				returnToEndpoint = returnToEndpoints.FirstOrDefault();
			}
			ErrorUtilities.VerifyProtocol(returnToEndpoint != null, OpenIdStrings.NoRelyingPartyEndpointDiscovered, relyingPartyRealm);

			var positiveAssertion = new PositiveAssertionResponse(returnToEndpoint) {
				ProviderEndpoint = providerEndpoint,
				ClaimedIdentifier = claimedIdentifier,
				LocalIdentifier = localIdentifier,
			};

			if (extensions != null) {
				foreach (IExtensionMessage extension in extensions) {
					positiveAssertion.Extensions.Add(extension);
				}
			}

			Reporting.RecordEventOccurrence(this, "PrepareUnsolicitedAssertion");
			return await this.Channel.PrepareResponseAsync(positiveAssertion, cancellationToken);
		}