diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2010-03-28 18:27:39 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2010-03-30 21:38:26 -0700 |
commit | 84343fa08e8f386aeb353cd8b2300c767fb43069 (patch) | |
tree | 3c7818f91f7fe928ecaac59da291f971d46396d4 /src | |
parent | d70e05fb90aa2d7acd01043913c34644123cbce7 (diff) | |
download | DotNetOpenAuth-84343fa08e8f386aeb353cd8b2300c767fb43069.zip DotNetOpenAuth-84343fa08e8f386aeb353cd8b2300c767fb43069.tar.gz DotNetOpenAuth-84343fa08e8f386aeb353cd8b2300c767fb43069.tar.bz2 |
Finished getting a reasonable story for partial trust scenarios with the problematic URI paths.
Diffstat (limited to 'src')
-rw-r--r-- | src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs | 101 | ||||
-rw-r--r-- | src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs | 2 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OpenId/Identifier.cs | 35 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OpenId/IdentifierDiscoveryResult.cs | 2 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OpenId/UriIdentifier.cs | 132 | ||||
-rw-r--r-- | src/DotNetOpenAuth/OpenId/XriIdentifier.cs | 2 |
6 files changed, 224 insertions, 50 deletions
diff --git a/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs index f1caa43..5dbaa4a 100644 --- a/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs @@ -118,6 +118,14 @@ namespace DotNetOpenAuth.Test.OpenId { [TestCase] public void ToStringTest() { Assert.AreEqual(this.goodUri, new UriIdentifier(this.goodUri).ToString()); + TestAsFullAndPartialTrust(fullTrust => { + Assert.AreEqual("http://abc/D./e.?Qq#Ff", new UriIdentifier("HTTP://ABC/D./e.?Qq#Ff").ToString()); + Assert.AreEqual("http://abc/D./e.?Qq", new UriIdentifier("HTTP://ABC/D./e.?Qq").ToString()); + Assert.AreEqual("http://abc/D./e.#Ff", new UriIdentifier("HTTP://ABC/D./e.#Ff").ToString()); + Assert.AreEqual("http://abc/", new UriIdentifier("HTTP://ABC").ToString()); + Assert.AreEqual("http://abc/?q", new UriIdentifier("HTTP://ABC?q").ToString()); + Assert.AreEqual("http://abc/#f", new UriIdentifier("HTTP://ABC#f").ToString()); + }); } [TestCase] @@ -128,6 +136,13 @@ namespace DotNetOpenAuth.Test.OpenId { Assert.AreNotEqual(new UriIdentifier(this.goodUri), new UriIdentifier(this.goodUri + "a")); Assert.AreNotEqual(null, new UriIdentifier(this.goodUri)); Assert.IsTrue(new UriIdentifier(this.goodUri).Equals(this.goodUri)); + + Assert.AreEqual(Identifier.Parse("HTTP://WWW.FOO.COM/abc", true), Identifier.Parse("http://www.foo.com/abc", true)); + Assert.AreEqual(Identifier.Parse("HTTP://WWW.FOO.COM/abc", true), Identifier.Parse("http://www.foo.com/abc", false)); + Assert.AreEqual(Identifier.Parse("HTTP://WWW.FOO.COM/abc", false), Identifier.Parse("http://www.foo.com/abc", false)); + Assert.AreNotEqual(Identifier.Parse("http://www.foo.com/abc", true), Identifier.Parse("http://www.foo.com/ABC", true)); + Assert.AreNotEqual(Identifier.Parse("http://www.foo.com/abc", true), Identifier.Parse("http://www.foo.com/ABC", false)); + Assert.AreNotEqual(Identifier.Parse("http://www.foo.com/abc", false), Identifier.Parse("http://www.foo.com/ABC", false)); } [TestCase] @@ -160,33 +175,39 @@ namespace DotNetOpenAuth.Test.OpenId { /// </remarks> [TestCase] public void TrailingPeriodsNotTrimmed() { - string claimedIdentifier = "https://me.yahoo.com/a/AsDf.#asdf"; - Identifier id = claimedIdentifier; - Assert.AreEqual(claimedIdentifier, id.OriginalString); - Assert.AreEqual(claimedIdentifier, id.ToString()); - - UriIdentifier idUri = new UriIdentifier(claimedIdentifier); - Assert.AreEqual(claimedIdentifier, idUri.OriginalString); - Assert.AreEqual(claimedIdentifier, idUri.ToString()); - Assert.AreEqual(claimedIdentifier, idUri.Uri.AbsoluteUri); - Assert.AreEqual(Uri.UriSchemeHttps, idUri.Uri.Scheme); // in case custom scheme tricks are played, this must still match - Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().ToString()); - Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().OriginalString); - Assert.AreEqual(id.ToString(), new UriIdentifier(idUri.Uri).ToString()); - - idUri = new UriIdentifier(new Uri(claimedIdentifier)); - Assert.AreEqual(claimedIdentifier, idUri.OriginalString); - Assert.AreEqual(claimedIdentifier, idUri.ToString()); - Assert.AreEqual(claimedIdentifier, idUri.Uri.AbsoluteUri); - Assert.AreEqual(Uri.UriSchemeHttps, idUri.Uri.Scheme); // in case custom scheme tricks are played, this must still match - Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().ToString()); - Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().OriginalString); - Assert.AreEqual(id.ToString(), new UriIdentifier(idUri.Uri).ToString()); - - claimedIdentifier = "https://me.yahoo.com:443/a/AsDf.#asdf"; - id = claimedIdentifier; - Assert.AreEqual(claimedIdentifier, id.OriginalString); - Assert.AreEqual("https://me.yahoo.com/a/AsDf.#asdf", id.ToString()); + TestAsFullAndPartialTrust(fullTrust => { + string claimedIdentifier = "https://me.yahoo.com/a/AsDf.#asdf"; + Identifier id = claimedIdentifier; + Assert.AreEqual(claimedIdentifier, id.OriginalString); + Assert.AreEqual(claimedIdentifier, id.ToString()); + + UriIdentifier idUri = new UriIdentifier(claimedIdentifier); + Assert.AreEqual(claimedIdentifier, idUri.OriginalString); + Assert.AreEqual(claimedIdentifier, idUri.ToString()); + if (fullTrust) { + Assert.AreEqual(claimedIdentifier, idUri.Uri.AbsoluteUri); + } + Assert.AreEqual(Uri.UriSchemeHttps, idUri.Uri.Scheme); // in case custom scheme tricks are played, this must still match + Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().ToString()); + Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().OriginalString); + Assert.AreEqual(id.ToString(), new UriIdentifier((Uri)idUri).ToString(), "Round tripping UriIdentifier->Uri->UriIdentifier failed."); + + idUri = new UriIdentifier(new Uri(claimedIdentifier)); + Assert.AreEqual(claimedIdentifier, idUri.OriginalString); + Assert.AreEqual(claimedIdentifier, idUri.ToString()); + if (fullTrust) { + Assert.AreEqual(claimedIdentifier, idUri.Uri.AbsoluteUri); + } + Assert.AreEqual(Uri.UriSchemeHttps, idUri.Uri.Scheme); // in case custom scheme tricks are played, this must still match + Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().ToString()); + Assert.AreEqual("https://me.yahoo.com/a/AsDf.", idUri.TrimFragment().OriginalString); + Assert.AreEqual(id.ToString(), new UriIdentifier((Uri)idUri).ToString(), "Round tripping UriIdentifier->Uri->UriIdentifier failed."); + + claimedIdentifier = "https://me.yahoo.com:443/a/AsDf.#asdf"; + id = claimedIdentifier; + Assert.AreEqual(claimedIdentifier, id.OriginalString); + Assert.AreEqual("https://me.yahoo.com/a/AsDf.#asdf", id.ToString()); + }); } [TestCase] @@ -241,5 +262,31 @@ namespace DotNetOpenAuth.Test.OpenId { var id = new UriIdentifier("http://server崎/村"); Assert.AreEqual("server崎", id.Uri.Host); } + + /// <summary> + /// Verifies SimpleUri behavior + /// </summary> + [TestCase] + public void SimpleUri() { + Assert.AreEqual("http://abc/D./e.?Qq#Ff", new UriIdentifier.SimpleUri("HTTP://ABC/D./e.?Qq#Ff").ToString()); + Assert.AreEqual("http://abc/D./e.?Qq", new UriIdentifier.SimpleUri("HTTP://ABC/D./e.?Qq").ToString()); + Assert.AreEqual("http://abc/D./e.#Ff", new UriIdentifier.SimpleUri("HTTP://ABC/D./e.#Ff").ToString()); + Assert.AreEqual("http://abc/", new UriIdentifier.SimpleUri("HTTP://ABC").ToString()); + Assert.AreEqual("http://abc/?q", new UriIdentifier.SimpleUri("HTTP://ABC?q").ToString()); + Assert.AreEqual("http://abc/#f", new UriIdentifier.SimpleUri("HTTP://ABC#f").ToString()); + } + + private static void TestAsFullAndPartialTrust(Action<bool> action) { + // Test a bunch of interesting URLs both with scheme substitution on and off. + Assert.IsTrue(UriIdentifier_Accessor.schemeSubstitution, "Expected scheme substitution to be working."); + action(true); + + UriIdentifier_Accessor.schemeSubstitution = false; + try { + action(false); + } finally { + UriIdentifier_Accessor.schemeSubstitution = true; + } + } } } diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index 623f951..ae54c3a 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -94,7 +94,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc)); Map<byte[]>(safeFromByteArray, safeToByteArray); Map<Realm>(realm => realm.ToString(), safeRealm); - Map<Identifier>(id => id.ToString(), safeIdentifier); + Map<Identifier>(id => id.SerializedString, safeIdentifier); Map<bool>(value => value.ToString().ToLowerInvariant(), safeBool); Map<CultureInfo>(c => c.Name, str => new CultureInfo(str)); Map<CultureInfo[]>(cs => string.Join(",", cs.Select(c => c.Name).ToArray()), str => str.Split(',').Select(s => new CultureInfo(s)).ToArray()); diff --git a/src/DotNetOpenAuth/OpenId/Identifier.cs b/src/DotNetOpenAuth/OpenId/Identifier.cs index 03889be..36ec784 100644 --- a/src/DotNetOpenAuth/OpenId/Identifier.cs +++ b/src/DotNetOpenAuth/OpenId/Identifier.cs @@ -39,6 +39,18 @@ namespace DotNetOpenAuth.OpenId { public string OriginalString { get; private set; } /// <summary> + /// Gets the Identifier in the form in which it should be serialized. + /// </summary> + /// <value> + /// For Identifiers that were originally deserialized, this is the exact same + /// string that was deserialized. For Identifiers instantiated in some other way, this is + /// the normalized form of the string used to instantiate the identifier. + /// </value> + internal virtual string SerializedString { + get { return this.IsDeserializedInstance ? this.OriginalString : this.ToString(); } + } + + /// <summary> /// Gets or sets a value indicating whether <see cref="Identifier"/> instances are considered equal /// based solely on their string reprsentations. /// </summary> @@ -59,7 +71,7 @@ namespace DotNetOpenAuth.OpenId { protected internal bool IsDiscoverySecureEndToEnd { get; private set; } /// <summary> - /// Gets or sets a value indicating whether this instance was initialized from + /// Gets a value indicating whether this instance was initialized from /// deserializing a message. /// </summary> /// <remarks> @@ -68,7 +80,7 @@ namespace DotNetOpenAuth.OpenId { /// But if the Identifier is initialized locally, we can and should normalize it /// before serializing it. /// </remarks> - protected bool OriginalStringAsToString { get; private set; } + protected bool IsDeserializedInstance { get; private set; } /// <summary> /// Converts the string representation of an Identifier to its strong type. @@ -138,12 +150,12 @@ namespace DotNetOpenAuth.OpenId { /// whether it is an XRI or URI. /// </summary> /// <param name="identifier">Either a URI or XRI identifier.</param> - /// <param name="preserveExactValue">if set to <c>true</c> this Identifier will serialize exactly as given rather than in its normalized form.</param> + /// <param name="serializeExactValue">if set to <c>true</c> this Identifier will serialize exactly as given rather than in its normalized form.</param> /// <returns> /// An <see cref="Identifier"/> instance for the given value. /// </returns> [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Some of these identifiers are not properly formatted to be Uris at this stage.")] - public static Identifier Parse(string identifier, bool preserveExactValue) { + public static Identifier Parse(string identifier, bool serializeExactValue) { Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(identifier)); Contract.Ensures(Contract.Result<Identifier>() != null); @@ -154,7 +166,7 @@ namespace DotNetOpenAuth.OpenId { id = new UriIdentifier(identifier); } - id.OriginalStringAsToString = preserveExactValue; + id.IsDeserializedInstance = serializeExactValue; return id; } @@ -245,6 +257,19 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> + /// Reparses the specified identifier in order to be assured that the concrete type that + /// implements the identifier is one of the well-known ones. + /// </summary> + /// <param name="identifier">The identifier.</param> + /// <returns>Either <see cref="XriIdentifier"/> or <see cref="UriIdentifier"/>.</returns> + internal static Identifier Reparse(Identifier identifier) { + Contract.Requires<ArgumentNullException>(identifier != null); + Contract.Ensures(Contract.Result<Identifier>() != null); + + return Parse(identifier, identifier.IsDeserializedInstance); + } + + /// <summary> /// Returns an <see cref="Identifier"/> that has no URI fragment. /// Quietly returns the original <see cref="Identifier"/> if it is not /// a <see cref="UriIdentifier"/> or no fragment exists. diff --git a/src/DotNetOpenAuth/OpenId/IdentifierDiscoveryResult.cs b/src/DotNetOpenAuth/OpenId/IdentifierDiscoveryResult.cs index 3190920..c851f24 100644 --- a/src/DotNetOpenAuth/OpenId/IdentifierDiscoveryResult.cs +++ b/src/DotNetOpenAuth/OpenId/IdentifierDiscoveryResult.cs @@ -89,7 +89,7 @@ namespace DotNetOpenAuth.OpenId { // not a derived type that will override expected behavior. // Elsewhere in this class, we count on the fact that this property // is either UriIdentifier or XriIdentifier. MockIdentifier messes it up. - this.claimedIdentifier = value != null ? Identifier.Parse(value) : null; + this.claimedIdentifier = value != null ? Identifier.Reparse(value) : null; } } diff --git a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs index c49df45..c1eb837 100644 --- a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs +++ b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs @@ -12,6 +12,7 @@ namespace DotNetOpenAuth.OpenId { using System.Linq; using System.Reflection; using System.Security; + using System.Text; using System.Text.RegularExpressions; using System.Web.UI.HtmlControls; using System.Xml; @@ -86,7 +87,6 @@ namespace DotNetOpenAuth.OpenId { } catch (SecurityException) { // We must be running in partial trust. Nothing more we can do. Logger.OpenId.Warn("Unable to coerce .NET to stop compressing URI paths due to partial trust limitations. Some URL identifiers may be unable to complete login."); - schemeSubstitution = false; } } @@ -141,7 +141,7 @@ namespace DotNetOpenAuth.OpenId { uriAsString = NormalSchemeToSpecialRoundTrippingScheme(uriAsString); } - if (!TryCanonicalize(new UriBuilder(uriAsString), out uri)) { + if (!TryCanonicalize(uriAsString, out uri)) { throw new UriFormatException(); } if (requireSslDiscovery && uri.Scheme != Uri.UriSchemeHttps) { @@ -164,6 +164,26 @@ namespace DotNetOpenAuth.OpenId { internal bool SchemeImplicitlyPrepended { get; private set; } /// <summary> + /// Gets a value indicating whether this Identifier has characters or patterns that + /// the <see cref="Uri"/> class normalizes away and invalidating the Identifier. + /// </summary> + internal bool ProblematicNormalization { + get { + if (schemeSubstitution) { + // With full trust, we have no problematic URIs + return false; + } + + var simpleUri = new SimpleUri(this.OriginalString); + if (simpleUri.Path.EndsWith(".", StringComparison.Ordinal) || simpleUri.Path.Contains("./")) { + return true; + } + + return false; + } + } + + /// <summary> /// Converts a <see cref="UriIdentifier"/> instance to a <see cref="Uri"/> instance. /// </summary> /// <param name="identifier">The identifier to convert to an ordinary <see cref="Uri"/> instance.</param> @@ -225,7 +245,11 @@ namespace DotNetOpenAuth.OpenId { /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>. /// </returns> public override string ToString() { - return this.OriginalStringAsToString ? this.OriginalString : Uri.AbsoluteUri; + if (this.ProblematicNormalization) { + return new SimpleUri(this.OriginalString).ToString(); + } else { + return this.Uri.AbsoluteUri; + } } /// <summary> @@ -399,7 +423,7 @@ namespace DotNetOpenAuth.OpenId { } // Use a UriBuilder because it helps to normalize the URL as well. - return TryCanonicalize(new UriBuilder(uri), out canonicalUri); + return TryCanonicalize(uri, out canonicalUri); } catch (UriFormatException) { // We try not to land here with checks in the try block, but just in case. return false; @@ -407,9 +431,9 @@ namespace DotNetOpenAuth.OpenId { } /// <summary> - /// Removes the fragment from a URL and sets the host to lowercase. + /// Fixes up the scheme if appropriate. /// </summary> - /// <param name="uriBuilder">The URI builder with the value to canonicalize.</param> + /// <param name="uri">The URI to canonicalize.</param> /// <param name="canonicalUri">The resulting canonical URI.</param> /// <returns><c>true</c> if the canonicalization was successful; <c>false</c> otherwise.</returns> /// <remarks> @@ -419,17 +443,19 @@ namespace DotNetOpenAuth.OpenId { /// For this, you should lookup the value stored in IAuthenticationResponse.ClaimedIdentifier. /// </remarks> [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "The user will see the result of this operation and they want to see it in lower case.")] - private static bool TryCanonicalize(UriBuilder uriBuilder, out Uri canonicalUri) { - Contract.Requires<ArgumentNullException>(uriBuilder != null); - - uriBuilder.Host = uriBuilder.Host.ToLowerInvariant(); + private static bool TryCanonicalize(string uri, out Uri canonicalUri) { + Contract.Requires<ArgumentNullException>(uri != null); if (schemeSubstitution) { + UriBuilder uriBuilder = new UriBuilder(uri); + // Swap out our round-trippable scheme for the publishable (hidden) scheme. uriBuilder.Scheme = uriBuilder.Scheme == roundTrippingHttpParser.RegisteredScheme ? publishableHttpParser.RegisteredScheme : publishableHttpsParser.RegisteredScheme; + canonicalUri = uriBuilder.Uri; + } else { + canonicalUri = new Uri(uri); } - canonicalUri = uriBuilder.Uri; return true; } @@ -473,6 +499,86 @@ namespace DotNetOpenAuth.OpenId { #endif /// <summary> + /// A simple URI class that doesn't suffer from the parsing problems of the <see cref="Uri"/> class. + /// </summary> + internal class SimpleUri { + /// <summary> + /// URI characters that separate the URI Path from subsequent elements. + /// </summary> + private static readonly char[] PathEndingCharacters = new char[] { '?', '#' }; + + /// <summary> + /// Initializes a new instance of the <see cref="SimpleUri"/> class. + /// </summary> + /// <param name="value">The value.</param> + internal SimpleUri(string value) { + Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(value)); + + // Leverage the Uri class's parsing where we can. + Uri uri = new Uri(value); + this.Scheme = uri.Scheme; + this.Authority = uri.Authority; + this.Query = uri.Query; + this.Fragment = uri.Fragment; + + // Get the Path out ourselves, since the default Uri parser compresses it too much for OpenID. + int schemeLength = value.IndexOf(Uri.SchemeDelimiter, StringComparison.Ordinal); + int hostStart = schemeLength + Uri.SchemeDelimiter.Length; + int hostFinish = value.IndexOf('/', hostStart); + if (hostFinish < 0) { + this.Path = "/"; + } else { + int pathFinish = value.IndexOfAny(PathEndingCharacters, hostFinish); + if (pathFinish < 0) { + this.Path = value.Substring(hostFinish); + } else { + this.Path = value.Substring(hostFinish, pathFinish - hostFinish); + } + } + } + + /// <summary> + /// Gets the scheme. + /// </summary> + /// <value>The scheme.</value> + public string Scheme { get; private set; } + + /// <summary> + /// Gets the authority. + /// </summary> + /// <value>The authority.</value> + public string Authority { get; private set; } + + /// <summary> + /// Gets the path of the URI. + /// </summary> + /// <value>The path from the URI.</value> + public string Path { get; private set; } + + /// <summary> + /// Gets the query. + /// </summary> + /// <value>The query.</value> + public string Query { get; private set; } + + /// <summary> + /// Gets the fragment. + /// </summary> + /// <value>The fragment.</value> + public string Fragment { get; private set; } + + /// <summary> + /// Returns a <see cref="System.String"/> that represents this instance. + /// </summary> + /// <returns> + /// A <see cref="System.String"/> that represents this instance. + /// </returns> + public override string ToString() { + return this.Scheme + Uri.SchemeDelimiter + this.Authority + this.Path + this.Query + this.Fragment; + } + } + + /// <summary> /// A URI parser that does not compress paths, such as trimming trailing periods from path segments. /// </summary> private class NonPathCompressingUriParser : GenericUriParser { @@ -506,10 +612,6 @@ namespace DotNetOpenAuth.OpenId { /// Initializes this parser with the actual scheme it should appear to be. /// </summary> /// <param name="hideNonStandardScheme">if set to <c>true</c> Uris using this scheme will look like they're using the original standard scheme.</param> - /// <returns> - /// A value indicating whether this parser will be able to complete its task. - /// It can return <c>false</c> under partial trust conditions. - /// </returns> internal void Initialize(bool hideNonStandardScheme) { if (schemeField == null) { schemeField = typeof(UriParser).GetField("m_Scheme", BindingFlags.NonPublic | BindingFlags.Instance); diff --git a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs index 6835b8c..729f603 100644 --- a/src/DotNetOpenAuth/OpenId/XriIdentifier.cs +++ b/src/DotNetOpenAuth/OpenId/XriIdentifier.cs @@ -121,7 +121,7 @@ namespace DotNetOpenAuth.OpenId { /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>. /// </returns> public override string ToString() { - return this.OriginalStringAsToString ? this.OriginalString : this.CanonicalXri; + return this.CanonicalXri; } /// <summary> |