Пример #1
0
        /// <summary>
        /// Verifies that the positive assertion data matches the results of
        /// discovery on the Claimed Identifier.
        /// </summary>
        /// <param name="relyingParty">The relying party.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>
        /// A task that completes with the asynchronous operation.
        /// </returns>
        /// <exception cref="ProtocolException">Thrown when the Provider is asserting that a user controls an Identifier
        /// when discovery on that Identifier contradicts what the Provider says.
        /// This would be an indication of either a misconfigured Provider or
        /// an attempt by someone to spoof another user's identity with a rogue Provider.</exception>
        private async Task VerifyDiscoveryMatchesAssertionAsync(OpenIdRelyingParty relyingParty, CancellationToken cancellationToken)
        {
            Logger.OpenId.Debug("Verifying assertion matches identifier discovery results...");

            // Ensure that we abide by the RP's rules regarding RequireSsl for this discovery step.
            Identifier claimedId = this.Response.ClaimedIdentifier;

            if (relyingParty.SecuritySettings.RequireSsl)
            {
                if (!claimedId.TryRequireSsl(out claimedId))
                {
                    Logger.OpenId.ErrorFormat("This site is configured to accept only SSL-protected OpenIDs, but {0} was asserted and must be rejected.", this.Response.ClaimedIdentifier);
                    ErrorUtilities.ThrowProtocol(OpenIdStrings.RequireSslNotSatisfiedByAssertedClaimedId, this.Response.ClaimedIdentifier);
                }
            }

            // Check whether this particular identifier presents a problem with HTTP discovery
            // due to limitations in the .NET Uri class.
            UriIdentifier claimedIdUri = claimedId as UriIdentifier;

            if (claimedIdUri != null && claimedIdUri.ProblematicNormalization)
            {
                ErrorUtilities.VerifyProtocol(relyingParty.SecuritySettings.AllowApproximateIdentifierDiscovery, OpenIdStrings.ClaimedIdentifierDefiesDotNetNormalization);
                Logger.OpenId.WarnFormat("Positive assertion for claimed identifier {0} cannot be precisely verified under partial trust hosting due to .NET limitation.  An approximate verification will be attempted.", claimedId);
            }

            // While it LOOKS like we're performing discovery over HTTP again
            // Yadis.IdentifierDiscoveryCachePolicy is set to HttpRequestCacheLevel.CacheIfAvailable
            // which means that the .NET runtime is caching our discoveries for us.  This turns out
            // to be very fast and keeps our code clean and easily verifiable as correct and secure.
            // CAUTION: if this discovery is ever made to be skipped based on previous discovery
            // data that was saved to the return_to URL, be careful to verify that that information
            // is signed by the RP before it's considered reliable.  In 1.x stateless mode, this RP
            // doesn't (and can't) sign its own return_to URL, so its cached discovery information
            // is merely a hint that must be verified by performing discovery again here.
            var discoveryResults = await relyingParty.DiscoverAsync(claimedId, cancellationToken);

            ErrorUtilities.VerifyProtocol(
                discoveryResults.Contains(this.Endpoint),
                OpenIdStrings.IssuedAssertionFailsIdentifierDiscovery,
                this.Endpoint,
                discoveryResults.ToStringDeferred(true));
        }
Пример #2
0
        //ERIC'S CODE
        //private async Task VerifyDiscoveryMatchesAssertionAsync_ccp(OpenIdRelyingParty relyingParty, CancellationToken cancellationToken)
        public async Task VerifyDiscoveryMatchesAssertionAsync_ccp(OpenIdRelyingParty relyingParty, CancellationToken cancellationToken)
        {
            Logger.OpenId.Debug("Verifying assertion matches identifier discovery results...");

            // Ensure that we abide by the RP's rules regarding RequireSsl for this discovery step.
            Identifier claimedId = this.Response.ClaimedIdentifier;

            /*
             * if (relyingParty.SecuritySettings.RequireSsl)
             * {
             *  if (!claimedId.TryRequireSsl(out claimedId))
             *  {
             *      Logger.OpenId.ErrorFormat("This site is configured to accept only SSL-protected OpenIDs, but {0} was asserted and must be rejected.", this.Response.ClaimedIdentifier);
             *      ErrorUtilities.ThrowProtocol(OpenIdStrings.RequireSslNotSatisfiedByAssertedClaimedId, this.Response.ClaimedIdentifier);
             *  }
             * }
             */

            // Check whether this particular identifier presents a problem with HTTP discovery
            // due to limitations in the .NET Uri class.
            UriIdentifier claimedIdUri = claimedId as UriIdentifier;

            /*
             * if (claimedIdUri != null && claimedIdUri.ProblematicNormalization)
             * {
             *  ErrorUtilities.VerifyProtocol(relyingParty.SecuritySettings.AllowApproximateIdentifierDiscovery, OpenIdStrings.ClaimedIdentifierDefiesDotNetNormalization);
             *  Logger.OpenId.WarnFormat("Positive assertion for claimed identifier {0} cannot be precisely verified under partial trust hosting due to .NET limitation.  An approximate verification will be attempted.", claimedId);
             * }
             */
            var discoveryResults = await relyingParty.DiscoverAsync(claimedId, cancellationToken);

            ErrorUtilities.VerifyProtocol(
                discoveryResults.Contains(this.Endpoint),
                OpenIdStrings.IssuedAssertionFailsIdentifierDiscovery,
                this.Endpoint,
                discoveryResults.ToStringDeferred(true));
        }
Пример #3
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>
		/// <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>
		/// Verifies that the positive assertion data matches the results of
		/// discovery on the Claimed Identifier.
		/// </summary>
		/// <param name="relyingParty">The relying party.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <returns>
		/// A task that completes with the asynchronous operation.
		/// </returns>
		/// <exception cref="ProtocolException">Thrown when the Provider is asserting that a user controls an Identifier
		/// when discovery on that Identifier contradicts what the Provider says.
		/// This would be an indication of either a misconfigured Provider or
		/// an attempt by someone to spoof another user's identity with a rogue Provider.</exception>
		private async Task VerifyDiscoveryMatchesAssertionAsync(OpenIdRelyingParty relyingParty, CancellationToken cancellationToken) {
			Logger.OpenId.Debug("Verifying assertion matches identifier discovery results...");

			// Ensure that we abide by the RP's rules regarding RequireSsl for this discovery step.
			Identifier claimedId = this.Response.ClaimedIdentifier;
			if (relyingParty.SecuritySettings.RequireSsl) {
				if (!claimedId.TryRequireSsl(out claimedId)) {
					Logger.OpenId.ErrorFormat("This site is configured to accept only SSL-protected OpenIDs, but {0} was asserted and must be rejected.", this.Response.ClaimedIdentifier);
					ErrorUtilities.ThrowProtocol(OpenIdStrings.RequireSslNotSatisfiedByAssertedClaimedId, this.Response.ClaimedIdentifier);
				}
			}

			// Check whether this particular identifier presents a problem with HTTP discovery
			// due to limitations in the .NET Uri class.
			UriIdentifier claimedIdUri = claimedId as UriIdentifier;
			if (claimedIdUri != null && claimedIdUri.ProblematicNormalization) {
				ErrorUtilities.VerifyProtocol(relyingParty.SecuritySettings.AllowApproximateIdentifierDiscovery, OpenIdStrings.ClaimedIdentifierDefiesDotNetNormalization);
				Logger.OpenId.WarnFormat("Positive assertion for claimed identifier {0} cannot be precisely verified under partial trust hosting due to .NET limitation.  An approximate verification will be attempted.", claimedId);
			}

			// While it LOOKS like we're performing discovery over HTTP again
			// Yadis.IdentifierDiscoveryCachePolicy is set to HttpRequestCacheLevel.CacheIfAvailable
			// which means that the .NET runtime is caching our discoveries for us.  This turns out
			// to be very fast and keeps our code clean and easily verifiable as correct and secure.
			// CAUTION: if this discovery is ever made to be skipped based on previous discovery
			// data that was saved to the return_to URL, be careful to verify that that information
			// is signed by the RP before it's considered reliable.  In 1.x stateless mode, this RP
			// doesn't (and can't) sign its own return_to URL, so its cached discovery information
			// is merely a hint that must be verified by performing discovery again here.
			var discoveryResults = await relyingParty.DiscoverAsync(claimedId, cancellationToken);
			ErrorUtilities.VerifyProtocol(
				discoveryResults.Contains(this.Endpoint),
				OpenIdStrings.IssuedAssertionFailsIdentifierDiscovery,
				this.Endpoint,
				discoveryResults.ToStringDeferred(true));
		}