/// <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)); }
//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)); }
/// <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); }