/// <summary>
        /// Performs identifier discovery and creates associations and generates authentication requests
        /// on-demand for as long as new ones can be generated based on the results of Identifier discovery.
        /// </summary>
        internal static IEnumerable <AuthenticationRequest> Create(Identifier userSuppliedIdentifier,
                                                                   OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, bool createNewAssociationsAsNeeded)
        {
            // We have a long data validation and preparation process
            if (userSuppliedIdentifier == null)
            {
                throw new ArgumentNullException("userSuppliedIdentifier");
            }
            if (relyingParty == null)
            {
                throw new ArgumentNullException("relyingParty");
            }
            if (realm == null)
            {
                throw new ArgumentNullException("realm");
            }

            userSuppliedIdentifier = userSuppliedIdentifier.TrimFragment();
            if (relyingParty.Settings.RequireSsl)
            {
                // Rather than check for successful SSL conversion at this stage,
                // We'll wait for secure discovery to fail on the new identifier.
                userSuppliedIdentifier.TryRequireSsl(out userSuppliedIdentifier);
            }

            if (Logger.IsWarnEnabled && returnToUrl.Query != null)
            {
                NameValueCollection returnToArgs = HttpUtility.ParseQueryString(returnToUrl.Query);
                foreach (string key in returnToArgs)
                {
                    if (OpenIdRelyingParty.ShouldParameterBeStrippedFromReturnToUrl(key))
                    {
                        Logger.WarnFormat("OpenId argument \"{0}\" found in return_to URL.  This can corrupt an OpenID response.", key);
                        break;
                    }
                }
            }

            // Throw an exception now if the realm and the return_to URLs don't match
            // as required by the provider.  We could wait for the provider to test this and
            // fail, but this will be faster and give us a better error message.
            if (!realm.Contains(returnToUrl))
            {
                throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
                                                        Strings.ReturnToNotUnderRealm, returnToUrl, realm));
            }

            // Perform discovery right now (not deferred).
            var serviceEndpoints = userSuppliedIdentifier.Discover();

            // Call another method that defers request generation.
            return(CreateInternal(userSuppliedIdentifier, relyingParty, realm, returnToUrl, serviceEndpoints, createNewAssociationsAsNeeded));
        }
Exemple #2
0
        /// <summary>
        /// Gets a value indicating whether verification of the return URL claimed by the Relying Party
        /// succeeded.
        /// </summary>
        /// <param name="provider">The OpenIdProvider that is performing the RP discovery.</param>
        /// <returns>Result of realm discovery.</returns>
        private RelyingPartyDiscoveryResult IsReturnUrlDiscoverableCore(OpenIdProvider provider)
        {
            Contract.Requires <ArgumentNullException>(provider != null);

            ErrorUtilities.VerifyInternal(this.Realm != null, "Realm should have been read or derived by now.");

            try {
                if (this.SecuritySettings.RequireSsl && this.Realm.Scheme != Uri.UriSchemeHttps)
                {
                    Logger.OpenId.WarnFormat("RP discovery failed because RequireSsl is true and RP discovery would begin at insecure URL {0}.", this.Realm);
                    return(RelyingPartyDiscoveryResult.NoServiceDocument);
                }

                var returnToEndpoints = this.Realm.DiscoverReturnToEndpoints(provider.Channel.WebRequestHandler, false);
                if (returnToEndpoints == null)
                {
                    return(RelyingPartyDiscoveryResult.NoServiceDocument);
                }

                foreach (var returnUrl in returnToEndpoints)
                {
                    Realm discoveredReturnToUrl = returnUrl.ReturnToEndpoint;

                    // The spec requires that the return_to URLs given in an RPs XRDS doc
                    // do not contain wildcards.
                    if (discoveredReturnToUrl.DomainWildcard)
                    {
                        Logger.Yadis.WarnFormat("Realm {0} contained return_to URL {1} which contains a wildcard, which is not allowed.", Realm, discoveredReturnToUrl);
                        continue;
                    }

                    // Use the same rules as return_to/realm matching to check whether this
                    // URL fits the return_to URL we were given.
                    if (discoveredReturnToUrl.Contains(this.RequestMessage.ReturnTo))
                    {
                        // no need to keep looking after we find a match
                        return(RelyingPartyDiscoveryResult.Success);
                    }
                }
            } catch (ProtocolException ex) {
                // Don't do anything else.  We quietly fail at return_to verification and return false.
                Logger.Yadis.InfoFormat("Relying party discovery at URL {0} failed.  {1}", Realm, ex);
                return(RelyingPartyDiscoveryResult.NoServiceDocument);
            } catch (WebException ex) {
                // Don't do anything else.  We quietly fail at return_to verification and return false.
                Logger.Yadis.InfoFormat("Relying party discovery at URL {0} failed.  {1}", Realm, ex);
                return(RelyingPartyDiscoveryResult.NoServiceDocument);
            }

            return(RelyingPartyDiscoveryResult.NoMatchingReturnTo);
        }
        /// <summary>
        /// Performs identifier discovery, creates associations and generates authentication requests
        /// on-demand for as long as new ones can be generated based on the results of Identifier discovery.
        /// </summary>
        /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
        /// <param name="relyingParty">The relying party.</param>
        /// <param name="realm">The realm.</param>
        /// <param name="returnToUrl">The return_to base URL.</param>
        /// <param name="createNewAssociationsAsNeeded">if set to <c>true</c>, associations that do not exist between this Relying Party and the asserting Providers are created before the authentication request is created.</param>
        /// <returns>A sequence of authentication requests, any of which constitutes a valid identity assertion on the Claimed Identifier.</returns>
        internal static IEnumerable <AuthenticationRequest> Create(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, bool createNewAssociationsAsNeeded)
        {
            // We have a long data validation and preparation process
            ErrorUtilities.VerifyArgumentNotNull(userSuppliedIdentifier, "userSuppliedIdentifier");
            ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
            ErrorUtilities.VerifyArgumentNotNull(realm, "realm");

            // Normalize the portion of the return_to path that correlates to the realm for capitalization.
            // (so that if a web app base path is /MyApp/, but the URL of this request happens to be
            // /myapp/login.aspx, we bump up the return_to Url to use /MyApp/ so it matches the realm.
            UriBuilder returnTo = new UriBuilder(returnToUrl);

            if (returnTo.Path.StartsWith(realm.AbsolutePath, StringComparison.OrdinalIgnoreCase) &&
                !returnTo.Path.StartsWith(realm.AbsolutePath, StringComparison.Ordinal))
            {
                returnTo.Path = realm.AbsolutePath + returnTo.Path.Substring(realm.AbsolutePath.Length);
                returnToUrl   = returnTo.Uri;
            }

            userSuppliedIdentifier = userSuppliedIdentifier.TrimFragment();
            if (relyingParty.SecuritySettings.RequireSsl)
            {
                // Rather than check for successful SSL conversion at this stage,
                // We'll wait for secure discovery to fail on the new identifier.
                userSuppliedIdentifier.TryRequireSsl(out userSuppliedIdentifier);
            }

            if (Logger.IsWarnEnabled && returnToUrl.Query != null)
            {
                NameValueCollection returnToArgs = HttpUtility.ParseQueryString(returnToUrl.Query);
                foreach (string key in returnToArgs)
                {
                    if (OpenIdRelyingParty.IsOpenIdSupportingParameter(key))
                    {
                        Logger.WarnFormat("OpenID argument \"{0}\" found in return_to URL.  This can corrupt an OpenID response.", key);
                    }
                }
            }

            // Throw an exception now if the realm and the return_to URLs don't match
            // as required by the provider.  We could wait for the provider to test this and
            // fail, but this will be faster and give us a better error message.
            ErrorUtilities.VerifyProtocol(realm.Contains(returnToUrl), OpenIdStrings.ReturnToNotUnderRealm, returnToUrl, realm);

            // Perform discovery right now (not deferred).
            var serviceEndpoints = userSuppliedIdentifier.Discover(relyingParty.WebRequestHandler);

            // Call another method that defers request generation.
            return(CreateInternal(userSuppliedIdentifier, relyingParty, realm, returnToUrl, serviceEndpoints, createNewAssociationsAsNeeded));
        }
        /// <summary>
        /// Performs identifier discovery and creates associations and generates authentication requests
        /// on-demand for as long as new ones can be generated based on the results of Identifier discovery.
        /// </summary>
        internal static IEnumerable <AuthenticationRequest> Create(Identifier userSuppliedIdentifier,
                                                                   OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, bool createNewAssociationsAsNeeded)
        {
            // We have a long data validation and preparation process
            if (userSuppliedIdentifier == null)
            {
                throw new ArgumentNullException("userSuppliedIdentifier");
            }
            if (relyingParty == null)
            {
                throw new ArgumentNullException("relyingParty");
            }
            if (realm == null)
            {
                throw new ArgumentNullException("realm");
            }

            userSuppliedIdentifier = userSuppliedIdentifier.TrimFragment();
            if (relyingParty.Settings.RequireSsl)
            {
                // Rather than check for successful SSL conversion at this stage,
                // We'll wait for secure discovery to fail on the new identifier.
                userSuppliedIdentifier.TryRequireSsl(out userSuppliedIdentifier);
            }
            //Logger.InfoFormat("Creating authentication request for user supplied Identifier: {0}",
            //	userSuppliedIdentifier);
            //Logger.DebugFormat("Realm: {0}", realm);
            //Logger.DebugFormat("Return To: {0}", returnToUrl);
            //Logger.DebugFormat("RequireSsl: {0}", userSuppliedIdentifier.IsDiscoverySecureEndToEnd);



            // Throw an exception now if the realm and the return_to URLs don't match
            // as required by the provider.  We could wait for the provider to test this and
            // fail, but this will be faster and give us a better error message.
            if (!realm.Contains(returnToUrl))
            {
                throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
                                                        Strings.ReturnToNotUnderRealm, returnToUrl, realm));
            }

            // Perform discovery right now (not deferred).
            var serviceEndpoints = userSuppliedIdentifier.Discover();

            // Call another method that defers request generation.
            return(CreateInternal(userSuppliedIdentifier, relyingParty, realm, returnToUrl, serviceEndpoints, createNewAssociationsAsNeeded));
        }
		/// <summary>
		/// Performs identifier discovery and creates associations and generates authentication requests
		/// on-demand for as long as new ones can be generated based on the results of Identifier discovery.
		/// </summary>
		internal static IEnumerable<AuthenticationRequest> Create(Identifier userSuppliedIdentifier,
			OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, bool createNewAssociationsAsNeeded) {
			// We have a long data validation and preparation process
			if (userSuppliedIdentifier == null) throw new ArgumentNullException("userSuppliedIdentifier");
			if (relyingParty == null) throw new ArgumentNullException("relyingParty");
			if (realm == null) throw new ArgumentNullException("realm");

			userSuppliedIdentifier = userSuppliedIdentifier.TrimFragment();
			if (relyingParty.Settings.RequireSsl) {
				// Rather than check for successful SSL conversion at this stage,
				// We'll wait for secure discovery to fail on the new identifier.
				userSuppliedIdentifier.TryRequireSsl(out userSuppliedIdentifier);
			}

			if (Logger.IsWarnEnabled && returnToUrl.Query != null) {
				NameValueCollection returnToArgs = HttpUtility.ParseQueryString(returnToUrl.Query);
				foreach (string key in returnToArgs) {
					if (OpenIdRelyingParty.ShouldParameterBeStrippedFromReturnToUrl(key)) {
						Logger.WarnFormat("OpenId argument \"{0}\" found in return_to URL.  This can corrupt an OpenID response.", key);
						break;
					}
				}
			}

			// Throw an exception now if the realm and the return_to URLs don't match
			// as required by the provider.  We could wait for the provider to test this and
			// fail, but this will be faster and give us a better error message.
			if (!realm.Contains(returnToUrl))
				throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
					Strings.ReturnToNotUnderRealm, returnToUrl, realm));

			// Perform discovery right now (not deferred).
			var serviceEndpoints = userSuppliedIdentifier.Discover();

			// Call another method that defers request generation.
			return CreateInternal(userSuppliedIdentifier, relyingParty, realm, returnToUrl, serviceEndpoints, createNewAssociationsAsNeeded);
		}
        internal static AuthenticationRequest Create(Identifier userSuppliedIdentifier,
            OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl)
        {
            if (userSuppliedIdentifier == null) throw new ArgumentNullException("userSuppliedIdentifier");
            if (relyingParty == null) throw new ArgumentNullException("relyingParty");
            if (realm == null) throw new ArgumentNullException("realm");

            userSuppliedIdentifier = userSuppliedIdentifier.TrimFragment();
            if (relyingParty.Settings.RequireSsl) {
                // Rather than check for successful SSL conversion at this stage,
                // We'll wait for secure discovery to fail on the new identifier.
                userSuppliedIdentifier.TryRequireSsl(out userSuppliedIdentifier);
            }
            Logger.InfoFormat("Creating authentication request for user supplied Identifier: {0}",
                userSuppliedIdentifier);
            Logger.DebugFormat("Realm: {0}", realm);
            Logger.DebugFormat("Return To: {0}", returnToUrl);

            if (Logger.IsWarnEnabled && returnToUrl.Query != null) {
                NameValueCollection returnToArgs = HttpUtility.ParseQueryString(returnToUrl.Query);
                foreach (string key in returnToArgs) {
                    if (OpenIdRelyingParty.ShouldParameterBeStrippedFromReturnToUrl(key)) {
                        Logger.WarnFormat("OpenId argument \"{0}\" found in return_to URL.  This can corrupt an OpenID response.", key);
                        break;
                    }
                }
            }

            var endpoints = new List<ServiceEndpoint>(userSuppliedIdentifier.Discover());
            ServiceEndpoint endpoint = selectEndpoint(endpoints.AsReadOnly(), relyingParty);
            if (endpoint == null)
                throw new OpenIdException(Strings.OpenIdEndpointNotFound);
            Logger.DebugFormat("Discovered provider endpoint: {0}", endpoint);

            // Throw an exception now if the realm and the return_to URLs don't match
            // as required by the provider.  We could wait for the provider to test this and
            // fail, but this will be faster and give us a better error message.
            if (!realm.Contains(returnToUrl))
                throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
                    Strings.ReturnToNotUnderRealm, returnToUrl, realm));

            string token = new Token(endpoint).Serialize(relyingParty.Store);
            // Retrieve the association, but don't create one, as a creation was already
            // attempted by the selectEndpoint method.
            Association association = relyingParty.Store != null ? getAssociation(relyingParty, endpoint, false) : null;

            return new AuthenticationRequest(
                token, association, endpoint, realm, returnToUrl, relyingParty);
        }
		/// <summary>
		/// Performs identifier discovery, creates associations and generates authentication requests
		/// on-demand for as long as new ones can be generated based on the results of Identifier discovery.
		/// </summary>
		/// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
		/// <param name="relyingParty">The relying party.</param>
		/// <param name="realm">The realm.</param>
		/// <param name="returnToUrl">The return_to base URL.</param>
		/// <param name="createNewAssociationsAsNeeded">if set to <c>true</c>, associations that do not exist between this Relying Party and the asserting Providers are created before the authentication request is created.</param>
		/// <returns>
		/// 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) {
			Contract.Requires<ArgumentNullException>(userSuppliedIdentifier != null);
			Contract.Requires<ArgumentNullException>(relyingParty != null);
			Contract.Requires<ArgumentNullException>(realm != null);
			Contract.Ensures(Contract.Result<IEnumerable<AuthenticationRequest>>() != null);

			// Normalize the portion of the return_to path that correlates to the realm for capitalization.
			// (so that if a web app base path is /MyApp/, but the URL of this request happens to be
			// /myapp/login.aspx, we bump up the return_to Url to use /MyApp/ so it matches the realm.
			UriBuilder returnTo = new UriBuilder(returnToUrl);
			if (returnTo.Path.StartsWith(realm.AbsolutePath, StringComparison.OrdinalIgnoreCase) &&
				!returnTo.Path.StartsWith(realm.AbsolutePath, StringComparison.Ordinal)) {
				returnTo.Path = realm.AbsolutePath + returnTo.Path.Substring(realm.AbsolutePath.Length);
				returnToUrl = returnTo.Uri;
			}

			userSuppliedIdentifier = userSuppliedIdentifier.TrimFragment();
			if (relyingParty.SecuritySettings.RequireSsl) {
				// Rather than check for successful SSL conversion at this stage,
				// We'll wait for secure discovery to fail on the new identifier.
				if (!userSuppliedIdentifier.TryRequireSsl(out userSuppliedIdentifier)) {
					// But at least log the failure.
					Logger.OpenId.WarnFormat("RequireSsl mode is on, so discovery on insecure identifier {0} will yield no results.", userSuppliedIdentifier);
				}
			}

			if (Logger.OpenId.IsWarnEnabled && returnToUrl.Query != null) {
				NameValueCollection returnToArgs = HttpUtility.ParseQueryString(returnToUrl.Query);
				foreach (string key in returnToArgs) {
					if (OpenIdRelyingParty.IsOpenIdSupportingParameter(key)) {
						Logger.OpenId.WarnFormat("OpenID argument \"{0}\" found in return_to URL.  This can corrupt an OpenID response.", key);
					}
				}
			}

			// Throw an exception now if the realm and the return_to URLs don't match
			// as required by the provider.  We could wait for the provider to test this and
			// fail, but this will be faster and give us a better error message.
			ErrorUtilities.VerifyProtocol(realm.Contains(returnToUrl), OpenIdStrings.ReturnToNotUnderRealm, returnToUrl, realm);

			// Perform discovery right now (not deferred).
			IEnumerable<IdentifierDiscoveryResult> serviceEndpoints;
			try {
				var results = relyingParty.Discover(userSuppliedIdentifier).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.
				// For a discussion on this topic, see
				// http://groups.google.com/group/dotnetopenid/browse_thread/thread/4b5a8c6b2210f387/5e25910e4d2252c8
				// Usually the Discover method we called will automatically filter this for us, but
				// just to be sure, we'll do it here as well since the RP may be configured to allow
				// these dual identifiers for assertion verification purposes.
				var opIdentifiers = results.Where(result => result.ClaimedIdentifier == result.Protocol.ClaimedIdentifierForOPIdentifier).CacheGeneratedResults();
				var claimedIdentifiers = results.Where(result => result.ClaimedIdentifier != result.Protocol.ClaimedIdentifierForOPIdentifier);
				serviceEndpoints = opIdentifiers.Any() ? opIdentifiers : claimedIdentifiers;
			} catch (ProtocolException ex) {
				Logger.Yadis.ErrorFormat("Error while performing discovery on: \"{0}\": {1}", userSuppliedIdentifier, ex);
				serviceEndpoints = Enumerable.Empty<IdentifierDiscoveryResult>();
			}

			// Filter disallowed endpoints.
			serviceEndpoints = relyingParty.SecuritySettings.FilterEndpoints(serviceEndpoints);

			// Call another method that defers request generation.
			return CreateInternal(userSuppliedIdentifier, relyingParty, realm, returnToUrl, serviceEndpoints, createNewAssociationsAsNeeded);
		}
Exemple #8
0
        /// <summary>
        /// Performs identifier discovery, creates associations and generates authentication requests
        /// on-demand for as long as new ones can be generated based on the results of Identifier discovery.
        /// </summary>
        /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
        /// <param name="relyingParty">The relying party.</param>
        /// <param name="realm">The realm.</param>
        /// <param name="returnToUrl">The return_to base URL.</param>
        /// <param name="createNewAssociationsAsNeeded">if set to <c>true</c>, associations that do not exist between this Relying Party and the asserting Providers are created before the authentication request is created.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// 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 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");

            // Normalize the portion of the return_to path that correlates to the realm for capitalization.
            // (so that if a web app base path is /MyApp/, but the URL of this request happens to be
            // /myapp/login.aspx, we bump up the return_to Url to use /MyApp/ so it matches the realm.
            UriBuilder returnTo = new UriBuilder(returnToUrl);

            if (returnTo.Path.StartsWith(realm.AbsolutePath, StringComparison.OrdinalIgnoreCase) &&
                !returnTo.Path.StartsWith(realm.AbsolutePath, StringComparison.Ordinal))
            {
                returnTo.Path = realm.AbsolutePath + returnTo.Path.Substring(realm.AbsolutePath.Length);
                returnToUrl   = returnTo.Uri;
            }

            userSuppliedIdentifier = userSuppliedIdentifier.TrimFragment();
            if (relyingParty.SecuritySettings.RequireSsl)
            {
                // Rather than check for successful SSL conversion at this stage,
                // We'll wait for secure discovery to fail on the new identifier.
                if (!userSuppliedIdentifier.TryRequireSsl(out userSuppliedIdentifier))
                {
                    // But at least log the failure.
                    Logger.OpenId.WarnFormat("RequireSsl mode is on, so discovery on insecure identifier {0} will yield no results.", userSuppliedIdentifier);
                }
            }

            if (Logger.OpenId.IsWarnEnabled && returnToUrl.Query != null)
            {
                NameValueCollection returnToArgs = HttpUtility.ParseQueryString(returnToUrl.Query);
                foreach (string key in returnToArgs)
                {
                    if (OpenIdRelyingParty.IsOpenIdSupportingParameter(key))
                    {
                        Logger.OpenId.WarnFormat("OpenID argument \"{0}\" found in return_to URL.  This can corrupt an OpenID response.", key);
                    }
                }
            }

            // Throw an exception now if the realm and the return_to URLs don't match
            // as required by the provider.  We could wait for the provider to test this and
            // fail, but this will be faster and give us a better error message.
            ErrorUtilities.VerifyProtocol(realm.Contains(returnToUrl), OpenIdStrings.ReturnToNotUnderRealm, returnToUrl, realm);

            // Perform discovery right now (not deferred).
            IEnumerable <IdentifierDiscoveryResult> serviceEndpoints;

            try {
                var identifierDiscoveryResults = await relyingParty.DiscoverAsync(userSuppliedIdentifier, cancellationToken);

                var results = identifierDiscoveryResults.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.
                // For a discussion on this topic, see
                // http://groups.google.com/group/dotnetopenid/browse_thread/thread/4b5a8c6b2210f387/5e25910e4d2252c8
                // Usually the Discover method we called will automatically filter this for us, but
                // just to be sure, we'll do it here as well since the RP may be configured to allow
                // these dual identifiers for assertion verification purposes.
                var opIdentifiers      = results.Where(result => result.ClaimedIdentifier == result.Protocol.ClaimedIdentifierForOPIdentifier).CacheGeneratedResults();
                var claimedIdentifiers = results.Where(result => result.ClaimedIdentifier != result.Protocol.ClaimedIdentifierForOPIdentifier);
                serviceEndpoints = opIdentifiers.Any() ? opIdentifiers : claimedIdentifiers;
            } catch (ProtocolException ex) {
                Logger.Yadis.ErrorFormat("Error while performing discovery on: \"{0}\": {1}", userSuppliedIdentifier, ex);
                serviceEndpoints = Enumerable.Empty <IdentifierDiscoveryResult>();
            }

            // Filter disallowed endpoints.
            serviceEndpoints = relyingParty.SecuritySettings.FilterEndpoints(serviceEndpoints);

            // Call another method that defers request generation.
            return(await CreateInternalAsync(userSuppliedIdentifier, relyingParty, realm, returnToUrl, serviceEndpoints, createNewAssociationsAsNeeded, cancellationToken));
        }
        /// <summary>
        /// Performs identifier discovery, creates associations and generates authentication requests
        /// on-demand for as long as new ones can be generated based on the results of Identifier discovery.
        /// </summary>
        /// <param name="userSuppliedIdentifier">The user supplied identifier.</param>
        /// <param name="relyingParty">The relying party.</param>
        /// <param name="realm">The realm.</param>
        /// <param name="returnToUrl">The return_to base URL.</param>
        /// <param name="createNewAssociationsAsNeeded">if set to <c>true</c>, associations that do not exist between this Relying Party and the asserting Providers are created before the authentication request is created.</param>
        /// <returns>
        /// 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)
        {
            Contract.Requires(userSuppliedIdentifier != null);
            Contract.Requires(relyingParty != null);
            Contract.Requires(realm != null);
            Contract.Ensures(Contract.Result<IEnumerable<AuthenticationRequest>>() != null);

            // We have a long data validation and preparation process
            ErrorUtilities.VerifyArgumentNotNull(userSuppliedIdentifier, "userSuppliedIdentifier");
            ErrorUtilities.VerifyArgumentNotNull(relyingParty, "relyingParty");
            ErrorUtilities.VerifyArgumentNotNull(realm, "realm");

            // Normalize the portion of the return_to path that correlates to the realm for capitalization.
            // (so that if a web app base path is /MyApp/, but the URL of this request happens to be
            // /myapp/login.aspx, we bump up the return_to Url to use /MyApp/ so it matches the realm.
            UriBuilder returnTo = new UriBuilder(returnToUrl);
            if (returnTo.Path.StartsWith(realm.AbsolutePath, StringComparison.OrdinalIgnoreCase) &&
                !returnTo.Path.StartsWith(realm.AbsolutePath, StringComparison.Ordinal)) {
                returnTo.Path = realm.AbsolutePath + returnTo.Path.Substring(realm.AbsolutePath.Length);
                returnToUrl = returnTo.Uri;
            }

            userSuppliedIdentifier = userSuppliedIdentifier.TrimFragment();
            if (relyingParty.SecuritySettings.RequireSsl) {
                // Rather than check for successful SSL conversion at this stage,
                // We'll wait for secure discovery to fail on the new identifier.
                if (!userSuppliedIdentifier.TryRequireSsl(out userSuppliedIdentifier)) {
                    // But at least log the failure.
                    Logger.OpenId.WarnFormat("RequireSsl mode is on, so discovery on insecure identifier {0} will yield no results.", userSuppliedIdentifier);
                }
            }

            if (Logger.OpenId.IsWarnEnabled && returnToUrl.Query != null) {
                NameValueCollection returnToArgs = HttpUtility.ParseQueryString(returnToUrl.Query);
                foreach (string key in returnToArgs) {
                    if (OpenIdRelyingParty.IsOpenIdSupportingParameter(key)) {
                        Logger.OpenId.WarnFormat("OpenID argument \"{0}\" found in return_to URL.  This can corrupt an OpenID response.", key);
                    }
                }
            }

            // Throw an exception now if the realm and the return_to URLs don't match
            // as required by the provider.  We could wait for the provider to test this and
            // fail, but this will be faster and give us a better error message.
            ErrorUtilities.VerifyProtocol(realm.Contains(returnToUrl), OpenIdStrings.ReturnToNotUnderRealm, returnToUrl, realm);

            // Perform discovery right now (not deferred).
            IEnumerable<ServiceEndpoint> serviceEndpoints;
            try {
                serviceEndpoints = userSuppliedIdentifier.Discover(relyingParty.WebRequestHandler);
            } catch (ProtocolException ex) {
                Logger.Yadis.ErrorFormat("Error while performing discovery on: \"{0}\": {1}", userSuppliedIdentifier, ex);
                serviceEndpoints = EmptyList<ServiceEndpoint>.Instance;
            }

            // Filter disallowed endpoints.
            serviceEndpoints = relyingParty.SecuritySettings.FilterEndpoints(serviceEndpoints);

            // Call another method that defers request generation.
            return CreateInternal(userSuppliedIdentifier, relyingParty, realm, returnToUrl, serviceEndpoints, createNewAssociationsAsNeeded);
        }
        internal CheckIdRequest(OpenIdProvider provider) : base(provider)
        {
            // handle the mandatory protocol fields
            string mode = Util.GetRequiredArg(Query, Protocol.openid.mode);

            if (Protocol.Args.Mode.checkid_immediate.Equals(mode, StringComparison.Ordinal))
            {
                Immediate = true;
            }
            else if (Protocol.Args.Mode.checkid_setup.Equals(mode, StringComparison.Ordinal))
            {
                Immediate = false;                 // implied
            }
            else
            {
                throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
                                                        Strings.InvalidOpenIdQueryParameterValue, Protocol.openid.mode, mode), Query);
            }

            // The spec says claimed_id and identity can both be either present or
            // absent.  But for now we don't have or support extensions that don't
            // use these parameters, so we require them.  In the future that may change.
            if (Protocol.Version.Major >= 2)
            {
                claimedIdentifier = Util.GetRequiredIdentifierArg(Query, Protocol.openid.claimed_id);
            }
            localIdentifier = Util.GetRequiredIdentifierArg(Query, Protocol.openid.identity);
            // The spec says return_to is optional, but what good is authenticating
            // a user if the user won't be sent back?
            ReturnTo          = Util.GetRequiredUriArg(Query, Protocol.openid.return_to);
            Realm             = Util.GetOptionalRealmArg(Query, Protocol.openid.Realm) ?? ReturnTo;
            AssociationHandle = Util.GetOptionalArg(Query, Protocol.openid.assoc_handle);

            if (!Realm.Contains(ReturnTo))
            {
                throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
                                                        Strings.ReturnToNotUnderRealm, ReturnTo.AbsoluteUri, Realm), Query);
            }

            if (Protocol.Version.Major >= 2)
            {
                if (LocalIdentifier == Protocol.ClaimedIdentifierForOPIdentifier ^
                    ClaimedIdentifier == Protocol.ClaimedIdentifierForOPIdentifier)
                {
                    throw new OpenIdException(string.Format(CultureInfo.CurrentCulture,
                                                            Strings.MatchingArgumentsExpected, Protocol.openid.claimed_id,
                                                            Protocol.openid.identity, Protocol.ClaimedIdentifierForOPIdentifier),
                                              Query);
                }
            }

            if (ClaimedIdentifier == Protocol.ClaimedIdentifierForOPIdentifier &&
                Protocol.ClaimedIdentifierForOPIdentifier != null)
            {
                // Force the OP to deal with identifier_select by nulling out the two identifiers.
                IsDirectedIdentity = true;
                claimedIdentifier  = null;
                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.
            IsDelegatedIdentifier = ClaimedIdentifier != null && ClaimedIdentifier != LocalIdentifier;
        }