summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2009-06-25 19:51:02 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2009-06-25 19:51:02 -0700
commit3b2fe4819cf449d1ab88178d055239c64b3cfd5e (patch)
treee191391cc71af22a854a06fee0079dbb74405752 /src
parent97e3fc44a6911289baf3435febc0b003e56ad4e8 (diff)
parentf3ce247dfaa965011c7f8417d00e0fa3dfad4a35 (diff)
downloadDotNetOpenAuth-3b2fe4819cf449d1ab88178d055239c64b3cfd5e.zip
DotNetOpenAuth-3b2fe4819cf449d1ab88178d055239c64b3cfd5e.tar.gz
DotNetOpenAuth-3b2fe4819cf449d1ab88178d055239c64b3cfd5e.tar.bz2
Merge branch 'master' into contracts
Conflicts: src/DotNetOpenAuth.vsmdi src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs src/DotNetOpenAuth/DotNetOpenAuth.csproj src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs src/DotNetOpenAuth/OAuth/ConsumerBase.cs src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs src/DotNetOpenAuth/OAuth/ServiceProvider.cs src/DotNetOpenAuth/OpenId/Protocol.cs src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs src/DotNetOpenAuth/OpenId/Provider/Request.cs src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.BuildTasks/ChangeAssemblyReference.cs47
-rw-r--r--src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs50
-rw-r--r--src/DotNetOpenAuth.BuildTasks/CheckAdminRights.cs30
-rw-r--r--src/DotNetOpenAuth.BuildTasks/CompareFiles.cs85
-rw-r--r--src/DotNetOpenAuth.BuildTasks/CustomMsBuildTasks.sln20
-rw-r--r--src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj66
-rw-r--r--src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs51
-rw-r--r--src/DotNetOpenAuth.BuildTasks/Properties/AssemblyInfo.cs36
-rw-r--r--src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs56
-rw-r--r--src/DotNetOpenAuth.BuildTasks/SetEnvironmentVariable.cs33
-rw-r--r--src/DotNetOpenAuth.BuildTasks/SignatureVerification.cs45
-rw-r--r--src/DotNetOpenAuth.BuildTasks/SnToolTask.cs28
-rw-r--r--src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj17
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/EnumerableCacheTests.cs130
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs1
-rw-r--r--src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs2
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs2
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs2
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs75
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs18
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs14
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs3
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/ChannelElements/UriOrOobEncodingTests.cs68
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs6
-rw-r--r--src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs38
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs4
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs147
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs128
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs83
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs11
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs149
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs62
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAnonymousResponseTests.cs14
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/RelyingPartySecuritySettingsTests.cs24
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs40
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs2
-rw-r--r--src/DotNetOpenAuth.sln1
-rw-r--r--src/DotNetOpenAuth.vsmdi252
-rw-r--r--src/DotNetOpenAuth/ComponentModel/ConverterBase.cs32
-rw-r--r--src/DotNetOpenAuth/ComponentModel/IdentifierConverter.cs69
-rw-r--r--src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverter.cs19
-rw-r--r--src/DotNetOpenAuth/ComponentModel/UriConverter.cs2
-rw-r--r--src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs14
-rw-r--r--src/DotNetOpenAuth/Configuration/OAuthConsumerElement.cs34
-rw-r--r--src/DotNetOpenAuth/Configuration/OAuthConsumerSecuritySettingsElement.cs33
-rw-r--r--src/DotNetOpenAuth/Configuration/OAuthElement.cs48
-rw-r--r--src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs34
-rw-r--r--src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs74
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdElement.cs25
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs15
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs1
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs15
-rw-r--r--src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs53
-rw-r--r--src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs7
-rw-r--r--src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs33
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj59
-rw-r--r--src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs11
-rw-r--r--src/DotNetOpenAuth/Messaging/Channel.cs22
-rw-r--r--src/DotNetOpenAuth/Messaging/EnumerableCache.cs244
-rw-r--r--src/DotNetOpenAuth/Messaging/ErrorUtilities.cs51
-rw-r--r--src/DotNetOpenAuth/Messaging/HostErrorException.cs64
-rw-r--r--src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs44
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs2
-rw-r--r--src/DotNetOpenAuth/Messaging/MessagingUtilities.cs57
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/IMessagePartNullEncoder.cs18
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs11
-rw-r--r--src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs17
-rw-r--r--src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs26
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerCertificateProvider.cs23
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerDescription.cs59
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderAccessToken.cs45
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs52
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs37
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs39
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs6
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthIdentity.cs63
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs89
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs60
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs58
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs190
-rw-r--r--src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs78
-rw-r--r--src/DotNetOpenAuth/OAuth/ConsumerBase.cs37
-rw-r--r--src/DotNetOpenAuth/OAuth/ConsumerSecuritySettings.cs18
-rw-r--r--src/DotNetOpenAuth/OAuth/DesktopConsumer.cs21
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs6
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs13
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs3
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs20
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs7
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs18
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs15
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs20
-rw-r--r--src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs19
-rw-r--r--src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs30
-rw-r--r--src/DotNetOpenAuth/OAuth/OAuthStrings.resx12
-rw-r--r--src/DotNetOpenAuth/OAuth/Protocol.cs105
-rw-r--r--src/DotNetOpenAuth/OAuth/SecuritySettings.cs18
-rw-r--r--src/DotNetOpenAuth/OAuth/ServiceProvider.cs135
-rw-r--r--src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs15
-rw-r--r--src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs25
-rw-r--r--src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs58
-rw-r--r--src/DotNetOpenAuth/OAuth/WebConsumer.cs7
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs139
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs72
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx123
-rw-r--r--src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs117
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs26
-rw-r--r--src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXAttributeFormats.cs45
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs373
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs6
-rw-r--r--src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs45
-rw-r--r--src/DotNetOpenAuth/OpenId/OpenIdStrings.resx15
-rw-r--r--src/DotNetOpenAuth/OpenId/Protocol.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs1
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs12
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs10
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs37
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IDirectedIdentityIdentifierProvider.cs82
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs59
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs54
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/IRequest.cs17
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs93
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs228
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs3
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs23
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/Request.cs21
-rw-r--r--src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs107
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs8
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs59
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs12
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs13
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs2
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs11
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs43
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs13
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs17
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js33
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs177
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs42
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs317
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js150
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs753
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js174
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs21
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs40
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs80
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs4
-rw-r--r--src/DotNetOpenAuth/OpenId/RelyingParty/WellKnownProviders.cs33
-rw-r--r--src/DotNetOpenAuth/OpenId/SecuritySettings.cs8
-rw-r--r--src/DotNetOpenAuth/OpenId/UriIdentifier.cs4
-rw-r--r--src/DotNetOpenAuth/Strings.Designer.cs11
-rw-r--r--src/DotNetOpenAuth/Strings.resx3
-rw-r--r--src/DotNetOpenAuth/Yadis/HtmlParser.cs16
-rw-r--r--src/DotNetOpenAuth/Yadis/Yadis.cs5
-rw-r--r--src/official-build-key.pubbin0 -> 160 bytes
165 files changed, 7519 insertions, 643 deletions
diff --git a/src/DotNetOpenAuth.BuildTasks/ChangeAssemblyReference.cs b/src/DotNetOpenAuth.BuildTasks/ChangeAssemblyReference.cs
new file mode 100644
index 0000000..3ad5eb0
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/ChangeAssemblyReference.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Linq;
+using System.IO;
+using System.Xml;
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.BuildEngine;
+
+namespace DotNetOpenAuth.BuildTasks {
+ /// <summary>
+ /// Replaces Reference items HintPaths in a set of projects.
+ /// </summary>
+ public class ChangeAssemblyReference : Task {
+ /// <summary>
+ /// The projects to alter.
+ /// </summary>
+ [Required]
+ public ITaskItem[] Projects { get; set; }
+ /// <summary>
+ /// The project reference to remove.
+ /// </summary>
+ [Required]
+ public string OldReference { get; set; }
+ /// <summary>
+ /// The assembly reference to add.
+ /// </summary>
+ [Required]
+ public string NewReference { get; set; }
+
+ const string msbuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003";
+ public override bool Execute() {
+ foreach (var project in Projects) {
+ Project doc = new Project();
+ doc.Load(project.ItemSpec);
+
+ var reference = doc.GetEvaluatedItemsByName("Reference").OfType<BuildItem>().
+ Where(item => string.Equals(item.GetMetadata("HintPath"), OldReference, StringComparison.OrdinalIgnoreCase)).Single();
+ reference.SetMetadata("HintPath", NewReference);
+
+ doc.Save(project.ItemSpec);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs b/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs
new file mode 100644
index 0000000..72415d9
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/ChangeProjectReferenceToAssemblyReference.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Linq;
+using System.IO;
+using System.Xml;
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.BuildEngine;
+
+namespace DotNetOpenAuth.BuildTasks {
+ /// <summary>
+ /// Replaces ProjectReference items in a set of projects with Reference items.
+ /// </summary>
+ public class ChangeProjectReferenceToAssemblyReference : Task {
+ /// <summary>
+ /// The projects to alter.
+ /// </summary>
+ [Required]
+ public ITaskItem[] Projects { get; set; }
+ /// <summary>
+ /// The project reference to remove.
+ /// </summary>
+ [Required]
+ public string ProjectReference { get; set; }
+ /// <summary>
+ /// The assembly reference to add.
+ /// </summary>
+ [Required]
+ public string Reference { get; set; }
+
+ const string msbuildNamespace = "http://schemas.microsoft.com/developer/msbuild/2003";
+ public override bool Execute() {
+ foreach (var project in Projects) {
+ Project doc = new Project();
+ doc.Load(project.ItemSpec);
+
+ var projectReference = doc.EvaluatedItems.OfType<BuildItem>().Where(
+ item => item.Name == "ProjectReference" && item.Include == ProjectReference).Single();
+ doc.RemoveItem(projectReference);
+
+ var newReference = doc.AddNewItem("Reference", Path.GetFileNameWithoutExtension(Reference));
+ newReference.SetMetadata("HintPath", Reference);
+
+ doc.Save(project.ItemSpec);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/CheckAdminRights.cs b/src/DotNetOpenAuth.BuildTasks/CheckAdminRights.cs
new file mode 100644
index 0000000..9cfd35d
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/CheckAdminRights.cs
@@ -0,0 +1,30 @@
+//-----------------------------------------------------------------------
+// <copyright file="CheckAdminRights.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System.Security.Principal;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ public class CheckAdminRights : Task {
+ /// <summary>
+ /// Gets or sets a value indicating whether this process has elevated permissions.
+ /// </summary>
+ [Output]
+ public bool IsElevated { get; set; }
+
+ /// <summary>
+ /// Executes this instance.
+ /// </summary>
+ public override bool Execute() {
+ WindowsIdentity id = WindowsIdentity.GetCurrent();
+ WindowsPrincipal p = new WindowsPrincipal(id);
+ this.IsElevated = p.IsInRole(WindowsBuiltInRole.Administrator);
+
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/CompareFiles.cs b/src/DotNetOpenAuth.BuildTasks/CompareFiles.cs
new file mode 100644
index 0000000..691df20
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/CompareFiles.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Framework;
+using System.IO;
+
+namespace DotNetOpenAuth.BuildTasks {
+ public class CompareFiles : Task {
+ /// <summary>
+ /// One set of items to compare.
+ /// </summary>
+ [Required]
+ public ITaskItem[] OriginalItems { get; set; }
+
+ /// <summary>
+ /// The other set of items to compare.
+ /// </summary>
+ [Required]
+ public ITaskItem[] NewItems { get; set; }
+
+ /// <summary>
+ /// Gets whether the items lists contain items that are identical going down the list.
+ /// </summary>
+ [Output]
+ public bool AreSame { get; private set; }
+
+ /// <summary>
+ /// Same as <see cref="AreSame"/>, but opposite.
+ /// </summary>
+ [Output]
+ public bool AreChanged { get { return !AreSame; } }
+
+ public override bool Execute() {
+ AreSame = AreFilesIdentical();
+ return true;
+ }
+
+ private bool AreFilesIdentical() {
+ if (OriginalItems.Length != NewItems.Length) {
+ return false;
+ }
+
+ for (int i = 0; i < OriginalItems.Length; i++) {
+ if (!IsContentOfFilesTheSame(OriginalItems[i].ItemSpec, NewItems[i].ItemSpec)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private bool IsContentOfFilesTheSame(string file1, string file2) {
+ // If exactly one file is missing, that's different.
+ if (File.Exists(file1) ^ File.Exists(file2)) return false;
+ // If both are missing, that's the same.
+ if (!File.Exists(file1)) return true;
+ // If both are present, we need to do a content comparison.
+ using (FileStream fileStream1 = File.OpenRead(file1)) {
+ using (FileStream fileStream2 = File.OpenRead(file2)) {
+ if (fileStream1.Length != fileStream2.Length) return false;
+ byte[] buffer1 = new byte[4096];
+ byte[] buffer2 = new byte[buffer1.Length];
+ int bytesRead;
+ do {
+ bytesRead = fileStream1.Read(buffer1, 0, buffer1.Length);
+ if (fileStream2.Read(buffer2, 0, buffer2.Length) != bytesRead) {
+ // We should never get here since we compared file lengths, but
+ // this is a sanity check.
+ return false;
+ }
+ for (int i = 0; i < bytesRead; i++) {
+ if (buffer1[i] != buffer2[i]) {
+ return false;
+ }
+ }
+ } while (bytesRead == buffer1.Length);
+ }
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/CustomMsBuildTasks.sln b/src/DotNetOpenAuth.BuildTasks/CustomMsBuildTasks.sln
new file mode 100644
index 0000000..6eae4e0
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/CustomMsBuildTasks.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.BuildTasks", "DotNetOpenAuth.BuildTasks.csproj", "{AC231A51-EF60-437C-A33F-AF8ADEB8EB74}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AC231A51-EF60-437C-A33F-AF8ADEB8EB74}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj
new file mode 100644
index 0000000..b198a22
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/DotNetOpenAuth.BuildTasks.csproj
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>9.0.30729</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{AC231A51-EF60-437C-A33F-AF8ADEB8EB74}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>DotNetOpenAuth.BuildTasks</RootNamespace>
+ <AssemblyName>DotNetOpenAuth.BuildTasks</AssemblyName>
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>..\..\lib\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>..\..\lib\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Microsoft.Build.Engine" />
+ <Reference Include="Microsoft.Build.Framework" />
+ <Reference Include="Microsoft.Build.Utilities.v3.5">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core">
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>
+ </Reference>
+ <Reference Include="System.Data" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="ChangeProjectReferenceToAssemblyReference.cs" />
+ <Compile Include="CompareFiles.cs" />
+ <Compile Include="ChangeAssemblyReference.cs" />
+ <Compile Include="GetBuildVersion.cs" />
+ <Compile Include="CheckAdminRights.cs" />
+ <Compile Include="ReSignDelaySignedAssemblies.cs" />
+ <Compile Include="SetEnvironmentVariable.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="SignatureVerification.cs" />
+ <Compile Include="SnToolTask.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project> \ No newline at end of file
diff --git a/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs b/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs
new file mode 100644
index 0000000..e40eb78
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/GetBuildVersion.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Framework;
+using System.IO;
+
+namespace DotNetOpenAuth.BuildTasks {
+ public class GetBuildVersion : Task {
+
+ /// <summary>
+ /// Gets the version string to use in the compiled assemblies.
+ /// </summary>
+ [Output]
+ public string Version { get; private set; }
+
+ /// <summary>
+ /// The file that contains the version base (Major.Minor.Build) to use.
+ /// </summary>
+ [Required]
+ public string VersionFile { get; set; }
+
+ public override bool Execute() {
+ try {
+ Version typedVersion = ReadVersionFromFile();
+ typedVersion = new Version(typedVersion.Major, typedVersion.Minor, typedVersion.Build, CalculateJDate(DateTime.Now));
+ Version = typedVersion.ToString();
+ } catch (ArgumentOutOfRangeException ex) {
+ Log.LogErrorFromException(ex);
+ return false;
+ }
+
+ return true;
+ }
+
+ private Version ReadVersionFromFile() {
+ string[] lines = File.ReadAllLines(VersionFile);
+ string versionLine = lines[0];
+ return new Version(versionLine);
+ }
+
+ private int CalculateJDate(DateTime date) {
+ int yearLastDigit = date.Year % 10;
+ DateTime firstOfYear = new DateTime(date.Year, 1, 1);
+ int dayOfYear = (date - firstOfYear).Days + 1;
+ int jdate = yearLastDigit * 1000 + dayOfYear;
+ return jdate;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.BuildTasks/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..6fdcc21
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CustomMsBuildTasks")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("CustomMsBuildTasks")]
+[assembly: AssemblyCopyright("Copyright © 2008")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("952e3aaa-5dc6-4b71-8c9c-6b485263be19")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs b/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs
new file mode 100644
index 0000000..a0ba386
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/ReSignDelaySignedAssemblies.cs
@@ -0,0 +1,56 @@
+//-----------------------------------------------------------------------
+// <copyright file="ReSignDelaySignedAssemblies.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using Microsoft.Build.Framework;
+ using Microsoft.Build.Utilities;
+
+ public class ReSignDelaySignedAssemblies : SnToolTask {
+ /// <summary>
+ /// Gets or sets the key file to use for signing.
+ /// </summary>
+ public ITaskItem KeyFile { get; set; }
+
+ /// <summary>
+ /// Gets or sets the key container.
+ /// </summary>
+ public ITaskItem KeyContainer { get; set; }
+
+ /// <summary>
+ /// Gets or sets the assemblies to re-sign.
+ /// </summary>
+ public ITaskItem[] Assemblies { get; set; }
+
+ /// <summary>
+ /// Generates the command line commands.
+ /// </summary>
+ protected override string GenerateCommandLineCommands() {
+ ////if (this.Assemblies.Length != 1) {
+ //// throw new NotSupportedException("Exactly 1 assembly for signing is supported.");
+ ////}
+ CommandLineBuilder args = new CommandLineBuilder();
+ args.AppendSwitch("-q");
+
+ if (this.KeyFile != null) {
+ args.AppendSwitch("-R");
+ } else if (this.KeyContainer != null) {
+ args.AppendSwitch("-Rc");
+ } else {
+ throw new InvalidOperationException("Either KeyFile or KeyContainer must be set.");
+ }
+
+ args.AppendFileNameIfNotNull(this.Assemblies[0]);
+ if (this.KeyFile != null) {
+ args.AppendFileNameIfNotNull(this.KeyFile);
+ } else {
+ args.AppendFileNameIfNotNull(this.KeyContainer);
+ }
+
+ return args.ToString();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/SetEnvironmentVariable.cs b/src/DotNetOpenAuth.BuildTasks/SetEnvironmentVariable.cs
new file mode 100644
index 0000000..2de5976
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/SetEnvironmentVariable.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.Build.Utilities;
+using Microsoft.Build.Framework;
+
+namespace DotNetOpenAuth.BuildTasks {
+ public class SetEnvironmentVariable : Task {
+ public SetEnvironmentVariable() {
+ Scope = EnvironmentVariableTarget.Process;
+ }
+
+ /// <summary>
+ /// The name of the environment variable to set or clear.
+ /// </summary>
+ [Required]
+ public string Name { get; set; }
+ /// <summary>
+ /// The value of the environment variable, or the empty string to clear it.
+ /// </summary>
+ [Required]
+ public string Value { get; set; }
+ /// <summary>
+ /// The target environment for the variable. Machine, User, or Process.
+ /// </summary>
+ public EnvironmentVariableTarget Scope { get; set; }
+
+ public override bool Execute() {
+ Environment.SetEnvironmentVariable(Name, Value, Scope);
+ return true;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/SignatureVerification.cs b/src/DotNetOpenAuth.BuildTasks/SignatureVerification.cs
new file mode 100644
index 0000000..2e69926
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/SignatureVerification.cs
@@ -0,0 +1,45 @@
+//-----------------------------------------------------------------------
+// <copyright file="SignatureVerification.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using Microsoft.Build.Utilities;
+
+ public class SignatureVerification : SnToolTask {
+ /// <summary>
+ /// Gets or sets a value indicating whether to register the given assembly and public key token
+ /// for skip verification or clear any pre-existing skip verification entry.
+ /// </summary>
+ public bool SkipVerification { get; set; }
+
+ /// <summary>
+ /// Gets or sets the name of the assembly.
+ /// </summary>
+ /// <value>The name of the assembly.</value>
+ public string AssemblyName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the public key token.
+ /// </summary>
+ /// <value>The public key token.</value>
+ public string PublicKeyToken { get; set; }
+
+ /// <summary>
+ /// Generates the command line commands.
+ /// </summary>
+ protected override string GenerateCommandLineCommands() {
+ CommandLineBuilder builder = new CommandLineBuilder();
+ builder.AppendSwitch("-q");
+ if (this.SkipVerification) {
+ builder.AppendSwitch("-Vr");
+ } else {
+ builder.AppendSwitch("-Vu");
+ }
+
+ builder.AppendFileNameIfNotNull(this.AssemblyName + "," + this.PublicKeyToken);
+ return builder.ToString();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.BuildTasks/SnToolTask.cs b/src/DotNetOpenAuth.BuildTasks/SnToolTask.cs
new file mode 100644
index 0000000..9ba94eb
--- /dev/null
+++ b/src/DotNetOpenAuth.BuildTasks/SnToolTask.cs
@@ -0,0 +1,28 @@
+//-----------------------------------------------------------------------
+// <copyright file="SnToolTask.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.BuildTasks {
+ using System;
+ using System.IO;
+ using Microsoft.Build.Utilities;
+
+ public abstract class SnToolTask : ToolTask {
+ /// <summary>
+ /// Gets the name of the tool.
+ /// </summary>
+ /// <value>The name of the tool.</value>
+ protected override string ToolName {
+ get { return "sn.exe"; }
+ }
+
+ /// <summary>
+ /// Generates the full path to tool.
+ /// </summary>
+ protected override string GenerateFullPathToTool() {
+ return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Microsoft SDKs\Windows\v6.0A\bin\" + this.ToolName);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
index 49e9b9e..680a9ef 100644
--- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
+++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
@@ -80,9 +80,10 @@
<CodeContractsRunInBackground>True</CodeContractsRunInBackground>
<CodeContractsShowSquigglies>False</CodeContractsShowSquigglies>
</PropertyGroup>
- <PropertyGroup Condition=" '$(Sign)' == 'true' ">
+ <PropertyGroup>
<SignAssembly>true</SignAssembly>
- <AssemblyOriginatorKeyFile>..\official-build-key.pfx</AssemblyOriginatorKeyFile>
+ <DelaySign>true</DelaySign>
+ <AssemblyOriginatorKeyFile>..\official-build-key.pub</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'CodeAnalysis|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -129,6 +130,10 @@
<HintPath>..\..\lib\Microsoft.Contracts.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Moq, Version=3.1.416.3, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\lib\Moq.dll</HintPath>
+ </Reference>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Core">
@@ -159,6 +164,7 @@
<Compile Include="Hosting\HttpHost.cs" />
<Compile Include="Hosting\TestingWorkerRequest.cs" />
<Compile Include="Messaging\CollectionAssert.cs" />
+ <Compile Include="Messaging\EnumerableCacheTests.cs" />
<Compile Include="Messaging\ErrorUtilitiesTests.cs" />
<Compile Include="Messaging\MessageSerializerTests.cs" />
<Compile Include="Messaging\Reflection\MessageDescriptionTests.cs" />
@@ -198,9 +204,11 @@
<Compile Include="OAuth\ChannelElements\OAuthChannelTests.cs" />
<Compile Include="OAuth\ChannelElements\PlaintextSigningBindingElementTest.cs" />
<Compile Include="OAuth\ChannelElements\SigningBindingElementBaseTests.cs" />
+ <Compile Include="OAuth\ChannelElements\UriOrOobEncodingTests.cs" />
<Compile Include="OAuth\ConsumerDescription.cs" />
<Compile Include="OAuth\ProtocolTests.cs" />
<Compile Include="OAuth\ServiceProviderDescriptionTests.cs" />
+ <Compile Include="OAuth\ServiceProviderTests.cs" />
<Compile Include="OpenId\AssociationsTests.cs" />
<Compile Include="OpenId\AssociationTests.cs" />
<Compile Include="OpenId\AuthenticationTests.cs" />
@@ -216,6 +224,8 @@
<Compile Include="OpenId\Extensions\AttributeExchange\AttributeValuesTests.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\StoreRequestTests.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\StoreResponseTests.cs" />
+ <Compile Include="OpenId\Extensions\ExtensionsInteropHelperOPTests.cs" />
+ <Compile Include="OpenId\Extensions\ExtensionsInteropHelperRPResponseTests.cs" />
<Compile Include="OpenId\Extensions\ProviderAuthenticationPolicy\PapeRoundTripTests.cs" />
<Compile Include="OpenId\Extensions\ProviderAuthenticationPolicy\PolicyRequestTests.cs" />
<Compile Include="OpenId\Extensions\ProviderAuthenticationPolicy\PolicyResponseTests.cs" />
@@ -224,6 +234,7 @@
<Compile Include="OpenId\Extensions\SimpleRegistration\ClaimsRequestTests.cs" />
<Compile Include="OpenId\Extensions\UI\UIRequestTests.cs" />
<Compile Include="OpenId\IdentifierTests.cs" />
+ <Compile Include="OpenId\Extensions\ExtensionsInteropHelperRPRequestTests.cs" />
<Compile Include="OpenId\Messages\AssociateDiffieHellmanRequestTests.cs" />
<Compile Include="OpenId\Messages\AssociateRequestTests.cs" />
<Compile Include="OpenId\Messages\AssociateUnsuccessfulResponseTests.cs" />
@@ -242,6 +253,8 @@
<Compile Include="OpenId\OpenIdCoordinator.cs" />
<Compile Include="OpenId\AssociationHandshakeTests.cs" />
<Compile Include="OpenId\OpenIdTestBase.cs" />
+ <Compile Include="OpenId\Provider\PerformanceTests.cs" />
+ <Compile Include="OpenId\ProviderEndpointDescriptionTests.cs" />
<Compile Include="OpenId\Provider\AnonymousRequestTests.cs" />
<Compile Include="OpenId\Provider\AuthenticationRequestTest.cs" />
<Compile Include="OpenId\Provider\HostProcessedRequestTests.cs" />
diff --git a/src/DotNetOpenAuth.Test/Messaging/EnumerableCacheTests.cs b/src/DotNetOpenAuth.Test/Messaging/EnumerableCacheTests.cs
new file mode 100644
index 0000000..55f4394
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/Messaging/EnumerableCacheTests.cs
@@ -0,0 +1,130 @@
+//-----------------------------------------------------------------------
+// <copyright file="EnumerableCacheTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// This code is released under the Microsoft Public License (Ms-PL).
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Linq;
+ using DotNetOpenAuth.Messaging;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ /// <summary>
+ /// Tests for cached enumeration.
+ /// </summary>
+ [TestClass]
+ public class EnumerableCacheTests {
+ /// <summary>
+ /// The number of times the generator method's implementation is started.
+ /// </summary>
+ private int generatorInvocations;
+
+ /// <summary>
+ /// The number of times the end of the generator method's implementation is reached.
+ /// </summary>
+ private int generatorCompleted;
+
+ /// <summary>
+ /// Gets or sets the test context.
+ /// </summary>
+ public TestContext TestContext { get; set; }
+
+ /// <summary>
+ /// Sets up a test.
+ /// </summary>
+ [TestInitialize]
+ public void Setup() {
+ this.generatorInvocations = 0;
+ this.generatorCompleted = 0;
+ }
+
+ [TestMethod]
+ public void EnumerableCache() {
+ // Baseline
+ var generator = this.NumberGenerator();
+ var list1 = generator.ToList();
+ var list2 = generator.ToList();
+ Assert.AreEqual(2, this.generatorInvocations);
+ CollectionAssert.AreEqual(list1, list2);
+
+ // Cache behavior
+ this.generatorInvocations = 0;
+ this.generatorCompleted = 0;
+ generator = this.NumberGenerator().CacheGeneratedResults();
+ var list3 = generator.ToList();
+ var list4 = generator.ToList();
+ Assert.AreEqual(1, this.generatorInvocations);
+ Assert.AreEqual(1, this.generatorCompleted);
+ CollectionAssert.AreEqual(list1, list3);
+ CollectionAssert.AreEqual(list1, list4);
+ }
+
+ [TestMethod]
+ public void GeneratesOnlyRequiredElements() {
+ var generator = this.NumberGenerator().CacheGeneratedResults();
+ Assert.AreEqual(0, this.generatorInvocations);
+ generator.Take(2).ToList();
+ Assert.AreEqual(1, this.generatorInvocations);
+ Assert.AreEqual(0, this.generatorCompleted, "Only taking part of the list should not have completed the generator.");
+ }
+
+ [TestMethod]
+ public void PassThruDoubleCache() {
+ var cache1 = this.NumberGenerator().CacheGeneratedResults();
+ var cache2 = cache1.CacheGeneratedResults();
+ Assert.AreSame(cache1, cache2, "Two caches were set up rather than just sharing the first one.");
+ }
+
+ [TestMethod]
+ public void PassThruList() {
+ var list = this.NumberGenerator().ToList();
+ var cache = list.CacheGeneratedResults();
+ Assert.AreSame(list, cache);
+ }
+
+ [TestMethod]
+ public void PassThruArray() {
+ var array = this.NumberGenerator().ToArray();
+ var cache = array.CacheGeneratedResults();
+ Assert.AreSame(array, cache);
+ }
+
+ [TestMethod]
+ public void PassThruCollection() {
+ var collection = new Collection<int>();
+ var cache = collection.CacheGeneratedResults();
+ Assert.AreSame(collection, cache);
+ }
+
+ /// <summary>
+ /// Tests calling IEnumerator.Current before first call to MoveNext.
+ /// </summary>
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void EnumerableCacheCurrentThrowsBefore() {
+ var foo = this.NumberGenerator().CacheGeneratedResults().GetEnumerator().Current;
+ }
+
+ /// <summary>
+ /// Tests calling IEnumerator.Current after MoveNext returns false.
+ /// </summary>
+ [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+ public void EnumerableCacheCurrentThrowsAfter() {
+ var enumerator = this.NumberGenerator().CacheGeneratedResults().GetEnumerator();
+ while (enumerator.MoveNext()) {
+ }
+ var foo = enumerator.Current;
+ }
+
+ private IEnumerable<int> NumberGenerator() {
+ this.generatorInvocations++;
+ for (int i = 10; i < 15; i++) {
+ yield return i;
+ }
+ this.generatorCompleted++;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs b/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs
index dd59bae..4cdaa39 100644
--- a/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/HttpRequestInfoTests.cs
@@ -26,6 +26,7 @@ namespace DotNetOpenAuth.Test.Messaging {
Assert.AreEqual(request.Url.Query, info.Query);
Assert.AreEqual(request.QueryString["a"], info.QueryString["a"]);
Assert.AreEqual(request.Url, info.Url);
+ Assert.AreEqual(request.Url, info.UrlBeforeRewriting);
Assert.AreEqual(request.HttpMethod, info.HttpMethod);
}
diff --git a/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs b/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs
index 0a11a75..accb182 100644
--- a/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs
+++ b/src/DotNetOpenAuth.Test/Messaging/MessagingTestBase.cs
@@ -70,7 +70,7 @@ namespace DotNetOpenAuth.Test {
}
HttpRequestInfo request = new HttpRequestInfo {
HttpMethod = method,
- Url = requestUri.Uri,
+ UrlBeforeRewriting = requestUri.Uri,
Headers = headers,
InputStream = ms,
};
diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs
index 2e5a9ce..46e3373 100644
--- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs
@@ -41,7 +41,7 @@ namespace DotNetOpenAuth.Test.Mocks {
internal CoordinatingHttpRequestInfo(MessageReceivingEndpoint recipient) {
this.recipient = recipient;
if (recipient != null) {
- this.Url = recipient.Location;
+ this.UrlBeforeRewriting = recipient.Location;
}
if (recipient == null || (recipient.AllowedMethods & HttpDeliveryMethods.GetRequest) != 0) {
diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs
index 2e4ccad..7dc369b 100644
--- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuthChannel.cs
@@ -111,7 +111,7 @@ namespace DotNetOpenAuth.Test.Mocks {
if (signedMessage != null) {
string httpMethod = this.GetHttpMethod(signedMessage.HttpMethods);
requestInfo.HttpMethod = httpMethod;
- requestInfo.Url = message.Recipient;
+ requestInfo.UrlBeforeRewriting = message.Recipient;
signedMessage.HttpMethod = httpMethod;
}
diff --git a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs
index be3c563..48547b7 100644
--- a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs
@@ -9,12 +9,13 @@ namespace DotNetOpenAuth.Test.Mocks {
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
+ using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
internal class InMemoryTokenManager : IConsumerTokenManager, IServiceProviderTokenManager {
- private Dictionary<string, string> consumersAndSecrets = new Dictionary<string, string>();
- private Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>();
+ private KeyedCollectionDelegate<string, ConsumerInfo> consumers = new KeyedCollectionDelegate<string, ConsumerInfo>(c => c.Key);
+ private KeyedCollectionDelegate<string, TokenInfo> tokens = new KeyedCollectionDelegate<string, TokenInfo>(t => t.Token);
/// <summary>
/// Request tokens that have been issued, and whether they have been authorized yet.
@@ -29,11 +30,11 @@ namespace DotNetOpenAuth.Test.Mocks {
#region IConsumerTokenManager Members
public string ConsumerKey {
- get { return this.consumersAndSecrets.Keys.Single(); }
+ get { return this.consumers.Single().Key; }
}
public string ConsumerSecret {
- get { return this.consumersAndSecrets.Values.Single(); }
+ get { return this.consumers.Single().Secret; }
}
#endregion
@@ -41,11 +42,11 @@ namespace DotNetOpenAuth.Test.Mocks {
#region ITokenManager Members
public string GetTokenSecret(string token) {
- return this.tokensAndSecrets[token];
+ return this.tokens[token].Secret;
}
public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) {
- this.tokensAndSecrets[response.Token] = response.TokenSecret;
+ this.tokens.Add(new TokenInfo { ConsumerKey = request.ConsumerKey, Token = response.Token, Secret = response.TokenSecret });
this.requestTokens.Add(response.Token, false);
}
@@ -70,8 +71,8 @@ namespace DotNetOpenAuth.Test.Mocks {
////Debug.Assert(this.requestTokens[requestToken], "Unauthorized token should not be exchanged for access token.");
this.requestTokens.Remove(requestToken);
this.accessTokens.Add(accessToken);
- this.tokensAndSecrets.Remove(requestToken);
- this.tokensAndSecrets[accessToken] = accessTokenSecret;
+ this.tokens.Remove(requestToken);
+ this.tokens.Add(new TokenInfo { Token = accessToken, Secret = accessTokenSecret });
}
/// <summary>
@@ -93,8 +94,16 @@ namespace DotNetOpenAuth.Test.Mocks {
#region IServiceProviderTokenManager Members
- public string GetConsumerSecret(string consumerKey) {
- return this.consumersAndSecrets[consumerKey];
+ public IConsumerDescription GetConsumer(string consumerKey) {
+ return this.consumers[consumerKey];
+ }
+
+ public IServiceProviderRequestToken GetRequestToken(string token) {
+ return this.tokens[token];
+ }
+
+ public IServiceProviderAccessToken GetAccessToken(string token) {
+ return this.tokens[token];
}
#endregion
@@ -105,7 +114,7 @@ namespace DotNetOpenAuth.Test.Mocks {
/// </summary>
/// <param name="consumerDescription">The consumer description.</param>
internal void AddConsumer(ConsumerDescription consumerDescription) {
- this.consumersAndSecrets.Add(consumerDescription.ConsumerKey, consumerDescription.ConsumerSecret);
+ this.consumers.Add(new ConsumerInfo { Key = consumerDescription.ConsumerKey, Secret = consumerDescription.ConsumerSecret });
}
/// <summary>
@@ -119,5 +128,49 @@ namespace DotNetOpenAuth.Test.Mocks {
this.requestTokens[requestToken] = true;
}
+
+ private class TokenInfo : IServiceProviderRequestToken, IServiceProviderAccessToken {
+ internal TokenInfo() {
+ this.CreatedOn = DateTime.Now;
+ }
+
+ public string ConsumerKey { get; set; }
+
+ public DateTime CreatedOn { get; set; }
+
+ public string Token { get; set; }
+
+ public string VerificationCode { get; set; }
+
+ public Uri Callback { get; set; }
+
+ public Version ConsumerVersion { get; set; }
+
+ public string Username { get; set; }
+
+ public string[] Roles { get; set; }
+
+ public DateTime? ExpirationDate { get; set; }
+
+ internal string Secret { get; set; }
+ }
+
+ private class ConsumerInfo : IConsumerDescription {
+ #region IConsumerDescription Members
+
+ public string Key { get; set; }
+
+ public string Secret { get; set; }
+
+ public System.Security.Cryptography.X509Certificates.X509Certificate2 Certificate { get; set; }
+
+ public Uri Callback { get; set; }
+
+ public DotNetOpenAuth.OAuth.VerificationCodeFormat VerificationCodeFormat { get; set; }
+
+ public int VerificationCodeLength { get; set; }
+
+ #endregion
+ }
}
}
diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
index 923e6c1..d19d908 100644
--- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
+++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs
@@ -34,7 +34,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
base.SetUp();
this.webRequestHandler = new TestWebRequestHandler();
- this.signingElement = new RsaSha1SigningBindingElement();
+ this.signingElement = new RsaSha1SigningBindingElement(new InMemoryTokenManager());
this.nonceStore = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
this.channel = new OAuthChannel(this.signingElement, this.nonceStore, new InMemoryTokenManager(), new TestMessageFactory());
this.accessor = OAuthChannel_Accessor.AttachShadow(this.channel);
@@ -48,22 +48,22 @@ namespace DotNetOpenAuth.Test.ChannelElements {
[TestMethod, ExpectedException(typeof(ArgumentNullException))]
public void CtorNullStore() {
- new OAuthChannel(new RsaSha1SigningBindingElement(), null, new InMemoryTokenManager(), new TestMessageFactory());
+ new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), null, new InMemoryTokenManager(), new TestMessageFactory());
}
[TestMethod, ExpectedException(typeof(ArgumentNullException))]
public void CtorNullTokenManager() {
- new OAuthChannel(new RsaSha1SigningBindingElement(), this.nonceStore, null, new TestMessageFactory());
+ new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, null, new TestMessageFactory());
}
[TestMethod]
public void CtorSimpleConsumer() {
- new OAuthChannel(new RsaSha1SigningBindingElement(), this.nonceStore, (IConsumerTokenManager)new InMemoryTokenManager());
+ new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, (IConsumerTokenManager)new InMemoryTokenManager());
}
[TestMethod]
public void CtorSimpleServiceProvider() {
- new OAuthChannel(new RsaSha1SigningBindingElement(), this.nonceStore, (IServiceProviderTokenManager)new InMemoryTokenManager());
+ new OAuthChannel(new RsaSha1SigningBindingElement(new InMemoryTokenManager()), this.nonceStore, (IServiceProviderTokenManager)new InMemoryTokenManager());
}
[TestMethod]
@@ -84,9 +84,9 @@ namespace DotNetOpenAuth.Test.ChannelElements {
HttpRequestInfo requestInfo = CreateHttpRequestInfo(HttpDeliveryMethods.PostRequest, fields);
// Now add another field to the request URL
- UriBuilder builder = new UriBuilder(requestInfo.Url);
+ UriBuilder builder = new UriBuilder(requestInfo.UrlBeforeRewriting);
builder.Query = "Name=Andrew";
- requestInfo.Url = builder.Uri;
+ requestInfo.UrlBeforeRewriting = builder.Uri;
requestInfo.RawUrl = builder.Path + builder.Query + builder.Fragment;
// Finally, add an Authorization header
@@ -289,7 +289,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
}
HttpRequestInfo request = new HttpRequestInfo {
HttpMethod = method,
- Url = requestUri.Uri,
+ UrlBeforeRewriting = requestUri.Uri,
RawUrl = requestUri.Path + requestUri.Query + requestUri.Fragment,
Headers = headers,
InputStream = ms,
@@ -301,7 +301,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
private static HttpRequestInfo ConvertToRequestInfo(HttpWebRequest request, Stream postEntity) {
HttpRequestInfo info = new HttpRequestInfo {
HttpMethod = request.Method,
- Url = request.RequestUri,
+ UrlBeforeRewriting = request.RequestUri,
RawUrl = request.RequestUri.AbsolutePath + request.RequestUri.Query + request.RequestUri.Fragment,
Headers = request.Headers,
InputStream = postEntity,
diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs
index ca63b50..627db8f 100644
--- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs
+++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs
@@ -4,9 +4,9 @@
// </copyright>
//-----------------------------------------------------------------------
-namespace DotNetOpenAuth.Test.ChannelElements
-{
+namespace DotNetOpenAuth.Test.ChannelElements {
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -17,7 +17,7 @@ namespace DotNetOpenAuth.Test.ChannelElements
public void HttpsSignatureGeneration() {
SigningBindingElementBase target = new PlaintextSigningBindingElement();
MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest);
- ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint);
+ ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version);
message.ConsumerSecret = "cs";
message.TokenSecret = "ts";
Assert.IsNotNull(target.ProcessOutgoingMessage(message));
@@ -29,7 +29,7 @@ namespace DotNetOpenAuth.Test.ChannelElements
public void HttpsSignatureVerification() {
MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest);
ITamperProtectionChannelBindingElement target = new PlaintextSigningBindingElement();
- ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint);
+ ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version);
message.ConsumerSecret = "cs";
message.TokenSecret = "ts";
message.SignatureMethod = "PLAINTEXT";
@@ -41,7 +41,7 @@ namespace DotNetOpenAuth.Test.ChannelElements
public void HttpsSignatureVerificationNotApplicable() {
SigningBindingElementBase target = new PlaintextSigningBindingElement();
MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest);
- ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint);
+ ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version);
message.ConsumerSecret = "cs";
message.TokenSecret = "ts";
message.SignatureMethod = "ANOTHERALGORITHM";
@@ -53,7 +53,7 @@ namespace DotNetOpenAuth.Test.ChannelElements
public void HttpSignatureGeneration() {
SigningBindingElementBase target = new PlaintextSigningBindingElement();
MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("http://localtest", HttpDeliveryMethods.GetRequest);
- ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint);
+ ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version);
message.ConsumerSecret = "cs";
message.TokenSecret = "ts";
@@ -67,7 +67,7 @@ namespace DotNetOpenAuth.Test.ChannelElements
public void HttpSignatureVerification() {
SigningBindingElementBase target = new PlaintextSigningBindingElement();
MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("http://localtest", HttpDeliveryMethods.GetRequest);
- ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint);
+ ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version);
message.ConsumerSecret = "cs";
message.TokenSecret = "ts";
message.SignatureMethod = "PLAINTEXT";
diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs
index 93c0b3f..6e566c8 100644
--- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs
+++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Test.ChannelElements {
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OAuth;
using DotNetOpenAuth.OAuth.ChannelElements;
using DotNetOpenAuth.OAuth.Messages;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -63,7 +64,7 @@ namespace DotNetOpenAuth.Test.ChannelElements {
internal static UnauthorizedTokenRequest CreateTestRequestTokenMessage(MessageDescriptionCollection messageDescriptions, MessageReceivingEndpoint endpoint) {
endpoint = endpoint ?? new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest);
- UnauthorizedTokenRequest message = new UnauthorizedTokenRequest(endpoint);
+ UnauthorizedTokenRequest message = new UnauthorizedTokenRequest(endpoint, Protocol.V10.Version);
message.ConsumerKey = "nerdbank.org";
((ITamperResistantOAuthMessage)message).ConsumerSecret = "nerdbanksecret";
var signedMessage = (ITamperResistantOAuthMessage)message;
diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/UriOrOobEncodingTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/UriOrOobEncodingTests.cs
new file mode 100644
index 0000000..40fc93e
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/UriOrOobEncodingTests.cs
@@ -0,0 +1,68 @@
+//-----------------------------------------------------------------------
+// <copyright file="UriOrOobEncodingTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.OAuth.ChannelElements;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class UriOrOobEncodingTests : TestBase {
+ private UriOrOobEncoding encoding;
+
+ [TestInitialize]
+ public void Setup() {
+ this.encoding = new UriOrOobEncoding();
+ }
+
+ /// <summary>
+ /// Verifies null value encoding
+ /// </summary>
+ [TestMethod]
+ public void NullValueEncoding() {
+ Assert.AreEqual("oob", this.encoding.EncodedNullValue);
+ }
+
+ /// <summary>
+ /// Verifies decoding "oob" results in a null uri.
+ /// </summary>
+ [TestMethod]
+ public void DecodeOobToNullUri() {
+ Assert.IsNull(this.encoding.Decode("oob"));
+ }
+
+ /// <summary>
+ /// Verifies that decoding an empty string generates an exception.
+ /// </summary>
+ [TestMethod, ExpectedException(typeof(UriFormatException))]
+ public void DecodeEmptyStringFails() {
+ this.encoding.Decode(string.Empty);
+ }
+
+ /// <summary>
+ /// Verifies proper decoding/encoding of a Uri
+ /// </summary>
+ [TestMethod]
+ public void UriEncodeDecode() {
+ Uri original = new Uri("http://somehost/p?q=a#frag");
+ string encodedValue = this.encoding.Encode(original);
+ Assert.AreEqual(original.AbsoluteUri, encodedValue);
+ Uri decoded = (Uri)this.encoding.Decode(encodedValue);
+ Assert.AreEqual(original, decoded);
+ }
+
+ /// <summary>
+ /// Verifies failure to decode a relative Uri
+ /// </summary>
+ [TestMethod, ExpectedException(typeof(UriFormatException))]
+ public void RelativeUriDecodeFails() {
+ this.encoding.Decode("../a/b");
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs
index 6a2551a..ce8070b 100644
--- a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs
+++ b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs
@@ -12,7 +12,7 @@ namespace DotNetOpenAuth.Test {
public class ProtocolTests {
[TestMethod]
public void Default() {
- Assert.AreSame(Protocol.V10, Protocol.Default);
+ Assert.AreSame(Protocol.V10a, Protocol.Default);
}
[TestMethod]
@@ -23,12 +23,12 @@ namespace DotNetOpenAuth.Test {
[TestMethod]
public void AuthorizationHeaderScheme() {
- Assert.AreEqual("OAuth", Protocol.V10.AuthorizationHeaderScheme);
+ Assert.AreEqual("OAuth", Protocol.AuthorizationHeaderScheme);
}
[TestMethod]
public void ParameterPrefix() {
- Assert.AreEqual("oauth_", Protocol.V10.ParameterPrefix);
+ Assert.AreEqual("oauth_", Protocol.ParameterPrefix);
}
}
}
diff --git a/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs
new file mode 100644
index 0000000..2a443ce
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OAuth/ServiceProviderTests.cs
@@ -0,0 +1,38 @@
+//-----------------------------------------------------------------------
+// <copyright file="ServiceProviderTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OAuth {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ServiceProviderTests : TestBase {
+ /// <summary>
+ /// Verifies the CreateVerificationCode method.
+ /// </summary>
+ [TestMethod]
+ public void CreateVerificationCode() {
+ this.TestCode(VerificationCodeFormat.Numeric, 3, MessagingUtilities.Digits);
+ this.TestCode(VerificationCodeFormat.AlphaLower, 5, MessagingUtilities.LowercaseLetters);
+ this.TestCode(VerificationCodeFormat.AlphaUpper, 5, MessagingUtilities.UppercaseLetters);
+ this.TestCode(VerificationCodeFormat.AlphaNumericNoLookAlikes, 8, MessagingUtilities.AlphaNumericNoLookAlikes);
+ }
+
+ private void TestCode(VerificationCodeFormat format, int length, string allowableCharacters) {
+ string code = ServiceProvider.CreateVerificationCode(format, length);
+ TestContext.WriteLine("{0} of length {2}: {1}", format, code, length);
+ Assert.AreEqual(length, code.Length);
+ foreach (char ch in code) {
+ Assert.IsTrue(allowableCharacters.Contains(ch));
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs
index f88af0d..af3b1b1 100644
--- a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs
@@ -375,8 +375,8 @@ namespace DotNetOpenAuth.Test.OpenId {
var unencryptedResponse = (AssociateUnencryptedResponse)associateSuccessfulResponse;
}
} else {
- Assert.IsNull(associationManagerAccessor.associationStore.GetAssociation(opDescription.Endpoint, new SecuritySettings(false)));
- Assert.IsNull(coordinator.Provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, new SecuritySettings(true)));
+ Assert.IsNull(associationManagerAccessor.associationStore.GetAssociation(opDescription.Endpoint, new RelyingPartySecuritySettings()));
+ Assert.IsNull(coordinator.Provider.AssociationStore.GetAssociation(AssociationRelyingPartyType.Smart, new ProviderSecuritySettings()));
}
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs
index 08432ff..29797dc 100644
--- a/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/ChannelElements/ExtensionsBindingElementTests.cs
@@ -32,7 +32,7 @@ namespace DotNetOpenAuth.Test.OpenId.ChannelElements {
this.factory = new StandardOpenIdExtensionFactory();
this.factory.RegisterExtension(MockOpenIdExtension.Factory);
- this.rpElement = new ExtensionsBindingElement(this.factory);
+ this.rpElement = new ExtensionsBindingElement(this.factory, new RelyingPartySecuritySettings());
this.rpElement.Channel = new TestChannel(this.MessageDescriptions);
this.request = new SignedResponseRequest(Protocol.Default.Version, OpenIdTestBase.OPUri, AuthenticationRequestMode.Immediate);
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs
new file mode 100644
index 0000000..9f849ea
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperOPTests.cs
@@ -0,0 +1,147 @@
+//-----------------------------------------------------------------------
+// <copyright file="ExtensionsInteropHelperOPTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.Extensions {
+ using System.Collections.Generic;
+ using System.Linq;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Extensions;
+ using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.OpenId.Provider;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ExtensionsInteropHelperOPTests : OpenIdTestBase {
+ private AuthenticationRequest request;
+ private IList<IExtensionMessage> extensions;
+
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+
+ var op = this.CreateProvider();
+ var rpRequest = new CheckIdRequest(Protocol.Default.Version, OPUri, DotNetOpenAuth.OpenId.RelyingParty.AuthenticationRequestMode.Setup);
+ rpRequest.ReturnTo = RPUri;
+ this.extensions = rpRequest.Extensions;
+ this.request = new AuthenticationRequest(op, rpRequest);
+ this.request.IsAuthenticated = true;
+ }
+
+ /// <summary>
+ /// Verifies no extensions appear as no extensions
+ /// </summary>
+ [TestMethod]
+ public void NoRequestedExtensions() {
+ var sreg = ExtensionsInteropHelper.UnifyExtensionsAsSreg(this.request);
+ Assert.IsNull(sreg);
+
+ // Make sure we're still able to send an sreg response.
+ var sregResponse = new ClaimsResponse();
+ this.request.AddResponseExtension(sregResponse);
+ ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request);
+ var extensions = this.GetResponseExtensions();
+ Assert.AreSame(sregResponse, extensions.Single());
+ }
+
+ /// <summary>
+ /// Verifies sreg coming in is seen as sreg.
+ /// </summary>
+ [TestMethod]
+ public void UnifyExtensionsAsSregWithSreg() {
+ var sregInjected = new ClaimsRequest {
+ Nickname = DemandLevel.Request,
+ };
+ this.extensions.Add(sregInjected);
+ var sreg = ExtensionsInteropHelper.UnifyExtensionsAsSreg(this.request);
+ Assert.AreSame(sregInjected, sreg);
+ Assert.AreEqual(DemandLevel.Request, sreg.Nickname);
+ Assert.AreEqual(DemandLevel.NoRequest, sreg.FullName);
+
+ var sregResponse = new ClaimsResponse();
+ this.request.AddResponseExtension(sregResponse);
+ ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request);
+ var extensions = this.GetResponseExtensions();
+ Assert.AreSame(sregResponse, extensions.Single());
+ }
+
+ /// <summary>
+ /// Verifies AX coming in looks like sreg.
+ /// </summary>
+ [TestMethod]
+ public void UnifyExtensionsAsSregWithAX() {
+ this.ParameterizedAXTest(AXAttributeFormats.AXSchemaOrg);
+ }
+
+ /// <summary>
+ /// Verifies AX coming in looks like sreg.
+ /// </summary>
+ [TestMethod]
+ public void UnifyExtensionsAsSregWithAXSchemaOpenIdNet() {
+ this.ParameterizedAXTest(AXAttributeFormats.SchemaOpenIdNet);
+ }
+
+ /// <summary>
+ /// Verifies sreg and AX in one request has a preserved sreg request.
+ /// </summary>
+ [TestMethod]
+ public void UnifyExtensionsAsSregWithBothSregAndAX() {
+ var sregInjected = new ClaimsRequest {
+ Nickname = DemandLevel.Request,
+ };
+ this.extensions.Add(sregInjected);
+ var axInjected = new FetchRequest();
+ axInjected.Attributes.AddOptional(WellKnownAttributes.Contact.Email);
+ this.extensions.Add(axInjected);
+ var sreg = ExtensionsInteropHelper.UnifyExtensionsAsSreg(this.request);
+ Assert.AreSame(sregInjected, sreg);
+ Assert.AreEqual(DemandLevel.Request, sreg.Nickname);
+ Assert.AreEqual(DemandLevel.NoRequest, sreg.Email);
+
+ var sregResponseInjected = new ClaimsResponse {
+ Nickname = "andy",
+ };
+ this.request.AddResponseExtension(sregResponseInjected);
+ var axResponseInjected = new FetchResponse();
+ axResponseInjected.Attributes.Add(WellKnownAttributes.Contact.Email, "a@b.com");
+ this.request.AddResponseExtension(axResponseInjected);
+ ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request);
+ var extensions = this.GetResponseExtensions();
+ var sregResponse = extensions.OfType<ClaimsResponse>().Single();
+ Assert.AreEqual("andy", sregResponse.Nickname);
+ var axResponse = extensions.OfType<FetchResponse>().Single();
+ Assert.AreEqual("a@b.com", axResponse.GetAttributeValue(WellKnownAttributes.Contact.Email));
+ }
+
+ private IList<IExtensionMessage> GetResponseExtensions() {
+ IProtocolMessageWithExtensions response = (IProtocolMessageWithExtensions)this.request.Response;
+ return response.Extensions;
+ }
+
+ private void ParameterizedAXTest(AXAttributeFormats format) {
+ var axInjected = new FetchRequest();
+ axInjected.Attributes.AddOptional(ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.Alias, format));
+ axInjected.Attributes.AddRequired(ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.FullName, format));
+ this.extensions.Add(axInjected);
+ var sreg = ExtensionsInteropHelper.UnifyExtensionsAsSreg(this.request);
+ Assert.AreSame(sreg, this.request.GetExtension<ClaimsRequest>());
+ Assert.AreEqual(DemandLevel.Request, sreg.Nickname);
+ Assert.AreEqual(DemandLevel.Require, sreg.FullName);
+ Assert.AreEqual(DemandLevel.NoRequest, sreg.Language);
+
+ var sregResponse = new ClaimsResponse {
+ Nickname = "andy",
+ };
+ this.request.AddResponseExtension(sregResponse);
+ ExtensionsInteropHelper.ConvertSregToMatchRequest(this.request);
+ var extensions = this.GetResponseExtensions();
+ var axResponse = extensions.OfType<FetchResponse>().Single();
+ Assert.AreEqual("andy", axResponse.GetAttributeValue(ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.Alias, format)));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs
new file mode 100644
index 0000000..ba5e335
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPRequestTests.cs
@@ -0,0 +1,128 @@
+//-----------------------------------------------------------------------
+// <copyright file="ExtensionsInteropHelperRPRequestTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId {
+ using System.Linq;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Extensions;
+ using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ExtensionsInteropHelperRPRequestTests : OpenIdTestBase {
+ private AuthenticationRequest authReq;
+ private ClaimsRequest sreg;
+
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+
+ var rp = CreateRelyingParty(true);
+ Identifier identifier = this.GetMockIdentifier(ProtocolVersion.V20);
+ this.authReq = (AuthenticationRequest)rp.CreateRequest(identifier, RPRealmUri, RPUri);
+ this.sreg = new ClaimsRequest {
+ Nickname = DemandLevel.Request,
+ FullName = DemandLevel.Request,
+ BirthDate = DemandLevel.Request,
+ Email = DemandLevel.Require,
+ Country = DemandLevel.Request,
+ PostalCode = DemandLevel.Request,
+ Gender = DemandLevel.Request,
+ Language = DemandLevel.Request,
+ TimeZone = DemandLevel.Request,
+ };
+ }
+
+ /// <summary>
+ /// Verifies that without an Sreg extension to copy from, no AX extension request is added.
+ /// </summary>
+ [TestMethod]
+ public void SpreadSregToAXNoExtensions() {
+ ExtensionsInteropHelper.SpreadSregToAX(this.authReq, AXAttributeFormats.AXSchemaOrg);
+ Assert.AreEqual(0, this.authReq.AppliedExtensions.Count());
+ }
+
+ /// <summary>
+ /// Verifies that Sreg requests are correctly copied to axschema.org AX requests.
+ /// </summary>
+ [TestMethod]
+ public void SpreadSregToAXBasic() {
+ this.authReq.AddExtension(this.sreg);
+ ExtensionsInteropHelper.SpreadSregToAX(this.authReq, AXAttributeFormats.AXSchemaOrg);
+ var ax = this.authReq.AppliedExtensions.OfType<FetchRequest>().Single();
+ Assert.IsFalse(ax.Attributes[WellKnownAttributes.Name.Alias].IsRequired);
+ Assert.IsFalse(ax.Attributes[WellKnownAttributes.Name.FullName].IsRequired);
+ Assert.IsFalse(ax.Attributes[WellKnownAttributes.BirthDate.WholeBirthDate].IsRequired);
+ Assert.IsTrue(ax.Attributes[WellKnownAttributes.Contact.Email].IsRequired);
+ Assert.IsFalse(ax.Attributes[WellKnownAttributes.Contact.HomeAddress.Country].IsRequired);
+ Assert.IsFalse(ax.Attributes[WellKnownAttributes.Contact.HomeAddress.PostalCode].IsRequired);
+ Assert.IsFalse(ax.Attributes[WellKnownAttributes.Person.Gender].IsRequired);
+ Assert.IsFalse(ax.Attributes[WellKnownAttributes.Preferences.Language].IsRequired);
+ Assert.IsFalse(ax.Attributes[WellKnownAttributes.Preferences.TimeZone].IsRequired);
+ }
+
+ /// <summary>
+ /// Verifies that sreg can spread to multiple AX schemas.
+ /// </summary>
+ [TestMethod]
+ public void SpreadSregToAxMultipleSchemas() {
+ this.authReq.AddExtension(this.sreg);
+ ExtensionsInteropHelper.SpreadSregToAX(this.authReq, AXAttributeFormats.AXSchemaOrg | AXAttributeFormats.SchemaOpenIdNet);
+ var ax = this.authReq.AppliedExtensions.OfType<FetchRequest>().Single();
+ Assert.IsTrue(ax.Attributes.Contains(WellKnownAttributes.Name.Alias));
+ Assert.IsTrue(ax.Attributes.Contains(ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.Alias, AXAttributeFormats.SchemaOpenIdNet)));
+ Assert.IsFalse(ax.Attributes.Contains(ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.Alias, AXAttributeFormats.OpenIdNetSchema)));
+ }
+
+ /// <summary>
+ /// Verifies no spread if the OP advertises sreg support.
+ /// </summary>
+ [TestMethod]
+ public void SpreadSregToAxNoOpIfOPSupportsSreg() {
+ this.authReq.AddExtension(this.sreg);
+ this.InjectAdvertisedTypeUri(DotNetOpenAuth.OpenId.Extensions.SimpleRegistration.Constants.sreg_ns);
+ ExtensionsInteropHelper.SpreadSregToAX(this.authReq, AXAttributeFormats.All);
+ Assert.IsFalse(this.authReq.AppliedExtensions.OfType<FetchRequest>().Any());
+ }
+
+ /// <summary>
+ /// Verifies a targeted AX request if the OP advertises a recognized type URI format.
+ /// </summary>
+ [TestMethod]
+ public void SpreadSregToAxTargetedAtOPFormat() {
+ this.authReq.AddExtension(this.sreg);
+ this.InjectAdvertisedTypeUri(WellKnownAttributes.Name.FullName);
+ ExtensionsInteropHelper.SpreadSregToAX(this.authReq, AXAttributeFormats.OpenIdNetSchema);
+ var ax = this.authReq.AppliedExtensions.OfType<FetchRequest>().Single();
+ Assert.IsFalse(ax.Attributes.Contains(ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Contact.Email, AXAttributeFormats.OpenIdNetSchema)));
+ Assert.IsTrue(ax.Attributes.Contains(WellKnownAttributes.Contact.Email));
+ }
+
+ /// <summary>
+ /// Verifies that TransformAXFormat correctly translates AX schema Type URIs.
+ /// </summary>
+ [TestMethod]
+ public void TransformAXFormatTest() {
+ Assert.AreEqual(WellKnownAttributes.Name.Alias, ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.Alias, AXAttributeFormats.AXSchemaOrg));
+ Assert.AreEqual("http://schema.openid.net/namePerson/friendly", ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.Alias, AXAttributeFormats.SchemaOpenIdNet));
+ Assert.AreEqual("http://openid.net/schema/namePerson/friendly", ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.Alias, AXAttributeFormats.OpenIdNetSchema));
+ }
+
+ /// <summary>
+ /// Injects the advertised type URI into the list of advertised services for the authentication request.
+ /// </summary>
+ /// <param name="typeUri">The type URI.</param>
+ private void InjectAdvertisedTypeUri(string typeUri) {
+ var serviceEndpoint = ServiceEndpoint_Accessor.AttachShadow(((ServiceEndpoint)this.authReq.Provider));
+ serviceEndpoint.ProviderDescription = ProviderEndpointDescription_Accessor.AttachShadow(
+ new ProviderEndpointDescription(
+ serviceEndpoint.ProviderDescription.Endpoint,
+ serviceEndpoint.ProviderDescription.Capabilities.Concat(new[] { typeUri })));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs
new file mode 100644
index 0000000..5fe05c1
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/Extensions/ExtensionsInteropHelperRPResponseTests.cs
@@ -0,0 +1,83 @@
+//-----------------------------------------------------------------------
+// <copyright file="ExtensionsInteropHelperRPResponseTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId {
+ using System.Collections.Generic;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Extensions;
+ using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ExtensionsInteropHelperRPResponseTests : OpenIdTestBase {
+ private IAuthenticationResponse response;
+ private IList<IExtensionMessage> extensions;
+
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+
+ IndirectSignedResponse responseMessage = new IndirectSignedResponse(Protocol.Default.Version, RPUri);
+ this.extensions = responseMessage.Extensions;
+ this.response = new DotNetOpenAuth.OpenId.RelyingParty.PositiveAnonymousResponse(responseMessage);
+ }
+
+ /// <summary>
+ /// Verifies that with no extensions present, UnifyExtensionsAsSreg returns an empty ClaimsResponse.
+ /// </summary>
+ [TestMethod]
+ public void UnifyExtensionsAsSregNoExtensions() {
+ var sreg = ExtensionsInteropHelper.UnifyExtensionsAsSreg(this.response, true);
+ Assert.IsNotNull(sreg);
+ Assert.IsNull(sreg.Nickname);
+ }
+
+ /// <summary>
+ /// Verifies that with sreg and AX extensions present, the sreg extension is returned.
+ /// </summary>
+ [TestMethod]
+ public void UnifyExtensionsAsSregWithSreg() {
+ var sregInjected = new ClaimsResponse {
+ Nickname = "andy",
+ };
+ var axInjected = new FetchResponse();
+ axInjected.Attributes.Add(WellKnownAttributes.Name.Alias, "nate");
+ this.extensions.Add(sregInjected);
+ this.extensions.Add(axInjected);
+ var sreg = ExtensionsInteropHelper.UnifyExtensionsAsSreg(this.response, true);
+ Assert.AreSame(sregInjected, sreg);
+ Assert.AreEqual("andy", sreg.Nickname);
+ }
+
+ /// <summary>
+ /// Verifies UnifyExtensionsAsSreg correctly converts AX to sreg.
+ /// </summary>
+ [TestMethod]
+ public void UnifyExtensionsAsSregFromAXSchemaOrg() {
+ var axInjected = new FetchResponse();
+ axInjected.Attributes.Add(WellKnownAttributes.Name.Alias, "nate");
+ this.extensions.Add(axInjected);
+ var sreg = ExtensionsInteropHelper.UnifyExtensionsAsSreg(this.response, true);
+ Assert.AreEqual("nate", sreg.Nickname);
+ }
+
+ /// <summary>
+ /// Verifies UnifyExtensionsAsSreg correctly converts AX in a non-standard format to sreg.
+ /// </summary>
+ [TestMethod]
+ public void UnifyExtensionsasSregFromSchemaOpenIdNet() {
+ var axInjected = new FetchResponse();
+ axInjected.Attributes.Add(ExtensionsInteropHelper_Accessor.TransformAXFormat(WellKnownAttributes.Name.Alias, AXAttributeFormats.SchemaOpenIdNet), "nate");
+ this.extensions.Add(axInjected);
+ var sreg = ExtensionsInteropHelper.UnifyExtensionsAsSreg(this.response, true);
+ Assert.AreEqual("nate", sreg.Nickname);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs b/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs
index 8fa5580..59c818c 100644
--- a/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/OpenIdTestBase.cs
@@ -187,7 +187,16 @@ namespace DotNetOpenAuth.Test.OpenId {
/// </summary>
/// <returns>The new instance.</returns>
protected OpenIdRelyingParty CreateRelyingParty() {
- var rp = new OpenIdRelyingParty(new StandardRelyingPartyApplicationStore());
+ return this.CreateRelyingParty(false);
+ }
+
+ /// <summary>
+ /// Creates a standard <see cref="OpenIdRelyingParty"/> instance for general testing.
+ /// </summary>
+ /// <param name="stateless">if set to <c>true</c> a stateless RP is created.</param>
+ /// <returns>The new instance.</returns>
+ protected OpenIdRelyingParty CreateRelyingParty(bool stateless) {
+ var rp = new OpenIdRelyingParty(stateless ? null : new StandardRelyingPartyApplicationStore());
rp.Channel.WebRequestHandler = this.MockResponder.MockWebRequestHandler;
return rp;
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs
index e2a173b..accbd97 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs
@@ -34,7 +34,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider {
Assert.IsNotNull(userSetupUrl);
// Now construct a new request as if it had just come in.
- HttpRequestInfo httpRequest = new HttpRequestInfo { Url = userSetupUrl };
+ HttpRequestInfo httpRequest = new HttpRequestInfo { UrlBeforeRewriting = userSetupUrl };
var setupRequest = AuthenticationRequest_Accessor.AttachShadow(provider.GetRequest(httpRequest));
CheckIdRequest_Accessor setupRequestMessage = setupRequest.RequestMessage;
diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs
index 76a46d0..28b2b55 100644
--- a/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/Provider/OpenIdProviderTests.cs
@@ -90,7 +90,7 @@ namespace DotNetOpenAuth.Test.OpenId.Provider {
[TestMethod]
public void GetRequest() {
HttpRequestInfo httpInfo = new HttpRequestInfo();
- httpInfo.Url = new Uri("http://someUri");
+ httpInfo.UrlBeforeRewriting = new Uri("http://someUri");
Assert.IsNull(this.provider.GetRequest(httpInfo), "An irrelevant request should return null.");
var providerDescription = new ProviderEndpointDescription(OpenIdTestBase.OPUri, Protocol.Default.Version);
diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs
new file mode 100644
index 0000000..82384f8
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/Provider/PerformanceTests.cs
@@ -0,0 +1,149 @@
+//-----------------------------------------------------------------------
+// <copyright file="PerformanceTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId.Provider {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.ChannelElements;
+ using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.OpenId.Provider;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class PerformanceTests : OpenIdTestBase {
+ private const string SharedAssociationHandle = "handle";
+ private static readonly TimeSpan TestRunTime = TimeSpan.FromSeconds(3);
+ private OpenIdProvider provider;
+
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+
+ this.provider = CreateProvider();
+ }
+
+ [TestMethod]
+ public void AssociateDH() {
+ var associateRequest = this.CreateAssociateRequest(OPUri);
+ Stopwatch timer = new Stopwatch();
+ timer.Start();
+ int iterations;
+ for (iterations = 0; timer.ElapsedMilliseconds < TestRunTime.TotalMilliseconds; iterations++) {
+ IRequest request = this.provider.GetRequest(associateRequest);
+ var response = this.provider.PrepareResponse(request);
+ Assert.IsInstanceOfType(response.OriginalMessage, typeof(AssociateSuccessfulResponse));
+ }
+ timer.Stop();
+ double executionsPerSecond = GetExecutionsPerSecond(iterations, timer);
+ TestContext.WriteLine("Created {0} associations in {1}, or {2} per second.", iterations, timer.Elapsed, executionsPerSecond);
+ Assert.IsTrue(executionsPerSecond >= 2, "Too slow");
+ }
+
+ [TestMethod]
+ public void AssociateClearText() {
+ var associateRequest = this.CreateAssociateRequest(OPUriSsl); // SSL will cause a plaintext association
+ Stopwatch timer = new Stopwatch();
+ timer.Start();
+ int iterations;
+ for (iterations = 0; timer.ElapsedMilliseconds < TestRunTime.TotalMilliseconds; iterations++) {
+ IRequest request = this.provider.GetRequest(associateRequest);
+ var response = this.provider.PrepareResponse(request);
+ Assert.IsInstanceOfType(response.OriginalMessage, typeof(AssociateSuccessfulResponse));
+ }
+ timer.Stop();
+ double executionsPerSecond = GetExecutionsPerSecond(iterations, timer);
+ TestContext.WriteLine("Created {0} associations in {1}, or {2} per second.", iterations, timer.Elapsed, executionsPerSecond);
+ Assert.IsTrue(executionsPerSecond > 1000, "Too slow.");
+ }
+
+ [TestMethod]
+ public void CheckIdSharedHmacSha1Association() {
+ Protocol protocol = Protocol.Default;
+ string assocType = protocol.Args.SignatureAlgorithm.HMAC_SHA1;
+ double executionsPerSecond = this.ParameterizedCheckIdTest(protocol, assocType);
+ Assert.IsTrue(executionsPerSecond > 500, "Too slow");
+ }
+
+ [TestMethod]
+ public void CheckIdSharedHmacSha256Association() {
+ Protocol protocol = Protocol.Default;
+ string assocType = protocol.Args.SignatureAlgorithm.HMAC_SHA256;
+ double executionsPerSecond = this.ParameterizedCheckIdTest(protocol, assocType);
+ Assert.IsTrue(executionsPerSecond > 400, "Too slow");
+ }
+
+ private static double GetExecutionsPerSecond(int iterations, Stopwatch timer) {
+ return (double)iterations / (timer.ElapsedMilliseconds / 1000);
+ }
+
+ private double ParameterizedCheckIdTest(Protocol protocol, string assocType) {
+ Association assoc = HmacShaAssociation.Create(
+ protocol,
+ assocType,
+ AssociationRelyingPartyType.Smart,
+ this.provider.SecuritySettings);
+ this.provider.AssociationStore.StoreAssociation(AssociationRelyingPartyType.Smart, assoc);
+ var checkidRequest = this.CreateCheckIdRequest(true);
+ Stopwatch timer = new Stopwatch();
+ timer.Start();
+ int iterations;
+ for (iterations = 0; timer.ElapsedMilliseconds < TestRunTime.TotalMilliseconds; iterations++) {
+ var request = (IAuthenticationRequest)this.provider.GetRequest(checkidRequest);
+ request.IsAuthenticated = true;
+ var response = this.provider.PrepareResponse(request);
+ Assert.IsInstanceOfType(response.OriginalMessage, typeof(PositiveAssertionResponse));
+ }
+ timer.Stop();
+ double executionsPerSecond = GetExecutionsPerSecond(iterations, timer);
+ TestContext.WriteLine("Responded to {0} checkid messages in {1}; or {2} authentications per second.", iterations, timer.Elapsed, executionsPerSecond);
+ return executionsPerSecond;
+ }
+
+ private HttpRequestInfo CreateAssociateRequest(Uri opEndpoint) {
+ var rp = CreateRelyingParty(true);
+ AssociateRequest associateMessage = AssociateRequest.Create(rp.SecuritySettings, new ProviderEndpointDescription(opEndpoint, Protocol.Default.Version));
+ Channel rpChannel = rp.Channel;
+ MemoryStream ms = new MemoryStream();
+ StreamWriter mswriter = new StreamWriter(ms);
+ mswriter.Write(MessagingUtilities.CreateQueryString(rpChannel.MessageDescriptions.GetAccessor(associateMessage)));
+ mswriter.Flush();
+ ms.Position = 0;
+ var headers = new WebHeaderCollection();
+ headers.Add(HttpRequestHeader.ContentType, Channel.HttpFormUrlEncoded);
+ var httpRequest = new HttpRequestInfo("POST", opEndpoint, opEndpoint.PathAndQuery, headers, ms);
+ return httpRequest;
+ }
+
+ private HttpRequestInfo CreateCheckIdRequest(bool sharedAssociation) {
+ var rp = CreateRelyingParty(true);
+ CheckIdRequest checkidMessage = new CheckIdRequest(
+ Protocol.Default.Version,
+ OPUri,
+ DotNetOpenAuth.OpenId.RelyingParty.AuthenticationRequestMode.Setup);
+ if (sharedAssociation) {
+ checkidMessage.AssociationHandle = SharedAssociationHandle;
+ }
+ checkidMessage.ClaimedIdentifier = OPLocalIdentifiers[0];
+ checkidMessage.LocalIdentifier = OPLocalIdentifiers[0];
+ checkidMessage.Realm = RPRealmUri;
+ checkidMessage.ReturnTo = RPUri;
+ Channel rpChannel = rp.Channel;
+ UriBuilder receiver = new UriBuilder(OPUri);
+ receiver.Query = MessagingUtilities.CreateQueryString(rpChannel.MessageDescriptions.GetAccessor(checkidMessage));
+ var headers = new WebHeaderCollection();
+ var httpRequest = new HttpRequestInfo("GET", receiver.Uri, receiver.Uri.PathAndQuery, headers, null);
+ return httpRequest;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs b/src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs
new file mode 100644
index 0000000..005b8a0
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/OpenId/ProviderEndpointDescriptionTests.cs
@@ -0,0 +1,62 @@
+//-----------------------------------------------------------------------
+// <copyright file="ProviderEndpointDescriptionTests.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.OpenId {
+ using System;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.Messages;
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ [TestClass]
+ public class ProviderEndpointDescriptionTests : OpenIdTestBase {
+ private ProviderEndpointDescription se;
+
+ private string[] v20TypeUris = { Protocol.V20.ClaimedIdentifierServiceTypeURI };
+
+ [TestInitialize]
+ public override void SetUp() {
+ base.SetUp();
+
+ this.se = new ProviderEndpointDescription(OPUri, Protocol.V20.Version);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void IsExtensionSupportedNullType() {
+ this.se.IsExtensionSupported((Type)null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void IsExtensionSupportedNullString() {
+ this.se.IsExtensionSupported((string)null);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentException))]
+ public void IsExtensionSupportedEmptyString() {
+ this.se.IsExtensionSupported(string.Empty);
+ }
+
+ [TestMethod, ExpectedException(typeof(ArgumentNullException))]
+ public void IsExtensionSupportedNullExtension() {
+ this.se.IsExtensionSupported((IOpenIdMessageExtension)null);
+ }
+
+ [TestMethod]
+ public void IsExtensionSupported() {
+ this.se = new ProviderEndpointDescription(OPUri, this.v20TypeUris);
+ Assert.IsFalse(this.se.IsExtensionSupported<ClaimsRequest>());
+ Assert.IsFalse(this.se.IsExtensionSupported(new ClaimsRequest()));
+ Assert.IsFalse(this.se.IsExtensionSupported("http://someextension/typeuri"));
+
+ this.se = new ProviderEndpointDescription(
+ OPUri,
+ new[] { Protocol.V20.ClaimedIdentifierServiceTypeURI, "http://someextension", Constants.sreg_ns });
+ Assert.IsTrue(this.se.IsExtensionSupported<ClaimsRequest>());
+ Assert.IsTrue(this.se.IsExtensionSupported(new ClaimsRequest()));
+ Assert.IsTrue(this.se.IsExtensionSupported("http://someextension"));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAnonymousResponseTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAnonymousResponseTests.cs
index 6452420..1418513 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAnonymousResponseTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/PositiveAnonymousResponseTests.cs
@@ -36,7 +36,21 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
Assert.IsNull(response.ClaimedIdentifier);
Assert.IsNull(response.FriendlyIdentifierForDisplay);
Assert.IsNull(response.Exception);
+ Assert.IsNull(response.Provider);
Assert.AreSame(ext, response.GetUntrustedExtension<ClaimsResponse>());
}
+
+ /// <summary>
+ /// Verifies the Provider property.
+ /// </summary>
+ [TestMethod]
+ public void ProviderTest() {
+ var responseMessage = new IndirectSignedResponse(Protocol.V20.Version, this.returnTo);
+ responseMessage.ProviderEndpoint = OPUri;
+ var response = new PositiveAnonymousResponse(responseMessage);
+ Assert.IsNotNull(response.Provider);
+ Assert.AreEqual(OPUri, response.Provider.Uri);
+ Assert.AreEqual(responseMessage.Version, response.Provider.Version);
+ }
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/RelyingPartySecuritySettingsTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/RelyingPartySecuritySettingsTests.cs
index 3f1cea0..851939e 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/RelyingPartySecuritySettingsTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/RelyingPartySecuritySettingsTests.cs
@@ -40,5 +40,29 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
this.settings.RequireSsl = false;
Assert.IsFalse(this.settings.RequireSsl);
}
+
+ /// <summary>
+ /// Verifies that the <see cref="RelyingPartySecuritySettings.RequireDirectedIdentity"/>
+ /// property getter/setter are implemented correctly.
+ /// </summary>
+ [TestMethod]
+ public void RequireDirectedIdentity() {
+ this.settings.RequireDirectedIdentity = true;
+ Assert.IsTrue(this.settings.RequireDirectedIdentity);
+ this.settings.RequireDirectedIdentity = false;
+ Assert.IsFalse(this.settings.RequireDirectedIdentity);
+ }
+
+ /// <summary>
+ /// Verifies that the <see cref="RelyingPartySecuritySettings.RequireAssociation"/>
+ /// property getter/setter are implemented correctly.
+ /// </summary>
+ [TestMethod]
+ public void RequireAssociation() {
+ this.settings.RequireAssociation = true;
+ Assert.IsTrue(this.settings.RequireAssociation);
+ this.settings.RequireAssociation = false;
+ Assert.IsFalse(this.settings.RequireAssociation);
+ }
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs
index 7b71eef..bd09bc9 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RelyingParty/ServiceEndpointTests.cs
@@ -173,46 +173,6 @@ namespace DotNetOpenAuth.Test.OpenId.RelyingParty {
Assert.AreEqual("=!9B72.7DD1.50A9.5CCD", se.FriendlyIdentifierForDisplay);
}
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void IsExtensionSupportedNullType() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- se.IsExtensionSupported((Type)null);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void IsExtensionSupportedNullString() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- se.IsExtensionSupported((string)null);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentException))]
- public void IsExtensionSupportedEmptyString() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- se.IsExtensionSupported(string.Empty);
- }
-
- [TestMethod, ExpectedException(typeof(ArgumentNullException))]
- public void IsExtensionSupportedNullExtension() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- se.IsExtensionSupported((IOpenIdMessageExtension)null);
- }
-
- [TestMethod]
- public void IsExtensionSupported() {
- ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
- Assert.IsFalse(se.IsExtensionSupported<ClaimsRequest>());
- Assert.IsFalse(se.IsExtensionSupported(new ClaimsRequest()));
- Assert.IsFalse(se.IsExtensionSupported("http://someextension/typeuri"));
-
- ProviderEndpointDescription ped = new ProviderEndpointDescription(
- OPUri,
- new[] { Protocol.V20.ClaimedIdentifierServiceTypeURI, "http://someextension", Constants.sreg_ns });
- se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, ped, this.servicePriority, this.uriPriority);
- Assert.IsTrue(se.IsExtensionSupported<ClaimsRequest>());
- Assert.IsTrue(se.IsExtensionSupported(new ClaimsRequest()));
- Assert.IsTrue(se.IsExtensionSupported("http://someextension"));
- }
-
[TestMethod]
public void IsTypeUriPresent() {
ServiceEndpoint se = ServiceEndpoint.CreateForClaimedIdentifier(this.claimedXri, this.userSuppliedXri, this.localId, new ProviderEndpointDescription(this.providerEndpoint, this.v20TypeUris), this.servicePriority, this.uriPriority);
diff --git a/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs
index 92379da..5a5182f 100644
--- a/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/UriIdentifierTests.cs
@@ -391,7 +391,7 @@ namespace DotNetOpenAuth.Test.OpenId {
// the ServiceEndpoint.Equals method.
Assert.AreEqual(expectSreg ? 2 : 1, se.ProviderSupportedServiceTypeUris.Count);
Assert.IsTrue(se.ProviderSupportedServiceTypeUris.Contains(protocol.ClaimedIdentifierServiceTypeURI));
- Assert.AreEqual(expectSreg, se.IsExtensionSupported(new ClaimsRequest()));
+ Assert.AreEqual(expectSreg, se.IsExtensionSupported<ClaimsRequest>());
}
private void DiscoverXrds(string page, ProtocolVersion version, Identifier expectedLocalId, string providerEndpoint) {
diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln
index 30b308c..9e3c0af 100644
--- a/src/DotNetOpenAuth.sln
+++ b/src/DotNetOpenAuth.sln
@@ -17,6 +17,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{CD57219F-24F4-4136-8741-6063D0D7A031}"
ProjectSection(SolutionItems) = preProject
..\doc\specs\OAuth Core 1.0.htm = ..\doc\specs\OAuth Core 1.0.htm
+ ..\doc\specs\OAuth Core 1.0a (Draft 3).htm = ..\doc\specs\OAuth Core 1.0a (Draft 3).htm
..\doc\specs\OpenID OAuth Extension.htm = ..\doc\specs\OpenID OAuth Extension.htm
..\doc\specs\openid-attribute-exchange-1_0.html = ..\doc\specs\openid-attribute-exchange-1_0.html
..\doc\specs\openid-authentication-1_1.html = ..\doc\specs\openid-authentication-1_1.html
diff --git a/src/DotNetOpenAuth.vsmdi b/src/DotNetOpenAuth.vsmdi
index d19f6ea..38e2e9a 100644
--- a/src/DotNetOpenAuth.vsmdi
+++ b/src/DotNetOpenAuth.vsmdi
@@ -11,35 +11,42 @@
<Description>Fast running unit tests</Description>
<TestLinks>
<TestLink id="89de77d8-729a-7efe-9667-71b1f5d78859" name="CtorBadXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4ba7ca33-72f1-3fc6-d37c-65134eda904d" name="AddDeclaredValueThatAlreadyExists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="06b350b0-79d1-9393-7620-cd919061898c" name="ParseEndUserSuppliedUriIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="300ae1f7-fc61-1d41-b262-f8c830b6e115" name="RemoveTest1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9104f36f-6652-dcbb-a8ae-0d6fc34d76ed" name="AddCallbackArgumentClearsPreviousArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="12304550-5647-5e61-64b4-a5e051f20a03" name="IsExtensionSupported" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="734dd45c-6320-26a9-e412-62ecacfd285a" name="CtorNullAttribute" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="70c08ce3-cbd0-d553-61c0-a6d2ca203dc4" name="IsExtensionSupportedNullExtension" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="1d5fb5a9-e15c-d99c-7a7e-95a4c4d123c2" name="DirectRequestsUsePost" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="6b218bf7-a4e9-8dac-d2c2-9bc3ee3ffc3e" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b32b6295-d4a9-3369-f072-28a71e84d4e8" name="SerializationWithUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d6088ffe-ccf5-9738-131b-0fc1bc7e3707" name="TrimFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="af7cb01c-950e-23d7-0f32-082b7af8b382" name="CtorNullToObject" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="1c531011-403a-0821-d630-d5433d968f31" name="CtorFromRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="73c6c979-205d-2216-d98d-2dd136b352c6" name="UtcCreationDateConvertsToUniversal" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a8bd3730-1660-dca9-87ec-23bc9dc39ab9" name="CtorGoodXriSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e7aacb49-62ef-637d-ada2-0a12d836414d" name="ExtensionFactory" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="32e95494-d0bb-cfc7-a8d6-652f8816c6b4" name="ReadFromResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="49a266cf-4ab6-3fdc-f4fd-21533f42c7cb" name="CtorWithProtocolMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d12e8df0-1195-ab75-2275-7c8f854ddf98" name="UserSetupUrl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="21cf1f9a-063f-395a-f8aa-92c190c69146" name="SignaturesMatchKnownGood" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d570770a-74e4-50ec-8eb9-91bd81c093ad" name="ParseNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a9f7897c-b299-807b-0612-384732cd10c9" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f4bec8d2-0531-34ab-8d50-bca260b58c61" name="ReadFromRequestWithContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a314e3b9-36a5-bfbb-3e15-e5003f22cf87" name="Serialize" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9bdc56c0-33ce-b46c-4031-bd3252b499a6" name="PrivateAssociationPositive" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f334cc44-b2d0-2d67-358a-532def3bee80" name="ContainsKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="1bfbe1e1-3827-824f-27ad-4c990b0e22ab" name="Defaults" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a14ddf08-796b-6cf1-a9bf-856dd50520fa" name="RequiredProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="ca9f3da7-e19f-b58b-54fe-54fa56ab9556" name="AddByKeyAndValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d766edce-59de-a03d-830a-0f0477521cff" name="ApplyHeadersToResponseNullAspNetResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="84e718d7-bb82-e7d1-31be-471e2c154053" name="Item" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="889ba616-43dc-8a7f-ee13-46288969d617" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="fdf5b3df-239b-26fd-c1a2-152057195b7e" name="ReadFromRequestForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="555edc3b-5abf-7e46-b4f6-ddf44800b5df" name="SpreadSregToAXBasic" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="069995aa-4136-610b-3f41-df80a138c244" name="AppendQueryArgsNullUriBuilder" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="149a95cf-a538-f853-e11b-3133c15579c5" name="RequestTokenUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f3cbbcda-49ff-fc43-140b-f362081654c3" name="CtorNullTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="fa52f2db-fc1e-ba31-cc5e-0bcc05998187" name="NoValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="65752c29-fa1f-7b88-bbec-5329af8db4d8" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cd219db4-4f6e-8ff4-f957-c8428d38c118" name="HttpSignatureGeneration" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="cd1142a5-f77a-5626-a739-65eb0228bf7d" name="ProtocolDetection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="68532d6d-a0cf-5883-17e2-6060707ba9ae" name="DecodeOobToNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="188ce83b-3117-adb5-4b89-12f2b09be1de" name="CtorSimpleConsumer" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="93c157e8-1293-3aff-f616-66502872b37d" name="DiscoveryRequiresSslIgnoresInsecureEndpointsInXrds" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f6ecb459-cc64-36ee-438c-4514e9413586" name="AddAttributeByPrimitives" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
@@ -47,34 +54,32 @@
<TestLink id="c87bee54-0edd-1051-b5f8-2233692249ba" name="DiscoverCommunityInameCanonicalIDs" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5fa10f12-3de5-1783-0a97-9802d5469dfa" name="AddAttributeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5ab06bb5-d047-8c3a-6b91-5951e0a55cc5" name="ToStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="454165a2-c26e-5740-09a9-d234db052ba3" name="InvalidRealmNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="11108b79-f360-9f7c-aebc-2d11bebff96a" name="ReadFromRequestForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9026e58c-8582-0852-3c3c-9eadfd544cbc" name="VerifyNonZeroLengthOnEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="40e1121e-8ff3-df73-203b-04baab671a0c" name="ImplicitConversionToStringTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="779b1f99-fe67-185c-f165-66787bf6e39a" name="BasicEncodingTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="724cc3e8-c13c-5cc6-ce14-25c51ad6297d" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="456c3468-9081-4879-7e7e-8299bd8c7f68" name="IsReadOnly" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d6951a97-9d0b-31c1-7a29-01fbb986c5a9" name="SpoofedClaimedIdDetectionSolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c6b3ebb0-58f7-249a-5944-0d31a600f679" name="IsExtensionSupportedNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="315b5857-7697-8222-f94c-f6f10d539491" name="BaseSignatureStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="1cde79f8-99b7-7090-f898-ba96a607875f" name="IsReturnUrlDiscoverableValidButNoMatch" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="910f8448-5454-8ae5-cba3-690c7f375576" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="83aba528-c8ea-f464-177e-2ea8ae2cfd0b" name="Birthdates" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f063a3c6-5a36-2801-53d7-5142416199a9" name="ImplicitConversionFromStringTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="b63c4b89-3889-6dcf-8890-c92fc44c0b10" name="VerifyBadTimestampIsRejected" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="9126d9e0-14dc-490b-3cd3-d3e424d38f9e" name="BinarySerialization" storage="..\bin\debug\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4c399759-263f-5eba-8855-de14f197e647" name="QueryStringContainPrefixedParametersNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7ca16e07-126d-58ac-2ac5-a09a8bf77592" name="InvalidRealmBadWildcard1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="1757957f-17bb-ef9f-39f8-c008863ec033" name="AssuranceLevels" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="c11e5541-0a92-85ab-4f90-0db7766ebdcb" name="CtorUnsolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="8aecb3a5-2cb5-143d-aa99-9514fa8dfacb" name="AddAttributeByValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ef6cebca-f8da-edf6-0217-8bb854710090" name="DiscoveryCommunityInameDelegateWithoutCanonicalID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="b09311d4-4dea-6786-3e59-9c62fe16e301" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ad56539c-6156-5f62-a98a-b24ae0159cc6" name="XmlSerialization" storage="..\bin\debug\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b384002f-26a9-7dde-c3f6-9ceff34dd8e2" name="GetRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="54eae9ed-bed1-eeda-b6ea-045c8f7e2ba5" name="SendIndirectMessage301GetNullFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="2d0ee03a-f082-768c-a0db-574ac8efeffb" name="Valid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="6c20a52a-bab7-e84e-faca-fd79ec5303d9" name="CtorCountZero" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="7fb8d29c-c8ea-7f88-ed42-ae7368d6a429" name="CtorNullStore" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ab653060-5bec-3dc6-78ee-a5ef7d393b1d" name="AddPolicyMultipleTimes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="50986611-9de6-a112-2fe8-691210989f45" name="IsTypeUriPresent" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="14ce54ee-5507-ac70-5514-99b7b83ba3d6" name="ExtensionFactories" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b4b00582-dcc9-7672-0c02-52432b074a92" name="GetNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8fd673c8-977a-7b66-72cb-38c7054796c7" name="DiscoverRequireSslWithSecureRedirects" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3e2f1dad-3684-587c-9039-8d116582be10" name="GetReturnToArgumentEmptyKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="163c8ba8-f829-c21e-a5a1-3c4565ec4425" name="UnifyExtensionsAsSregNoExtensions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="054484ce-12c5-83ad-49a4-b241cd81557d" name="ClaimedIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e8337858-a320-8aad-51aa-402e65a90b75" name="ReplayDetectionTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="507cd1b6-1010-0bca-cf7f-f96e3f4f6c6c" name="QueryBeforeSettingUrl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
@@ -84,9 +89,8 @@
<TestLink id="85a71d28-5f2f-75ce-9008-94982438bb5f" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f17424d2-ed4b-1ea0-a339-733f5092d9d0" name="MaximumAuthenticationAgeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="7debb527-142a-6ca6-3b9b-1e131c18e801" name="AccessTokenUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="98e7a0f9-ab6c-7ff1-3a2c-00d8244e1bec" name="CommonMethods" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="44afc59c-60fc-3179-b5a6-1e58e7752d54" name="ApplyHeadersToResponseNullHeaders" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="af7cb01c-950e-23d7-0f32-082b7af8b382" name="CtorNullToObject" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="066ce22f-103c-56ee-0250-d9e28d43ffcd" name="Values" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ccfda025-cb1a-a2ff-78bd-5e9af885ae0b" name="ToDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a260d196-066f-b0ae-a40e-fb9d962b28a4" name="XrdsDirectDiscovery_20" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="13acd546-c82e-324c-220d-34f42a6d705e" name="DeserializeSimple" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="b56cdf04-0d29-8b13-468c-fb4b4258c619" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
@@ -96,38 +100,39 @@
<TestLink id="3fc3ac8d-7772-b620-0927-f4bd3a24ce2f" name="SendNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5f3758b3-1410-c742-e623-b964c01b0633" name="AuthenticationTimeUtcConvertsToUtc" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="9d4a230d-9e74-dc1b-ecdc-bf875b56e1b3" name="CtorNullVersion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7b1fb2c4-39c0-0d39-700c-96d992f5a01f" name="AuthenticationTimeUtcSetUnspecified" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f8d8d555-7a04-ab6e-918a-3dae32f4b52b" name="IsExtensionSupportedNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="00089858-d849-1e5f-4fb5-31d8d0590233" name="VerifyArgumentNotNullThrows" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3e676e31-3b6d-9d12-febd-d632ece804ec" name="RPRejectsMismatchingAssociationAndSessionBitLengths" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="54a65e0b-1857-72b9-797b-fe3d9a082131" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="352d9fd6-cf38-4b72-478f-e3e17ace55f5" name="NoValueLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d067c55c-3715-ed87-14a2-c07349813c94" name="IsDirectedIdentity" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3aa4e498-fd14-8274-22da-895436c1659e" name="AssociateUnencrypted" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8fc08a6d-6dcf-6256-42ff-073d4e4b6859" name="RequireDirectedIdentity" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2a7b77c3-27d5-7788-e664-5d20118d223b" name="OPRejectsHttpNoEncryptionAssociateRequests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="1f46ce86-bc66-3f5c-4061-3f851cf6dd7f" name="HtmlDiscover_20" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="2f9d176e-4137-63bd-ee2a-6b79fde70d0d" name="Clear" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="fc7af2d7-6262-d761-335b-ef3ec029484d" name="DeserializeVerifyElementOrdering" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="df159af7-abf5-089c-b592-e6f535dab1c1" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d647fd93-40b3-24d5-25fc-661c0d58335c" name="SendIndirectMessageFormPostNullMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="577b8522-8516-4f62-22db-76227bf82f4c" name="UserSetupUrlNotRequiredInV1SetupOrV2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="ac4ff1af-8333-e54e-0322-27d8824d7573" name="RequestUsingAuthorizationHeader" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e78ab82c-3b49-468a-b2ad-ca038e98ff07" name="GetEnumerator" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="8e86c2fd-24b9-44c5-7cda-d66aa7cd4418" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e344ba35-96b7-d441-c174-8c8b295fd157" name="AddCallbackArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5435ab79-de25-e2fc-0b2d-b05d5686d27d" name="IsUrlWithinRealmTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e2287de6-cbd2-4298-3fb8-297013749e70" name="SendIndirectMessageFormPostNullFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="db8d66cc-8206-57cc-0ce5-c8117813d77c" name="UnifyExtensionsasSregFromSchemaOpenIdNet" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="ef8a2274-4e58-0dde-4c5c-7f286865fc3a" name="SendReplayProtectedMessageSetsNonce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0f80456a-5465-dd68-bfb0-ba27b676187c" name="EqualsTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="0d99e0a9-295e-08a6-bc31-2abb79c00ff8" name="IsReturnUrlDiscoverableRequireSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="38239ff0-1dfd-1116-55df-2790243dc768" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="04be6602-31a2-f4ae-8fdb-b9ad2ac370be" name="PrepareMessageForReceiving" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="2c2b48d0-8009-e7e0-9ff4-34f9973f59da" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5dd2e6c9-ff0f-48de-3a1a-cbab61370843" name="SetCountNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="182203f3-5a16-b736-ea8c-b59f6bf7df66" name="InvalidRealmTwoWildcards2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4fd5baa2-8f39-8bf6-db8f-aa92592bfc06" name="CtorRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8346368c-9c8a-de76-18dd-5faeeac3917d" name="OPRejectsMismatchingAssociationAndSessionTypes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="385c302d-b546-c164-2a59-2e35f75d7d60" name="RemoveStructDeclaredProperty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="870cce9d-5b17-953c-028f-827ec8b56da2" name="GetInvalidMessageType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="97f0277a-86e6-5b5a-8419-c5253cabf2e0" name="UserAuthorizationUriTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c9d67d40-1903-8319-0f7c-d70db4846380" name="SendWithoutAspNetContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="83271647-7da8-70b1-48a3-f1253a636088" name="IsExtensionSupportedEmptyString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d66a3b7a-1738-f6b3-aed1-e9bc80734ae9" name="CtorNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f3f84a10-317f-817a-1988-fddc10b75c20" name="AddTwoAttributes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="58f848e5-42d7-1508-f9b5-7691337e6da9" name="IsExtensionSupportedEmptyString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="00ed61cd-46cd-9c0e-f044-38d784c8bcfb" name="DecodeEmptyStringFails" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="2d82ac4b-99b4-a132-eb62-d943e02d1498" name="ApplyHeadersToResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="643c9887-3f12-300e-fdac-17ae59652712" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e03f0038-5bb7-92f2-87a7-00a7d2c31a77" name="MessageExpirationWithoutTamperResistance" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
@@ -137,11 +142,12 @@
<TestLink id="f4b313bb-cebc-a854-ffbd-6c955d850a05" name="VerifyGoodTimestampIsAccepted" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="77047207-0571-72d5-71bd-586b878bcc0c" name="Base64Member" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f919a731-cc5c-88c6-5582-639b272d64fc" name="IsReturnUrlDiscoverableValidResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3c438474-63f3-b56c-dcba-1ed923fcdbdd" name="CreateResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="4a00f3ab-f405-95a7-d745-2fcf7787eb56" name="GetNonexistentHandle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f4537b23-bb5e-5c6f-da53-64b34472f0dc" name="ChannelGetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="75fa4664-bb0e-3a54-de29-c18ac712b231" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="779b1f99-fe67-185c-f165-66787bf6e39a" name="BasicEncodingTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="94ba9fd3-851d-13b2-e273-6294b167c13e" name="HttpsSignatureVerification" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ff78d828-b437-aaeb-e48a-85a5ad1fe396" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5460f9c6-ec9d-969d-5aff-b946d6776e25" name="CtorWithNullProtocolMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d5f4e610-eabe-1dc0-ab3f-7c9dcb17fcc3" name="CtorImpliedLocalIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="64d8c630-76c6-e420-937b-19c889dd7f59" name="CtorNonMessageType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
@@ -149,77 +155,83 @@
<TestLink id="be41e9c1-ecde-cc80-37d0-4126225e4cda" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="48115dc0-1323-bab0-c540-695a2160e0a3" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a79e43c9-ad5a-5543-51ff-22271ec87ab0" name="PrepareMessageForSendingNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7ca16e07-126d-58ac-2ac5-a09a8bf77592" name="InvalidRealmBadWildcard1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="51a08d94-c327-4d28-1f0c-f7920ea54870" name="ValidMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="832dbf28-5bf2-bd95-9029-bf798349d917" name="GetCallbackArguments" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5f02e24c-2972-c598-ca71-ea362b2fe7d8" name="SecuritySettingsSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="11108b79-f360-9f7c-aebc-2d11bebff96a" name="ReadFromRequestForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="53cbbf4a-89d3-122b-0d88-662f3022ce26" name="OpenIdMaxAuthenticationTime" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="20646985-c84a-db8e-f982-ec55d61eaacd" name="ResponseNonceSetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="c2c78c43-7f50-ffc3-affb-e60de2b76c94" name="CreateQueryStringNullDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="dc6af36d-0efc-9291-a603-2af7bac2a269" name="ErrorMessagesAsHttp400" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ef20222d-b2e2-d593-17fa-512041020643" name="InvalidRealmNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cc584a29-684c-75e8-3d77-96201d9ba537" name="ProviderTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3c67903e-15ce-9ed4-34c8-f77059af79ca" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="b2e1bba0-ab24-cdd5-906c-a3655814ab2d" name="SendSetsTimestamp" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="2f1a3fc4-77ec-2ae3-668c-9e18f9ab0ebe" name="SendIndirectMessage301Get" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e4403d9e-73c1-967d-345c-4a2c83880d4e" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a145f430-8062-5ad7-0cf5-b51eba0f8de7" name="HttpsSignatureGeneration" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="066ce22f-103c-56ee-0250-d9e28d43ffcd" name="Values" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="6ce37652-2f47-6952-fb6d-568c2ca85224" name="TransformAXFormatTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e9cceef5-383d-92f0-a8bb-f3e207582836" name="RealmReturnToMismatchV2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="bb542259-4c10-4b88-1b3c-f842b0bb49a9" name="ImmediateVsSetupModes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="71564ca7-7845-92b3-7433-2f2beeb6b9f7" name="VerifyNonZeroLengthOnNonEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3aa4e498-fd14-8274-22da-895436c1659e" name="AssociateUnencrypted" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="7ea157db-cf32-529f-f1d3-b3351f17725a" name="CtorSimpleServiceProvider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="c905ca57-e427-3833-c2dd-17ca9f6962cd" name="SendIndirectMessageFormPost" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="06ec5bce-5a78-89c3-0cda-fa8bddfea27d" name="SetCountZero" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="50594141-1a00-b4ab-d794-5b06e67327e5" name="IsTypeUriPresentNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="98e7a0f9-ab6c-7ff1-3a2c-00d8244e1bec" name="CommonMethods" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="c7f6459d-9e6e-b4bc-cae8-65f5a3785403" name="SendIndirectMessageNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="aa79cdf5-e0bc-194e-fdbb-78369c19c30f" name="ConstantFieldMemberInvalidValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="6daa360b-71e4-a972-143f-01b801fada84" name="DeserializeWithExtraFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="dd9e3279-2d7e-e88e-ccfa-ef213055fc3d" name="SendDirectedNoRecipientMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="b384002f-26a9-7dde-c3f6-9ceff34dd8e2" name="GetRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a145f430-8062-5ad7-0cf5-b51eba0f8de7" name="HttpsSignatureGeneration" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="81f670d0-d314-c53c-9d91-c0765dfc30c1" name="MessagePartsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="efd570c9-5e74-17e4-f332-ac257c8e8aff" name="RealmReturnToMismatchV1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="fda58c48-e03a-73a3-4294-9a49e776ffb6" name="CtorWithTextMessageAndInnerException" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="660ad25a-b02b-1b17-7d6e-3af3303fa7bc" name="ModeEncoding" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="ae384709-e9a4-0142-20ba-6adb6b40b3e2" name="CtorStringHttpsSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="3b70dd09-384d-5b99-222b-dc8ce8e791f2" name="SecuritySettingsSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3bb818b4-5423-ad91-8cd9-8606ec85d2cb" name="ReadFromRequestAuthorizationScattered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f787ae5d-b8fc-0862-a527-9157d11bbed7" name="UntrustedWebRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="8df5d75f-bd4d-ce4e-2faf-6106b623de42" name="AddAttributeRequestStrangeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="51a08d94-c327-4d28-1f0c-f7920ea54870" name="ValidMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9aa6a81e-c198-c0fd-0252-003b856b7674" name="ConstantFieldMemberValidValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="bc3e979b-09ea-c45d-5714-2d1fb00244cf" name="IncomingMessageMissingRequiredParameters" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="051a85ed-eef9-9437-507d-d6208b6a8f74" name="DiscoveryWithRedirects" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="be6a14aa-c0d9-cf61-286a-236b92239597" name="EnumerableCache" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5a4df395-962e-0b7c-de71-abcb7e8930db" name="CreateFiltersDelegatingIdentifiers" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="bf73c7f2-33b1-8e18-c4f6-cb8609388754" name="DiscoveryRequireSslWithInsecureXrdsInSecureHttpHeader" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="b71e8878-b20e-5d96-bce4-7f10831ceaf8" name="AddPolicies" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f17128c1-5953-5391-ed75-c33774eacbfc" name="LastLineNotTerminated" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e071a119-c7e9-1a55-b132-72e161fea598" name="CtorAndProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a6e464af-42df-1ba4-17e5-b955352664b5" name="RPOnlyRenegotiatesOnce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4fd5baa2-8f39-8bf6-db8f-aa92592bfc06" name="CtorRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="71564ca7-7845-92b3-7433-2f2beeb6b9f7" name="VerifyNonZeroLengthOnNonEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="0290975f-02ce-d8a7-d723-5dae623cab46" name="CtorNullTokenManager" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e1958fc5-a979-88b2-b593-3bc89ad6ad4e" name="GetEnumeratorUntyped" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="3b535521-90c8-7f49-545f-bcfc4ad16d40" name="UnresponsiveProvidersComeLast" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="77934ac4-bd65-7ad8-9c53-9c9447f9e175" name="GetReturnToArgumentAndNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="4bd86299-18d7-abbe-e5d2-1afad17279e9" name="Parse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="0443f5f8-aa08-80d5-dcc6-261802debe5a" name="XrdsDirectDiscovery_10" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="6c95f443-463e-2856-f500-b9029645e44c" name="RequestNullRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3744e1f1-6be1-f27f-78e9-5410d356ccf4" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="afafb5ef-662e-2da3-35b8-1d67bb0d79ce" name="AddPolicies" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d0a92f93-9bb4-1821-81cf-e9b50e3e7d62" name="SendDirectMessageResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="17267cde-a296-8293-5bd1-9ca629817e4b" name="OpenIdRelyingParty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="cab73921-470b-331f-e601-b44805b67c81" name="GetAttributeValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5a77a48f-00d6-da6f-5ef7-c897ebf8fe6b" name="EscapeUriDataStringRfc3986Tests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="14ce54ee-5507-ac70-5514-99b7b83ba3d6" name="ExtensionFactories" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="ebd84587-bbc2-9889-c500-b6fbdf2bf209" name="GetRequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="565140c9-c9fe-9466-1e39-740d7e368cb5" name="TryParse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="30a8eab6-6423-26af-da1a-ec304935fe43" name="RemoveNonexistentHandle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e9bc3f63-aeb1-d84d-8abc-fc6ed77955e6" name="SignedResponsesIncludeExtraDataInSignature" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="9e209599-5924-f624-48de-ed31588cb425" name="EncodeDecode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fe55cc74-98eb-c6c7-622f-77ad3e304c10" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3027bfe5-3612-7089-16cc-d6a2a556a41f" name="Transport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="95e1fc36-2500-2721-1919-35e9e8349a1c" name="AddPolicyMultipleTimes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="9af943f7-b289-1a24-8e3e-bfbd7a55b4c7" name="CtorGoodUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="ae1ef27c-fbfe-c57e-a1e0-c1ef9de4ea23" name="CommonProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0f80456a-5465-dd68-bfb0-ba27b676187c" name="EqualsTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="196be55e-a3e5-adf3-9f15-13ba6cce0701" name="ValidRealmsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="bb68f1dc-3854-fc11-2ea6-d45f892d76fa" name="NistAssuranceLevelSetVarious" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="88aaa032-b18a-b334-937b-66837c5f987c" name="AssociateRenegotiateBitLength" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="dd5be0e2-a1fc-3369-0b11-78b728eeaba5" name="CtorNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="0362a92c-a21c-f718-6b1e-3d154c14acd0" name="RequestUsingPost" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="264cd371-e830-c09b-5511-18f54d4c69d5" name="RespondSimpleValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="4a009f39-66b1-9cc5-ea8b-13b75ab22a5b" name="ContainsKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9bdc56c0-33ce-b46c-4031-bd3252b499a6" name="PrivateAssociationPositive" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="cf58b7b9-9718-f6cd-1839-fc53174598f2" name="IsExtensionSupportedNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="32604ca2-2577-9c33-f778-ff7e4c985ce5" name="RequestTokenUriWithOAuthParametersTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="fdf439d0-3b74-4d32-d395-d5a2559ed88b" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="3df1f62b-4fb4-d399-cf7f-40b72001d9d6" name="CtorUnsolicited" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8d0df47c-c381-0487-6c19-77548ad7fc13" name="UnifyExtensionsAsSregWithBothSregAndAX" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="90f06a50-7b81-05ec-3dc0-7b3e8ade8cfa" name="NormalizeCase" storage="..\bin\debug\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="8b11aa63-4c0f-41ff-f70c-882aacf939fe" name="CtorCountNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="fba4d9a6-d8c7-a063-7c07-4a27c38c94a9" name="InvalidRealmBadWildcard3" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
@@ -230,23 +242,25 @@
<TestLink id="7c048c58-c456-3406-995f-adb742cc2501" name="DeserializeInvalidMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="704a32d0-3f50-d462-f767-fd9cf1981b7f" name="ProviderVersion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f362baf3-da5b-1b8c-39ae-7c9b2051270a" name="AuthenticationTimeUtcSetUtc" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3bb818b4-5423-ad91-8cd9-8606ec85d2cb" name="ReadFromRequestAuthorizationScattered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="55b078e4-3933-d4e0-1151-a0a61321638e" name="ReadFromRequestAuthorization" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="cbfeb75b-d031-7df3-c281-3c9e1c450042" name="CtorFromRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="02333934-cfea-2fb6-5e08-7a24be050f44" name="CreateRequestsOnNonOpenID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9302541c-9c30-9ce7-66db-348ee4e9f6ee" name="UnifyExtensionsAsSregWithSreg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="997253fb-7591-c151-1705-02976b400f27" name="AddAttributeTwice" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="88ae5661-da27-91c5-4d78-1f43cd716127" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8346368c-9c8a-de76-18dd-5faeeac3917d" name="OPRejectsMismatchingAssociationAndSessionTypes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2fb2563b-b908-2fad-5efc-522a68c76780" name="ValidMessageNoNonceReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3c438474-63f3-b56c-dcba-1ed923fcdbdd" name="CreateResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ef6cebca-f8da-edf6-0217-8bb854710090" name="DiscoveryCommunityInameDelegateWithoutCanonicalID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="d12e8df0-1195-ab75-2275-7c8f854ddf98" name="UserSetupUrl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="47706bc6-7bee-0385-62b4-4f9cec6cc702" name="CtorWithTextMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="2495fc9b-d766-5ae7-7324-f044c4ce1242" name="AddNullValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="fb6c270f-ff72-73f4-b8b3-82851537427c" name="MultiVersionedMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="ed7efca3-c3c1-bc4a-cef7-eaf984749355" name="ValidMessageReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3fe0b432-dbb4-b334-e504-a83fe5ffdbaf" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c9d67d40-1903-8319-0f7c-d70db4846380" name="SendWithoutAspNetContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="47e8fae9-542d-1ebb-e17c-568cf9594539" name="RelativeUriDecodeFails" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a2b3835e-8edb-89aa-ba6c-f10b28a3af81" name="ReadFromRequestQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e97cee09-4163-d83f-f65f-14e424294172" name="ExtensionsAreIdentifiedAsSignedOrUnsigned" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f4fd129a-a7c3-dc1e-2b4a-5059a4207a8a" name="Send" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="cc0031b8-1fdb-cd87-97c1-c6f893c296e0" name="TooManyBindingElementsProvidingSameProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2e23dc5a-93ea-11a5-d00d-02d294794e5f" name="AssociateDiffieHellmanOverHttps" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="32532d1f-d817-258d-ca72-021772bfc185" name="UriEncodeDecode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="152e7a3a-21f9-eabf-0065-08597a0cc9a6" name="AuthorizationHeaderScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="93041654-1050-3878-6b90-656a7e2e3cfd" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e2ab77b2-a6dc-f165-1485-140b9b3d916f" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
@@ -255,10 +269,14 @@
<TestLink id="abb0610a-c06f-0767-ac99-f37a2b573d1b" name="ParameterPrefix" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="b2b54c72-1d26-8c28-ebf5-7a5a4beeec43" name="VerifyNonZeroLengthOnNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f3345901-2e76-34dd-32f1-0b312d6e1c1e" name="IsReturnUrlDiscoverableNotSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="46877579-ba4c-c30c-38c4-9c6ad3922390" name="InsufficientlyProtectedMessageReceived" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="1deb0ca9-923a-8ef7-7a24-d5d5af04acdf" name="SpecAppendixAExample" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c63c9935-54a0-398a-f44b-214e17faf1f1" name="SendDirectMessageResponseHonorsHttpStatusCodes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b32b6295-d4a9-3369-f072-28a71e84d4e8" name="SerializationWithUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2fb2563b-b908-2fad-5efc-522a68c76780" name="ValidMessageNoNonceReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ee052b02-681f-2303-3cc6-37f7b2319579" name="RequireAssociation" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="fb0e478e-0f55-b257-75fe-2ab60b57292e" name="SendInvalidMessageTransport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a4aa113a-57b5-a52c-c4e3-f70d6702badb" name="Default" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ad56539c-6156-5f62-a98a-b24ae0159cc6" name="XmlSerialization" storage="..\bin\debug\dotnetopenauth.test.dll" enabled="false" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="1da7c479-bf01-2d12-8645-b4f7007dcfec" name="LanguagePreferenceEncoding" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="547cfee6-bbb4-6138-a114-fc0eb6cc94f6" name="PrivateAssociationTampered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5803e93d-e256-86ba-e10e-499d2f813c6d" name="Trivial" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
@@ -266,40 +284,40 @@
<TestLink id="78f622a3-750c-12c5-afc6-470c1bf71d85" name="ProtocolDetectionWithoutClues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="0c1a0323-092a-34b3-1601-1f941569efab" name="CtorGoodXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="8538caf8-48bd-7cf8-6ad8-15e1c3766f92" name="CtorNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="1b66e135-bdab-c2ed-18d8-aa89b46a57fc" name="RPRejectsUnrecognizedAssociationType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="fa2e5bbd-4c41-f2b1-e875-38c6ef011fa1" name="RandomCharactersTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f4893153-bb84-bf45-7889-8350a7e1db66" name="DiscoveryRequireSslWithInsecureXrdsInSecureHtmlHead" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5218fba2-d1af-e1f4-7641-9ae1d4975430" name="DirectResponsesSentUsingKeyValueForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a1a0178c-cd4a-1651-8535-3c9ee3d40821" name="ToDictionaryWithNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="0f36556d-ece7-eb70-8597-a9d085165c2c" name="Sign" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="19d2219e-c04d-fa3a-5e26-92448f35f21d" name="RespondNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="eb932fc7-76c7-b63f-e1e6-a59dea8e4da1" name="AddAttribute" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a59c5dc0-de4d-8136-8545-2e2e9616de46" name="SerializationWithXri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="0aa1bc22-0b26-3977-5983-5dc4a454cea5" name="OptionalNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="c205832e-711c-62d0-5f5e-78f1250ea7cc" name="AuthenticationTimeUtcSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="3536ba12-fdb0-2ac9-3fef-00a2dd8e9a65" name="SharedAssociationTampered" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e7b8770d-b26c-e7b3-e80e-fac46285f59d" name="PassThruList" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="14acb719-f090-018f-b870-9a5acb1d7179" name="AddAuthLevelTypes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="aef95d4e-ad69-0eca-6528-7fce78512336" name="EqualityTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8375c7bb-b539-3396-885a-a3ca220078ec" name="InsufficientlyProtectedMessageSent" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="1b66e135-bdab-c2ed-18d8-aa89b46a57fc" name="RPRejectsUnrecognizedAssociationType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="10245b55-8130-e0aa-e211-4a16fa14d0b1" name="ClearValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="1ea8bd07-75a5-bfc0-5f8c-1a78d04240c2" name="TryGetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="7b89844d-f60a-fb66-c48d-e483864c66b5" name="RespondTooManyValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="b9cda1a0-83cd-cf4b-b61f-4faa75fa37ba" name="ReceivedReplayProtectedMessageTwice" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="7f9c4a9e-de7a-555c-543d-db89b757588e" name="AppendQueryArgs" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e9cceef5-383d-92f0-a8bb-f3e207582836" name="RealmReturnToMismatchV2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e6b412e5-3a53-e717-6393-254e1c93e239" name="PassThruDoubleCache" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9aa6a81e-c198-c0fd-0252-003b856b7674" name="ConstantFieldMemberValidValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d6b120b7-fc16-6815-927e-af382cd44bbd" name="ReceivedInvalidSignature" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="b71d12f6-58a1-cf82-d06e-e57c0a3ea55c" name="RPRejectsUnencryptedSuggestion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="77e5af06-b02d-692e-b32f-40ea39e77fbd" name="FriendlyIdentifierForDisplay" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3c67903e-15ce-9ed4-34c8-f77059af79ca" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="87593646-8db5-fb47-3a5b-bf84d7d828c2" name="InvalidMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="65f16786-7296-ee46-8a8f-82f18b211234" name="AddByKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c891c6bc-da47-d4ab-b450-f3e3a0d6cba8" name="NoAssociationNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f3f84a10-317f-817a-1988-fddc10b75c20" name="AddTwoAttributes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7f3144c7-95a1-affa-1a37-9e6169c19be6" name="SharedAssociationNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9bf0528f-c3ab-9a38-fd8a-fd14bade0d0b" name="EnumerableCacheCurrentThrowsAfter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f3af5fd8-f661-dc4f-4539-947b081a8b54" name="ReceivedReplayProtectedMessageJustOnce" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a14ddf08-796b-6cf1-a9bf-856dd50520fa" name="RequiredProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="79c0d33a-f7f2-fd69-1b4d-57ee3ece2cca" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="73c6c979-205d-2216-d98d-2dd136b352c6" name="UtcCreationDateConvertsToUniversal" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5b4fee50-7c15-8c6b-3398-c82279646e5f" name="RequiredOptionalLists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="59295023-d248-e9c4-68b9-65f6ea38490c" name="VerifyArgumentNotNullDoesNotThrow" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e137b84a-d2a7-9af6-d15d-a92417668ccf" name="Transport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a94ee2ec-02df-b535-1d2e-0c5db9c76b49" name="ReceiveUnrecognizedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5dcd69c3-e979-7316-4551-a73fe4645dcd" name="SecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="cea48223-04e2-d336-0ac4-255c514bd188" name="RoundTripFullStackTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="ba35acc7-78d2-6710-57ac-6843210d4202" name="UserSetupUrlRequiredInV1Immediate" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f18b514c-4f78-5421-8bdf-8b0f1fdf2282" name="HandleLifecycle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
@@ -307,73 +325,80 @@
<TestLink id="6badbaa8-33d1-13c4-c1f9-aef73a9ac5bf" name="InvalidRawBirthdate" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="0435e38a-71f2-d58d-9c07-d97d830a1578" name="ExtensionResponsesAreSigned" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="2f2ea001-a4f8-ff0d-5d12-74180e0bf610" name="HttpsSignatureVerificationNotApplicable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fe55cc74-98eb-c6c7-622f-77ad3e304c10" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2cfefc4a-918a-3e16-0670-53eb33634525" name="GeneratesOnlyRequiredElements" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="ca5360e1-ca08-d00f-6ade-7c9441db4294" name="CreateQueryStringEmptyCollection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="4735a071-3c06-509b-05f5-912ab0e39f13" name="InvalidRealmBadProtocol" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="6ef9df5a-d069-0103-5260-593808f232da" name="XrdsDiscoveryFromHead" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="6fbd433d-cd54-b206-6df3-fbd591690a4d" name="HtmlDiscover_11" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="175b6eb8-a448-4e07-7fed-001355edc128" name="PassThruArray" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="777af676-ee70-0e16-799b-85b9ec33cd63" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d083396b-db68-1318-e62b-6dc9f89e26bd" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="2f6a81c5-cd04-0ca0-22ee-d4213f9cf147" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="72d3f240-67f2-0b04-bd31-a99c3f7a8e12" name="SharedAssociationPositive" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3cd9447e-9ffd-f706-37bb-e7eb5828e430" name="InvalidRealmEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="30f3c12b-e510-de63-5acd-ae8e32866592" name="CreateQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="28fe030c-d36e-13cf-475c-7813210bf886" name="AddAttributeRequestAgain" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="e7a41771-7dda-be44-0755-e06300f3cd92" name="IsSaneTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2e23dc5a-93ea-11a5-d00d-02d294794e5f" name="AssociateDiffieHellmanOverHttps" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e344ba35-96b7-d441-c174-8c8b295fd157" name="AddCallbackArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ff78d828-b437-aaeb-e48a-85a5ad1fe396" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f41ce7ab-5500-7eea-ab4d-8c646bffff23" name="HttpSchemePrepended" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="069995aa-4136-610b-3f41-df80a138c244" name="AppendQueryArgsNullUriBuilder" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2237b8ce-94ce-28c1-7eb2-14e59f47e926" name="UnifyExtensionsAsSregFromAXSchemaOrg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7cdabb8a-aefa-e90e-c32e-047404b64c2d" name="SerializeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="457d6b32-d224-8a06-5e34-dbef3e935655" name="HttpSignatureVerification" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="309fdc0f-150c-5992-9a79-63be5f479d89" name="RequiredProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="2e1b27e8-2e3e-0290-2bee-d88e2914efd9" name="SpreadSregToAXNoExtensions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="c15c3ab5-e969-efc9-366d-78ebc43ce08f" name="Fetch" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="7bf8e806-68a1-86bc-8d91-9a99d237d35c" name="CreateRequestMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a883dc73-d6be-e59a-6da2-0db1d4452679" name="BindingElementsOrdering" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="196be55e-a3e5-adf3-9f15-13ba6cce0701" name="ValidRealmsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="df159af7-abf5-089c-b592-e6f535dab1c1" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5dcd69c3-e979-7316-4551-a73fe4645dcd" name="SecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f533bf9e-daa1-b26a-4789-372f3a9291d6" name="TryRequireSslAdjustsIdentifier" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f44fb549-fc8a-7469-6eed-09d9f86cebff" name="SendDirectMessageResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="313faac6-6357-5468-2d4d-4c9fba001678" name="TryParseNoThrow" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="7cbe4350-38d0-db7e-335c-93d9398fc95b" name="ExtensionOnlyFacadeLevel" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5f6e9836-2630-b53c-4121-64900bde5628" name="IsExtensionSupportedNullExtension" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5f02e24c-2972-c598-ca71-ea362b2fe7d8" name="SecuritySettingsSetNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="2f5cfa57-bcb4-39af-e769-2d7c34e2598e" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="65752c29-fa1f-7b88-bbec-5329af8db4d8" name="IsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="c351c660-d583-d869-0129-2e312665d815" name="CtorBlank" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9026e58c-8582-0852-3c3c-9eadfd544cbc" name="VerifyNonZeroLengthOnEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="809afd59-8f10-ce37-6630-06b59351a05a" name="CommonProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="02333934-cfea-2fb6-5e08-7a24be050f44" name="CreateRequestsOnNonOpenID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5298ecb0-bcad-9022-8b93-87793eb2c669" name="UnsolicitedDelegatingIdentifierRejection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="068dcefa-8f2b-52c3-fe79-576c84c5648b" name="CtorBlank" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="782d64c8-46af-a624-b3f6-a65aeaa57bfe" name="LastLineNotTerminatedLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="cd219db4-4f6e-8ff4-f957-c8428d38c118" name="HttpSignatureGeneration" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="864578a5-61a2-bc5d-1d19-17093885bea3" name="InvalidRealmTwoWildcards1" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5298ecb0-bcad-9022-8b93-87793eb2c669" name="UnsolicitedDelegatingIdentifierRejection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ab653060-5bec-3dc6-78ee-a5ef7d393b1d" name="AddPolicyMultipleTimes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="2599d559-d036-5dd2-0b5b-fb229c3bf486" name="InvalidRealmBadWildcard2" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="54a65e0b-1857-72b9-797b-fe3d9a082131" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="64b41c6c-2b67-af35-0c93-df41bd6f2dbb" name="Store" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8a5c9404-1e77-68cf-229a-ef7ed413e6e7" name="OptionalNonNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3744e1f1-6be1-f27f-78e9-5410d356ccf4" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="79c0d33a-f7f2-fd69-1b4d-57ee3ece2cca" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="937b85f4-1ef3-84d1-a567-8bba079a33a9" name="Properties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9104f36f-6652-dcbb-a8ae-0d6fc34d76ed" name="AddCallbackArgumentClearsPreviousArgument" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="c057a3e5-b527-62a9-c19b-abb82e6be621" name="SendIndirectMessage301GetEmptyRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="0dc9284e-cba4-9d87-8955-19639578c70d" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="384fecbf-f18e-edcb-a2eb-fb0322f031aa" name="ApplyHeadersToResponseNullListenerResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e3a3b3b6-e05f-0a99-e20c-af91a9065819" name="AssociateRequestDeterminedBySecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d3c4624f-f78a-2ff3-199a-77c922059718" name="Best" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7f3144c7-95a1-affa-1a37-9e6169c19be6" name="SharedAssociationNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="aedfde98-4357-5b63-7dca-cced838ee416" name="Provider" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="21fa5a5c-c04b-831f-c537-fc8be1bf65b2" name="IsExtensionSupported" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9e59b8d8-2fc4-b425-b5c4-c0a9fde3bf4d" name="SetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7b1fb2c4-39c0-0d39-700c-96d992f5a01f" name="AuthenticationTimeUtcSetUnspecified" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="ddf4f3ec-07bb-09e8-b5e8-0837cb8cb684" name="IsReturnUrlDiscoverableNoResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a18ae750-318b-bb1f-c2b3-c31da845c085" name="Count" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="a8bd3730-1660-dca9-87ec-23bc9dc39ab9" name="CtorGoodXriSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5b451648-5ca1-4395-333d-bbcb098f4a45" name="NoRequestedExtensions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="cb9a8325-abf5-5d97-a94e-a6d34f2b51e1" name="AssociateRenegotiateLimitedByRPSecuritySettings" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="9fae8ab4-8436-eba1-3e4b-51511998fa8e" name="UnsolicitedAssertionRejected" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7cf52613-a212-8a0f-843f-37f08740c125" name="SpreadSregToAxNoOpIfOPSupportsSreg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="7c8eac5a-0455-e038-0e9a-10e59d459452" name="CtorUriHttpSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f69f1c0c-e258-95fb-4fcb-ad14bfc40e3c" name="Discover" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ccfda025-cb1a-a2ff-78bd-5e9af885ae0b" name="ToDictionary" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="64142858-d52e-be06-d11f-6be326c6176b" name="RespondTwoValues" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="03b47440-3d09-ab28-97f1-39809f5703b6" name="NormalizeCase" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="34357633-4745-6fba-9316-493d3c6c5b90" name="ParseEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="85a0dec0-983c-8f21-b093-a2179624cc88" name="UnifyExtensionsAsSregWithSreg" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5c66a1b8-5b20-2e3b-8427-d6ff4640ac53" name="BadRequestsGenerateValidErrorResponses" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="4ba7ca33-72f1-3fc6-d37c-65134eda904d" name="AddDeclaredValueThatAlreadyExists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="06ec5bce-5a78-89c3-0cda-fa8bddfea27d" name="SetCountZero" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a6ea74e5-8681-4eb4-a51b-5051e5f7603c" name="NonFieldOrPropertyMember" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="f6979feb-7016-4e2b-14e2-e6c2c392419f" name="RemoveByKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="46ec24da-deb7-27c7-6dc6-52090e4fd1fb" name="Serialize" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e9a5efc6-fde8-8fa4-0bda-2675a4a7e06b" name="DefaultReferenceTypeDeclaredPropertyHasNoKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="b70b4bd5-6dae-b4ad-349c-c3ad70603773" name="ReadFromRequestQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="2a7b77c3-27d5-7788-e664-5d20118d223b" name="OPRejectsHttpNoEncryptionAssociateRequests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="809afd59-8f10-ce37-6630-06b59351a05a" name="CommonProperties" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3fe0b432-dbb4-b334-e504-a83fe5ffdbaf" name="EqualityTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="98a2ece8-c9e6-e6f3-c65e-f915b22077fa" name="RequestUsingGet" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c79dd056-8fff-3393-f125-4b83cf02cb3b" name="RequireSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="da8fcfa9-bd2c-eca0-ecbf-90364f84e8e5" name="AddExtraFieldThatAlreadyExists" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="fb91e9dd-fc3b-d8a7-a5d7-d215d5ba880f" name="CtorStringHttpSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5211652f-1c25-fd4b-890d-05d2178a60e2" name="ExtensionFactories" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
@@ -381,64 +406,69 @@
<TestLink id="e2b1ae2a-8f30-b6b3-bca6-ef28fc5a0175" name="ClaimedIdAndLocalIdSpecifiedIsValid" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="58d69d1e-3bd2-3379-0af1-188f9cff2dd0" name="IsTypeUriPresentEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="cc9200bf-1399-d40a-9754-6415f0b7bcf8" name="CreateRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="7cdabb8a-aefa-e90e-c32e-047404b64c2d" name="SerializeTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="ee7a04ba-0419-e08f-b838-01ec0f2a838e" name="UnsolicitedAssertion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="f6979feb-7016-4e2b-14e2-e6c2c392419f" name="RemoveByKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="58df167c-cf19-351c-cb09-5c52ae9f97be" name="DeserializeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="454165a2-c26e-5740-09a9-d234db052ba3" name="InvalidRealmNullUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a778f331-f14e-9d6e-f942-a023423540f6" name="Ctor" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="ae8b7cba-696e-2362-d5e1-79a9c202a994" name="EmptyLineLoose" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="be00d3ef-f24d-eb8a-d251-4d691736ee6f" name="AddAttributeRequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="593e1d86-a6c2-c937-a1b4-6d25a595a1f1" name="EnumerableCacheCurrentThrowsBefore" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="030ac3cf-cfb6-ca47-f822-ec1d2979f0b3" name="Defaults" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d5d2c553-97db-8d6c-c984-982299d6091d" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ef20222d-b2e2-d593-17fa-512041020643" name="InvalidRealmNullString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="628a417f-4ddb-5965-bd4a-86c8de565c8f" name="AssociateDiffieHellmanOverHttp" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="10c44d72-2789-2afe-3b27-091dea97546e" name="RequestNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="724cc3e8-c13c-5cc6-ce14-25c51ad6297d" name="Mode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="24506e06-a678-66cc-48ee-b7f11f18a6e8" name="StripXriScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="c79dd056-8fff-3393-f125-4b83cf02cb3b" name="RequireSsl" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="a314e3b9-36a5-bfbb-3e15-e5003f22cf87" name="Serialize" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5a0d31d9-9c70-2a28-3e8c-46e8e047ac2d" name="ReceiveNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="439c8c16-2ba5-eb3b-b631-ce50ec48eba0" name="CtorNullMember" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="4b44b825-36cc-77f8-3a4a-5892c540f577" name="GetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="495dd486-08dd-d365-7a84-67d96fef8460" name="SendIndirectedUndirectedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="4c399759-263f-5eba-8855-de14f197e647" name="QueryStringContainPrefixedParametersNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="671ddaf5-238d-a517-b0f3-d79bd591a396" name="EmptyMailAddress" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d8118997-ecf7-7130-f068-5e2bc867786d" name="SerializeNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="e9c2087b-1c52-5bb9-bf4e-9046cf281e36" name="DiscoverRequireSslWithInsecureRedirect" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="25e2c593-2e69-6215-90c0-67f269939865" name="CtorEmptyTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e1e9dde8-30e6-6ce0-d5a6-4e22e0347ac4" name="UnifyExtensionsAsSregWithAX" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f50a0bdb-380e-30f6-492a-a6dd9664d0f0" name="ExtensionOnlyChannelLevel" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="5716de48-f311-944f-45ff-872d68bc2bcf" name="IsExtensionSupportedNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="55b078e4-3933-d4e0-1151-a0a61321638e" name="ReadFromRequestAuthorization" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="64b41c6c-2b67-af35-0c93-df41bd6f2dbb" name="Store" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="44afc59c-60fc-3179-b5a6-1e58e7752d54" name="ApplyHeadersToResponseNullHeaders" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="29e45877-ca7a-85de-5c39-6d43befe1a1e" name="DiscoveryRequireSslWithInsecureXrdsButSecureLinkTags" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="599add9e-e9eb-5e8a-ce6b-6dc73c2bb408" name="DataContractNamespace" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f583b298-139a-e733-dde6-f9dc4b73d4bf" name="SendDirectMessageResponseHonorsHttpStatusCodes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3027bfe5-3612-7089-16cc-d6a2a556a41f" name="Transport" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="b4b00582-dcc9-7672-0c02-52432b074a92" name="GetNullType" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="44ced969-83dd-201d-a660-e3744ee81cf8" name="ConstructorTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="1c5d54e2-d96a-d3a6-aeac-95f137b96421" name="CommonMethods" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="bc3e979b-09ea-c45d-5714-2d1fb00244cf" name="IncomingMessageMissingRequiredParameters" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="0f36556d-ece7-eb70-8597-a9d085165c2c" name="Sign" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8375c7bb-b539-3396-885a-a3ca220078ec" name="InsufficientlyProtectedMessageSent" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="924b5295-0d39-5c89-8794-22518091e05a" name="CtorNullToString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a63c169c-4e9a-bcba-b7cd-c4c5280cd652" name="PrepareMessageForSendingNonExtendableMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c891c6bc-da47-d4ab-b450-f3e3a0d6cba8" name="NoAssociationNegative" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="63944cb8-4c61-c42c-906f-986fa793370b" name="SignatureTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="77934ac4-bd65-7ad8-9c53-9c9447f9e175" name="GetReturnToArgumentAndNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="309fdc0f-150c-5992-9a79-63be5f479d89" name="RequiredProtection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="5a77a48f-00d6-da6f-5ef7-c897ebf8fe6b" name="EscapeUriDataStringRfc3986Tests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="735b7a56-0f6f-77d8-8968-6708792a7ce8" name="UnifyExtensionsAsSregWithAXSchemaOpenIdNet" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="4a5b601d-475d-e6cc-1fec-19a2850681ad" name="Serializable" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="9bcc2d64-870f-7675-a314-fbb975446817" name="IsApprovedDeterminesReturnedMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="30a8eab6-6423-26af-da1a-ec304935fe43" name="RemoveNonexistentHandle" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="53bc1962-e7c2-04b6-cafa-0f6fde7592a9" name="ReadFromRequestNoContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3e676e31-3b6d-9d12-febd-d632ece804ec" name="RPRejectsMismatchingAssociationAndSessionBitLengths" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3c4e0438-94f5-a132-3949-8d94718e4839" name="PassThruCollection" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="0f56721c-ef8f-84be-28b7-d909614c2f85" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="41ff051f-03d5-5f06-c6e4-615360cac08a" name="ReadFromRequestDisallowedHttpMethod" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d647fd93-40b3-24d5-25fc-661c0d58335c" name="SendIndirectMessageFormPostNullMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="315b5857-7697-8222-f94c-f6f10d539491" name="BaseSignatureStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="62c6ee5b-ac29-461c-2373-bf620e948825" name="InvalidRealmNoScheme" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="115283b9-d95c-9a92-2197-96685ee8e96a" name="TwoExtensionsSameTypeUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="80719076-10fd-20a7-7ff3-a0aa2bc661cb" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="3cd9447e-9ffd-f706-37bb-e7eb5828e430" name="InvalidRealmEmpty" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="9684f7bf-cdda-a2c5-0822-29cb0add3835" name="ResponseNonceGetter" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="c4001e1c-75ad-236b-284f-318905d2bc3a" name="CreateRequestOnNonOpenID" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="b58e4791-68c0-1bc0-2e48-e1351459ee46" name="UserSetupUrlSetForV1Immediate" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="30f3c12b-e510-de63-5acd-ae8e32866592" name="CreateQueryString" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="32e95494-d0bb-cfc7-a8d6-652f8816c6b4" name="ReadFromResponse" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c0d7cfcc-4f7e-e7df-3de2-b578c4c3d6ee" name="SpreadSregToAxMultipleSchemas" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e1958fc5-a979-88b2-b593-3bc89ad6ad4e" name="GetEnumeratorUntyped" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f967c0af-c04c-d156-4faf-8978bfcab5d7" name="RequiredNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="248f0afc-979f-a86f-e7de-fdeb4f9dd3ea" name="CtorBadUri" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="fa2e5bbd-4c41-f2b1-e875-38c6ef011fa1" name="RandomCharactersTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="3e2f1dad-3684-587c-9039-8d116582be10" name="GetReturnToArgumentEmptyKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="536ecd26-4bda-a35e-5af8-666eb9b44940" name="NullValueEncoding" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="87593646-8db5-fb47-3a5b-bf84d7d828c2" name="InvalidMessageTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="8a5c9404-1e77-68cf-229a-ef7ed413e6e7" name="OptionalNonNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="09b892f2-96e9-45b7-d082-b0bb512c1dd4" name="RequiredNonNullableStruct" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="d5912d3e-441c-a20e-20a2-0b9f0220a762" name="ParameterNames" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="439c8c16-2ba5-eb3b-b631-ce50ec48eba0" name="CtorNullMember" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="feb0a53e-1592-b878-b70c-1a272d9c6908" name="SpreadSregToAxTargetedAtOPFormat" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="a6295302-c78f-4122-ce88-94fc30980262" name="CtorStringNoSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f12bf351-584c-bc51-c315-a67f1076927c" name="ReturnToDoesNotMatchRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="bdba0004-be80-f5c1-1aae-487db09bdf04" name="GetReturnToArgumentDoesNotReturnExtraArgs" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
@@ -446,27 +476,29 @@
<TestLink id="cb48421f-f4ff-3994-3abc-4be35f8bfd99" name="AssociateQuietlyFailsAfterHttpError" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="decb3fef-ef61-6794-5bc6-f7ff722a146e" name="EqualsTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="736a09b4-f56e-0176-6c1c-81db0fbe3412" name="CtorUriHttpsSchemeSecure" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="9f880280-aa8f-91bb-4a5f-3fe044b6815a" name="CreateVerificationCode" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="10a8b8e5-e147-838c-0708-be98d5e4490e" name="CtorFull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="6daa360b-71e4-a972-143f-01b801fada84" name="DeserializeWithExtraFields" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="65f16786-7296-ee46-8a8f-82f18b211234" name="AddByKeyValuePair" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="8bbc6a02-b5a4-ea8e-2a77-8d1b6671ceb5" name="ImplicitConverstionFromUriTests" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="d6088ffe-ccf5-9738-131b-0fc1bc7e3707" name="TrimFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="80719076-10fd-20a7-7ff3-a0aa2bc661cb" name="CtorNull" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="90d3c411-8895-a07f-7a21-258b9d43c5b2" name="InvalidMessageNoNonceReceivedTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="121983e3-1336-70cb-8d2a-498629e92bec" name="GetReturnToArgumentNullKey" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5e0c892d-7ad8-6d56-1f1d-2fb6236670d6" name="CtorDefault" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="c23e762d-4162-cb9e-47b3-455a568b5072" name="SendIndirectMessageFormPostEmptyRecipient" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="f70b368e-da33-bc64-6096-1b467d49a9d4" name="NonIdentityRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="46877579-ba4c-c30c-38c4-9c6ad3922390" name="InsufficientlyProtectedMessageReceived" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="53bc1962-e7c2-04b6-cafa-0f6fde7592a9" name="ReadFromRequestNoContext" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="b191e585-49d9-df8e-c156-307f798db169" name="AddAttributeRequest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="3772f97f-3fe6-3fc0-350d-4085e7c4329e" name="Test" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e7a41771-7dda-be44-0755-e06300f3cd92" name="IsSaneTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="44091d36-98db-2115-8647-7bd7cd308796" name="ToStringTest" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="9e59b8d8-2fc4-b425-b5c4-c0a9fde3bf4d" name="SetValue" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="ee7a04ba-0419-e08f-b838-01ec0f2a838e" name="UnsolicitedAssertion" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="90557d85-db17-e9ab-e17b-32d6cc9fd437" name="TrimFragment" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="534bee09-36e1-c3e0-f6af-bc191b10aa48" name="CtorNullSigner" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="9986fea9-8d64-9ada-60cb-ab95adb50fb7" name="ToStringDeferredEmptyMultiLine" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="de1cdd00-a226-0d43-62b6-0c1ad325be8c" name="RequiredMinAndMaxVersions" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="cbdfd707-7ba8-4b8f-9d58-17b125aa4cd4" name="SendIndirectMessage301GetNullMessage" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5aa4dfa9-9691-bfe0-7d81-587cfa519a55" name="DirectResponsesReceivedAsKeyValueForm" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
- <TestLink id="8fd673c8-977a-7b66-72cb-38c7054796c7" name="DiscoverRequireSslWithSecureRedirects" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="c63c9935-54a0-398a-f44b-214e17faf1f1" name="SendDirectMessageResponseHonorsHttpStatusCodes" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
<TestLink id="5271f941-565f-5977-6884-82cef09161db" name="ParseEndUserSuppliedXriIdentifer" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
</TestLinks>
</TestList>
@@ -477,6 +509,10 @@
<Description>All tests</Description>
<TestLinks>
<TestLink id="b350ddb1-f4e5-e79c-af5e-f4195767f294" name="TestPublic" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7c3603c8-3686-807b-7840-1f04f8f307f5" name="AssociateDH" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="7fa99410-3aa3-10c3-10a0-27bb9288c900" name="CheckIdSharedHmacSha1Association" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="85469029-ffe0-f6f7-b56a-9ffd48fa137b" name="AssociateClearText" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
+ <TestLink id="e6dadcc3-60ff-f60c-0c9a-2ebd5cf91df0" name="CheckIdSharedHmacSha256Association" storage="..\bin\debug\dotnetopenauth.test.dll" type="Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestElement, Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel, PublicKeyToken=b03f5f7f11d50a3a" />
</TestLinks>
</TestList>
<TestList name="Unit tests" id="f0eeb325-0558-48a3-9a99-952133d8148e" parentListId="8c43106b-9dc1-4907-a29f-aa66a61bf5b6" />
diff --git a/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs b/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs
index a7d58c7..37f9c78 100644
--- a/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs
+++ b/src/DotNetOpenAuth/ComponentModel/ConverterBase.cs
@@ -12,6 +12,9 @@ namespace DotNetOpenAuth.ComponentModel {
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
+using System.Reflection;
+ using System.Security;
+ using System.Security.Permissions;
/// <summary>
/// A design-time helper to allow Intellisense to aid typing
@@ -141,6 +144,10 @@ namespace DotNetOpenAuth.ComponentModel {
/// The conversion cannot be performed.
/// </exception>
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
+ if (destinationType.IsInstanceOfType(value)) {
+ return value;
+ }
+
T typedValue = (T)value;
if (destinationType == typeof(string)) {
return this.ConvertToString(typedValue);
@@ -152,6 +159,20 @@ namespace DotNetOpenAuth.ComponentModel {
}
/// <summary>
+ /// Creates an <see cref="InstanceDescriptor"/> instance, protecting against the LinkDemand.
+ /// </summary>
+ /// <param name="memberInfo">The member info.</param>
+ /// <param name="arguments">The arguments.</param>
+ /// <returns>A <see cref="InstanceDescriptor"/>, or <c>null</c> if sufficient permissions are unavailable.</returns>
+ protected static InstanceDescriptor CreateInstanceDescriptor(MemberInfo memberInfo, ICollection arguments) {
+ try {
+ return CreateInstanceDescriptorPrivate(memberInfo, arguments);
+ } catch (SecurityException) {
+ return null;
+ }
+ }
+
+ /// <summary>
/// Gets the standard values to suggest with Intellisense in the designer.
/// </summary>
/// <returns>A collection of the standard values.</returns>
@@ -185,5 +206,16 @@ namespace DotNetOpenAuth.ComponentModel {
/// <returns>The string representation of the object.</returns>
[Pure]
protected abstract string ConvertToString(T value);
+
+ /// <summary>
+ /// Creates an <see cref="InstanceDescriptor"/> instance, protecting against the LinkDemand.
+ /// </summary>
+ /// <param name="memberInfo">The member info.</param>
+ /// <param name="arguments">The arguments.</param>
+ /// <returns>A <see cref="InstanceDescriptor"/>.</returns>
+ [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
+ private static InstanceDescriptor CreateInstanceDescriptorPrivate(MemberInfo memberInfo, ICollection arguments) {
+ return new InstanceDescriptor(memberInfo, arguments);
+ }
}
}
diff --git a/src/DotNetOpenAuth/ComponentModel/IdentifierConverter.cs b/src/DotNetOpenAuth/ComponentModel/IdentifierConverter.cs
new file mode 100644
index 0000000..6ba9c4b
--- /dev/null
+++ b/src/DotNetOpenAuth/ComponentModel/IdentifierConverter.cs
@@ -0,0 +1,69 @@
+//-----------------------------------------------------------------------
+// <copyright file="IdentifierConverter.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.ComponentModel {
+ using System;
+ using System.Collections;
+ using System.ComponentModel.Design.Serialization;
+ using System.Reflection;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+
+ /// <summary>
+ /// A design-time helper to give an OpenID Identifier property an auto-complete functionality
+ /// listing the OP Identifiers in the <see cref="WellKnownProviders"/> class.
+ /// </summary>
+ public class IdentifierConverter : ConverterBase<Identifier> {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="IdentifierConverter"/> class.
+ /// </summary>
+ [Obsolete("This class is meant for design-time use within an IDE, and not meant to be used directly by runtime code.")]
+ public IdentifierConverter() {
+ }
+
+ /// <summary>
+ /// Converts a value from its string representation to its strongly-typed object.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <returns>The strongly-typed object.</returns>
+ protected override Identifier ConvertFrom(string value) {
+ return value;
+ }
+
+ /// <summary>
+ /// Creates the reflection instructions for recreating an instance later.
+ /// </summary>
+ /// <param name="value">The value to recreate later.</param>
+ /// <returns>
+ /// The description of how to recreate an instance.
+ /// </returns>
+ protected override InstanceDescriptor CreateFrom(Identifier value) {
+ if (value == null) {
+ return null;
+ }
+
+ MemberInfo identifierParse = typeof(Identifier).GetMethod("Parse", BindingFlags.Static | BindingFlags.Public);
+ return CreateInstanceDescriptor(identifierParse, new object[] { value.ToString() });
+ }
+
+ /// <summary>
+ /// Converts the strongly-typed value to a string.
+ /// </summary>
+ /// <param name="value">The value to convert.</param>
+ /// <returns>The string representation of the object.</returns>
+ protected override string ConvertToString(Identifier value) {
+ return value;
+ }
+
+ /// <summary>
+ /// Gets the standard values to suggest with Intellisense in the designer.
+ /// </summary>
+ /// <returns>A collection of the standard values.</returns>
+ protected override ICollection GetStandardValuesForCache() {
+ return SuggestedStringsConverter.GetStandardValuesForCacheShared(typeof(WellKnownProviders));
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverter.cs b/src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverter.cs
index 3b60bd7..1c8c555 100644
--- a/src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverter.cs
+++ b/src/DotNetOpenAuth/ComponentModel/SuggestedStringsConverter.cs
@@ -30,6 +30,19 @@ namespace DotNetOpenAuth.ComponentModel {
protected abstract Type WellKnownValuesType { get; }
/// <summary>
+ /// Gets the values of public static fields and properties on a given type.
+ /// </summary>
+ /// <param name="type">The type to reflect over.</param>
+ /// <returns>A collection of values.</returns>
+ internal static ICollection GetStandardValuesForCacheShared(Type type) {
+ var fields = from field in type.GetFields(BindingFlags.Static | BindingFlags.Public)
+ select field.GetValue(null);
+ var properties = from prop in type.GetProperties(BindingFlags.Static | BindingFlags.Public)
+ select prop.GetValue(null, null);
+ return (fields.Concat(properties)).ToArray();
+ }
+
+ /// <summary>
/// Converts a value from its string representation to its strongly-typed object.
/// </summary>
/// <param name="value">The value.</param>
@@ -68,11 +81,7 @@ namespace DotNetOpenAuth.ComponentModel {
/// <returns>A collection of the standard values.</returns>
[Pure]
protected override ICollection GetStandardValuesForCache() {
- var fields = from field in this.WellKnownValuesType.GetFields(BindingFlags.Static | BindingFlags.Public)
- select field.GetValue(null);
- var properties = from prop in this.WellKnownValuesType.GetProperties(BindingFlags.Static | BindingFlags.Public)
- select prop.GetValue(null, null);
- return (fields.Concat(properties)).ToArray();
+ return GetStandardValuesForCacheShared(this.WellKnownValuesType);
}
}
}
diff --git a/src/DotNetOpenAuth/ComponentModel/UriConverter.cs b/src/DotNetOpenAuth/ComponentModel/UriConverter.cs
index 4412199..cf8dde3 100644
--- a/src/DotNetOpenAuth/ComponentModel/UriConverter.cs
+++ b/src/DotNetOpenAuth/ComponentModel/UriConverter.cs
@@ -76,7 +76,7 @@ namespace DotNetOpenAuth.ComponentModel {
}
MemberInfo uriCtor = typeof(Uri).GetConstructor(new Type[] { typeof(string) });
- return new InstanceDescriptor(uriCtor, new object[] { value.AbsoluteUri });
+ return CreateInstanceDescriptor(uriCtor, new object[] { value.AbsoluteUri });
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs b/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs
index f535c38..7bd84d9 100644
--- a/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs
+++ b/src/DotNetOpenAuth/Configuration/DotNetOpenAuthSection.cs
@@ -30,6 +30,11 @@ namespace DotNetOpenAuth.Configuration {
private const string OpenIdElementName = "openid";
/// <summary>
+ /// The name of the &lt;oauth&gt; sub-element.
+ /// </summary>
+ private const string OAuthElementName = "oauth";
+
+ /// <summary>
/// Initializes a new instance of the <see cref="DotNetOpenAuthSection"/> class.
/// </summary>
internal DotNetOpenAuthSection() {
@@ -61,5 +66,14 @@ namespace DotNetOpenAuth.Configuration {
get { return (OpenIdElement)this[OpenIdElementName] ?? new OpenIdElement(); }
set { this[OpenIdElementName] = value; }
}
+
+ /// <summary>
+ /// Gets or sets the configuration for OAuth.
+ /// </summary>
+ [ConfigurationProperty(OAuthElementName)]
+ internal OAuthElement OAuth {
+ get { return (OAuthElement)this[OAuthElementName] ?? new OAuthElement(); }
+ set { this[OAuthElementName] = value; }
+ }
}
}
diff --git a/src/DotNetOpenAuth/Configuration/OAuthConsumerElement.cs b/src/DotNetOpenAuth/Configuration/OAuthConsumerElement.cs
new file mode 100644
index 0000000..b15c3e3
--- /dev/null
+++ b/src/DotNetOpenAuth/Configuration/OAuthConsumerElement.cs
@@ -0,0 +1,34 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthConsumerElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Configuration {
+ using System.Configuration;
+
+ /// <summary>
+ /// Represents the &lt;oauth/consumer&gt; element in the host's .config file.
+ /// </summary>
+ internal class OAuthConsumerElement : ConfigurationElement {
+ /// <summary>
+ /// Gets the name of the security sub-element.
+ /// </summary>
+ private const string SecuritySettingsConfigName = "security";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthConsumerElement"/> class.
+ /// </summary>
+ internal OAuthConsumerElement() {
+ }
+
+ /// <summary>
+ /// Gets or sets the security settings.
+ /// </summary>
+ [ConfigurationProperty(SecuritySettingsConfigName)]
+ public OAuthConsumerSecuritySettingsElement SecuritySettings {
+ get { return (OAuthConsumerSecuritySettingsElement)this[SecuritySettingsConfigName] ?? new OAuthConsumerSecuritySettingsElement(); }
+ set { this[SecuritySettingsConfigName] = value; }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Configuration/OAuthConsumerSecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OAuthConsumerSecuritySettingsElement.cs
new file mode 100644
index 0000000..5e75390
--- /dev/null
+++ b/src/DotNetOpenAuth/Configuration/OAuthConsumerSecuritySettingsElement.cs
@@ -0,0 +1,33 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthConsumerSecuritySettingsElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Configuration {
+ using System;
+ using System.Collections.Generic;
+ using System.Configuration;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.OAuth;
+
+ /// <summary>
+ /// Security settings that are applicable to consumers.
+ /// </summary>
+ internal class OAuthConsumerSecuritySettingsElement : ConfigurationElement {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthConsumerSecuritySettingsElement"/> class.
+ /// </summary>
+ internal OAuthConsumerSecuritySettingsElement() {
+ }
+
+ /// <summary>
+ /// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file.
+ /// </summary>
+ /// <returns>The newly created security settings object.</returns>
+ internal ConsumerSecuritySettings CreateSecuritySettings() {
+ return new ConsumerSecuritySettings();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Configuration/OAuthElement.cs b/src/DotNetOpenAuth/Configuration/OAuthElement.cs
new file mode 100644
index 0000000..282bdba
--- /dev/null
+++ b/src/DotNetOpenAuth/Configuration/OAuthElement.cs
@@ -0,0 +1,48 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Configuration {
+ using System.Configuration;
+
+ /// <summary>
+ /// Represents the &lt;oauth&gt; element in the host's .config file.
+ /// </summary>
+ internal class OAuthElement : ConfigurationElement {
+ /// <summary>
+ /// The name of the &lt;consumer&gt; sub-element.
+ /// </summary>
+ private const string ConsumerElementName = "consumer";
+
+ /// <summary>
+ /// The name of the &lt;serviceProvider&gt; sub-element.
+ /// </summary>
+ private const string ServiceProviderElementName = "serviceProvider";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthElement"/> class.
+ /// </summary>
+ internal OAuthElement() {
+ }
+
+ /// <summary>
+ /// Gets or sets the configuration specific for Consumers.
+ /// </summary>
+ [ConfigurationProperty(ConsumerElementName)]
+ internal OAuthConsumerElement Consumer {
+ get { return (OAuthConsumerElement)this[ConsumerElementName] ?? new OAuthConsumerElement(); }
+ set { this[ConsumerElementName] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the configuration specific for Service Providers.
+ /// </summary>
+ [ConfigurationProperty(ServiceProviderElementName)]
+ internal OAuthServiceProviderElement ServiceProvider {
+ get { return (OAuthServiceProviderElement)this[ServiceProviderElementName] ?? new OAuthServiceProviderElement(); }
+ set { this[ServiceProviderElementName] = value; }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs
new file mode 100644
index 0000000..5ff528d
--- /dev/null
+++ b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderElement.cs
@@ -0,0 +1,34 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthServiceProviderElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Configuration {
+ using System.Configuration;
+
+ /// <summary>
+ /// Represents the &lt;oauth/serviceProvider&gt; element in the host's .config file.
+ /// </summary>
+ internal class OAuthServiceProviderElement : ConfigurationElement {
+ /// <summary>
+ /// Gets the name of the security sub-element.
+ /// </summary>
+ private const string SecuritySettingsConfigName = "security";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthServiceProviderElement"/> class.
+ /// </summary>
+ internal OAuthServiceProviderElement() {
+ }
+
+ /// <summary>
+ /// Gets or sets the security settings.
+ /// </summary>
+ [ConfigurationProperty(SecuritySettingsConfigName)]
+ public OAuthServiceProviderSecuritySettingsElement SecuritySettings {
+ get { return (OAuthServiceProviderSecuritySettingsElement)this[SecuritySettingsConfigName] ?? new OAuthServiceProviderSecuritySettingsElement(); }
+ set { this[SecuritySettingsConfigName] = value; }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs
new file mode 100644
index 0000000..c58c023
--- /dev/null
+++ b/src/DotNetOpenAuth/Configuration/OAuthServiceProviderSecuritySettingsElement.cs
@@ -0,0 +1,74 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthServiceProviderSecuritySettingsElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Configuration {
+ using System;
+ using System.Collections.Generic;
+ using System.Configuration;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.OAuth;
+
+ /// <summary>
+ /// Security settings that are applicable to service providers.
+ /// </summary>
+ internal class OAuthServiceProviderSecuritySettingsElement : ConfigurationElement {
+ /// <summary>
+ /// Gets the name of the @minimumRequiredOAuthVersion attribute.
+ /// </summary>
+ private const string MinimumRequiredOAuthVersionConfigName = "minimumRequiredOAuthVersion";
+
+ /// <summary>
+ /// Gets the name of the @maxAuthorizationTime attribute.
+ /// </summary>
+ private const string MaximumRequestTokenTimeToLiveConfigName = "maxAuthorizationTime";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthServiceProviderSecuritySettingsElement"/> class.
+ /// </summary>
+ internal OAuthServiceProviderSecuritySettingsElement() {
+ }
+
+ /// <summary>
+ /// Gets or sets the minimum OAuth version a Consumer is required to support in order for this library to interoperate with it.
+ /// </summary>
+ /// <remarks>
+ /// Although the earliest versions of OAuth are supported, for security reasons it may be desirable to require the
+ /// remote party to support a later version of OAuth.
+ /// </remarks>
+ [ConfigurationProperty(MinimumRequiredOAuthVersionConfigName, DefaultValue = "V10")]
+ public ProtocolVersion MinimumRequiredOAuthVersion {
+ get { return (ProtocolVersion)this[MinimumRequiredOAuthVersionConfigName]; }
+ set { this[MinimumRequiredOAuthVersionConfigName] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the maximum time a user can take to complete authorization.
+ /// </summary>
+ /// <remarks>
+ /// This time limit serves as a security mitigation against brute force attacks to
+ /// compromise (unauthorized or authorized) request tokens.
+ /// Longer time limits is more friendly to slow users or consumers, while shorter
+ /// time limits provide better security.
+ /// </remarks>
+ [ConfigurationProperty(MaximumRequestTokenTimeToLiveConfigName, DefaultValue = "0:05")] // 5 minutes
+ [PositiveTimeSpanValidator]
+ public TimeSpan MaximumRequestTokenTimeToLive {
+ get { return (TimeSpan)this[MaximumRequestTokenTimeToLiveConfigName]; }
+ set { this[MaximumRequestTokenTimeToLiveConfigName] = value; }
+ }
+
+ /// <summary>
+ /// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file.
+ /// </summary>
+ /// <returns>The newly created security settings object.</returns>
+ internal ServiceProviderSecuritySettings CreateSecuritySettings() {
+ return new ServiceProviderSecuritySettings {
+ MinimumRequiredOAuthVersion = this.MinimumRequiredOAuthVersion,
+ };
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
index 257e3ec..fa21fb9 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdElement.cs
@@ -37,11 +37,16 @@ namespace DotNetOpenAuth.Configuration {
private const string XriResolverElementName = "xriResolver";
/// <summary>
- /// Gets the name of the @maxAuthenticationTime attribute.
+ /// The name of the @maxAuthenticationTime attribute.
/// </summary>
private const string MaxAuthenticationTimePropertyName = "maxAuthenticationTime";
/// <summary>
+ /// The name of the @cacheDiscovery attribute.
+ /// </summary>
+ private const string CacheDiscoveryPropertyName = "cacheDiscovery";
+
+ /// <summary>
/// Initializes a new instance of the <see cref="OpenIdElement"/> class.
/// </summary>
internal OpenIdElement() {
@@ -72,6 +77,24 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets a value indicating whether the results of Identifier discovery
+ /// should be cached.
+ /// </summary>
+ /// <value>
+ /// Use <c>true</c> to allow identifier discovery to immediately return cached results when available;
+ /// otherwise, use <c>false</c>.to force fresh results every time at the cost of slightly slower logins.
+ /// The default value is <c>true</c>.
+ /// </value>
+ /// <remarks>
+ /// When enabled, caching is done according to HTTP standards.
+ /// </remarks>
+ [ConfigurationProperty(CacheDiscoveryPropertyName, DefaultValue = true)]
+ internal bool CacheDiscovery {
+ get { return (bool)this[CacheDiscoveryPropertyName]; }
+ set { this[CacheDiscoveryPropertyName] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the configuration specific for Relying Parties.
/// </summary>
[ConfigurationProperty(RelyingPartyElementName)]
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs
index d84766b..b51ccfb 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdProviderElement.cs
@@ -20,6 +20,11 @@ namespace DotNetOpenAuth.Configuration {
private const string SecuritySettingsConfigName = "security";
/// <summary>
+ /// Gets the name of the &lt;behaviors&gt; sub-element.
+ /// </summary>
+ private const string BehaviorsElementName = "behaviors";
+
+ /// <summary>
/// The name of the custom store sub-element.
/// </summary>
private const string StoreConfigName = "store";
@@ -40,6 +45,16 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets the special behaviors to apply.
+ /// </summary>
+ [ConfigurationProperty(BehaviorsElementName, IsDefaultCollection = false)]
+ [ConfigurationCollection(typeof(TypeConfigurationCollection<IProviderBehavior>))]
+ public TypeConfigurationCollection<IProviderBehavior> Behaviors {
+ get { return (TypeConfigurationCollection<IProviderBehavior>)this[BehaviorsElementName] ?? new TypeConfigurationCollection<IProviderBehavior>(); }
+ set { this[BehaviorsElementName] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the type to use for storing application state.
/// </summary>
[ConfigurationProperty(StoreConfigName)]
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
index 72a6481..457955c 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdProviderSecuritySettingsElement.cs
@@ -113,6 +113,7 @@ namespace DotNetOpenAuth.Configuration {
Contract.Assume(element != null);
settings.AssociationLifetimes.Add(element.AssociationType, element.MaximumLifetime);
}
+
return settings;
}
}
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs
index e311969..cdf4fd3 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartyElement.cs
@@ -25,6 +25,11 @@ namespace DotNetOpenAuth.Configuration {
private const string SecuritySettingsConfigName = "security";
/// <summary>
+ /// Gets the name of the &lt;behaviors&gt; sub-element.
+ /// </summary>
+ private const string BehaviorsElementName = "behaviors";
+
+ /// <summary>
/// Initializes a new instance of the <see cref="OpenIdRelyingPartyElement"/> class.
/// </summary>
public OpenIdRelyingPartyElement() {
@@ -40,6 +45,16 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets the special behaviors to apply.
+ /// </summary>
+ [ConfigurationProperty(BehaviorsElementName, IsDefaultCollection = false)]
+ [ConfigurationCollection(typeof(TypeConfigurationCollection<IRelyingPartyBehavior>))]
+ public TypeConfigurationCollection<IRelyingPartyBehavior> Behaviors {
+ get { return (TypeConfigurationCollection<IRelyingPartyBehavior>)this[BehaviorsElementName] ?? new TypeConfigurationCollection<IRelyingPartyBehavior>(); }
+ set { this[BehaviorsElementName] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the type to use for storing application state.
/// </summary>
[ConfigurationProperty(StoreConfigName)]
diff --git a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs
index 7f7dd98..d10d9bd 100644
--- a/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs
+++ b/src/DotNetOpenAuth/Configuration/OpenIdRelyingPartySecuritySettingsElement.cs
@@ -36,6 +36,16 @@ namespace DotNetOpenAuth.Configuration {
private const string RequireSslConfigName = "requireSsl";
/// <summary>
+ /// Gets the name of the @requireDirectedIdentity attribute.
+ /// </summary>
+ private const string RequireDirectedIdentityConfigName = "requireDirectedIdentity";
+
+ /// <summary>
+ /// Gets the name of the @requireAssociation attribute.
+ /// </summary>
+ private const string RequireAssociationConfigName = "requireAssociation";
+
+ /// <summary>
/// Gets the name of the @rejectUnsolicitedAssertions attribute.
/// </summary>
private const string RejectUnsolicitedAssertionsConfigName = "rejectUnsolicitedAssertions";
@@ -46,6 +56,11 @@ namespace DotNetOpenAuth.Configuration {
private const string RejectDelegatingIdentifiersConfigName = "rejectDelegatingIdentifiers";
/// <summary>
+ /// Gets the name of the @ignoreUnsignedExtensions attribute.
+ /// </summary>
+ private const string IgnoreUnsignedExtensionsConfigName = "ignoreUnsignedExtensions";
+
+ /// <summary>
/// Gets the name of the @privateSecretMaximumAge attribute.
/// </summary>
private const string PrivateSecretMaximumAgeConfigName = "privateSecretMaximumAge";
@@ -66,6 +81,26 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets a value indicating whether only OP Identifiers will be discoverable
+ /// when creating authentication requests.
+ /// </summary>
+ [ConfigurationProperty(RequireDirectedIdentityConfigName, DefaultValue = false)]
+ public bool RequireDirectedIdentity {
+ get { return (bool)this[RequireDirectedIdentityConfigName]; }
+ set { this[RequireDirectedIdentityConfigName] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether authentication requests
+ /// will only be created where an association with the Provider can be established.
+ /// </summary>
+ [ConfigurationProperty(RequireAssociationConfigName, DefaultValue = false)]
+ public bool RequireAssociation {
+ get { return (bool)this[RequireAssociationConfigName]; }
+ set { this[RequireAssociationConfigName] = value; }
+ }
+
+ /// <summary>
/// Gets or sets the minimum OpenID version a Provider is required to support in order for this library to interoperate with it.
/// </summary>
/// <remarks>
@@ -134,6 +169,20 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets a value indicating whether unsigned extensions in authentication responses should be ignored.
+ /// </summary>
+ /// <value>The default value is <c>false</c>.</value>
+ /// <remarks>
+ /// When set to true, the <see cref="IAuthenticationResponse.GetUntrustedExtension"/> methods
+ /// will not return any extension that was not signed by the Provider.
+ /// </remarks>
+ [ConfigurationProperty(IgnoreUnsignedExtensionsConfigName, DefaultValue = false)]
+ public bool IgnoreUnsignedExtensions {
+ get { return (bool)this[IgnoreUnsignedExtensionsConfigName]; }
+ set { this[IgnoreUnsignedExtensionsConfigName] = value; }
+ }
+
+ /// <summary>
/// Initializes a programmatically manipulatable bag of these security settings with the settings from the config file.
/// </summary>
/// <returns>The newly created security settings object.</returns>
@@ -142,12 +191,16 @@ namespace DotNetOpenAuth.Configuration {
RelyingPartySecuritySettings settings = new RelyingPartySecuritySettings();
settings.RequireSsl = this.RequireSsl;
+ settings.RequireDirectedIdentity = this.RequireDirectedIdentity;
+ settings.RequireAssociation = this.RequireAssociation;
settings.MinimumRequiredOpenIdVersion = this.MinimumRequiredOpenIdVersion;
settings.MinimumHashBitLength = this.MinimumHashBitLength;
settings.MaximumHashBitLength = this.MaximumHashBitLength;
settings.PrivateSecretMaximumAge = this.PrivateSecretMaximumAge;
settings.RejectUnsolicitedAssertions = this.RejectUnsolicitedAssertions;
settings.RejectDelegatingIdentifiers = this.RejectDelegatingIdentifiers;
+ settings.IgnoreUnsignedExtensions = this.IgnoreUnsignedExtensions;
+
return settings;
}
}
diff --git a/src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs b/src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs
index db1e02f..8319e30 100644
--- a/src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs
+++ b/src/DotNetOpenAuth/Configuration/TypeConfigurationCollection.cs
@@ -47,8 +47,8 @@ namespace DotNetOpenAuth.Configuration {
internal IEnumerable<T> CreateInstances(bool allowInternals) {
Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
return from element in this.Cast<TypeConfigurationElement<T>>()
- where element.CustomType != null
- select element.CreateInstance(default(T), allowInternals);
+ where !element.IsEmpty
+ select element.CreateInstance(default(T), allowInternals);
}
/// <summary>
@@ -70,7 +70,8 @@ namespace DotNetOpenAuth.Configuration {
/// </returns>
protected override object GetElementKey(ConfigurationElement element) {
Contract.Assume(element != null); // this should be Contract.Requires in base class.
- return ((TypeConfigurationElement<T>)element).TypeName ?? string.Empty;
+ TypeConfigurationElement<T> typedElement = (TypeConfigurationElement<T>)element;
+ return (!string.IsNullOrEmpty(typedElement.TypeName) ? typedElement.TypeName : typedElement.XamlSource) ?? string.Empty;
}
}
}
diff --git a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
index 59e10cd..b19ecc6 100644
--- a/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
+++ b/src/DotNetOpenAuth/Configuration/TypeConfigurationElement.cs
@@ -8,7 +8,10 @@ namespace DotNetOpenAuth.Configuration {
using System;
using System.Configuration;
using System.Diagnostics.Contracts;
+ using System.IO;
using System.Reflection;
+ using System.Web;
+ using System.Windows.Markup;
using DotNetOpenAuth.Messaging;
/// <summary>
@@ -24,6 +27,11 @@ namespace DotNetOpenAuth.Configuration {
private const string CustomTypeConfigName = "type";
/// <summary>
+ /// The name of the attribute whose value is the path to the XAML file to deserialize to obtain the type.
+ /// </summary>
+ private const string XamlReaderSourceConfigName = "xaml";
+
+ /// <summary>
/// Initializes a new instance of the TypeConfigurationElement class.
/// </summary>
public TypeConfigurationElement() {
@@ -41,6 +49,15 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets or sets the path to the XAML file to deserialize to obtain the instance.
+ /// </summary>
+ [ConfigurationProperty(XamlReaderSourceConfigName)]
+ public string XamlSource {
+ get { return (string)this[XamlReaderSourceConfigName]; }
+ set { this[XamlReaderSourceConfigName] = value; }
+ }
+
+ /// <summary>
/// Gets the type described in the .config file.
/// </summary>
public Type CustomType {
@@ -48,6 +65,13 @@ namespace DotNetOpenAuth.Configuration {
}
/// <summary>
+ /// Gets a value indicating whether this type has no meaningful type to instantiate.
+ /// </summary>
+ public bool IsEmpty {
+ get { return this.CustomType == null && string.IsNullOrEmpty(this.XamlSource); }
+ }
+
+ /// <summary>
/// Creates an instance of the type described in the .config file.
/// </summary>
/// <param name="defaultValue">The value to return if no type is given in the .config file.</param>
@@ -75,6 +99,15 @@ namespace DotNetOpenAuth.Configuration {
ErrorUtilities.VerifyArgument((this.CustomType.Attributes & TypeAttributes.Public) != 0, Strings.ConfigurationTypeMustBePublic, this.CustomType.FullName);
}
return (T)Activator.CreateInstance(this.CustomType);
+ } else if (!string.IsNullOrEmpty(this.XamlSource)) {
+ string source = this.XamlSource;
+ if (source.StartsWith("~/", StringComparison.Ordinal)) {
+ ErrorUtilities.VerifyHost(HttpContext.Current != null, Strings.ConfigurationXamlReferenceRequiresHttpContext, this.XamlSource);
+ source = HttpContext.Current.Server.MapPath(source);
+ }
+ using (Stream xamlFile = File.OpenRead(source)) {
+ return (T)XamlReader.Load(xamlFile);
+ }
} else {
return defaultValue;
}
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index 766e60a..ce660e5 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -84,9 +84,10 @@
<CodeContractsRuntimeThrowOnFailure>True</CodeContractsRuntimeThrowOnFailure>
<CodeContractsRuntimeCallSiteRequires>False</CodeContractsRuntimeCallSiteRequires>
</PropertyGroup>
- <PropertyGroup Condition=" '$(Sign)' == 'true' ">
+ <PropertyGroup>
<SignAssembly>true</SignAssembly>
- <AssemblyOriginatorKeyFile>..\official-build-key.pfx</AssemblyOriginatorKeyFile>
+ <DelaySign>true</DelaySign>
+ <AssemblyOriginatorKeyFile>..\official-build-key.pub</AssemblyOriginatorKeyFile>
<DefineConstants>$(DefineConstants);StrongNameSigned</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'CodeAnalysis|AnyCPU' ">
@@ -134,6 +135,9 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\Microsoft.Contracts.dll</HintPath>
</Reference>
+ <Reference Include="PresentationFramework">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
<Reference Include="System" />
<Reference Include="System.configuration" />
<Reference Include="System.Core">
@@ -170,17 +174,26 @@
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
+ <Reference Include="WindowsBase">
+ <RequiredTargetFramework>3.0</RequiredTargetFramework>
+ </Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="ComponentModel\ClaimTypeSuggestions.cs" />
<Compile Include="ComponentModel\ConverterBase.cs" />
<Compile Include="ComponentModel\IssuersSuggestions.cs" />
+ <Compile Include="ComponentModel\IdentifierConverter.cs" />
<Compile Include="ComponentModel\SuggestedStringsConverter.cs" />
<Compile Include="ComponentModel\UriConverter.cs" />
<Compile Include="Configuration\AssociationTypeCollection.cs" />
<Compile Include="Configuration\AssociationTypeElement.cs" />
<Compile Include="Configuration\DotNetOpenAuthSection.cs" />
<Compile Include="Configuration\MessagingElement.cs" />
+ <Compile Include="Configuration\OAuthConsumerElement.cs" />
+ <Compile Include="Configuration\OAuthConsumerSecuritySettingsElement.cs" />
+ <Compile Include="Configuration\OAuthElement.cs" />
+ <Compile Include="Configuration\OAuthServiceProviderElement.cs" />
+ <Compile Include="Configuration\OAuthServiceProviderSecuritySettingsElement.cs" />
<Compile Include="Configuration\OpenIdElement.cs" />
<Compile Include="Configuration\OpenIdProviderElement.cs" />
<Compile Include="Configuration\OpenIdProviderSecuritySettingsElement.cs" />
@@ -212,6 +225,8 @@
<Compile Include="Messaging\CachedDirectWebResponse.cs" />
<Compile Include="Messaging\ChannelContract.cs" />
<Compile Include="Messaging\DirectWebRequestOptions.cs" />
+ <Compile Include="Messaging\EnumerableCache.cs" />
+ <Compile Include="Messaging\HostErrorException.cs" />
<Compile Include="Messaging\IHttpDirectResponse.cs" />
<Compile Include="Messaging\IExtensionMessage.cs" />
<Compile Include="Messaging\IMessage.cs" />
@@ -229,23 +244,31 @@
<Compile Include="Messaging\NetworkDirectWebResponse.cs" />
<Compile Include="Messaging\OutgoingWebResponseActionResult.cs" />
<Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" />
+ <Compile Include="Messaging\Reflection\IMessagePartNullEncoder.cs" />
<Compile Include="Messaging\Reflection\MessageDescriptionCollection.cs" />
<Compile Include="OAuth\ChannelElements\ICombinedOpenIdProviderTokenManager.cs" />
- <Compile Include="OAuth\ChannelElements\IConsumerCertificateProvider.cs" />
+ <Compile Include="OAuth\ChannelElements\IConsumerDescription.cs" />
<Compile Include="OAuth\ChannelElements\IConsumerTokenManager.cs" />
<Compile Include="OAuth\ChannelElements\IOpenIdOAuthTokenManager.cs" />
+ <Compile Include="OAuth\ChannelElements\IServiceProviderAccessToken.cs" />
<Compile Include="OAuth\ChannelElements\IServiceProviderTokenManager.cs" />
<Compile Include="OAuth\ChannelElements\OAuthConsumerMessageFactory.cs" />
<Compile Include="OAuth\ChannelElements\ITokenGenerator.cs" />
<Compile Include="OAuth\ChannelElements\ITokenManager.cs" />
<Compile Include="OAuth\ChannelElements\OAuthHttpMethodBindingElement.cs" />
+ <Compile Include="OAuth\ChannelElements\OAuthIdentity.cs" />
+ <Compile Include="OAuth\ChannelElements\OAuthPrincipal.cs" />
<Compile Include="OAuth\ChannelElements\PlaintextSigningBindingElement.cs" />
<Compile Include="OAuth\ChannelElements\HmacSha1SigningBindingElement.cs" />
+ <Compile Include="OAuth\ChannelElements\IServiceProviderRequestToken.cs" />
<Compile Include="OAuth\ChannelElements\SigningBindingElementBaseContract.cs" />
<Compile Include="OAuth\ChannelElements\SigningBindingElementChain.cs" />
<Compile Include="OAuth\ChannelElements\StandardTokenGenerator.cs" />
<Compile Include="OAuth\ChannelElements\TokenType.cs" />
+ <Compile Include="OAuth\ChannelElements\UriOrOobEncoding.cs" />
+ <Compile Include="OAuth\ChannelElements\TokenHandlingBindingElement.cs" />
<Compile Include="OAuth\ConsumerBase.cs" />
+ <Compile Include="OAuth\ConsumerSecuritySettings.cs" />
<Compile Include="OAuth\DesktopConsumer.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="OAuth\Messages\ITokenSecretContainingMessage.cs" />
@@ -256,11 +279,14 @@
<DesignTime>True</DesignTime>
<DependentUpon>OAuthStrings.resx</DependentUpon>
</Compile>
+ <Compile Include="OAuth\SecuritySettings.cs" />
<Compile Include="OAuth\ServiceProviderDescription.cs" />
<Compile Include="OAuth\Messages\ITokenContainingMessage.cs" />
<Compile Include="OAuth\Messages\SignedMessageBase.cs" />
<Compile Include="Messaging\Bindings\NonceMemoryStore.cs" />
<Compile Include="OAuth\ChannelElements\SigningBindingElementBase.cs" />
+ <Compile Include="OAuth\ServiceProviderSecuritySettings.cs" />
+ <Compile Include="OAuth\VerificationCodeFormat.cs" />
<Compile Include="OAuth\WebConsumer.cs" />
<Compile Include="Messaging\IDirectWebRequestHandler.cs" />
<Compile Include="OAuth\ChannelElements\ITamperResistantOAuthMessage.cs" />
@@ -314,6 +340,13 @@
<Compile Include="OpenId\Association.cs" />
<Compile Include="OpenId\AssociationMemoryStore.cs" />
<Compile Include="OpenId\Associations.cs" />
+ <Compile Include="OpenId\Behaviors\AXFetchAsSregTransform.cs" />
+ <Compile Include="OpenId\Behaviors\BehaviorStrings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>BehaviorStrings.resx</DependentUpon>
+ </Compile>
+ <Compile Include="OpenId\Behaviors\PpidGeneration.cs" />
<Compile Include="OpenId\ChannelElements\BackwardCompatibilityBindingElement.cs" />
<Compile Include="OpenId\ChannelElements\ExtensionsBindingElement.cs" />
<Compile Include="OpenId\ChannelElements\IOpenIdExtensionFactory.cs" />
@@ -331,6 +364,7 @@
<Compile Include="OpenId\Extensions\AliasManager.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\AttributeRequest.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\AttributeValues.cs" />
+ <Compile Include="OpenId\Extensions\AttributeExchange\AXAttributeFormats.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\AXUtilities.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\Constants.cs" />
<Compile Include="OpenId\Extensions\AttributeExchange\FetchRequest.cs" />
@@ -365,6 +399,7 @@
<Compile Include="OpenId\Extensions\UI\UIRequest.cs" />
<Compile Include="OpenId\Identifier.cs" />
<Compile Include="OpenId\IdentifierContract.cs" />
+ <Compile Include="OpenId\Extensions\ExtensionsInteropHelper.cs" />
<Compile Include="OpenId\Interop\AuthenticationResponseShim.cs" />
<Compile Include="OpenId\Interop\ClaimsResponseShim.cs" />
<Compile Include="OpenId\Interop\OpenIdRelyingPartyShim.cs" />
@@ -381,6 +416,7 @@
<Compile Include="OpenId\Messages\SignedResponseRequest.cs" />
<Compile Include="OpenId\NoDiscoveryIdentifier.cs" />
<Compile Include="OpenId\OpenIdUtilities.cs" />
+ <Compile Include="OpenId\Provider\PrivatePersonalIdentifierProviderBase.cs" />
<Compile Include="OpenId\Provider\AnonymousRequest.cs" />
<Compile Include="OpenId\Provider\AnonymousRequestEventArgs.cs" />
<Compile Include="OpenId\Provider\AuthenticationChallengeEventArgs.cs" />
@@ -389,11 +425,13 @@
<Compile Include="OpenId\Provider\HostProcessedRequest.cs" />
<Compile Include="OpenId\Provider\IAnonymousRequest.cs" />
<Compile Include="OpenId\Provider\IAuthenticationRequest.cs" />
+ <Compile Include="OpenId\Provider\IDirectedIdentityIdentifierProvider.cs" />
<Compile Include="OpenId\Provider\IHostProcessedRequest.cs" />
<Compile Include="OpenId\Provider\IdentityEndpoint.cs" />
<Compile Include="OpenId\Provider\IdentityEndpointNormalizationEventArgs.cs" />
<Compile Include="OpenId\Provider\IErrorReporting.cs" />
<Compile Include="OpenId\Provider\IProviderApplicationStore.cs" />
+ <Compile Include="OpenId\Provider\IProviderBehavior.cs" />
<Compile Include="OpenId\Provider\IRequest.cs" />
<Compile Include="OpenId\Provider\ProviderEndpoint.cs" />
<Compile Include="OpenId\Provider\RelyingPartyDiscoveryResult.cs" />
@@ -431,13 +469,17 @@
<Compile Include="OpenId\RelyingParty\AssociationPreference.cs" />
<Compile Include="OpenId\RelyingParty\AuthenticationRequest.cs" />
<Compile Include="OpenId\RelyingParty\AuthenticationRequestMode.cs" />
+ <Compile Include="OpenId\RelyingParty\IRelyingPartyBehavior.cs" />
+ <Compile Include="OpenId\RelyingParty\OpenIdRelyingPartyAjaxControlBase.cs" />
<Compile Include="OpenId\RelyingParty\IXrdsProviderEndpointContract.cs" />
<Compile Include="OpenId\RelyingParty\IAuthenticationRequestContract.cs" />
<Compile Include="OpenId\RelyingParty\NegativeAuthenticationResponse.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdAjaxTextBox.cs" />
+ <Compile Include="OpenId\RelyingParty\OpenIdButton.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdEventArgs.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdLogin.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdMobileTextBox.cs" />
+ <Compile Include="OpenId\RelyingParty\OpenIdRelyingPartyControlBase.cs" />
<Compile Include="OpenId\RelyingParty\OpenIdTextBox.cs" />
<Compile Include="OpenId\RelyingParty\PopupBehavior.cs" />
<Compile Include="OpenId\RelyingParty\PositiveAnonymousResponse.cs" />
@@ -465,6 +507,7 @@
<Compile Include="OpenId\RelyingParty\ServiceEndpoint.cs" />
<Compile Include="OpenId\OpenIdXrdsHelper.cs" />
<Compile Include="OpenId\RelyingParty\StandardRelyingPartyApplicationStore.cs" />
+ <Compile Include="OpenId\RelyingParty\WellKnownProviders.cs" />
<Compile Include="OpenId\SecuritySettings.cs" />
<Compile Include="Messaging\UntrustedWebRequestHandler.cs" />
<Compile Include="OpenId\UriIdentifier.cs" />
@@ -560,6 +603,16 @@
<EmbeddedResource Include="InfoCard\infocard_92x64.png" />
<EmbeddedResource Include="InfoCard\SupportingScript.js" />
</ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="OpenId\RelyingParty\OpenIdRelyingPartyControlBase.js" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="OpenId\Behaviors\BehaviorStrings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>BehaviorStrings.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
+ <EmbeddedResource Include="OpenId\RelyingParty\OpenIdRelyingPartyAjaxControlBase.js" />
+ </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\tools\DotNetOpenAuth.Versioning.targets" />
</Project> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
index 3347ba4..0a7ddbd 100644
--- a/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
+++ b/src/DotNetOpenAuth/Messaging/Bindings/StandardReplayProtectionBindingElement.cs
@@ -29,11 +29,6 @@ namespace DotNetOpenAuth.Messaging.Bindings {
private int nonceLength = 8;
/// <summary>
- /// A random number generator.
- /// </summary>
- private Random generator = new Random();
-
- /// <summary>
/// Initializes a new instance of the <see cref="StandardReplayProtectionBindingElement"/> class.
/// </summary>
/// <param name="nonceStore">The store where nonces will be persisted and checked.</param>
@@ -146,11 +141,7 @@ namespace DotNetOpenAuth.Messaging.Bindings {
/// </summary>
/// <returns>The nonce string.</returns>
private string GenerateUniqueFragment() {
- char[] nonce = new char[this.nonceLength];
- for (int i = 0; i < nonce.Length; i++) {
- nonce[i] = AllowedCharacters[this.generator.Next(AllowedCharacters.Length)];
- }
- return new string(nonce);
+ return MessagingUtilities.GetRandomString(this.nonceLength, AllowedCharacters);
}
}
}
diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs
index 7a7bebc..15e4f76 100644
--- a/src/DotNetOpenAuth/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth/Messaging/Channel.cs
@@ -28,6 +28,12 @@ namespace DotNetOpenAuth.Messaging {
[ContractClass(typeof(ChannelContract))]
public abstract class Channel : IDisposable {
/// <summary>
+ /// The content-type used on HTTP POST requests where the POST entity is a
+ /// URL-encoded series of key=value pairs.
+ /// </summary>
+ protected internal const string HttpFormUrlEncoded = "application/x-www-form-urlencoded";
+
+ /// <summary>
/// The encoding to use when writing out POST entity strings.
/// </summary>
private static readonly Encoding PostEntityEncoding = new UTF8Encoding(false);
@@ -316,10 +322,10 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Gets the protocol message embedded in the given HTTP request, if present.
+ /// Gets the protocol message embedded in the current HTTP request.
/// </summary>
/// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
- /// <returns>The deserialized message.</returns>
+ /// <returns>The deserialized message. Never null.</returns>
/// <remarks>
/// Requires an HttpContext.Current context.
/// </remarks>
@@ -332,11 +338,11 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
- /// Gets the protocol message that may be embedded in the given HTTP request.
+ /// Gets the protocol message embedded in the given HTTP request.
/// </summary>
/// <typeparam name="TRequest">The expected type of the message to be received.</typeparam>
/// <param name="httpRequest">The request to search for an embedded message.</param>
- /// <returns>The deserialized message, if one is found. Null otherwise.</returns>
+ /// <returns>The deserialized message. Never null.</returns>
/// <exception cref="ProtocolException">Thrown if the expected message was not recognized in the response.</exception>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Justification = "This returns and verifies the appropriate message type.")]
public TRequest ReadFromRequest<TRequest>(HttpRequestInfo httpRequest)
@@ -564,13 +570,13 @@ namespace DotNetOpenAuth.Messaging {
protected virtual IDirectedProtocolMessage ReadFromRequestCore(HttpRequestInfo request) {
Contract.Requires<ArgumentNullException>(request != null);
- Logger.Channel.DebugFormat("Incoming HTTP request: {0}", request.Url.AbsoluteUri);
+ Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.UrlBeforeRewriting.AbsoluteUri);
// Search Form data first, and if nothing is there search the QueryString
- Contract.Assume(request.Form != null && request.QueryString != null);
+ Contract.Assume(request.Form != null && request.QueryStringBeforeRewriting != null);
var fields = request.Form.ToDictionary();
if (fields.Count == 0 && request.HttpMethod != "POST") { // OpenID 2.0 section 4.1.2
- fields = request.QueryString.ToDictionary();
+ fields = request.QueryStringBeforeRewriting.ToDictionary();
}
return (IDirectedProtocolMessage)this.Receive(fields, request.GetRecipient());
@@ -848,7 +854,7 @@ namespace DotNetOpenAuth.Messaging {
Contract.Requires<ArgumentNullException>(httpRequest != null);
Contract.Requires<ArgumentNullException>(fields != null);
- httpRequest.ContentType = "application/x-www-form-urlencoded";
+ httpRequest.ContentType = HttpFormUrlEncoded;
// Setting the content-encoding to "utf-8" causes Google to reply
// with a 415 UnsupportedMediaType. But adding it doesn't buy us
diff --git a/src/DotNetOpenAuth/Messaging/EnumerableCache.cs b/src/DotNetOpenAuth/Messaging/EnumerableCache.cs
new file mode 100644
index 0000000..d343410
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/EnumerableCache.cs
@@ -0,0 +1,244 @@
+//-----------------------------------------------------------------------
+// <copyright file="EnumerableCache.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// This code is released under the Microsoft Public License (Ms-PL).
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+
+ /// <summary>
+ /// Extension methods for <see cref="IEnumerable&lt;T&gt;"/> types.
+ /// </summary>
+ public static class EnumerableCacheExtensions {
+ /// <summary>
+ /// Caches the results of enumerating over a given object so that subsequence enumerations
+ /// don't require interacting with the object a second time.
+ /// </summary>
+ /// <typeparam name="T">The type of element found in the enumeration.</typeparam>
+ /// <param name="sequence">The enumerable object.</param>
+ /// <returns>
+ /// Either a new enumerable object that caches enumerated results, or the original, <paramref name="sequence"/>
+ /// object if no caching is necessary to avoid additional CPU work.
+ /// </returns>
+ /// <remarks>
+ /// <para>This is designed for use on the results of generator methods (the ones with <c>yield return</c> in them)
+ /// so that only those elements in the sequence that are needed are ever generated, while not requiring
+ /// regeneration of elements that are enumerated over multiple times.</para>
+ /// <para>This can be a huge performance gain if enumerating multiple times over an expensive generator method.</para>
+ /// <para>Some enumerable types such as collections, lists, and already-cached generators do not require
+ /// any (additional) caching, and this method will simply return those objects rather than caching them
+ /// to avoid double-caching.</para>
+ /// </remarks>
+ public static IEnumerable<T> CacheGeneratedResults<T>(this IEnumerable<T> sequence) {
+ // Don't create a cache for types that don't need it.
+ if (sequence is IList<T> ||
+ sequence is ICollection<T> ||
+ sequence is Array ||
+ sequence is EnumerableCache<T>) {
+ return sequence;
+ }
+
+ return new EnumerableCache<T>(sequence);
+ }
+
+ /// <summary>
+ /// A wrapper for <see cref="IEnumerable&lt;T&gt;"/> types and returns a caching <see cref="IEnumerator&lt;T&gt;"/>
+ /// from its <see cref="IEnumerable&lt;T&gt;.GetEnumerator"/> method.
+ /// </summary>
+ /// <typeparam name="T">The type of element in the sequence.</typeparam>
+ private class EnumerableCache<T> : IEnumerable<T> {
+ /// <summary>
+ /// The results from enumeration of the live object that have been collected thus far.
+ /// </summary>
+ private List<T> cache;
+
+ /// <summary>
+ /// The original generator method or other enumerable object whose contents should only be enumerated once.
+ /// </summary>
+ private IEnumerable<T> generator;
+
+ /// <summary>
+ /// The enumerator we're using over the generator method's results.
+ /// </summary>
+ private IEnumerator<T> generatorEnumerator;
+
+ /// <summary>
+ /// The sync object our caching enumerators use when adding a new live generator method result to the cache.
+ /// </summary>
+ /// <remarks>
+ /// Although individual enumerators are not thread-safe, this <see cref="IEnumerable&lt;T&gt;"/> should be
+ /// thread safe so that multiple enumerators can be created from it and used from different threads.
+ /// </remarks>
+ private object generatorLock = new object();
+
+ /// <summary>
+ /// Initializes a new instance of the EnumerableCache class.
+ /// </summary>
+ /// <param name="generator">The generator.</param>
+ internal EnumerableCache(IEnumerable<T> generator) {
+ if (generator == null) {
+ throw new ArgumentNullException("generator");
+ }
+
+ this.generator = generator;
+ }
+
+ #region IEnumerable<T> Members
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the collection.
+ /// </summary>
+ /// <returns>
+ /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
+ /// </returns>
+ public IEnumerator<T> GetEnumerator() {
+ if (this.generatorEnumerator == null) {
+ this.cache = new List<T>();
+ this.generatorEnumerator = this.generator.GetEnumerator();
+ }
+
+ return new EnumeratorCache(this);
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ /// <summary>
+ /// Returns an enumerator that iterates through a collection.
+ /// </summary>
+ /// <returns>
+ /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
+ /// </returns>
+ System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
+ return this.GetEnumerator();
+ }
+
+ #endregion
+
+ /// <summary>
+ /// An enumerator that uses cached enumeration results whenever they are available,
+ /// and caches whatever results it has to pull from the original <see cref="IEnumerable&lt;T&gt;"/> object.
+ /// </summary>
+ private class EnumeratorCache : IEnumerator<T> {
+ /// <summary>
+ /// The parent enumeration wrapper class that stores the cached results.
+ /// </summary>
+ private EnumerableCache<T> parent;
+
+ /// <summary>
+ /// The position of this enumerator in the cached list.
+ /// </summary>
+ private int cachePosition = -1;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EnumerableCache&lt;T&gt;.EnumeratorCache"/> class.
+ /// </summary>
+ /// <param name="parent">The parent cached enumerable whose GetEnumerator method is calling this constructor.</param>
+ internal EnumeratorCache(EnumerableCache<T> parent) {
+ if (parent == null) {
+ throw new ArgumentNullException("parent");
+ }
+
+ this.parent = parent;
+ }
+
+ #region IEnumerator<T> Members
+
+ /// <summary>
+ /// Gets the element in the collection at the current position of the enumerator.
+ /// </summary>
+ /// <returns>
+ /// The element in the collection at the current position of the enumerator.
+ /// </returns>
+ public T Current {
+ get {
+ if (this.cachePosition < 0 || this.cachePosition >= this.parent.cache.Count) {
+ throw new InvalidOperationException();
+ }
+
+ return this.parent.cache[this.cachePosition];
+ }
+ }
+
+ #endregion
+
+ #region IEnumerator Properties
+
+ /// <summary>
+ /// Gets the element in the collection at the current position of the enumerator.
+ /// </summary>
+ /// <returns>
+ /// The element in the collection at the current position of the enumerator.
+ /// </returns>
+ object System.Collections.IEnumerator.Current {
+ get { return this.Current; }
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ /// <summary>
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// </summary>
+ public void Dispose() {
+ this.Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ #region IEnumerator Methods
+
+ /// <summary>
+ /// Advances the enumerator to the next element of the collection.
+ /// </summary>
+ /// <returns>
+ /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
+ /// </returns>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The collection was modified after the enumerator was created.
+ /// </exception>
+ public bool MoveNext() {
+ this.cachePosition++;
+ if (this.cachePosition >= this.parent.cache.Count) {
+ lock (this.parent.generatorLock) {
+ if (this.parent.generatorEnumerator.MoveNext()) {
+ this.parent.cache.Add(this.parent.generatorEnumerator.Current);
+ } else {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Sets the enumerator to its initial position, which is before the first element in the collection.
+ /// </summary>
+ /// <exception cref="T:System.InvalidOperationException">
+ /// The collection was modified after the enumerator was created.
+ /// </exception>
+ public void Reset() {
+ this.cachePosition = -1;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected virtual void Dispose(bool disposing) {
+ // Nothing to do here.
+ }
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
index 50cd118..88a1243 100644
--- a/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/ErrorUtilities.cs
@@ -35,10 +35,18 @@ namespace DotNetOpenAuth.Messaging {
/// Throws an internal error exception.
/// </summary>
/// <param name="errorMessage">The error message.</param>
+ /// <returns>Nothing. But included here so callers can "throw" this method for C# safety.</returns>
/// <exception cref="InternalErrorException">Always thrown.</exception>
[Pure]
- internal static void ThrowInternal(string errorMessage) {
- VerifyInternal(false, errorMessage);
+ internal static Exception ThrowInternal(string errorMessage) {
+ // Since internal errors are really bad, take this chance to
+ // help the developer find the cause by breaking into the
+ // debugger if one is attached.
+ if (Debugger.IsAttached) {
+ Debugger.Break();
+ }
+
+ throw new InternalErrorException(errorMessage);
}
/// <summary>
@@ -52,14 +60,7 @@ namespace DotNetOpenAuth.Messaging {
Contract.Ensures(condition);
Contract.EnsuresOnThrow<InternalErrorException>(!condition);
if (!condition) {
- // Since internal errors are really bad, take this chance to
- // help the developer find the cause by breaking into the
- // debugger if one is attached.
- if (Debugger.IsAttached) {
- Debugger.Break();
- }
-
- throw new InternalErrorException(errorMessage);
+ ThrowInternal(errorMessage);
}
}
@@ -170,6 +171,24 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Throws a <see cref="HostErrorException"/> if some <paramref name="condition"/> evaluates to false.
+ /// </summary>
+ /// <param name="condition">True to do nothing; false to throw the exception.</param>
+ /// <param name="errorMessage">The error message for the exception.</param>
+ /// <param name="args">The string formatting arguments, if any.</param>
+ /// <exception cref="HostErrorException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
+ [Pure]
+ internal static void VerifyHost(bool condition, string errorMessage, params object[] args) {
+ Contract.Requires(args != null);
+ Contract.Ensures(condition);
+ Contract.EnsuresOnThrow<ProtocolException>(!condition);
+ Contract.Assume(errorMessage != null);
+ if (!condition) {
+ throw new HostErrorException(string.Format(CultureInfo.CurrentCulture, errorMessage, args));
+ }
+ }
+
+ /// <summary>
/// Throws a <see cref="ProtocolException"/> if some <paramref name="condition"/> evaluates to false.
/// </summary>
/// <param name="condition">True to do nothing; false to throw the exception.</param>
@@ -203,7 +222,17 @@ namespace DotNetOpenAuth.Messaging {
Contract.EnsuresOnThrow<ProtocolException>(!condition);
Contract.Assume(message != null);
if (!condition) {
- throw new ProtocolException(string.Format(CultureInfo.CurrentCulture, message, args));
+ var exception = new ProtocolException(string.Format(CultureInfo.CurrentCulture, message, args));
+ if (Logger.Messaging.IsErrorEnabled) {
+ Logger.Messaging.Error(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ "Protocol error: {0}{1}{2}",
+ exception.Message,
+ Environment.NewLine,
+ new StackTrace()));
+ }
+ throw exception;
}
}
diff --git a/src/DotNetOpenAuth/Messaging/HostErrorException.cs b/src/DotNetOpenAuth/Messaging/HostErrorException.cs
new file mode 100644
index 0000000..0ab9e51
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/HostErrorException.cs
@@ -0,0 +1,64 @@
+//-----------------------------------------------------------------------
+// <copyright file="HostErrorException.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// An exception to call out a configuration or runtime failure on the part of the
+ /// (web) application that is hosting this library.
+ /// </summary>
+ /// <remarks>
+ /// <para>This exception is used rather than <see cref="ProtocolException"/> for those errors
+ /// that should never be caught because they indicate a major error in the app itself
+ /// or its configuration.</para>
+ /// <para>It is an internal exception to assist in making it uncatchable.</para>
+ /// </remarks>
+ [Serializable]
+ internal class HostErrorException : Exception {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HostErrorException"/> class.
+ /// </summary>
+ internal HostErrorException() {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HostErrorException"/> class.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ internal HostErrorException(string message)
+ : base(message) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HostErrorException"/> class.
+ /// </summary>
+ /// <param name="message">The message.</param>
+ /// <param name="inner">The inner exception.</param>
+ internal HostErrorException(string message, Exception inner)
+ : base(message, inner) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="HostErrorException"/> class.
+ /// </summary>
+ /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
+ /// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext"/> that contains contextual information about the source or destination.</param>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The <paramref name="info"/> parameter is null.
+ /// </exception>
+ /// <exception cref="T:System.Runtime.Serialization.SerializationException">
+ /// The class name is null or <see cref="P:System.Exception.HResult"/> is zero (0).
+ /// </exception>
+ protected HostErrorException(
+ System.Runtime.Serialization.SerializationInfo info,
+ System.Runtime.Serialization.StreamingContext context)
+ : base(info, context) { }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs b/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs
index 309bad3..cbbe28e 100644
--- a/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs
+++ b/src/DotNetOpenAuth/Messaging/HttpDeliveryMethods.cs
@@ -26,7 +26,7 @@ namespace DotNetOpenAuth.Messaging {
AuthorizationHeaderRequest = 0x1,
/// <summary>
- /// As the HTTP POST request body with a content-type of application/x-www-form-urlencoded.
+ /// As the HTTP POST request body with a content-type of application/x-www-form-urlencoded.
/// </summary>
PostRequest = 0x2,
diff --git a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
index 815de68..34f55e9 100644
--- a/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
+++ b/src/DotNetOpenAuth/Messaging/HttpRequestInfo.cs
@@ -61,7 +61,8 @@ namespace DotNetOpenAuth.Messaging {
Contract.Ensures(this.queryString == request.QueryString);
this.HttpMethod = request.HttpMethod;
- this.Url = GetPublicFacingUrl(request);
+ this.Url = request.Url;
+ this.UrlBeforeRewriting = GetPublicFacingUrl(request);
this.RawUrl = request.RawUrl;
this.Headers = GetHeaderCollection(request.Headers);
this.InputStream = request.InputStream;
@@ -91,6 +92,7 @@ namespace DotNetOpenAuth.Messaging {
this.HttpMethod = httpMethod;
this.Url = requestUrl;
+ this.UrlBeforeRewriting = requestUrl;
this.RawUrl = rawUrl;
this.Headers = headers;
this.InputStream = inputStream;
@@ -105,6 +107,7 @@ namespace DotNetOpenAuth.Messaging {
this.HttpMethod = listenerRequest.HttpMethod;
this.Url = listenerRequest.Url;
+ this.UrlBeforeRewriting = listenerRequest.Url;
this.RawUrl = listenerRequest.RawUrl;
this.Headers = new WebHeaderCollection();
foreach (string key in listenerRequest.Headers) {
@@ -126,6 +129,7 @@ namespace DotNetOpenAuth.Messaging {
this.HttpMethod = request.Method;
this.Headers = request.Headers;
this.Url = requestUri;
+ this.UrlBeforeRewriting = requestUri;
this.RawUrl = MakeUpRawUrlFromUrl(requestUri);
}
@@ -149,6 +153,7 @@ namespace DotNetOpenAuth.Messaging {
this.HttpMethod = request.Method;
this.Url = request.RequestUri;
+ this.UrlBeforeRewriting = request.RequestUri;
this.RawUrl = MakeUpRawUrlFromUrl(request.RequestUri);
this.Headers = GetHeaderCollection(request.Headers);
this.InputStream = null;
@@ -193,29 +198,13 @@ namespace DotNetOpenAuth.Messaging {
internal string RawUrl { get; set; }
/// <summary>
- /// Gets the full URL of a request before rewriting.
+ /// Gets or sets the full public URL used by the remote client to initiate this request,
+ /// before any URL rewriting and before any changes made by web farm load distributors.
/// </summary>
- internal Uri UrlBeforeRewriting {
- get {
- Contract.Ensures(Contract.Result<Uri>() != null || this.Url == null || this.RawUrl == null);
-
- if (this.Url == null || this.RawUrl == null) {
- return null;
- }
-
- // We use Request.Url for the full path to the server, and modify it
- // with Request.RawUrl to capture both the cookieless session "directory" if it exists
- // and the original path in case URL rewriting is going on. We don't want to be
- // fooled by URL rewriting because we're comparing the actual URL with what's in
- // the return_to parameter in some cases.
- // Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless
- // session, but not the URL rewriting problem.
- return new Uri(this.Url, this.RawUrl);
- }
- }
+ internal Uri UrlBeforeRewriting { get; set; }
/// <summary>
- /// Gets the query part of the URL (The ? and everything after it).
+ /// Gets the query part of the URL (The ? and everything after it), after URL rewriting.
/// </summary>
internal string Query {
get { return this.Url != null ? this.Url.Query : null; }
@@ -238,7 +227,7 @@ namespace DotNetOpenAuth.Messaging {
get {
Contract.Ensures(Contract.Result<NameValueCollection>() != null);
if (this.form == null) {
- if (this.HttpMethod == "POST" && this.Headers[HttpRequestHeader.ContentType] == "application/x-www-form-urlencoded") {
+ if (this.HttpMethod == "POST" && this.Headers[HttpRequestHeader.ContentType] == Channel.HttpFormUrlEncoded) {
StreamReader reader = new StreamReader(this.InputStream);
long originalPosition = 0;
if (this.InputStream.CanSeek) {
@@ -302,7 +291,7 @@ namespace DotNetOpenAuth.Messaging {
/// <c>true</c> if this request's URL was rewritten; otherwise, <c>false</c>.
/// </value>
internal bool IsUrlRewritten {
- get { return this.Url.PathAndQuery != this.RawUrl; }
+ get { return this.Url != this.UrlBeforeRewriting; }
}
/// <summary>
@@ -359,7 +348,14 @@ namespace DotNetOpenAuth.Messaging {
return publicRequestUri.Uri;
} else {
// Failover to the method that works for non-web farm enviroments.
- return request.Url;
+ // We use Request.Url for the full path to the server, and modify it
+ // with Request.RawUrl to capture both the cookieless session "directory" if it exists
+ // and the original path in case URL rewriting is going on. We don't want to be
+ // fooled by URL rewriting because we're comparing the actual URL with what's in
+ // the return_to parameter in some cases.
+ // Response.ApplyAppPathModifier(builder.Path) would have worked for the cookieless
+ // session, but not the URL rewriting problem.
+ return new Uri(request.Url, request.RawUrl);
}
}
diff --git a/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs b/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs
index 82910d5..22c660c 100644
--- a/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagePartAttribute.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.Messaging {
using System;
+ using System.Diagnostics;
using System.Net.Security;
using System.Reflection;
@@ -13,6 +14,7 @@ namespace DotNetOpenAuth.Messaging {
/// Applied to fields and properties that form a key/value in a protocol message.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = true, AllowMultiple = true)]
+ [DebuggerDisplay("MessagePartAttribute {Name}")]
public sealed class MessagePartAttribute : Attribute {
/// <summary>
/// The overridden name to use as the serialized name for the property.
diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
index c8c4817..27cc97f 100644
--- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
+++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs
@@ -31,6 +31,32 @@ namespace DotNetOpenAuth.Messaging {
internal static readonly RandomNumberGenerator CryptoRandomDataGenerator = new RNGCryptoServiceProvider();
/// <summary>
+ /// A pseudo-random data generator (NOT cryptographically strong random data)
+ /// </summary>
+ internal static readonly Random NonCryptoRandomDataGenerator = new Random();
+
+ /// <summary>
+ /// The uppercase alphabet.
+ /// </summary>
+ internal const string UppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ /// <summary>
+ /// The lowercase alphabet.
+ /// </summary>
+ internal const string LowercaseLetters = "abcdefghijklmnopqrstuvwxyz";
+
+ /// <summary>
+ /// The set of base 10 digits.
+ /// </summary>
+ internal const string Digits = "0123456789";
+
+ /// <summary>
+ /// The set of digits, and alphabetic letters (upper and lowercase) that are clearly
+ /// visually distinguishable.
+ /// </summary>
+ internal const string AlphaNumericNoLookAlikes = "23456789abcdefghjkmnpqrstwxyzABCDEFGHJKMNPQRSTWXYZ";
+
+ /// <summary>
/// The set of characters that are unreserved in RFC 2396 but are NOT unreserved in RFC 3986.
/// </summary>
private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
@@ -139,6 +165,17 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Gets a buffer of random data (not cryptographically strong).
+ /// </summary>
+ /// <param name="length">The length of the sequence to generate.</param>
+ /// <returns>The generated values, which may contain zeros.</returns>
+ internal static byte[] GetNonCryptoRandomData(int length) {
+ byte[] buffer = new byte[length];
+ NonCryptoRandomDataGenerator.NextBytes(buffer);
+ return buffer;
+ }
+
+ /// <summary>
/// Gets a cryptographically strong random sequence of values.
/// </summary>
/// <param name="length">The length of the sequence to generate.</param>
@@ -162,6 +199,24 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Gets a random string made up of a given set of allowable characters.
+ /// </summary>
+ /// <param name="length">The length of the desired random string.</param>
+ /// <param name="allowableCharacters">The allowable characters.</param>
+ /// <returns>A random string.</returns>
+ internal static string GetRandomString(int length, string allowableCharacters) {
+ Contract.Requires(length >= 0);
+ Contract.Requires(allowableCharacters != null && allowableCharacters.Length >= 2);
+
+ char[] randomString = new char[length];
+ for (int i = 0; i < length; i++) {
+ randomString[i] = allowableCharacters[NonCryptoRandomDataGenerator.Next(allowableCharacters.Length)];
+ }
+
+ return new string(randomString);
+ }
+
+ /// <summary>
/// Adds a set of HTTP headers to an <see cref="HttpResponse"/> instance,
/// taking care to set some headers to the appropriate properties of
/// <see cref="HttpResponse" />
@@ -532,7 +587,7 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="request">The request to get recipient information from.</param>
/// <returns>The recipient.</returns>
internal static MessageReceivingEndpoint GetRecipient(this HttpRequestInfo request) {
- return new MessageReceivingEndpoint(request.Url, request.HttpMethod == "GET" ? HttpDeliveryMethods.GetRequest : HttpDeliveryMethods.PostRequest);
+ return new MessageReceivingEndpoint(request.UrlBeforeRewriting, request.HttpMethod == "GET" ? HttpDeliveryMethods.GetRequest : HttpDeliveryMethods.PostRequest);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartNullEncoder.cs b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartNullEncoder.cs
new file mode 100644
index 0000000..7581550
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/Reflection/IMessagePartNullEncoder.cs
@@ -0,0 +1,18 @@
+//-----------------------------------------------------------------------
+// <copyright file="IMessagePartNullEncoder.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Reflection {
+ /// <summary>
+ /// A message part encoder that has a special encoding for a null value.
+ /// </summary>
+ public interface IMessagePartNullEncoder : IMessagePartEncoder {
+ /// <summary>
+ /// Gets the string representation to include in a serialized message
+ /// when the message part has a <c>null</c> value.
+ /// </summary>
+ string EncodedNullValue { get; }
+ }
+}
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
index 32409bc..84a4327 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.Messaging.Reflection {
using System;
using System.Collections.Generic;
+ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
@@ -19,6 +20,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// Describes an individual member of a message and assists in its serialization.
/// </summary>
[ContractVerification(true)]
+ [DebuggerDisplay("MessagePart {Name}")]
internal class MessagePart {
/// <summary>
/// A map of converters that help serialize custom objects to string values and back again.
@@ -130,10 +132,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null);
}
} else {
- var encoder = GetEncoder(attribute.Encoder);
- this.converter = new ValueMapping(
- obj => encoder.Encode(obj),
- str => encoder.Decode(str));
+ this.converter = new ValueMapping(GetEncoder(attribute.Encoder));
}
// readonly and const fields are considered legal, and "constants" for message transport.
@@ -301,7 +300,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// An instance of the appropriate type for setting the member.
/// </returns>
private object ToValue(string value) {
- return value == null ? null : this.converter.StringToValue(value);
+ return this.converter.StringToValue(value);
}
/// <summary>
@@ -312,7 +311,7 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// The string representation of the member's value.
/// </returns>
private string ToString(object value) {
- return value == null ? null : this.converter.ValueToString(value);
+ return this.converter.ValueToString(value);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
index 4d23c95..6510e8b 100644
--- a/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
+++ b/src/DotNetOpenAuth/Messaging/Reflection/ValueMapping.cs
@@ -16,12 +16,12 @@ namespace DotNetOpenAuth.Messaging.Reflection {
/// <summary>
/// The mapping function that converts some custom type to a string.
/// </summary>
- internal Func<object, string> ValueToString;
+ internal readonly Func<object, string> ValueToString;
/// <summary>
/// The mapping function that converts a string to some custom type.
/// </summary>
- internal Func<string, object> StringToValue;
+ internal readonly Func<string, object> StringToValue;
/// <summary>
/// Initializes a new instance of the <see cref="ValueMapping"/> struct.
@@ -35,5 +35,18 @@ namespace DotNetOpenAuth.Messaging.Reflection {
this.ValueToString = toString;
this.StringToValue = toValue;
}
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ValueMapping"/> struct.
+ /// </summary>
+ /// <param name="encoder">The encoder.</param>
+ internal ValueMapping(IMessagePartEncoder encoder) {
+ ErrorUtilities.VerifyArgumentNotNull(encoder, "encoder");
+ var nullEncoder = encoder as IMessagePartNullEncoder;
+ string nullString = nullEncoder != null ? nullEncoder.EncodedNullValue : null;
+
+ this.ValueToString = obj => (obj != null) ? encoder.Encode(obj) : nullString;
+ this.StringToValue = str => (str != null) ? encoder.Decode(str) : null;
+ }
}
}
diff --git a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
index bafda84..9969558 100644
--- a/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
+++ b/src/DotNetOpenAuth/Messaging/StandardWebRequestHandler.cs
@@ -136,6 +136,7 @@ namespace DotNetOpenAuth.Messaging {
// We don't want to blindly set all ServicePoints to not use the Expect header
// as that would be a security hole allowing any visitor to a web site change
// the web site's global behavior when calling that host.
+ Logger.Http.InfoFormat("HTTP POST to {0} resulted in 417 Expectation Failed. Changing ServicePoint to not use Expect: Continue next time.", request.RequestUri);
request.ServicePoint.Expect100Continue = false; // TODO: investigate that CAS may throw here
// An alternative to ServicePoint if we don't have permission to set that,
@@ -175,6 +176,31 @@ namespace DotNetOpenAuth.Messaging {
#endregion
/// <summary>
+ /// Determines whether an exception was thrown because of the remote HTTP server returning HTTP 417 Expectation Failed.
+ /// </summary>
+ /// <param name="ex">The caught exception.</param>
+ /// <returns>
+ /// <c>true</c> if the failure was originally caused by a 417 Exceptation Failed error; otherwise, <c>false</c>.
+ /// </returns>
+ internal static bool IsExceptionFrom417ExpectationFailed(Exception ex) {
+ while (ex != null) {
+ WebException webEx = ex as WebException;
+ if (webEx != null) {
+ HttpWebResponse response = webEx.Response as HttpWebResponse;
+ if (response != null) {
+ if (response.StatusCode == HttpStatusCode.ExpectationFailed) {
+ return true;
+ }
+ }
+ }
+
+ ex = ex.InnerException;
+ }
+
+ return false;
+ }
+
+ /// <summary>
/// Initiates a POST request and prepares for sending data.
/// </summary>
/// <param name="request">The HTTP request with information about the remote party to contact.</param>
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerCertificateProvider.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerCertificateProvider.cs
deleted file mode 100644
index 7e6ae54..0000000
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerCertificateProvider.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-//-----------------------------------------------------------------------
-// <copyright file="IConsumerCertificateProvider.cs" company="Andrew Arnott">
-// Copyright (c) Andrew Arnott. All rights reserved.
-// </copyright>
-//-----------------------------------------------------------------------
-
-namespace DotNetOpenAuth.OAuth.ChannelElements {
- using System.Security.Cryptography.X509Certificates;
-
- /// <summary>
- /// A provider that hosts can implement to hook up their RSA-SHA1 binding elements
- /// to their list of known Consumers' certificates.
- /// </summary>
- public interface IConsumerCertificateProvider {
- /// <summary>
- /// Gets the certificate that can be used to verify the signature of an incoming
- /// message from a Consumer.
- /// </summary>
- /// <param name="consumerMessage">The incoming message from some Consumer.</param>
- /// <returns>The public key from the Consumer's X.509 Certificate, if one can be found; otherwise <c>null</c>.</returns>
- X509Certificate2 GetCertificate(ITamperResistantOAuthMessage consumerMessage);
- }
-}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerDescription.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerDescription.cs
new file mode 100644
index 0000000..db505d5
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerDescription.cs
@@ -0,0 +1,59 @@
+//-----------------------------------------------------------------------
+// <copyright file="IConsumerDescription.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Security.Cryptography.X509Certificates;
+
+ /// <summary>
+ /// A description of a consumer from a Service Provider's point of view.
+ /// </summary>
+ public interface IConsumerDescription {
+ /// <summary>
+ /// Gets the Consumer key.
+ /// </summary>
+ string Key { get; }
+
+ /// <summary>
+ /// Gets the consumer secret.
+ /// </summary>
+ string Secret { get; }
+
+ /// <summary>
+ /// Gets the certificate that can be used to verify the signature of an incoming
+ /// message from a Consumer.
+ /// </summary>
+ /// <returns>The public key from the Consumer's X.509 Certificate, if one can be found; otherwise <c>null</c>.</returns>
+ /// <remarks>
+ /// This property must be implemented only if the RSA-SHA1 algorithm is supported by the Service Provider.
+ /// </remarks>
+ X509Certificate2 Certificate { get; }
+
+ /// <summary>
+ /// Gets the callback URI that this consumer has pre-registered with the service provider, if any.
+ /// </summary>
+ /// <value>A URI that user authorization responses should be directed to; or <c>null</c> if no preregistered callback was arranged.</value>
+ Uri Callback { get; }
+
+ /// <summary>
+ /// Gets the verification code format that is most appropriate for this consumer
+ /// when a callback URI is not available.
+ /// </summary>
+ /// <value>A set of characters that can be easily keyed in by the user given the Consumer's
+ /// application type and form factor.</value>
+ /// <remarks>
+ /// The value <see cref="OAuth.VerificationCodeFormat.IncludedInCallback"/> should NEVER be returned
+ /// since this property is only used in no callback scenarios anyway.
+ /// </remarks>
+ VerificationCodeFormat VerificationCodeFormat { get; }
+
+ /// <summary>
+ /// Gets the length of the verification code to issue for this Consumer.
+ /// </summary>
+ /// <value>A positive number, generally at least 4.</value>
+ int VerificationCodeLength { get; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderAccessToken.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderAccessToken.cs
new file mode 100644
index 0000000..329ac4d
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderAccessToken.cs
@@ -0,0 +1,45 @@
+//-----------------------------------------------------------------------
+// <copyright file="IServiceProviderAccessToken.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// A description of an access token and its metadata as required by a Service Provider.
+ /// </summary>
+ public interface IServiceProviderAccessToken {
+ /// <summary>
+ /// Gets the token itself.
+ /// </summary>
+ string Token { get; }
+
+ /// <summary>
+ /// Gets the expiration date (local time) for the access token.
+ /// </summary>
+ /// <value>The expiration date, or <c>null</c> if there is no expiration date.</value>
+ DateTime? ExpirationDate { get; }
+
+ /// <summary>
+ /// Gets the username of the principal that will be impersonated by this access token.
+ /// </summary>
+ /// <value>
+ /// The name of the user who authorized the OAuth request token originally.
+ /// </value>
+ string Username { get; }
+
+ /// <summary>
+ /// Gets the roles that the OAuth principal should belong to.
+ /// </summary>
+ /// <value>
+ /// The roles that the user belongs to, or a subset of these according to the rights
+ /// granted when the user authorized the request token.
+ /// </value>
+ string[] Roles { get; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs
new file mode 100644
index 0000000..6dfa416
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs
@@ -0,0 +1,52 @@
+//-----------------------------------------------------------------------
+// <copyright file="IServiceProviderRequestToken.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using DotNetOpenAuth.OAuth.Messages;
+
+ /// <summary>
+ /// A description of a request token and its metadata as required by a Service Provider
+ /// </summary>
+ public interface IServiceProviderRequestToken {
+ /// <summary>
+ /// Gets the token itself.
+ /// </summary>
+ string Token { get; }
+
+ /// <summary>
+ /// Gets the consumer key that requested this token.
+ /// </summary>
+ string ConsumerKey { get; }
+
+ /// <summary>
+ /// Gets the (local) date that this request token was first created on.
+ /// </summary>
+ DateTime CreatedOn { get; }
+
+ /// <summary>
+ /// Gets or sets the callback associated specifically with this token, if any.
+ /// </summary>
+ /// <value>The callback URI; or <c>null</c> if no callback was specifically assigned to this token.</value>
+ Uri Callback { get; set; }
+
+ /// <summary>
+ /// Gets or sets the verifier that the consumer must include in the <see cref="AuthorizedTokenRequest"/>
+ /// message to exchange this request token for an access token.
+ /// </summary>
+ /// <value>The verifier code, or <c>null</c> if none has been assigned (yet).</value>
+ string VerificationCode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the version of the Consumer that requested this token.
+ /// </summary>
+ /// <remarks>
+ /// This property is used to determine whether a <see cref="VerificationCode"/> must be
+ /// generated when the user authorizes the Consumer or not.
+ /// </remarks>
+ Version ConsumerVersion { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs
index e1c1e3f..02ebffb 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs
@@ -16,12 +16,39 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
public interface IServiceProviderTokenManager : ITokenManager {
/// <summary>
- /// Gets the Consumer Secret for a given a Consumer Key.
+ /// Gets the Consumer description for a given a Consumer Key.
/// </summary>
/// <param name="consumerKey">The Consumer Key.</param>
- /// <returns>The Consumer Secret.</returns>
- /// <exception cref="ArgumentException">Thrown if the consumer key cannot be found.</exception>
- /// <exception cref="InvalidOperationException">May be thrown if called when the signature algorithm does not require a consumer secret, such as when RSA-SHA1 is used.</exception>
- string GetConsumerSecret(string consumerKey);
+ /// <returns>A description of the consumer. Never null.</returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the consumer key cannot be found.</exception>
+ IConsumerDescription GetConsumer(string consumerKey);
+
+ /// <summary>
+ /// Gets details on the named request token.
+ /// </summary>
+ /// <param name="token">The request token.</param>
+ /// <returns>A description of the token. Never null.</returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the token cannot be found.</exception>
+ /// <remarks>
+ /// It is acceptable for implementations to find the token, see that it has expired,
+ /// delete it from the database and then throw <see cref="KeyNotFoundException"/>,
+ /// or alternatively it can return the expired token anyway and the OAuth channel will
+ /// log and throw the appropriate error.
+ /// </remarks>
+ IServiceProviderRequestToken GetRequestToken(string token);
+
+ /// <summary>
+ /// Gets details on the named access token.
+ /// </summary>
+ /// <param name="token">The access token.</param>
+ /// <returns>A description of the token. Never null.</returns>
+ /// <exception cref="KeyNotFoundException">Thrown if the token cannot be found.</exception>
+ /// <remarks>
+ /// It is acceptable for implementations to find the token, see that it has expired,
+ /// delete it from the database and then throw <see cref="KeyNotFoundException"/>,
+ /// or alternatively it can return the expired token anyway and the OAuth channel will
+ /// log and throw the appropriate error.
+ /// </remarks>
+ IServiceProviderAccessToken GetAccessToken(string token);
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
index cd34f6f..a36287f 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs
@@ -63,7 +63,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <see cref="OAuthConsumerMessageFactory"/> or <see cref="OAuthServiceProviderMessageFactory"/>.
/// </param>
internal OAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager, IMessageFactory messageTypeProvider)
- : base(messageTypeProvider, new OAuthHttpMethodBindingElement(), signingBindingElement, new StandardExpirationBindingElement(), new StandardReplayProtectionBindingElement(store)) {
+ : base(messageTypeProvider, InitializeBindingElements(signingBindingElement, store, tokenManager)) {
Contract.Requires<ArgumentNullException>(tokenManager != null);
this.TokenManager = tokenManager;
@@ -120,7 +120,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
string authorization = request.Headers[HttpRequestHeader.Authorization];
if (authorization != null) {
string[] authorizationSections = authorization.Split(';'); // TODO: is this the right delimiter?
- string oauthPrefix = Protocol.Default.AuthorizationHeaderScheme + " ";
+ string oauthPrefix = Protocol.AuthorizationHeaderScheme + " ";
// The Authorization header may have multiple uses, and OAuth may be just one of them.
// Go through each one looking for an OAuth one.
@@ -140,8 +140,10 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
}
// Scrape the entity
- foreach (string key in request.Form) {
- fields.Add(key, request.Form[key]);
+ if (string.Equals(request.Headers[HttpRequestHeader.ContentType], HttpFormUrlEncoded, StringComparison.Ordinal)) {
+ foreach (string key in request.Form) {
+ fields.Add(key, request.Form[key]);
+ }
}
// Scrape the query string
@@ -155,7 +157,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
// Add receiving HTTP transport information required for signature generation.
var signedMessage = message as ITamperResistantOAuthMessage;
if (signedMessage != null) {
- signedMessage.Recipient = request.Url;
+ signedMessage.Recipient = request.UrlBeforeRewriting;
signedMessage.HttpMethod = request.HttpMethod;
}
@@ -230,6 +232,29 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
}
/// <summary>
+ /// Initializes the binding elements for the OAuth channel.
+ /// </summary>
+ /// <param name="signingBindingElement">The signing binding element.</param>
+ /// <param name="store">The nonce store.</param>
+ /// <param name="tokenManager">The token manager.</param>
+ /// <returns>An array of binding elements used to initialize the channel.</returns>
+ private static IChannelBindingElement[] InitializeBindingElements(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager) {
+ var bindingElements = new List<IChannelBindingElement> {
+ new OAuthHttpMethodBindingElement(),
+ signingBindingElement,
+ new StandardExpirationBindingElement(),
+ new StandardReplayProtectionBindingElement(store),
+ };
+
+ var spTokenManager = tokenManager as IServiceProviderTokenManager;
+ if (spTokenManager != null) {
+ bindingElements.Insert(0, new TokenHandlingBindingElement(spTokenManager));
+ }
+
+ return bindingElements.ToArray();
+ }
+
+ /// <summary>
/// Uri-escapes the names and values in a dictionary per OAuth 1.0 section 5.1.
/// </summary>
/// <param name="source">The dictionary with names and values to encode.</param>
@@ -295,7 +320,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
httpRequest.Method = GetHttpMethod(requestMessage);
StringBuilder authorization = new StringBuilder();
- authorization.Append(protocol.AuthorizationHeaderScheme);
+ authorization.Append(Protocol.AuthorizationHeaderScheme);
authorization.Append(" ");
foreach (var pair in fields) {
string key = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Key);
@@ -354,7 +379,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
ErrorUtilities.VerifyInternal(consumerKey == consumerTokenManager.ConsumerKey, "The token manager consumer key and the consumer key set earlier do not match!");
return consumerTokenManager.ConsumerSecret;
} else {
- return ((IServiceProviderTokenManager)this.TokenManager).GetConsumerSecret(consumerKey);
+ return ((IServiceProviderTokenManager)this.TokenManager).GetConsumer(consumerKey).Secret;
}
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs
index 5d76afc..327b923 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs
@@ -42,7 +42,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
MessageBase message = null;
if (fields.ContainsKey("oauth_token")) {
- message = new UserAuthorizationResponse(recipient.Location);
+ Protocol protocol = fields.ContainsKey("oauth_verifier") ? Protocol.V10a : Protocol.V10;
+ message = new UserAuthorizationResponse(recipient.Location, protocol.Version);
}
if (message != null) {
@@ -87,7 +88,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
var unauthorizedTokenRequest = request as UnauthorizedTokenRequest;
var authorizedTokenRequest = request as AuthorizedTokenRequest;
if (unauthorizedTokenRequest != null) {
- message = new UnauthorizedTokenResponse(unauthorizedTokenRequest);
+ Protocol protocol = fields.ContainsKey("oauth_callback_confirmed") ? Protocol.V10a : Protocol.V10;
+ message = new UnauthorizedTokenResponse(unauthorizedTokenRequest, protocol.Version);
} else if (authorizedTokenRequest != null) {
message = new AuthorizedTokenResponse(authorizedTokenRequest);
} else {
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthIdentity.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthIdentity.cs
new file mode 100644
index 0000000..0de2c15
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthIdentity.cs
@@ -0,0 +1,63 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthIdentity.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Diagnostics.Contracts;
+ using System.Runtime.InteropServices;
+ using System.Security.Principal;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// Represents an OAuth consumer that is impersonating a known user on the system.
+ /// </summary>
+ [Serializable]
+ [ComVisible(true)]
+ public class OAuthIdentity : IIdentity {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthIdentity"/> class.
+ /// </summary>
+ /// <param name="username">The username.</param>
+ internal OAuthIdentity(string username) {
+ Contract.Requires(!String.IsNullOrEmpty(username));
+ ErrorUtilities.VerifyNonZeroLength(username, "username");
+ this.Name = username;
+ }
+
+ #region IIdentity Members
+
+ /// <summary>
+ /// Gets the type of authentication used.
+ /// </summary>
+ /// <value>The constant "OAuth"</value>
+ /// <returns>
+ /// The type of authentication used to identify the user.
+ /// </returns>
+ public string AuthenticationType {
+ get { return "OAuth"; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the user has been authenticated.
+ /// </summary>
+ /// <value>The value <c>true</c></value>
+ /// <returns>true if the user was authenticated; otherwise, false.
+ /// </returns>
+ public bool IsAuthenticated {
+ get { return true; }
+ }
+
+ /// <summary>
+ /// Gets the name of the user who authorized the OAuth token the consumer is using for authorization.
+ /// </summary>
+ /// <returns>
+ /// The name of the user on whose behalf the code is running.
+ /// </returns>
+ public string Name { get; private set; }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs
new file mode 100644
index 0000000..689c388
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs
@@ -0,0 +1,89 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthPrincipal.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Runtime.InteropServices;
+ using System.Security.Principal;
+
+ /// <summary>
+ /// Represents an OAuth consumer that is impersonating a known user on the system.
+ /// </summary>
+ [Serializable]
+ [ComVisible(true)]
+ public class OAuthPrincipal : IPrincipal {
+ /// <summary>
+ /// The roles this user belongs to.
+ /// </summary>
+ private ICollection<string> roles;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class.
+ /// </summary>
+ /// <param name="token">The access token.</param>
+ internal OAuthPrincipal(IServiceProviderAccessToken token)
+ : this(token.Username, token.Roles) {
+ Contract.Requires(token != null);
+
+ this.AccessToken = token.Token;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class.
+ /// </summary>
+ /// <param name="identity">The identity.</param>
+ /// <param name="roles">The roles this user belongs to.</param>
+ internal OAuthPrincipal(OAuthIdentity identity, string[] roles) {
+ this.Identity = identity;
+ this.roles = roles;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class.
+ /// </summary>
+ /// <param name="username">The username.</param>
+ /// <param name="roles">The roles this user belongs to.</param>
+ internal OAuthPrincipal(string username, string[] roles)
+ : this(new OAuthIdentity(username), roles) {
+ }
+
+ /// <summary>
+ /// Gets the access token used to create this principal.
+ /// </summary>
+ /// <value>A non-empty string.</value>
+ public string AccessToken { get; private set; }
+
+ #region IPrincipal Members
+
+ /// <summary>
+ /// Gets the identity of the current principal.
+ /// </summary>
+ /// <value></value>
+ /// <returns>
+ /// The <see cref="T:System.Security.Principal.IIdentity"/> object associated with the current principal.
+ /// </returns>
+ public IIdentity Identity { get; private set; }
+
+ /// <summary>
+ /// Determines whether the current principal belongs to the specified role.
+ /// </summary>
+ /// <param name="role">The name of the role for which to check membership.</param>
+ /// <returns>
+ /// true if the current principal is a member of the specified role; otherwise, false.
+ /// </returns>
+ /// <remarks>
+ /// The role membership check uses <see cref="StringComparer.OrdinalIgnoreCase"/>.
+ /// </remarks>
+ public bool IsInRole(string role) {
+ return this.roles.Contains(role, StringComparer.OrdinalIgnoreCase);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs
index 767a0b9..429615a 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs
@@ -52,29 +52,51 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </remarks>
public virtual IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) {
MessageBase message = null;
+ Protocol protocol = Protocol.V10; // default to assuming the less-secure 1.0 instead of 1.0a until we prove otherwise.
+ string token;
+ fields.TryGetValue("oauth_token", out token);
- if (fields.ContainsKey("oauth_consumer_key") &&
- !fields.ContainsKey("oauth_token")) {
- message = new UnauthorizedTokenRequest(recipient);
- } else if (fields.ContainsKey("oauth_consumer_key") &&
- fields.ContainsKey("oauth_token")) {
- // Discern between RequestAccessToken and AccessProtectedResources,
- // which have all the same parameters, by figuring out what type of token
- // is in the token parameter.
- bool tokenTypeIsAccessToken = this.tokenManager.GetTokenType(fields["oauth_token"]) == TokenType.AccessToken;
+ try {
+ if (fields.ContainsKey("oauth_consumer_key") && !fields.ContainsKey("oauth_token")) {
+ protocol = fields.ContainsKey("oauth_callback") ? Protocol.V10a : Protocol.V10;
+ message = new UnauthorizedTokenRequest(recipient, protocol.Version);
+ } else if (fields.ContainsKey("oauth_consumer_key") && fields.ContainsKey("oauth_token")) {
+ // Discern between RequestAccessToken and AccessProtectedResources,
+ // which have all the same parameters, by figuring out what type of token
+ // is in the token parameter.
+ bool tokenTypeIsAccessToken = this.tokenManager.GetTokenType(token) == TokenType.AccessToken;
- message = tokenTypeIsAccessToken ? (MessageBase)new AccessProtectedResourceRequest(recipient) :
- new AuthorizedTokenRequest(recipient);
- } else {
- // fail over to the message with no required fields at all.
- message = new UserAuthorizationRequest(recipient);
- }
+ if (tokenTypeIsAccessToken) {
+ message = (MessageBase)new AccessProtectedResourceRequest(recipient, protocol.Version);
+ } else {
+ // Discern between 1.0 and 1.0a requests by checking on the consumer version we stored
+ // when the consumer first requested an unauthorized token.
+ protocol = Protocol.Lookup(this.tokenManager.GetRequestToken(token).ConsumerVersion);
+ message = new AuthorizedTokenRequest(recipient, protocol.Version);
+ }
+ } else {
+ // fail over to the message with no required fields at all.
+ if (token != null) {
+ protocol = Protocol.Lookup(this.tokenManager.GetRequestToken(token).ConsumerVersion);
+ }
- if (message != null) {
- message.SetAsIncoming();
- }
+ // If a callback parameter is included, that suggests either the consumer
+ // is following OAuth 1.0 instead of 1.0a, or that a hijacker is trying
+ // to attack. Either way, if the consumer started out as a 1.0a, keep it
+ // that way, and we'll just ignore the oauth_callback included in this message
+ // by virtue of the UserAuthorizationRequest message not including it in its
+ // 1.0a payload.
+ message = new UserAuthorizationRequest(recipient, protocol.Version);
+ }
- return message;
+ if (message != null) {
+ message.SetAsIncoming();
+ }
+
+ return message;
+ } catch (KeyNotFoundException ex) {
+ throw ErrorUtilities.Wrap(ex, OAuthStrings.TokenNotFound);
+ }
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs
index 779f2c5..4f8b5e5 100644
--- a/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs
@@ -6,6 +6,7 @@
namespace DotNetOpenAuth.OAuth.ChannelElements {
using System;
+ using System.Diagnostics.Contracts;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
@@ -16,15 +17,24 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
public class RsaSha1SigningBindingElement : SigningBindingElementBase {
/// <summary>
+ /// The name of the hash algorithm to use.
+ /// </summary>
+ private const string HashAlgorithmName = "RSA-SHA1";
+
+ /// <summary>
+ /// The token manager for the service provider.
+ /// </summary>
+ private IServiceProviderTokenManager tokenManager;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="RsaSha1SigningBindingElement"/> class
/// for use by Consumers.
/// </summary>
/// <param name="signingCertificate">The certificate used to sign outgoing messages.</param>
public RsaSha1SigningBindingElement(X509Certificate2 signingCertificate)
- : this() {
- if (signingCertificate == null) {
- throw new ArgumentNullException("signingCertificate");
- }
+ : base(HashAlgorithmName) {
+ Contract.Requires(signingCertificate != null);
+ ErrorUtilities.VerifyArgumentNotNull(signingCertificate, "signingCertificate");
this.SigningCertificate = signingCertificate;
}
@@ -33,21 +43,21 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// Initializes a new instance of the <see cref="RsaSha1SigningBindingElement"/> class
/// for use by Service Providers.
/// </summary>
- public RsaSha1SigningBindingElement()
- : base("RSA-SHA1") {
+ /// <param name="tokenManager">The token manager.</param>
+ public RsaSha1SigningBindingElement(IServiceProviderTokenManager tokenManager)
+ : base(HashAlgorithmName) {
+ Contract.Requires(tokenManager != null);
+ ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager");
+
+ this.tokenManager = tokenManager;
}
/// <summary>
- /// Gets or sets the certificate used to sign outgoing messages.
+ /// Gets or sets the certificate used to sign outgoing messages. Used only by Consumers.
/// </summary>
public X509Certificate2 SigningCertificate { get; set; }
/// <summary>
- /// Gets or sets the consumer certificate provider.
- /// </summary>
- public IConsumerCertificateProvider ConsumerCertificateProvider { get; set; }
-
- /// <summary>
/// Calculates a signature for a given message.
/// </summary>
/// <param name="message">The message to sign.</param>
@@ -56,13 +66,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// This method signs the message per OAuth 1.0 section 9.3.
/// </remarks>
protected override string GetSignature(ITamperResistantOAuthMessage message) {
- if (message == null) {
- throw new ArgumentNullException("message");
- }
-
- if (this.SigningCertificate == null) {
- throw new InvalidOperationException(OAuthStrings.X509CertificateNotProvidedForSigning);
- }
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+ ErrorUtilities.VerifyOperation(this.SigningCertificate != null, OAuthStrings.X509CertificateNotProvidedForSigning);
string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message));
byte[] data = Encoding.ASCII.GetBytes(signatureBaseString);
@@ -80,16 +85,14 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// <c>true</c> if the signature on the message is valid; otherwise, <c>false</c>.
/// </returns>
protected override bool IsSignatureValid(ITamperResistantOAuthMessage message) {
- if (this.ConsumerCertificateProvider == null) {
- throw new InvalidOperationException(OAuthStrings.ConsumerCertificateProviderNotAvailable);
- }
+ ErrorUtilities.VerifyInternal(this.tokenManager != null, "No token manager available for fetching Consumer public certificates.");
string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message));
byte[] data = Encoding.ASCII.GetBytes(signatureBaseString);
byte[] carriedSignature = Convert.FromBase64String(message.Signature);
- X509Certificate2 cert = this.ConsumerCertificateProvider.GetCertificate(message);
+ X509Certificate2 cert = this.tokenManager.GetConsumer(message.ConsumerKey).Certificate;
if (cert == null) {
Logger.Signatures.WarnFormat("Incoming message from consumer '{0}' could not be matched with an appropriate X.509 certificate for signature verification.", message.ConsumerKey);
return false;
@@ -105,10 +108,11 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
/// </summary>
/// <returns>A new instance of the binding element.</returns>
protected override ITamperProtectionChannelBindingElement Clone() {
- return new RsaSha1SigningBindingElement() {
- ConsumerCertificateProvider = this.ConsumerCertificateProvider,
- SigningCertificate = this.SigningCertificate,
- };
+ if (this.tokenManager != null) {
+ return new RsaSha1SigningBindingElement(this.tokenManager);
+ } else {
+ return new RsaSha1SigningBindingElement(this.SigningCertificate);
+ }
}
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs
new file mode 100644
index 0000000..ce7bb98
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs
@@ -0,0 +1,190 @@
+//-----------------------------------------------------------------------
+// <copyright file="TokenHandlingBindingElement.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth.Messages;
+
+ /// <summary>
+ /// A binding element for Service Providers to manage the
+ /// callbacks and verification codes on applicable messages.
+ /// </summary>
+ internal class TokenHandlingBindingElement : IChannelBindingElement {
+ /// <summary>
+ /// The token manager offered by the service provider.
+ /// </summary>
+ private IServiceProviderTokenManager tokenManager;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TokenHandlingBindingElement"/> class.
+ /// </summary>
+ /// <param name="tokenManager">The token manager.</param>
+ internal TokenHandlingBindingElement(IServiceProviderTokenManager tokenManager) {
+ Contract.Requires(tokenManager != null);
+ ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager");
+
+ this.tokenManager = tokenManager;
+ }
+
+ #region IChannelBindingElement Members
+
+ /// <summary>
+ /// Gets or sets the channel that this binding element belongs to.
+ /// </summary>
+ /// <remarks>
+ /// This property is set by the channel when it is first constructed.
+ /// </remarks>
+ public Channel Channel { get; set; }
+
+ /// <summary>
+ /// Gets the protection commonly offered (if any) by this binding element.
+ /// </summary>
+ /// <remarks>
+ /// This value is used to assist in sorting binding elements in the channel stack.
+ /// </remarks>
+ public MessageProtections Protection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Prepares a message for sending based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The message to prepare for sending.</param>
+ /// <returns>
+ /// The protections (if any) that this binding element applied to the message.
+ /// Null if this binding element did not even apply to this binding element.
+ /// </returns>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+
+ var userAuthResponse = message as UserAuthorizationResponse;
+ if (userAuthResponse != null && userAuthResponse.Version >= Protocol.V10a.Version) {
+ this.tokenManager.GetRequestToken(userAuthResponse.RequestToken).VerificationCode = userAuthResponse.VerificationCode;
+ return MessageProtections.None;
+ }
+
+ // Hook to store the token and secret on its way down to the Consumer.
+ var grantRequestTokenResponse = message as UnauthorizedTokenResponse;
+ if (grantRequestTokenResponse != null) {
+ this.tokenManager.StoreNewRequestToken(grantRequestTokenResponse.RequestMessage, grantRequestTokenResponse);
+ this.tokenManager.GetRequestToken(grantRequestTokenResponse.RequestToken).ConsumerVersion = grantRequestTokenResponse.Version;
+ if (grantRequestTokenResponse.RequestMessage.Callback != null) {
+ this.tokenManager.GetRequestToken(grantRequestTokenResponse.RequestToken).Callback = grantRequestTokenResponse.RequestMessage.Callback;
+ }
+
+ return MessageProtections.None;
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Performs any transformation on an incoming message that may be necessary and/or
+ /// validates an incoming message based on the rules of this channel binding element.
+ /// </summary>
+ /// <param name="message">The incoming message to process.</param>
+ /// <returns>
+ /// The protections (if any) that this binding element applied to the message.
+ /// Null if this binding element did not even apply to this binding element.
+ /// </returns>
+ /// <exception cref="ProtocolException">
+ /// Thrown when the binding element rules indicate that this message is invalid and should
+ /// NOT be processed.
+ /// </exception>
+ /// <remarks>
+ /// Implementations that provide message protection must honor the
+ /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable.
+ /// </remarks>
+ public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+
+ var authorizedTokenRequest = message as AuthorizedTokenRequest;
+ if (authorizedTokenRequest != null) {
+ if (authorizedTokenRequest.Version >= Protocol.V10a.Version) {
+ string expectedVerifier = this.tokenManager.GetRequestToken(authorizedTokenRequest.RequestToken).VerificationCode;
+ ErrorUtilities.VerifyProtocol(string.Equals(authorizedTokenRequest.VerificationCode, expectedVerifier, StringComparison.Ordinal), OAuthStrings.IncorrectVerifier);
+ return MessageProtections.None;
+ }
+
+ this.VerifyThrowTokenTimeToLive(authorizedTokenRequest);
+ }
+
+ var userAuthorizationRequest = message as UserAuthorizationRequest;
+ if (userAuthorizationRequest != null) {
+ this.VerifyThrowTokenTimeToLive(userAuthorizationRequest);
+ }
+
+ var accessResourceRequest = message as AccessProtectedResourceRequest;
+ if (accessResourceRequest != null) {
+ this.VerifyThrowTokenNotExpired(accessResourceRequest);
+ }
+
+ return null;
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Ensures that access tokens have not yet expired.
+ /// </summary>
+ /// <param name="message">The incoming message carrying the access token.</param>
+ private void VerifyThrowTokenNotExpired(AccessProtectedResourceRequest message) {
+ ErrorUtilities.VerifyArgumentNotNull(message, "message");
+
+ try {
+ IServiceProviderAccessToken token = this.tokenManager.GetAccessToken(message.AccessToken);
+ if (token.ExpirationDate.HasValue && DateTime.Now >= token.ExpirationDate.Value.ToLocalTime()) {
+ Logger.OAuth.ErrorFormat(
+ "OAuth access token {0} rejected because it expired at {1}, and it is now {2}.",
+ token.Token,
+ token.ExpirationDate.Value,
+ DateTime.Now);
+ ErrorUtilities.ThrowProtocol(OAuthStrings.TokenNotFound);
+ }
+ } catch (KeyNotFoundException ex) {
+ throw ErrorUtilities.Wrap(ex, OAuthStrings.TokenNotFound);
+ }
+ }
+
+ /// <summary>
+ /// Ensures that short-lived request tokens included in incoming messages have not expired.
+ /// </summary>
+ /// <param name="message">The incoming message.</param>
+ /// <exception cref="ProtocolException">Thrown when the token in the message has expired.</exception>
+ private void VerifyThrowTokenTimeToLive(ITokenContainingMessage message) {
+ ErrorUtilities.VerifyInternal(!(message is AccessProtectedResourceRequest), "We shouldn't be verifying TTL on access tokens.");
+ if (message == null) {
+ return;
+ }
+
+ try {
+ IServiceProviderRequestToken token = this.tokenManager.GetRequestToken(message.Token);
+ TimeSpan ttl = DotNetOpenAuthSection.Configuration.OAuth.ServiceProvider.SecuritySettings.MaximumRequestTokenTimeToLive;
+ if (DateTime.Now >= token.CreatedOn.ToLocalTime() + ttl) {
+ Logger.OAuth.ErrorFormat(
+ "OAuth request token {0} rejected because it was originally issued at {1}, expired at {2}, and it is now {3}.",
+ token.Token,
+ token.CreatedOn,
+ token.CreatedOn + ttl,
+ DateTime.Now);
+ ErrorUtilities.ThrowProtocol(OAuthStrings.TokenNotFound);
+ }
+ } catch (KeyNotFoundException ex) {
+ throw ErrorUtilities.Wrap(ex, OAuthStrings.TokenNotFound);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs
new file mode 100644
index 0000000..5aedc9d
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoding.cs
@@ -0,0 +1,78 @@
+//-----------------------------------------------------------------------
+// <copyright file="UriOrOobEncoding.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
+
+ /// <summary>
+ /// An URI encoder that translates null <see cref="Uri"/> references as "oob"
+ /// instead of an empty/missing argument.
+ /// </summary>
+ internal class UriOrOobEncoding : IMessagePartNullEncoder {
+ /// <summary>
+ /// The string constant "oob", used to indicate an out-of-band configuration.
+ /// </summary>
+ private const string OutOfBandConfiguration = "oob";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UriOrOobEncoding"/> class.
+ /// </summary>
+ public UriOrOobEncoding() {
+ }
+
+ #region IMessagePartNullEncoder Members
+
+ /// <summary>
+ /// Gets the string representation to include in a serialized message
+ /// when the message part has a <c>null</c> value.
+ /// </summary>
+ /// <value></value>
+ public string EncodedNullValue {
+ get { return OutOfBandConfiguration; }
+ }
+
+ #endregion
+
+ #region IMessagePartEncoder Members
+
+ /// <summary>
+ /// Encodes the specified value.
+ /// </summary>
+ /// <param name="value">The value. Guaranteed to never be null.</param>
+ /// <returns>
+ /// The <paramref name="value"/> in string form, ready for message transport.
+ /// </returns>
+ public string Encode(object value) {
+ ErrorUtilities.VerifyArgumentNotNull(value, "value");
+
+ Uri uriValue = (Uri)value;
+ return uriValue.AbsoluteUri;
+ }
+
+ /// <summary>
+ /// Decodes the specified value.
+ /// </summary>
+ /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param>
+ /// <returns>
+ /// The deserialized form of the given string.
+ /// </returns>
+ /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception>
+ public object Decode(string value) {
+ if (string.Equals(value, OutOfBandConfiguration, StringComparison.Ordinal)) {
+ return null;
+ } else {
+ return new Uri(value, UriKind.Absolute);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
index 32d7f58..6b9b628 100644
--- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
+++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs
@@ -10,6 +10,7 @@ namespace DotNetOpenAuth.OAuth {
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Net;
+ using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OAuth.ChannelElements;
@@ -32,6 +33,7 @@ namespace DotNetOpenAuth.OAuth {
INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge);
this.OAuthChannel = new OAuthChannel(signingElement, store, tokenManager);
this.ServiceProvider = serviceDescription;
+ this.SecuritySettings = DotNetOpenAuthSection.Configuration.OAuth.Consumer.SecuritySettings.CreateSecuritySettings();
}
/// <summary>
@@ -61,6 +63,11 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
+ /// Gets the security settings for this consumer.
+ /// </summary>
+ internal ConsumerSecuritySettings SecuritySettings { get; private set; }
+
+ /// <summary>
/// Gets or sets the channel to use for sending/receiving messages.
/// </summary>
internal OAuthChannel OAuthChannel { get; set; }
@@ -159,7 +166,7 @@ namespace DotNetOpenAuth.OAuth {
Contract.Requires<ArgumentNullException>(endpoint != null);
Contract.Requires<ArgumentNullException>(!String.IsNullOrEmpty(accessToken));
- AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint) {
+ AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint, this.ServiceProvider.Version) {
AccessToken = accessToken,
ConsumerKey = this.ConsumerKey,
};
@@ -181,18 +188,26 @@ namespace DotNetOpenAuth.OAuth {
/// <returns>The pending user agent redirect based message to be sent as an HttpResponse.</returns>
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Justification = "Two results")]
protected internal UserAuthorizationRequest PrepareRequestUserAuthorization(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, out string requestToken) {
- // Obtain an unauthorized request token.
- var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint) {
+ // Obtain an unauthorized request token. Assume the OAuth version given in the service description.
+ var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint, this.ServiceProvider.Version) {
ConsumerKey = this.ConsumerKey,
+ Callback = callback,
};
var tokenAccessor = this.Channel.MessageDescriptions.GetAccessor(token);
tokenAccessor.AddExtraParameters(requestParameters);
var requestTokenResponse = this.Channel.Request<UnauthorizedTokenResponse>(token);
this.TokenManager.StoreNewRequestToken(token, requestTokenResponse);
- // Request user authorization.
+ // Fine-tune our understanding of the SP's supported OAuth version if it's wrong.
+ if (this.ServiceProvider.Version != requestTokenResponse.Version) {
+ Logger.OAuth.WarnFormat("Expected OAuth service provider at endpoint {0} to use OAuth {1} but {2} was detected. Adjusting service description to new version.", this.ServiceProvider.RequestTokenEndpoint, this.ServiceProvider.Version, requestTokenResponse.Version);
+ this.ServiceProvider.ProtocolVersion = Protocol.Lookup(requestTokenResponse.Version).ProtocolVersion;
+ }
+
+ // Request user authorization. The OAuth version will automatically include
+ // or drop the callback that we're setting here.
ITokenContainingMessage assignedRequestToken = requestTokenResponse;
- var requestAuthorization = new UserAuthorizationRequest(this.ServiceProvider.UserAuthorizationEndpoint, assignedRequestToken.Token) {
+ var requestAuthorization = new UserAuthorizationRequest(this.ServiceProvider.UserAuthorizationEndpoint, assignedRequestToken.Token, requestTokenResponse.Version) {
Callback = callback,
};
var requestAuthorizationAccessor = this.Channel.MessageDescriptions.GetAccessor(requestAuthorization);
@@ -205,13 +220,17 @@ namespace DotNetOpenAuth.OAuth {
/// Exchanges a given request token for access token.
/// </summary>
/// <param name="requestToken">The request token that the user has authorized.</param>
- /// <returns>The access token assigned by the Service Provider.</returns>
- protected AuthorizedTokenResponse ProcessUserAuthorization(string requestToken) {
- Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(requestToken));
+ /// <param name="verifier">The verifier code.</param>
+ /// <returns>
+ /// The access token assigned by the Service Provider.
+ /// </returns>
+ protected AuthorizedTokenResponse ProcessUserAuthorization(string requestToken, string verifier) {
+ Contract.Requires(!String.IsNullOrEmpty(requestToken));
Contract.Ensures(Contract.Result<AuthorizedTokenResponse>() != null);
- var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint) {
+ var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, this.ServiceProvider.Version) {
RequestToken = requestToken,
+ VerificationCode = verifier,
ConsumerKey = this.ConsumerKey,
};
var grantAccess = this.Channel.Request<AuthorizedTokenResponse>(requestAccess);
diff --git a/src/DotNetOpenAuth/OAuth/ConsumerSecuritySettings.cs b/src/DotNetOpenAuth/OAuth/ConsumerSecuritySettings.cs
new file mode 100644
index 0000000..bb2fbaa
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/ConsumerSecuritySettings.cs
@@ -0,0 +1,18 @@
+//-----------------------------------------------------------------------
+// <copyright file="ConsumerSecuritySettings.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth {
+ /// <summary>
+ /// Security settings that are applicable to consumers.
+ /// </summary>
+ internal class ConsumerSecuritySettings : SecuritySettings {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ConsumerSecuritySettings"/> class.
+ /// </summary>
+ internal ConsumerSecuritySettings() {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/DesktopConsumer.cs b/src/DotNetOpenAuth/OAuth/DesktopConsumer.cs
index ca74a77..f9c1a94 100644
--- a/src/DotNetOpenAuth/OAuth/DesktopConsumer.cs
+++ b/src/DotNetOpenAuth/OAuth/DesktopConsumer.cs
@@ -49,8 +49,25 @@ namespace DotNetOpenAuth.OAuth {
/// </summary>
/// <param name="requestToken">The request token that the user has authorized.</param>
/// <returns>The access token assigned by the Service Provider.</returns>
- public new AuthorizedTokenResponse ProcessUserAuthorization(string requestToken) {
- return base.ProcessUserAuthorization(requestToken);
+ [Obsolete("Use the ProcessUserAuthorization method that takes a verifier parameter instead.")]
+ public AuthorizedTokenResponse ProcessUserAuthorization(string requestToken) {
+ return this.ProcessUserAuthorization(requestToken, null);
+ }
+
+ /// <summary>
+ /// Exchanges a given request token for access token.
+ /// </summary>
+ /// <param name="requestToken">The request token that the user has authorized.</param>
+ /// <param name="verifier">The verifier code typed in by the user. Must not be <c>Null</c> for OAuth 1.0a service providers and later.</param>
+ /// <returns>
+ /// The access token assigned by the Service Provider.
+ /// </returns>
+ public new AuthorizedTokenResponse ProcessUserAuthorization(string requestToken, string verifier) {
+ if (this.ServiceProvider.Version >= Protocol.V10a.Version) {
+ ErrorUtilities.VerifyNonZeroLength(verifier, "verifier");
+ }
+
+ return base.ProcessUserAuthorization(requestToken, verifier);
}
}
}
diff --git a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs
index 62e02de..b60fda4 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs
@@ -5,6 +5,7 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OAuth.Messages {
+ using System;
using System.Diagnostics.CodeAnalysis;
using DotNetOpenAuth.Messaging;
@@ -17,8 +18,9 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// Initializes a new instance of the <see cref="AccessProtectedResourceRequest"/> class.
/// </summary>
/// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param>
- protected internal AccessProtectedResourceRequest(MessageReceivingEndpoint serviceProvider)
- : base(MessageTransport.Direct, serviceProvider) {
+ /// <param name="version">The OAuth version.</param>
+ protected internal AccessProtectedResourceRequest(MessageReceivingEndpoint serviceProvider, Version version)
+ : base(MessageTransport.Direct, serviceProvider, version) {
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs
index 2d4793c..1228290 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs
@@ -5,6 +5,7 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OAuth.Messages {
+ using System;
using System.Globalization;
using DotNetOpenAuth.Messaging;
@@ -20,8 +21,9 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// Initializes a new instance of the <see cref="AuthorizedTokenRequest"/> class.
/// </summary>
/// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param>
- internal AuthorizedTokenRequest(MessageReceivingEndpoint serviceProvider)
- : base(MessageTransport.Direct, serviceProvider) {
+ /// <param name="version">The OAuth version.</param>
+ internal AuthorizedTokenRequest(MessageReceivingEndpoint serviceProvider, Version version)
+ : base(MessageTransport.Direct, serviceProvider, version) {
}
/// <summary>
@@ -33,6 +35,13 @@ namespace DotNetOpenAuth.OAuth.Messages {
}
/// <summary>
+ /// Gets or sets the verification code received by the Consumer from the Service Provider
+ /// in the <see cref="UserAuthorizationResponse.VerificationCode"/> property.
+ /// </summary>
+ [MessagePart("oauth_verifier", IsRequired = true, AllowEmpty = false, MinVersion = Protocol.V10aVersion)]
+ public string VerificationCode { get; set; }
+
+ /// <summary>
/// Gets or sets the unauthorized Request Token used to obtain authorization.
/// </summary>
[MessagePart("oauth_token", IsRequired = true)]
diff --git a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs
index 14413a5..0b14819 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs
@@ -5,6 +5,7 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OAuth.Messages {
+ using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using DotNetOpenAuth.Messaging;
@@ -19,7 +20,7 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// </summary>
/// <param name="originatingRequest">The originating request.</param>
protected internal AuthorizedTokenResponse(AuthorizedTokenRequest originatingRequest)
- : base(MessageProtections.None, originatingRequest) {
+ : base(MessageProtections.None, originatingRequest, originatingRequest.Version) {
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
index d5af466..07c630d 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs
@@ -62,12 +62,15 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// </summary>
/// <param name="protectionRequired">The level of protection the message requires.</param>
/// <param name="originatingRequest">The request that asked for this direct response.</param>
- protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest) {
- Contract.Requires<ArgumentNullException>(originatingRequest != null);
+ /// <param name="version">The OAuth version.</param>
+ protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest, Version version) {
+ ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest");
+ ErrorUtilities.VerifyArgumentNotNull(version, "version");
this.protectionRequired = protectionRequired;
this.transport = MessageTransport.Direct;
this.originatingRequest = originatingRequest;
+ this.Version = version;
}
/// <summary>
@@ -76,14 +79,15 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// <param name="protectionRequired">The level of protection the message requires.</param>
/// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param>
/// <param name="recipient">The URI that a directed message will be delivered to.</param>
- protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient) {
- if (recipient == null) {
- throw new ArgumentNullException("recipient");
- }
+ /// <param name="version">The OAuth version.</param>
+ protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient, Version version) {
+ ErrorUtilities.VerifyArgumentNotNull(recipient, "recipient");
+ ErrorUtilities.VerifyArgumentNotNull(version, "version");
this.protectionRequired = protectionRequired;
this.transport = transport;
this.recipient = recipient;
+ this.Version = version;
}
#region IProtocolMessage Properties
@@ -163,9 +167,7 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// <summary>
/// Gets the version of the protocol this message is prepared to implement.
/// </summary>
- protected virtual Version Version {
- get { return new Version(1, 0); }
- }
+ protected internal Version Version { get; private set; }
/// <summary>
/// Gets the level of protection this message requires.
diff --git a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
index d1abb58..1d8ca21 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs
@@ -31,8 +31,9 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// </summary>
/// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param>
/// <param name="recipient">The URI that a directed message will be delivered to.</param>
- internal SignedMessageBase(MessageTransport transport, MessageReceivingEndpoint recipient)
- : base(MessageProtections.All, transport, recipient) {
+ /// <param name="version">The OAuth version.</param>
+ internal SignedMessageBase(MessageTransport transport, MessageReceivingEndpoint recipient, Version version)
+ : base(MessageProtections.All, transport, recipient, version) {
ITamperResistantOAuthMessage self = (ITamperResistantOAuthMessage)this;
HttpDeliveryMethods methods = ((IDirectedProtocolMessage)this).HttpMethods;
self.HttpMethod = (methods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET";
@@ -164,7 +165,7 @@ namespace DotNetOpenAuth.OAuth.Messages {
[MessagePart("oauth_version", IsRequired = false)]
private string OAuthVersion {
get {
- return Version.ToString();
+ return Protocol.Lookup(Version).PublishedVersion;
}
set {
diff --git a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs
index e491bad..9214d91 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs
@@ -5,8 +5,10 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OAuth.Messages {
+ using System;
using System.Collections.Generic;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth.ChannelElements;
/// <summary>
/// A direct message sent from Consumer to Service Provider to request a Request Token.
@@ -16,11 +18,23 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// Initializes a new instance of the <see cref="UnauthorizedTokenRequest"/> class.
/// </summary>
/// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param>
- protected internal UnauthorizedTokenRequest(MessageReceivingEndpoint serviceProvider)
- : base(MessageTransport.Direct, serviceProvider) {
+ /// <param name="version">The OAuth version.</param>
+ protected internal UnauthorizedTokenRequest(MessageReceivingEndpoint serviceProvider, Version version)
+ : base(MessageTransport.Direct, serviceProvider, version) {
}
/// <summary>
+ /// Gets or sets the absolute URL to which the Service Provider will redirect the
+ /// User back when the Obtaining User Authorization step is completed.
+ /// </summary>
+ /// <value>
+ /// The callback URL; or <c>null</c> if the Consumer is unable to receive
+ /// callbacks or a callback URL has been established via other means.
+ /// </value>
+ [MessagePart("oauth_callback", IsRequired = true, AllowEmpty = false, MinVersion = Protocol.V10aVersion, Encoder = typeof(UriOrOobEncoding))]
+ public Uri Callback { get; set; }
+
+ /// <summary>
/// Gets the extra, non-OAuth parameters that will be included in the message.
/// </summary>
public new IDictionary<string, string> ExtraData {
diff --git a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs
index 0a12706..5d34617 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs
@@ -26,7 +26,7 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// This constructor is used by the Service Provider to send the message.
/// </remarks>
protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest requestMessage, string requestToken, string tokenSecret)
- : this(requestMessage) {
+ : this(requestMessage, requestMessage.Version) {
Contract.Requires<ArgumentNullException>(requestToken != null);
Contract.Requires<ArgumentNullException>(tokenSecret != null);
@@ -38,9 +38,10 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// Initializes a new instance of the <see cref="UnauthorizedTokenResponse"/> class.
/// </summary>
/// <param name="originatingRequest">The originating request.</param>
+ /// <param name="version">The OAuth version.</param>
/// <remarks>This constructor is used by the consumer to deserialize the message.</remarks>
- protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest originatingRequest)
- : base(MessageProtections.None, originatingRequest) {
+ protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest originatingRequest, Version version)
+ : base(MessageProtections.None, originatingRequest, version) {
}
/// <summary>
@@ -85,5 +86,13 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// </summary>
[MessagePart("oauth_token_secret", IsRequired = true)]
protected internal string TokenSecret { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether the Service Provider recognized the callback parameter in the request.
+ /// </summary>
+ [MessagePart("oauth_callback_confirmed", IsRequired = true, MinVersion = Protocol.V10aVersion)]
+ private bool CallbackConfirmed {
+ get { return true; }
+ }
}
}
diff --git a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs
index f1af0bc..099729e 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs
@@ -21,8 +21,9 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// </summary>
/// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param>
/// <param name="requestToken">The request token.</param>
- internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider, string requestToken)
- : this(serviceProvider) {
+ /// <param name="version">The OAuth version.</param>
+ internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider, string requestToken, Version version)
+ : this(serviceProvider, version) {
this.RequestToken = requestToken;
}
@@ -30,8 +31,9 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// Initializes a new instance of the <see cref="UserAuthorizationRequest"/> class.
/// </summary>
/// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param>
- internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider)
- : base(MessageProtections.None, MessageTransport.Indirect, serviceProvider) {
+ /// <param name="version">The OAuth version.</param>
+ internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider, Version version)
+ : base(MessageProtections.None, MessageTransport.Indirect, serviceProvider, version) {
}
/// <summary>
@@ -51,6 +53,14 @@ namespace DotNetOpenAuth.OAuth.Messages {
}
/// <summary>
+ /// Gets a value indicating whether this is a safe OAuth authorization request.
+ /// </summary>
+ /// <value><c>true</c> if the Consumer is using OAuth 1.0a or later; otherwise, <c>false</c>.</value>
+ public bool IsUnsafeRequest {
+ get { return this.Version < Protocol.V10a.Version; }
+ }
+
+ /// <summary>
/// Gets or sets the Request Token obtained in the previous step.
/// </summary>
/// <remarks>
@@ -65,7 +75,7 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// Gets or sets a URL the Service Provider will use to redirect the User back
/// to the Consumer when Obtaining User Authorization is complete. Optional.
/// </summary>
- [MessagePart("oauth_callback", IsRequired = false)]
+ [MessagePart("oauth_callback", IsRequired = false, MaxVersion = "1.0")]
internal Uri Callback { get; set; }
}
}
diff --git a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs
index da6a909..69a327c 100644
--- a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs
+++ b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs
@@ -19,8 +19,9 @@ namespace DotNetOpenAuth.OAuth.Messages {
/// Initializes a new instance of the <see cref="UserAuthorizationResponse"/> class.
/// </summary>
/// <param name="consumer">The URI of the Consumer endpoint to send this message to.</param>
- internal UserAuthorizationResponse(Uri consumer)
- : base(MessageProtections.None, MessageTransport.Indirect, new MessageReceivingEndpoint(consumer, HttpDeliveryMethods.GetRequest)) {
+ /// <param name="version">The OAuth version.</param>
+ internal UserAuthorizationResponse(Uri consumer, Version version)
+ : base(MessageProtections.None, MessageTransport.Indirect, new MessageReceivingEndpoint(consumer, HttpDeliveryMethods.GetRequest), version) {
}
/// <summary>
@@ -32,6 +33,20 @@ namespace DotNetOpenAuth.OAuth.Messages {
}
/// <summary>
+ /// Gets or sets the verification code that must accompany the request to exchange the
+ /// authorized request token for an access token.
+ /// </summary>
+ /// <value>An unguessable value passed to the Consumer via the User and REQUIRED to complete the process.</value>
+ /// <remarks>
+ /// If the Consumer did not provide a callback URL, the Service Provider SHOULD display the value of the
+ /// verification code, and instruct the User to manually inform the Consumer that authorization is
+ /// completed. If the Service Provider knows a Consumer to be running on a mobile device or set-top box,
+ /// the Service Provider SHOULD ensure that the verifier value is suitable for manual entry.
+ /// </remarks>
+ [MessagePart("oauth_verifier", IsRequired = true, AllowEmpty = false, MinVersion = Protocol.V10aVersion)]
+ public string VerificationCode { get; set; }
+
+ /// <summary>
/// Gets or sets the Request Token.
/// </summary>
[MessagePart("oauth_token", IsRequired = true)]
diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
index 6eec124..3593446 100644
--- a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
+++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs
@@ -79,20 +79,20 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
- /// Looks up a localized string similar to The RSA-SHA1 signing binding element&apos;s consumer certificate provider has not been set, so no incoming messages from consumers using this signature method can be verified..
+ /// Looks up a localized string similar to Failure looking up secret for consumer or token..
/// </summary>
- internal static string ConsumerCertificateProviderNotAvailable {
+ internal static string ConsumerOrTokenSecretNotFound {
get {
- return ResourceManager.GetString("ConsumerCertificateProviderNotAvailable", resourceCulture);
+ return ResourceManager.GetString("ConsumerOrTokenSecretNotFound", resourceCulture);
}
}
/// <summary>
- /// Looks up a localized string similar to Failure looking up secret for consumer or token..
+ /// Looks up a localized string similar to oauth_verifier argument was incorrect..
/// </summary>
- internal static string ConsumerOrTokenSecretNotFound {
+ internal static string IncorrectVerifier {
get {
- return ResourceManager.GetString("ConsumerOrTokenSecretNotFound", resourceCulture);
+ return ResourceManager.GetString("IncorrectVerifier", resourceCulture);
}
}
@@ -133,6 +133,15 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
+ /// Looks up a localized string similar to This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}..
+ /// </summary>
+ internal static string MinimumConsumerVersionRequirementNotMet {
+ get {
+ return ResourceManager.GetString("MinimumConsumerVersionRequirementNotMet", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The request URL query MUST NOT contain any OAuth Protocol Parameters..
/// </summary>
internal static string RequestUrlMustNotHaveOAuthParameters {
@@ -160,6 +169,15 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
+ /// Looks up a localized string similar to A token in the message was not recognized by the service provider..
+ /// </summary>
+ internal static string TokenNotFound {
+ get {
+ return ResourceManager.GetString("TokenNotFound", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The RSA-SHA1 signing binding element has not been set with a certificate for signing..
/// </summary>
internal static string X509CertificateNotProvidedForSigning {
diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
index 0aa48f9..bbeeda9 100644
--- a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
+++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx
@@ -123,18 +123,21 @@
<data name="BadAccessTokenInProtectedResourceRequest" xml:space="preserve">
<value>The access token '{0}' is invalid or expired.</value>
</data>
- <data name="ConsumerCertificateProviderNotAvailable" xml:space="preserve">
- <value>The RSA-SHA1 signing binding element's consumer certificate provider has not been set, so no incoming messages from consumers using this signature method can be verified.</value>
- </data>
<data name="ConsumerOrTokenSecretNotFound" xml:space="preserve">
<value>Failure looking up secret for consumer or token.</value>
</data>
+ <data name="IncorrectVerifier" xml:space="preserve">
+ <value>oauth_verifier argument was incorrect.</value>
+ </data>
<data name="InvalidIncomingMessage" xml:space="preserve">
<value>An invalid OAuth message received and discarded.</value>
</data>
<data name="MessageNotAllowedExtraParameters" xml:space="preserve">
<value>The {0} message included extra data which is not allowed.</value>
</data>
+ <data name="MinimumConsumerVersionRequirementNotMet" xml:space="preserve">
+ <value>This OAuth service provider requires OAuth consumers to implement OAuth {0}, but this consumer appears to only support {1}.</value>
+ </data>
<data name="OpenIdOAuthExtensionRequiresSpecialTokenManagerInterface" xml:space="preserve">
<value>Use of the OpenID+OAuth extension requires that the token manager in use implement the {0} interface.</value>
</data>
@@ -150,6 +153,9 @@
<data name="SigningElementsMustShareSameProtection" xml:space="preserve">
<value>All signing elements must offer the same message protection.</value>
</data>
+ <data name="TokenNotFound" xml:space="preserve">
+ <value>A token in the message was not recognized by the service provider.</value>
+ </data>
<data name="X509CertificateNotProvidedForSigning" xml:space="preserve">
<value>The RSA-SHA1 signing binding element has not been set with a certificate for signing.</value>
</data>
diff --git a/src/DotNetOpenAuth/OAuth/Protocol.cs b/src/DotNetOpenAuth/OAuth/Protocol.cs
index 88615ff..f535b10 100644
--- a/src/DotNetOpenAuth/OAuth/Protocol.cs
+++ b/src/DotNetOpenAuth/OAuth/Protocol.cs
@@ -7,17 +7,34 @@
namespace DotNetOpenAuth.OAuth {
using System;
using System.Collections.Generic;
+ using System.Diagnostics;
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
/// <summary>
+ /// An enumeration of the OAuth protocol versions supported by this library.
+ /// </summary>
+ public enum ProtocolVersion {
+ /// <summary>
+ /// OAuth 1.0 specification
+ /// </summary>
+ V10,
+
+ /// <summary>
+ /// OAuth 1.0a specification
+ /// </summary>
+ V10a,
+ }
+
+ /// <summary>
/// Constants used in the OAuth protocol.
/// </summary>
/// <remarks>
/// OAuth Protocol Parameter names and values are case sensitive. Each OAuth Protocol Parameters MUST NOT appear more than once per request, and are REQUIRED unless otherwise noted,
/// per OAuth 1.0 section 5.
/// </remarks>
+ [DebuggerDisplay("OAuth {Version}")]
internal class Protocol {
/// <summary>
/// The namespace to use for V1.0 of the protocol.
@@ -25,63 +42,105 @@ namespace DotNetOpenAuth.OAuth {
internal const string DataContractNamespaceV10 = "http://oauth.net/core/1.0/";
/// <summary>
+ /// The prefix used for all key names in the protocol.
+ /// </summary>
+ internal const string ParameterPrefix = "oauth_";
+
+ /// <summary>
+ /// The string representation of a <see cref="Version"/> instance to be used to represent OAuth 1.0a.
+ /// </summary>
+ internal const string V10aVersion = "1.0.1";
+
+ /// <summary>
+ /// The scheme to use in Authorization header message requests.
+ /// </summary>
+ internal const string AuthorizationHeaderScheme = "OAuth";
+
+ /// <summary>
/// Gets the <see cref="Protocol"/> instance with values initialized for V1.0 of the protocol.
/// </summary>
internal static readonly Protocol V10 = new Protocol {
dataContractNamespace = DataContractNamespaceV10,
+ Version = new Version(1, 0),
+ ProtocolVersion = ProtocolVersion.V10,
};
/// <summary>
+ /// Gets the <see cref="Protocol"/> instance with values initialized for V1.0a of the protocol.
+ /// </summary>
+ internal static readonly Protocol V10a = new Protocol {
+ dataContractNamespace = DataContractNamespaceV10,
+ Version = new Version(V10aVersion),
+ ProtocolVersion = ProtocolVersion.V10a,
+ };
+
+ /// <summary>
+ /// A list of all supported OAuth versions, in order starting from newest version.
+ /// </summary>
+ internal static readonly List<Protocol> AllVersions = new List<Protocol>() { V10a, V10 };
+
+ /// <summary>
+ /// The default (or most recent) supported version of the OpenID protocol.
+ /// </summary>
+ internal static readonly Protocol Default = AllVersions[0];
+
+ /// <summary>
/// The namespace to use for this version of the protocol.
/// </summary>
private string dataContractNamespace;
/// <summary>
- /// The prefix used for all key names in the protocol.
+ /// Initializes a new instance of the <see cref="Protocol"/> class.
/// </summary>
- private string parameterPrefix = "oauth_";
+ internal Protocol() {
+ this.PublishedVersion = "1.0";
+ }
/// <summary>
- /// The scheme to use in Authorization header message requests.
+ /// Gets the version used to represent OAuth 1.0a.
/// </summary>
- private string authorizationHeaderScheme = "OAuth";
+ internal Version Version { get; private set; }
/// <summary>
- /// Gets the default <see cref="Protocol"/> instance.
+ /// Gets the version to declare on the wire.
/// </summary>
- internal static Protocol Default { get { return V10; } }
+ internal string PublishedVersion { get; private set; }
/// <summary>
- /// Gets the namespace to use for this version of the protocol.
+ /// Gets the <see cref="ProtocolVersion"/> enum value for the <see cref="Protocol"/> instance.
/// </summary>
- internal string DataContractNamespace {
- get { return this.dataContractNamespace; }
- }
+ internal ProtocolVersion ProtocolVersion { get; private set; }
/// <summary>
- /// Gets the prefix used for all key names in the protocol.
+ /// Gets the namespace to use for this version of the protocol.
/// </summary>
- internal string ParameterPrefix {
- get { return this.parameterPrefix; }
+ internal string DataContractNamespace {
+ get { return this.dataContractNamespace; }
}
/// <summary>
- /// Gets the scheme to use in Authorization header message requests.
+ /// Gets the OAuth Protocol instance to use for the given version.
/// </summary>
- internal string AuthorizationHeaderScheme {
- get { return this.authorizationHeaderScheme; }
+ /// <param name="version">The OAuth version to get.</param>
+ /// <returns>A matching <see cref="Protocol"/> instance.</returns>
+ public static Protocol Lookup(ProtocolVersion version) {
+ switch (version) {
+ case ProtocolVersion.V10: return Protocol.V10;
+ case ProtocolVersion.V10a: return Protocol.V10a;
+ default: throw new ArgumentOutOfRangeException("version");
+ }
}
/// <summary>
- /// Gets an instance of <see cref="Protocol"/> given a <see cref="Version"/>.
+ /// Gets the OAuth Protocol instance to use for the given version.
/// </summary>
- /// <param name="version">The version of the protocol that is desired.</param>
- /// <returns>The <see cref="Protocol"/> instance representing the requested version.</returns>
+ /// <param name="version">The OAuth version to get.</param>
+ /// <returns>A matching <see cref="Protocol"/> instance.</returns>
internal static Protocol Lookup(Version version) {
- switch (version.Major) {
- case 1: return Protocol.V10;
- default: throw new ArgumentOutOfRangeException("version");
- }
+ ErrorUtilities.VerifyArgumentNotNull(version, "version");
+ Protocol protocol = AllVersions.FirstOrDefault(p => p.Version == version);
+ ErrorUtilities.VerifyArgumentInRange(protocol != null, "version");
+ return protocol;
}
}
}
diff --git a/src/DotNetOpenAuth/OAuth/SecuritySettings.cs b/src/DotNetOpenAuth/OAuth/SecuritySettings.cs
new file mode 100644
index 0000000..3329f09
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/SecuritySettings.cs
@@ -0,0 +1,18 @@
+//-----------------------------------------------------------------------
+// <copyright file="SecuritySettings.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth {
+ /// <summary>
+ /// Security settings that may be applicable to both consumers and service providers.
+ /// </summary>
+ public class SecuritySettings {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="SecuritySettings"/> class.
+ /// </summary>
+ protected SecuritySettings() {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
index c59c1ed..df5cd4b 100644
--- a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
+++ b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs
@@ -6,11 +6,14 @@
namespace DotNetOpenAuth.OAuth {
using System;
+ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
using System.Globalization;
+ using System.Security.Principal;
using System.ServiceModel.Channels;
using System.Web;
+ using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Bindings;
using DotNetOpenAuth.OAuth.ChannelElements;
@@ -33,6 +36,11 @@ namespace DotNetOpenAuth.OAuth {
/// </remarks>
public class ServiceProvider : IDisposable {
/// <summary>
+ /// The length of the verifier code (in raw bytes before base64 encoding) to generate.
+ /// </summary>
+ private const int VerifierCodeLength = 5;
+
+ /// <summary>
/// The field behind the <see cref="OAuthChannel"/> property.
/// </summary>
private OAuthChannel channel;
@@ -52,7 +60,7 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="serviceDescription">The endpoints and behavior on the Service Provider.</param>
/// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param>
/// <param name="messageTypeProvider">An object that can figure out what type of message is being received for deserialization.</param>
- public ServiceProvider(ServiceProviderDescription serviceDescription, ITokenManager tokenManager, OAuthServiceProviderMessageFactory messageTypeProvider) {
+ public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager, OAuthServiceProviderMessageFactory messageTypeProvider) {
Contract.Requires<ArgumentNullException>(serviceDescription != null);
Contract.Requires<ArgumentNullException>(tokenManager != null);
Contract.Requires<ArgumentNullException>(messageTypeProvider != null);
@@ -62,6 +70,7 @@ namespace DotNetOpenAuth.OAuth {
this.ServiceDescription = serviceDescription;
this.OAuthChannel = new OAuthChannel(signingElement, store, tokenManager, messageTypeProvider);
this.TokenGenerator = new StandardTokenGenerator();
+ this.SecuritySettings = DotNetOpenAuthSection.Configuration.OAuth.ServiceProvider.SecuritySettings.CreateSecuritySettings();
}
/// <summary>
@@ -77,8 +86,8 @@ namespace DotNetOpenAuth.OAuth {
/// <summary>
/// Gets the persistence store for tokens and secrets.
/// </summary>
- public ITokenManager TokenManager {
- get { return this.OAuthChannel.TokenManager; }
+ public IServiceProviderTokenManager TokenManager {
+ get { return (IServiceProviderTokenManager)this.OAuthChannel.TokenManager; }
}
/// <summary>
@@ -89,6 +98,11 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
+ /// Gets the security settings for this service provider.
+ /// </summary>
+ public ServiceProviderSecuritySettings SecuritySettings { get; private set; }
+
+ /// <summary>
/// Gets or sets the channel to use for sending/receiving messages.
/// </summary>
internal OAuthChannel OAuthChannel {
@@ -97,15 +111,38 @@ namespace DotNetOpenAuth.OAuth {
}
set {
- if (this.channel != null) {
- this.channel.Sending -= this.OAuthChannel_Sending;
- }
-
+ Contract.Requires(value != null);
+ ErrorUtilities.VerifyArgumentNotNull(value, "value");
this.channel = value;
+ }
+ }
- if (this.channel != null) {
- this.channel.Sending += this.OAuthChannel_Sending;
- }
+ /// <summary>
+ /// Creates a cryptographically strong random verification code.
+ /// </summary>
+ /// <param name="format">The desired format of the verification code.</param>
+ /// <param name="length">The length of the code.
+ /// When <paramref name="format"/> is <see cref="VerificationCodeFormat.IncludedInCallback"/>,
+ /// this is the length of the original byte array before base64 encoding rather than the actual
+ /// length of the final string.</param>
+ /// <returns>The verification code.</returns>
+ public static string CreateVerificationCode(VerificationCodeFormat format, int length) {
+ Contract.Requires(length >= 0);
+ ErrorUtilities.VerifyArgumentInRange(length >= 0, "length");
+
+ switch (format) {
+ case VerificationCodeFormat.IncludedInCallback:
+ return MessagingUtilities.GetCryptoRandomDataAsBase64(length);
+ case VerificationCodeFormat.AlphaNumericNoLookAlikes:
+ return MessagingUtilities.GetRandomString(length, MessagingUtilities.AlphaNumericNoLookAlikes);
+ case VerificationCodeFormat.AlphaUpper:
+ return MessagingUtilities.GetRandomString(length, MessagingUtilities.UppercaseLetters);
+ case VerificationCodeFormat.AlphaLower:
+ return MessagingUtilities.GetRandomString(length, MessagingUtilities.LowercaseLetters);
+ case VerificationCodeFormat.Numeric:
+ return MessagingUtilities.GetRandomString(length, MessagingUtilities.Digits);
+ default:
+ throw new ArgumentOutOfRangeException("format");
}
}
@@ -149,7 +186,9 @@ namespace DotNetOpenAuth.OAuth {
/// <exception cref="ProtocolException">Thrown if an unexpected OAuth message is attached to the incoming request.</exception>
public UnauthorizedTokenRequest ReadTokenRequest(HttpRequestInfo request) {
UnauthorizedTokenRequest message;
- this.Channel.TryReadFromRequest(request, out message);
+ if (this.Channel.TryReadFromRequest(request, out message)) {
+ ErrorUtilities.VerifyProtocol(message.Version >= Protocol.Lookup(this.SecuritySettings.MinimumRequiredOAuthVersion).Version, OAuthStrings.MinimumConsumerVersionRequirementNotMet, this.SecuritySettings.MinimumRequiredOAuthVersion, message.Version);
+ }
return message;
}
@@ -160,9 +199,7 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="request">The token request message the Consumer sent that the Service Provider is now responding to.</param>
/// <returns>The response message to send using the <see cref="Channel"/>, after optionally adding extra data to it.</returns>
public UnauthorizedTokenResponse PrepareUnauthorizedTokenMessage(UnauthorizedTokenRequest request) {
- if (request == null) {
- throw new ArgumentNullException("request");
- }
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
string token = this.TokenGenerator.GenerateRequestToken(request.ConsumerKey);
string secret = this.TokenGenerator.GenerateSecret();
@@ -276,11 +313,27 @@ namespace DotNetOpenAuth.OAuth {
Contract.Requires<ArgumentNullException>(request != null);
ErrorUtilities.VerifyArgumentNotNull(request, "request");
- if (request.Callback != null) {
- return this.PrepareAuthorizationResponse(request, request.Callback);
+ // It is very important for us to ignore the oauth_callback argument in the
+ // UserAuthorizationRequest if the Consumer is a 1.0a consumer or else we
+ // open up a security exploit.
+ IServiceProviderRequestToken token = this.TokenManager.GetRequestToken(request.RequestToken);
+ Uri callback;
+ if (request.Version >= Protocol.V10a.Version) {
+ // In OAuth 1.0a, we'll prefer the token-specific callback to the pre-registered one.
+ if (token.Callback != null) {
+ callback = token.Callback;
+ } else {
+ IConsumerDescription consumer = this.TokenManager.GetConsumer(token.ConsumerKey);
+ callback = consumer.Callback;
+ }
} else {
- return null;
+ // In OAuth 1.0, we'll prefer the pre-registered callback over the token-specific one
+ // since 1.0 has a security weakness for user-modified callback URIs.
+ IConsumerDescription consumer = this.TokenManager.GetConsumer(token.ConsumerKey);
+ callback = consumer.Callback ?? request.Callback;
}
+
+ return callback != null ? this.PrepareAuthorizationResponse(request, callback) : null;
}
/// <summary>
@@ -289,7 +342,7 @@ namespace DotNetOpenAuth.OAuth {
/// </summary>
/// <param name="request">The Consumer's original authorization request.</param>
/// <param name="callback">The callback URI the consumer has previously registered
- /// with this service provider.</param>
+ /// with this service provider or that came in the <see cref="UnauthorizedTokenRequest"/>.</param>
/// <returns>
/// The message to send to the Consumer using <see cref="Channel"/>.
/// </returns>
@@ -298,9 +351,14 @@ namespace DotNetOpenAuth.OAuth {
Contract.Requires<ArgumentNullException>(request != null);
Contract.Requires<ArgumentNullException>(callback != null);
- var authorization = new UserAuthorizationResponse(request.Callback) {
+ var authorization = new UserAuthorizationResponse(callback, request.Version) {
RequestToken = request.RequestToken,
};
+
+ if (authorization.Version >= Protocol.V10a.Version) {
+ authorization.VerificationCode = CreateVerificationCode(VerificationCodeFormat.IncludedInCallback, VerifierCodeLength);
+ }
+
return authorization;
}
@@ -334,17 +392,10 @@ namespace DotNetOpenAuth.OAuth {
/// <param name="request">The Consumer's message requesting an access token.</param>
/// <returns>The HTTP response to actually send to the Consumer.</returns>
public AuthorizedTokenResponse PrepareAccessTokenMessage(AuthorizedTokenRequest request) {
- if (request == null) {
- throw new ArgumentNullException("request");
- }
+ Contract.Requires(request != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
- if (!this.TokenManager.IsRequestTokenAuthorized(request.RequestToken)) {
- throw new ProtocolException(
- string.Format(
- CultureInfo.CurrentCulture,
- OAuthStrings.AccessTokenNotAuthorized,
- request.RequestToken));
- }
+ ErrorUtilities.VerifyProtocol(this.TokenManager.IsRequestTokenAuthorized(request.RequestToken), OAuthStrings.AccessTokenNotAuthorized, request.RequestToken);
string accessToken = this.TokenGenerator.GenerateAccessToken(request.ConsumerKey);
string tokenSecret = this.TokenGenerator.GenerateSecret();
@@ -415,6 +466,19 @@ namespace DotNetOpenAuth.OAuth {
return accessMessage;
}
+ /// <summary>
+ /// Creates a security principal that may be used.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>The <see cref="IPrincipal"/> instance that can be used for access control of resources.</returns>
+ public OAuthPrincipal CreatePrincipal(AccessProtectedResourceRequest request) {
+ Contract.Requires(request != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ IServiceProviderAccessToken accessToken = this.TokenManager.GetAccessToken(request.AccessToken);
+ return new OAuthPrincipal(accessToken);
+ }
+
#region IDisposable Members
/// <summary>
@@ -436,18 +500,5 @@ namespace DotNetOpenAuth.OAuth {
}
#endregion
-
- /// <summary>
- /// Hooks the channel in order to perform some operations on some outgoing messages.
- /// </summary>
- /// <param name="sender">The source of the event.</param>
- /// <param name="e">The <see cref="DotNetOpenAuth.Messaging.ChannelEventArgs"/> instance containing the event data.</param>
- private void OAuthChannel_Sending(object sender, ChannelEventArgs e) {
- // Hook to store the token and secret on its way down to the Consumer.
- var grantRequestTokenResponse = e.Message as UnauthorizedTokenResponse;
- if (grantRequestTokenResponse != null) {
- this.TokenManager.StoreNewRequestToken(grantRequestTokenResponse.RequestMessage, grantRequestTokenResponse);
- }
- }
}
}
diff --git a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs
index 4636829..9014762 100644
--- a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs
+++ b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs
@@ -26,9 +26,15 @@ namespace DotNetOpenAuth.OAuth {
/// Initializes a new instance of the <see cref="ServiceProviderDescription"/> class.
/// </summary>
public ServiceProviderDescription() {
+ this.ProtocolVersion = Protocol.Default.ProtocolVersion;
}
/// <summary>
+ /// Gets or sets the OAuth version supported by the Service Provider.
+ /// </summary>
+ public ProtocolVersion ProtocolVersion { get; set; }
+
+ /// <summary>
/// Gets or sets the URL used to obtain an unauthorized Request Token,
/// described in Section 6.1 (Obtaining an Unauthorized Request Token).
/// </summary>
@@ -43,7 +49,7 @@ namespace DotNetOpenAuth.OAuth {
}
set {
- if (value != null && UriUtil.QueryStringContainPrefixedParameters(value.Location, OAuth.Protocol.V10.ParameterPrefix)) {
+ if (value != null && UriUtil.QueryStringContainPrefixedParameters(value.Location, OAuth.Protocol.ParameterPrefix)) {
throw new ArgumentException(OAuthStrings.RequestUrlMustNotHaveOAuthParameters);
}
@@ -77,6 +83,13 @@ namespace DotNetOpenAuth.OAuth {
public ITamperProtectionChannelBindingElement[] TamperProtectionElements { get; set; }
/// <summary>
+ /// Gets the OAuth version supported by the Service Provider.
+ /// </summary>
+ internal Version Version {
+ get { return Protocol.Lookup(this.ProtocolVersion).Version; }
+ }
+
+ /// <summary>
/// Creates a signing element that includes all the signing elements this service provider supports.
/// </summary>
/// <returns>The created signing element.</returns>
diff --git a/src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs b/src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs
new file mode 100644
index 0000000..b8e12fd
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/ServiceProviderSecuritySettings.cs
@@ -0,0 +1,25 @@
+//-----------------------------------------------------------------------
+// <copyright file="ServiceProviderSecuritySettings.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth {
+ using System;
+
+ /// <summary>
+ /// Security settings that are applicable to service providers.
+ /// </summary>
+ public class ServiceProviderSecuritySettings : SecuritySettings {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ServiceProviderSecuritySettings"/> class.
+ /// </summary>
+ internal ServiceProviderSecuritySettings() {
+ }
+
+ /// <summary>
+ /// Gets or sets the minimum required version of OAuth that must be implemented by a Consumer.
+ /// </summary>
+ public ProtocolVersion MinimumRequiredOAuthVersion { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs b/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs
new file mode 100644
index 0000000..d25c988
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuth/VerificationCodeFormat.cs
@@ -0,0 +1,58 @@
+//-----------------------------------------------------------------------
+// <copyright file="VerificationCodeFormat.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth {
+ /// <summary>
+ /// The different formats a user authorization verifier code can take
+ /// in order to be as secure as possible while being compatible with
+ /// the type of OAuth Consumer requesting access.
+ /// </summary>
+ /// <remarks>
+ /// Some Consumers may be set-top boxes, video games, mobile devies, etc.
+ /// with very limited character entry support and no ability to receive
+ /// a callback URI. OAuth 1.0a requires that these devices operators
+ /// must manually key in a verifier code, so in these cases it better
+ /// be possible to do so given the input options on that device.
+ /// </remarks>
+ public enum VerificationCodeFormat {
+ /// <summary>
+ /// The strongest verification code.
+ /// The best option for web consumers since a callback is usually an option.
+ /// </summary>
+ IncludedInCallback,
+
+ /// <summary>
+ /// A combination of upper and lowercase letters and numbers may be used,
+ /// allowing a computer operator to easily read from the screen and key
+ /// in the verification code.
+ /// </summary>
+ /// <remarks>
+ /// Some letters and numbers will be skipped where they are visually similar
+ /// enough that they can be difficult to distinguish when displayed with most fonts.
+ /// </remarks>
+ AlphaNumericNoLookAlikes,
+
+ /// <summary>
+ /// Only uppercase letters will be used in the verification code.
+ /// Verification codes are case-sensitive, so consumers with fixed
+ /// keyboards with only one character case option may require this option.
+ /// </summary>
+ AlphaUpper,
+
+ /// <summary>
+ /// Only lowercase letters will be used in the verification code.
+ /// Verification codes are case-sensitive, so consumers with fixed
+ /// keyboards with only one character case option may require this option.
+ /// </summary>
+ AlphaLower,
+
+ /// <summary>
+ /// Only the numbers 0-9 will be used in the verification code.
+ /// Must useful for consumers running on mobile phone devices.
+ /// </summary>
+ Numeric,
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth/WebConsumer.cs b/src/DotNetOpenAuth/OAuth/WebConsumer.cs
index 4cf8b99..8566036 100644
--- a/src/DotNetOpenAuth/OAuth/WebConsumer.cs
+++ b/src/DotNetOpenAuth/OAuth/WebConsumer.cs
@@ -42,7 +42,7 @@ namespace DotNetOpenAuth.OAuth {
/// Requires HttpContext.Current.
/// </remarks>
public UserAuthorizationRequest PrepareRequestUserAuthorization() {
- Uri callback = this.Channel.GetRequestFromContext().UrlBeforeRewriting.StripQueryArgumentsWithPrefix(Protocol.Default.ParameterPrefix);
+ Uri callback = this.Channel.GetRequestFromContext().UrlBeforeRewriting.StripQueryArgumentsWithPrefix(Protocol.ParameterPrefix);
return this.PrepareRequestUserAuthorization(callback, null, null);
}
@@ -119,7 +119,7 @@ namespace DotNetOpenAuth.OAuth {
}
// Prepare a message to exchange the request token for an access token.
- var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint) {
+ var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, this.ServiceProvider.Version) {
RequestToken = positiveAuthorization.RequestToken,
ConsumerKey = this.ConsumerKey,
};
@@ -145,7 +145,8 @@ namespace DotNetOpenAuth.OAuth {
UserAuthorizationResponse authorizationMessage;
if (this.Channel.TryReadFromRequest<UserAuthorizationResponse>(request, out authorizationMessage)) {
string requestToken = authorizationMessage.RequestToken;
- return this.ProcessUserAuthorization(requestToken);
+ string verifier = authorizationMessage.VerificationCode;
+ return this.ProcessUserAuthorization(requestToken, verifier);
} else {
return null;
}
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs
new file mode 100644
index 0000000..1c124d8
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/AXFetchAsSregTransform.cs
@@ -0,0 +1,139 @@
+//-----------------------------------------------------------------------
+// <copyright file="AXFetchAsSregTransform.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Behaviors {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Extensions;
+ using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.Provider;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+
+ /// <summary>
+ /// An Attribute Exchange and Simple Registration filter to make all incoming attribute
+ /// requests look like Simple Registration requests, and to convert the response
+ /// to the originally requested extension and format.
+ /// </summary>
+ public class AXFetchAsSregTransform : IRelyingPartyBehavior, IProviderBehavior {
+ /// <summary>
+ /// Initializes static members of the <see cref="AXFetchAsSregTransform"/> class.
+ /// </summary>
+ static AXFetchAsSregTransform() {
+ AXFormats = AXAttributeFormats.Common;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AXFetchAsSregTransform"/> class.
+ /// </summary>
+ public AXFetchAsSregTransform() {
+ }
+
+ /// <summary>
+ /// Gets or sets the AX attribute type URI formats this transform is willing to work with.
+ /// </summary>
+ public static AXAttributeFormats AXFormats { get; set; }
+
+ #region IRelyingPartyBehavior Members
+
+ /// <summary>
+ /// Applies a well known set of security requirements to a default set of security settings.
+ /// </summary>
+ /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param>
+ /// <remarks>
+ /// Care should be taken to never decrease security when applying a profile.
+ /// Profiles should only enhance security requirements to avoid being
+ /// incompatible with each other.
+ /// </remarks>
+ void IRelyingPartyBehavior.ApplySecuritySettings(RelyingPartySecuritySettings securitySettings) {
+ }
+
+ /// <summary>
+ /// Called when an authentication request is about to be sent.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <remarks>
+ /// Implementations should be prepared to be called multiple times on the same outgoing message
+ /// without malfunctioning.
+ /// </remarks>
+ void IRelyingPartyBehavior.OnOutgoingAuthenticationRequest(RelyingParty.IAuthenticationRequest request) {
+ request.SpreadSregToAX(AXFormats);
+ }
+
+ /// <summary>
+ /// Called when an incoming positive assertion is received.
+ /// </summary>
+ /// <param name="assertion">The positive assertion.</param>
+ void IRelyingPartyBehavior.OnIncomingPositiveAssertion(IAuthenticationResponse assertion) {
+ if (assertion.GetExtension<ClaimsResponse>() == null) {
+ ClaimsResponse sreg = assertion.UnifyExtensionsAsSreg(true);
+ ((PositiveAnonymousResponse)assertion).Response.Extensions.Add(sreg);
+ }
+ }
+
+ #endregion
+
+ #region IProviderBehavior Members
+
+ /// <summary>
+ /// Applies a well known set of security requirements to a default set of security settings.
+ /// </summary>
+ /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param>
+ /// <remarks>
+ /// Care should be taken to never decrease security when applying a profile.
+ /// Profiles should only enhance security requirements to avoid being
+ /// incompatible with each other.
+ /// </remarks>
+ void IProviderBehavior.ApplySecuritySettings(ProviderSecuritySettings securitySettings) {
+ // Nothing to do here.
+ }
+
+ /// <summary>
+ /// Called when a request is received by the Provider.
+ /// </summary>
+ /// <param name="request">The incoming request.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ /// <remarks>
+ /// Implementations may set a new value to <see cref="IRequest.SecuritySettings"/> but
+ /// should not change the properties on the instance of <see cref="ProviderSecuritySettings"/>
+ /// itself as that instance may be shared across many requests.
+ /// </remarks>
+ bool IProviderBehavior.OnIncomingRequest(IRequest request) {
+ var extensionRequest = request as Provider.HostProcessedRequest;
+ if (extensionRequest != null) {
+ if (extensionRequest.GetExtension<ClaimsRequest>() == null) {
+ ClaimsRequest sreg = extensionRequest.UnifyExtensionsAsSreg();
+ if (sreg != null) {
+ ((IProtocolMessageWithExtensions)extensionRequest.RequestMessage).Extensions.Add(sreg);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Called when the Provider is preparing to send a response to an authentication request.
+ /// </summary>
+ /// <param name="request">The request that is configured to generate the outgoing response.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ bool IProviderBehavior.OnOutgoingResponse(Provider.IAuthenticationRequest request) {
+ request.ConvertSregToMatchRequest();
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs
new file mode 100644
index 0000000..4166f19
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.Designer.cs
@@ -0,0 +1,72 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.4918
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Behaviors {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class BehaviorStrings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal BehaviorStrings() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DotNetOpenAuth.OpenId.Behaviors.BehaviorStrings", typeof(BehaviorStrings).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to No PPID provider has been configured..
+ /// </summary>
+ internal static string PpidProviderNotGiven {
+ get {
+ return ResourceManager.GetString("PpidProviderNotGiven", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx
new file mode 100644
index 0000000..23e3e73
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/BehaviorStrings.resx
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="PpidProviderNotGiven" xml:space="preserve">
+ <value>No PPID provider has been configured.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs b/src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs
new file mode 100644
index 0000000..b9a3dfc
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Behaviors/PpidGeneration.cs
@@ -0,0 +1,117 @@
+//-----------------------------------------------------------------------
+// <copyright file="PpidGeneration.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Behaviors {
+ using System;
+ using System.Linq;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy;
+ using DotNetOpenAuth.OpenId.Provider;
+
+ /// <summary>
+ /// Offers OpenID Providers automatic PPID Claimed Identifier generation when requested
+ /// by a PAPE request.
+ /// </summary>
+ /// <remarks>
+ /// <para>PPIDs are set on positive authentication responses when the PAPE request includes
+ /// the <see cref="AuthenticationPolicies.PrivatePersonalIdentifier"/> authentication policy.</para>
+ /// <para>The static member <see cref="PpidGeneration.PpidIdentifierProvider"/> MUST
+ /// be set prior to any PPID requests come in. Typically this should be set in the
+ /// <c>Application_Start</c> method in the global.asax.cs file.</para>
+ /// </remarks>
+ [Serializable]
+ public sealed class PpidGeneration : IProviderBehavior {
+ /// <summary>
+ /// Gets or sets the provider for generating PPID identifiers.
+ /// </summary>
+ public static IDirectedIdentityIdentifierProvider PpidIdentifierProvider { get; set; }
+
+ #region IProviderBehavior Members
+
+ /// <summary>
+ /// Applies a well known set of security requirements to a default set of security settings.
+ /// </summary>
+ /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param>
+ /// <remarks>
+ /// Care should be taken to never decrease security when applying a profile.
+ /// Profiles should only enhance security requirements to avoid being
+ /// incompatible with each other.
+ /// </remarks>
+ void IProviderBehavior.ApplySecuritySettings(ProviderSecuritySettings securitySettings) {
+ // No special security to apply here.
+ }
+
+ /// <summary>
+ /// Called when a request is received by the Provider.
+ /// </summary>
+ /// <param name="request">The incoming request.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ /// <remarks>
+ /// Implementations may set a new value to <see cref="IRequest.SecuritySettings"/> but
+ /// should not change the properties on the instance of <see cref="ProviderSecuritySettings"/>
+ /// itself as that instance may be shared across many requests.
+ /// </remarks>
+ bool IProviderBehavior.OnIncomingRequest(IRequest request) {
+ return false;
+ }
+
+ /// <summary>
+ /// Called when the Provider is preparing to send a response to an authentication request.
+ /// </summary>
+ /// <param name="request">The request that is configured to generate the outgoing response.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ bool IProviderBehavior.OnOutgoingResponse(IAuthenticationRequest request) {
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ // Nothing to do for negative assertions.
+ if (!request.IsAuthenticated.Value) {
+ return false;
+ }
+
+ var requestInternal = (Provider.AuthenticationRequest)request;
+ var responseMessage = (IProtocolMessageWithExtensions)requestInternal.Response;
+
+ // Only apply our special policies if the RP requested it.
+ var papeRequest = request.GetExtension<PolicyRequest>();
+ if (papeRequest != null) {
+ if (papeRequest.PreferredPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) {
+ ErrorUtilities.VerifyProtocol(request.ClaimedIdentifier == request.LocalIdentifier, OpenIdStrings.DelegatingIdentifiersNotAllowed);
+
+ if (PpidIdentifierProvider == null) {
+ Logger.OpenId.Error(BehaviorStrings.PpidProviderNotGiven);
+ return false;
+ }
+
+ // Mask the user's identity with a PPID.
+ if (PpidIdentifierProvider.IsUserLocalIdentifier(request.LocalIdentifier)) {
+ Identifier ppidIdentifier = PpidIdentifierProvider.GetIdentifier(request.LocalIdentifier, request.Realm);
+ requestInternal.ResetClaimedAndLocalIdentifiers(ppidIdentifier);
+ }
+
+ // Indicate that the RP is receiving a PPID claimed_id
+ var papeResponse = responseMessage.Extensions.OfType<PolicyResponse>().SingleOrDefault();
+ if (papeResponse == null) {
+ request.AddResponseExtension(papeResponse = new PolicyResponse());
+ }
+
+ if (!papeResponse.ActualPolicies.Contains(AuthenticationPolicies.PrivatePersonalIdentifier)) {
+ papeResponse.ActualPolicies.Add(AuthenticationPolicies.PrivatePersonalIdentifier);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
index d9c244f..63f7809 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/ExtensionsBindingElement.cs
@@ -23,13 +23,27 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
/// </summary>
internal class ExtensionsBindingElement : IChannelBindingElement {
/// <summary>
+ /// The security settings that apply to this binding element.
+ /// </summary>
+ private readonly SecuritySettings securitySettings;
+
+ /// <summary>
+ /// The security settings that apply to this relying party, if it is a relying party.
+ /// </summary>
+ private readonly RelyingPartySecuritySettings relyingPartySecuritySettings;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="ExtensionsBindingElement"/> class.
/// </summary>
/// <param name="extensionFactory">The extension factory.</param>
- internal ExtensionsBindingElement(IOpenIdExtensionFactory extensionFactory) {
+ /// <param name="securitySettings">The security settings.</param>
+ internal ExtensionsBindingElement(IOpenIdExtensionFactory extensionFactory, SecuritySettings securitySettings) {
ErrorUtilities.VerifyArgumentNotNull(extensionFactory, "extensionFactory");
+ ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
this.ExtensionFactory = extensionFactory;
+ this.securitySettings = securitySettings;
+ this.relyingPartySecuritySettings = securitySettings as RelyingPartySecuritySettings;
}
#region IChannelBindingElement Members
@@ -141,10 +155,12 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
// Now search again, considering ALL extensions whether they are signed or not,
// skipping the signed ones and adding the new ones as unsigned extensions.
- Func<string, bool> isNotSigned = typeUri => !extendableMessage.Extensions.Cast<IOpenIdMessageExtension>().Any(ext => ext.TypeUri == typeUri);
- foreach (IOpenIdMessageExtension unsignedExtension in this.GetExtensions(extendableMessage, false, isNotSigned)) {
- unsignedExtension.IsSignedByRemoteParty = false;
- extendableMessage.Extensions.Add(unsignedExtension);
+ if (this.relyingPartySecuritySettings == null || !this.relyingPartySecuritySettings.IgnoreUnsignedExtensions) {
+ Func<string, bool> isNotSigned = typeUri => !extendableMessage.Extensions.Cast<IOpenIdMessageExtension>().Any(ext => ext.TypeUri == typeUri);
+ foreach (IOpenIdMessageExtension unsignedExtension in this.GetExtensions(extendableMessage, false, isNotSigned)) {
+ unsignedExtension.IsSignedByRemoteParty = false;
+ extendableMessage.Extensions.Add(unsignedExtension);
+ }
}
return MessageProtections.None;
diff --git a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
index 8379521..d4664cf 100644
--- a/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
+++ b/src/DotNetOpenAuth/OpenId/ChannelElements/OpenIdChannel.cs
@@ -341,7 +341,7 @@ namespace DotNetOpenAuth.OpenId.ChannelElements {
var extensionFactory = OpenIdExtensionFactoryAggregator.LoadFromConfiguration();
List<IChannelBindingElement> elements = new List<IChannelBindingElement>(8);
- elements.Add(new ExtensionsBindingElement(extensionFactory));
+ elements.Add(new ExtensionsBindingElement(extensionFactory, securitySettings));
if (isRelyingPartyRole) {
elements.Add(new RelyingPartySecurityOptions(rpSecuritySettings));
elements.Add(new BackwardCompatibilityBindingElement());
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXAttributeFormats.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXAttributeFormats.cs
new file mode 100644
index 0000000..7e4ed73
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXAttributeFormats.cs
@@ -0,0 +1,45 @@
+//-----------------------------------------------------------------------
+// <copyright file="AXAttributeFormats.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
+ using System;
+
+ /// <summary>
+ /// The various Type URI formats an AX attribute may use by various remote parties.
+ /// </summary>
+ [Flags]
+ public enum AXAttributeFormats {
+ /// <summary>
+ /// No attribute format.
+ /// </summary>
+ None = 0x0,
+
+ /// <summary>
+ /// AX attributes should use the Type URI format starting with <c>http://axschema.org/</c>.
+ /// </summary>
+ AXSchemaOrg = 0x1,
+
+ /// <summary>
+ /// AX attributes should use the Type URI format starting with <c>http://schema.openid.net/</c>.
+ /// </summary>
+ SchemaOpenIdNet = 0x2,
+
+ /// <summary>
+ /// AX attributes should use the Type URI format starting with <c>http://openid.net/schema/</c>.
+ /// </summary>
+ OpenIdNetSchema = 0x4,
+
+ /// <summary>
+ /// All known schemas.
+ /// </summary>
+ All = 0xff,
+
+ /// <summary>
+ /// The most common schemas.
+ /// </summary>
+ Common = AXSchemaOrg | SchemaOpenIdNet,
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs
index 9845833..2b947f7 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/AttributeExchange/AXUtilities.cs
@@ -87,8 +87,8 @@ namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
bool countSent = false;
string countString;
if (fields.TryGetValue("count." + alias, out countString)) {
- if (!int.TryParse(countString, out count) || count <= 0) {
- Logger.OpenId.ErrorFormat("Failed to parse count.{0} value to a positive integer.", alias);
+ if (!int.TryParse(countString, out count) || count < 0) {
+ Logger.OpenId.ErrorFormat("Failed to parse count.{0} value to a non-negative integer.", alias);
continue;
}
countSent = true;
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs
new file mode 100644
index 0000000..6522d2e
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ExtensionsInteropHelper.cs
@@ -0,0 +1,373 @@
+//-----------------------------------------------------------------------
+// <copyright file="ExtensionsInteropHelper.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Extensions {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
+ using DotNetOpenAuth.OpenId.Extensions.SimpleRegistration;
+ using DotNetOpenAuth.OpenId.Messages;
+
+ /// <summary>
+ /// A set of methods designed to assist in improving interop across different
+ /// OpenID implementations and their extensions.
+ /// </summary>
+ public static class ExtensionsInteropHelper {
+ /// <summary>
+ /// The gender decoder to translate AX genders to Sreg.
+ /// </summary>
+ private static GenderEncoder genderEncoder = new GenderEncoder();
+
+ /// <summary>
+ /// Adds an Attribute Exchange (AX) extension to the authentication request
+ /// that asks for the same attributes as the Simple Registration (sreg) extension
+ /// that is already applied.
+ /// </summary>
+ /// <param name="request">The authentication request.</param>
+ /// <param name="attributeFormats">The attribute formats to use in the AX request.</param>
+ /// <remarks>
+ /// <para>If discovery on the user-supplied identifier yields hints regarding which
+ /// extensions and attribute formats the Provider supports, this method MAY ignore the
+ /// <paramref name="attributeFormat"/> argument and accomodate the Provider to minimize
+ /// the size of the request.</para>
+ /// <para>If the request does not carry an sreg extension, the method logs a warning but
+ /// otherwise quietly returns doing nothing.</para>
+ /// </remarks>
+ public static void SpreadSregToAX(this RelyingParty.IAuthenticationRequest request, AXAttributeFormats attributeFormats) {
+ Contract.Requires(request != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ var req = (RelyingParty.AuthenticationRequest)request;
+ var sreg = req.AppliedExtensions.OfType<ClaimsRequest>().SingleOrDefault();
+ if (sreg == null) {
+ Logger.OpenId.Warn("No Simple Registration (ClaimsRequest) extension present in the request to spread to AX.");
+ return;
+ }
+
+ if (req.Provider.IsExtensionSupported<ClaimsRequest>()) {
+ Logger.OpenId.Info("Skipping generation of AX request because the Identifier advertises the Provider supports the Sreg extension.");
+ return;
+ }
+
+ var ax = req.AppliedExtensions.OfType<FetchRequest>().SingleOrDefault();
+ if (ax == null) {
+ ax = new FetchRequest();
+ req.AddExtension(ax);
+ }
+
+ // Try to use just one AX Type URI format if we can figure out which type the OP accepts.
+ AXAttributeFormats detectedFormat;
+ if (TryDetectOPAttributeFormat(request, out detectedFormat)) {
+ Logger.OpenId.Info("Detected OP support for AX but not for Sreg. Removing Sreg extension request and using AX instead.");
+ attributeFormats = detectedFormat;
+ req.Extensions.Remove(sreg);
+ } else {
+ Logger.OpenId.Info("Could not determine whether OP supported Sreg or AX. Using both extensions.");
+ }
+
+ foreach (AXAttributeFormats format in ForEachFormat(attributeFormats)) {
+ FetchAttribute(ax, format, WellKnownAttributes.BirthDate.WholeBirthDate, sreg.BirthDate);
+ FetchAttribute(ax, format, WellKnownAttributes.Contact.HomeAddress.Country, sreg.Country);
+ FetchAttribute(ax, format, WellKnownAttributes.Contact.Email, sreg.Email);
+ FetchAttribute(ax, format, WellKnownAttributes.Name.FullName, sreg.FullName);
+ FetchAttribute(ax, format, WellKnownAttributes.Person.Gender, sreg.Gender);
+ FetchAttribute(ax, format, WellKnownAttributes.Preferences.Language, sreg.Language);
+ FetchAttribute(ax, format, WellKnownAttributes.Name.Alias, sreg.Nickname);
+ FetchAttribute(ax, format, WellKnownAttributes.Contact.HomeAddress.PostalCode, sreg.PostalCode);
+ FetchAttribute(ax, format, WellKnownAttributes.Preferences.TimeZone, sreg.TimeZone);
+ }
+ }
+
+ /// <summary>
+ /// Looks for Simple Registration and Attribute Exchange (all known formats)
+ /// response extensions and returns them as a Simple Registration extension.
+ /// </summary>
+ /// <param name="response">The authentication response.</param>
+ /// <param name="allowUnsigned">if set to <c>true</c> unsigned extensions will be included in the search.</param>
+ /// <returns>
+ /// The Simple Registration response if found,
+ /// or a fabricated one based on the Attribute Exchange extension if found,
+ /// or just an empty <see cref="ClaimsResponse"/> if there was no data.
+ /// Never <c>null</c>.</returns>
+ public static ClaimsResponse UnifyExtensionsAsSreg(this RelyingParty.IAuthenticationResponse response, bool allowUnsigned) {
+ Contract.Requires(response != null);
+ ErrorUtilities.VerifyArgumentNotNull(response, "response");
+
+ var resp = (RelyingParty.IAuthenticationResponse)response;
+ var sreg = allowUnsigned ? resp.GetUntrustedExtension<ClaimsResponse>() : resp.GetExtension<ClaimsResponse>();
+ if (sreg != null) {
+ return sreg;
+ }
+
+ AXAttributeFormats formats = AXAttributeFormats.All;
+ sreg = new ClaimsResponse();
+ var fetchResponse = allowUnsigned ? resp.GetUntrustedExtension<FetchResponse>() : resp.GetExtension<FetchResponse>();
+ if (fetchResponse != null) {
+ ((IOpenIdMessageExtension)sreg).IsSignedByRemoteParty = fetchResponse.IsSignedByProvider;
+ sreg.BirthDateRaw = fetchResponse.GetAttributeValue(WellKnownAttributes.BirthDate.WholeBirthDate, formats);
+ sreg.Country = fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.HomeAddress.Country, formats);
+ sreg.PostalCode = fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.HomeAddress.PostalCode, formats);
+ sreg.Email = fetchResponse.GetAttributeValue(WellKnownAttributes.Contact.Email, formats);
+ sreg.FullName = fetchResponse.GetAttributeValue(WellKnownAttributes.Name.FullName, formats);
+ sreg.Language = fetchResponse.GetAttributeValue(WellKnownAttributes.Preferences.Language, formats);
+ sreg.Nickname = fetchResponse.GetAttributeValue(WellKnownAttributes.Name.Alias, formats);
+ sreg.TimeZone = fetchResponse.GetAttributeValue(WellKnownAttributes.Preferences.TimeZone, formats);
+ string gender = fetchResponse.GetAttributeValue(WellKnownAttributes.Person.Gender, formats);
+ if (gender != null) {
+ sreg.Gender = (Gender)genderEncoder.Decode(gender);
+ }
+ }
+
+ return sreg;
+ }
+
+ /// <summary>
+ /// Looks for Simple Registration and Attribute Exchange (all known formats)
+ /// request extensions and returns them as a Simple Registration extension.
+ /// </summary>
+ /// <param name="request">The authentication request.</param>
+ /// <returns>
+ /// The Simple Registration request if found,
+ /// or a fabricated one based on the Attribute Exchange extension if found,
+ /// or <c>null</c> if no attribute extension request is found.</returns>
+ internal static ClaimsRequest UnifyExtensionsAsSreg(this Provider.IHostProcessedRequest request) {
+ Contract.Requires(request != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ var req = (Provider.AuthenticationRequest)request;
+ var sreg = req.GetExtension<ClaimsRequest>();
+ if (sreg != null) {
+ return sreg;
+ }
+
+ var ax = req.GetExtension<FetchRequest>();
+ if (ax != null) {
+ sreg = new ClaimsRequest();
+ sreg.Synthesized = true;
+ ((IProtocolMessageWithExtensions)req.RequestMessage).Extensions.Add(sreg);
+ sreg.BirthDate = GetDemandLevelFor(ax, WellKnownAttributes.BirthDate.WholeBirthDate);
+ sreg.Country = GetDemandLevelFor(ax, WellKnownAttributes.Contact.HomeAddress.Country);
+ sreg.Email = GetDemandLevelFor(ax, WellKnownAttributes.Contact.Email);
+ sreg.FullName = GetDemandLevelFor(ax, WellKnownAttributes.Name.FullName);
+ sreg.Gender = GetDemandLevelFor(ax, WellKnownAttributes.Person.Gender);
+ sreg.Language = GetDemandLevelFor(ax, WellKnownAttributes.Preferences.Language);
+ sreg.Nickname = GetDemandLevelFor(ax, WellKnownAttributes.Name.Alias);
+ sreg.PostalCode = GetDemandLevelFor(ax, WellKnownAttributes.Contact.HomeAddress.PostalCode);
+ sreg.TimeZone = GetDemandLevelFor(ax, WellKnownAttributes.Preferences.TimeZone);
+ }
+
+ return sreg;
+ }
+
+ /// <summary>
+ /// Converts the Simple Registration extension response to whatever format the original
+ /// attribute request extension came in.
+ /// </summary>
+ /// <param name="request">The authentication request with the response extensions already added.</param>
+ /// <remarks>
+ /// If the original attribute request came in as AX, the Simple Registration extension is converted
+ /// to an AX response and then the Simple Registration extension is removed from the response.
+ /// </remarks>
+ internal static void ConvertSregToMatchRequest(this Provider.IHostProcessedRequest request) {
+ var req = (Provider.HostProcessedRequest)request;
+ var response = (IProtocolMessageWithExtensions)req.Response;
+ var sregRequest = request.GetExtension<ClaimsRequest>();
+ if (sregRequest != null) {
+ if (sregRequest.Synthesized) {
+ var axRequest = request.GetExtension<FetchRequest>();
+ ErrorUtilities.VerifyInternal(axRequest != null, "How do we have a synthesized Sreg request without an AX request?");
+
+ var sregResponse = response.Extensions.OfType<ClaimsResponse>().SingleOrDefault();
+ if (sregResponse == null) {
+ // No Sreg response to copy from.
+ return;
+ }
+
+ // Remove the sreg response since the RP didn't ask for it.
+ response.Extensions.Remove(sregResponse);
+
+ AXAttributeFormats format = DetectAXFormat(axRequest.Attributes.Select(att => att.TypeUri));
+ if (format == AXAttributeFormats.None) {
+ // No recognized AX attributes were requested.
+ return;
+ }
+
+ var axResponse = response.Extensions.OfType<FetchResponse>().SingleOrDefault();
+ if (axResponse == null) {
+ axResponse = new FetchResponse();
+ response.Extensions.Add(axResponse);
+ }
+
+ AddAXAttributeValue(axResponse, WellKnownAttributes.BirthDate.WholeBirthDate, format, sregResponse.BirthDateRaw);
+ AddAXAttributeValue(axResponse, WellKnownAttributes.Contact.HomeAddress.Country, format, sregResponse.Country);
+ AddAXAttributeValue(axResponse, WellKnownAttributes.Contact.HomeAddress.PostalCode, format, sregResponse.PostalCode);
+ AddAXAttributeValue(axResponse, WellKnownAttributes.Contact.Email, format, sregResponse.Email);
+ AddAXAttributeValue(axResponse, WellKnownAttributes.Name.FullName, format, sregResponse.FullName);
+ AddAXAttributeValue(axResponse, WellKnownAttributes.Name.Alias, format, sregResponse.Nickname);
+ AddAXAttributeValue(axResponse, WellKnownAttributes.Preferences.TimeZone, format, sregResponse.TimeZone);
+ AddAXAttributeValue(axResponse, WellKnownAttributes.Preferences.Language, format, sregResponse.Language);
+ if (sregResponse.Gender.HasValue) {
+ AddAXAttributeValue(axResponse, WellKnownAttributes.Person.Gender, format, genderEncoder.Encode(sregResponse.Gender));
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the attribute value if available.
+ /// </summary>
+ /// <param name="fetchResponse">The AX fetch response extension to look for the attribute value.</param>
+ /// <param name="typeUri">The type URI of the attribute, using the axschema.org format of <see cref="WellKnownAttributes"/>.</param>
+ /// <param name="formats">The AX type URI formats to search.</param>
+ /// <returns>
+ /// The first value of the attribute, if available.
+ /// </returns>
+ internal static string GetAttributeValue(this FetchResponse fetchResponse, string typeUri, AXAttributeFormats formats) {
+ return ForEachFormat(formats).Select(format => fetchResponse.GetAttributeValue(TransformAXFormat(typeUri, format))).FirstOrDefault(s => s != null);
+ }
+
+ /// <summary>
+ /// Adds the AX attribute value to the response if it is non-empty.
+ /// </summary>
+ /// <param name="ax">The AX Fetch response to add the attribute value to.</param>
+ /// <param name="typeUri">The attribute type URI in axschema.org format.</param>
+ /// <param name="format">The target format of the actual attribute to write out.</param>
+ /// <param name="value">The value of the attribute.</param>
+ private static void AddAXAttributeValue(FetchResponse ax, string typeUri, AXAttributeFormats format, string value) {
+ if (!string.IsNullOrEmpty(value)) {
+ string targetTypeUri = TransformAXFormat(typeUri, format);
+ if (!ax.Attributes.Contains(targetTypeUri)) {
+ ax.Attributes.Add(targetTypeUri, value);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the demand level for an AX attribute.
+ /// </summary>
+ /// <param name="ax">The AX fetch request to search for the attribute.</param>
+ /// <param name="typeUri">The type URI of the attribute in axschema.org format.</param>
+ /// <returns>The demand level for the attribute.</returns>
+ private static DemandLevel GetDemandLevelFor(FetchRequest ax, string typeUri) {
+ Contract.Requires(ax != null);
+ Contract.Requires(!String.IsNullOrEmpty(typeUri));
+
+ foreach (AXAttributeFormats format in ForEachFormat(AXAttributeFormats.All)) {
+ string typeUriInFormat = TransformAXFormat(typeUri, format);
+ if (ax.Attributes.Contains(typeUriInFormat)) {
+ return ax.Attributes[typeUriInFormat].IsRequired ? DemandLevel.Require : DemandLevel.Request;
+ }
+ }
+
+ return DemandLevel.NoRequest;
+ }
+
+ /// <summary>
+ /// Tries to find the exact format of AX attribute Type URI supported by the Provider.
+ /// </summary>
+ /// <param name="request">The authentication request.</param>
+ /// <param name="attributeFormat">The attribute formats the RP will try if this discovery fails.</param>
+ /// <returns>The AX format(s) to use based on the Provider's advertised AX support.</returns>
+ private static bool TryDetectOPAttributeFormat(RelyingParty.IAuthenticationRequest request, out AXAttributeFormats attributeFormat) {
+ Contract.Requires(request != null);
+ var provider = (RelyingParty.ServiceEndpoint)request.Provider;
+ attributeFormat = DetectAXFormat(provider.ProviderDescription.Capabilities);
+ return attributeFormat != AXAttributeFormats.None;
+ }
+
+ /// <summary>
+ /// Detects the AX attribute type URI format from a given sample.
+ /// </summary>
+ /// <param name="typeURIs">The type URIs to scan for recognized formats.</param>
+ /// <returns>The first AX type URI format recognized in the list.</returns>
+ private static AXAttributeFormats DetectAXFormat(IEnumerable<string> typeURIs) {
+ Contract.Requires(typeURIs != null);
+
+ if (typeURIs.Any(uri => uri.StartsWith("http://axschema.org/", StringComparison.Ordinal))) {
+ return AXAttributeFormats.AXSchemaOrg;
+ }
+
+ if (typeURIs.Any(uri => uri.StartsWith("http://schema.openid.net/", StringComparison.Ordinal))) {
+ return AXAttributeFormats.SchemaOpenIdNet;
+ }
+
+ if (typeURIs.Any(uri => uri.StartsWith("http://openid.net/schema/", StringComparison.Ordinal))) {
+ return AXAttributeFormats.OpenIdNetSchema;
+ }
+
+ return AXAttributeFormats.None;
+ }
+
+ /// <summary>
+ /// Transforms an AX attribute type URI from the axschema.org format into a given format.
+ /// </summary>
+ /// <param name="axSchemaOrgFormatTypeUri">The ax schema org format type URI.</param>
+ /// <param name="targetFormat">The target format. Only one flag should be set.</param>
+ /// <returns>The AX attribute type URI in the target format.</returns>
+ private static string TransformAXFormat(string axSchemaOrgFormatTypeUri, AXAttributeFormats targetFormat) {
+ Contract.Requires(!String.IsNullOrEmpty(axSchemaOrgFormatTypeUri));
+
+ switch (targetFormat) {
+ case AXAttributeFormats.AXSchemaOrg:
+ return axSchemaOrgFormatTypeUri;
+ case AXAttributeFormats.SchemaOpenIdNet:
+ return axSchemaOrgFormatTypeUri.Replace("axschema.org", "schema.openid.net");
+ case AXAttributeFormats.OpenIdNetSchema:
+ return axSchemaOrgFormatTypeUri.Replace("axschema.org", "openid.net/schema");
+ default:
+ throw new ArgumentOutOfRangeException("targetFormat");
+ }
+ }
+
+ /// <summary>
+ /// Splits the AX attribute format flags into individual values for processing.
+ /// </summary>
+ /// <param name="formats">The formats to split up into individual flags.</param>
+ /// <returns>A sequence of individual flags.</returns>
+ private static IEnumerable<AXAttributeFormats> ForEachFormat(AXAttributeFormats formats) {
+ if ((formats & AXAttributeFormats.AXSchemaOrg) != 0) {
+ yield return AXAttributeFormats.AXSchemaOrg;
+ }
+
+ if ((formats & AXAttributeFormats.OpenIdNetSchema) != 0) {
+ yield return AXAttributeFormats.OpenIdNetSchema;
+ }
+
+ if ((formats & AXAttributeFormats.SchemaOpenIdNet) != 0) {
+ yield return AXAttributeFormats.SchemaOpenIdNet;
+ }
+ }
+
+ /// <summary>
+ /// Adds an attribute fetch request if it is not already present in the AX request.
+ /// </summary>
+ /// <param name="ax">The AX request to add the attribute request to.</param>
+ /// <param name="format">The format of the attribute's Type URI to use.</param>
+ /// <param name="axSchemaOrgFormatAttribute">The attribute in axschema.org format.</param>
+ /// <param name="demandLevel">The demand level.</param>
+ private static void FetchAttribute(FetchRequest ax, AXAttributeFormats format, string axSchemaOrgFormatAttribute, DemandLevel demandLevel) {
+ Contract.Requires(ax != null);
+ Contract.Requires(!String.IsNullOrEmpty(axSchemaOrgFormatAttribute));
+
+ string typeUri = TransformAXFormat(axSchemaOrgFormatAttribute, format);
+ if (!ax.Attributes.Contains(typeUri)) {
+ switch (demandLevel) {
+ case DemandLevel.Request:
+ ax.Attributes.AddOptional(typeUri);
+ break;
+ case DemandLevel.Require:
+ ax.Attributes.AddRequired(typeUri);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs
index d39692c..4392cd5 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/AuthenticationPolicies.cs
@@ -37,6 +37,12 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
public const string PhysicalMultiFactor = "http://schemas.openid.net/pape/policies/2007/06/multi-factor-physical";
/// <summary>
+ /// Indicates that the Provider MUST use a pair-wise pseudonym for the user that is persistent
+ /// and unique across the requesting realm as the openid.claimed_id and openid.identity (see Section 4.2).
+ /// </summary>
+ public const string PrivatePersonalIdentifier = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier";
+
+ /// <summary>
/// Used in a PAPE response to indicate that no PAPE authentication policies could be satisfied.
/// </summary>
/// <remarks>
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs
index a9adb4f..871ac27 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Extensions/SimpleRegistration/ClaimsRequest.cs
@@ -117,6 +117,12 @@ namespace DotNetOpenAuth.OpenId.Extensions.SimpleRegistration {
public DemandLevel TimeZone { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether this <see cref="ClaimsRequest"/> instance
+ /// is synthesized from an AX request at the Provider.
+ /// </summary>
+ internal bool Synthesized { get; set; }
+
+ /// <summary>
/// Gets or sets the value of the sreg.required parameter.
/// </summary>
/// <value>A comma-delimited list of sreg fields.</value>
diff --git a/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs b/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs
index 2b205d8..d44809f 100644
--- a/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs
+++ b/src/DotNetOpenAuth/OpenId/Interop/OpenIdRelyingPartyShim.cs
@@ -156,7 +156,7 @@ namespace DotNetOpenAuth.OpenId.Interop {
/// <returns>The Provider's response to a previous authentication request, or null if no response is present.</returns>
public AuthenticationResponseShim ProcessAuthentication(string url, string form) {
OpenIdRelyingParty rp = new OpenIdRelyingParty(null);
- HttpRequestInfo requestInfo = new HttpRequestInfo { Url = new Uri(url) };
+ HttpRequestInfo requestInfo = new HttpRequestInfo { UrlBeforeRewriting = new Uri(url) };
if (!string.IsNullOrEmpty(form)) {
requestInfo.HttpMethod = "POST";
requestInfo.InputStream = new MemoryStream(Encoding.Unicode.GetBytes(form));
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
index dc8c0ee..27dacfd 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.Designer.cs
@@ -70,6 +70,15 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Looks up a localized string similar to This is already a PPID Identifier..
+ /// </summary>
+ internal static string ArgumentIsPpidIdentifier {
+ get {
+ return ResourceManager.GetString("ArgumentIsPpidIdentifier", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to The requested association type &apos;{0}&apos; with session type &apos;{1}&apos; is unrecognized or not supported by this Provider due to security requirements..
/// </summary>
internal static string AssociationOrSessionTypeUnrecognizedOrNotSupported {
@@ -97,6 +106,15 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Looks up a localized string similar to No association store has been given but is required for the current configuration..
+ /// </summary>
+ internal static string AssociationStoreRequired {
+ get {
+ return ResourceManager.GetString("AssociationStoreRequired", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to If an association store is given, a nonce store must also be provided..
/// </summary>
internal static string AssociationStoreRequiresNonceStore {
@@ -407,6 +425,15 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Looks up a localized string similar to No identifier has been set..
+ /// </summary>
+ internal static string NoIdentifierSet {
+ get {
+ return ResourceManager.GetString("NoIdentifierSet", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to No XRDS document containing OpenID relying party endpoint information could be found at {0}..
/// </summary>
internal static string NoRelyingPartyEndpointDiscovered {
@@ -470,6 +497,15 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Looks up a localized string similar to This property value is not supported by this control..
+ /// </summary>
+ internal static string PropertyValueNotSupported {
+ get {
+ return ResourceManager.GetString("PropertyValueNotSupported", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Unable to determine the version of the OpenID protocol implemented by the Provider at endpoint &apos;{0}&apos;..
/// </summary>
internal static string ProviderVersionUnrecognized {
@@ -569,6 +605,15 @@ namespace DotNetOpenAuth.OpenId {
}
/// <summary>
+ /// Looks up a localized string similar to The property {0} had unexpected value {1}..
+ /// </summary>
+ internal static string UnexpectedEnumPropertyValue {
+ get {
+ return ResourceManager.GetString("UnexpectedEnumPropertyValue", resourceCulture);
+ }
+ }
+
+ /// <summary>
/// Looks up a localized string similar to Unexpected HTTP status code {0} {1} received in direct response..
/// </summary>
internal static string UnexpectedHttpStatusCode {
diff --git a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
index 331e502..bca813b 100644
--- a/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
+++ b/src/DotNetOpenAuth/OpenId/OpenIdStrings.resx
@@ -319,4 +319,19 @@ Discovered endpoint info:
<data name="XriResolutionDisabled" xml:space="preserve">
<value>XRI support has been disabled at this site.</value>
</data>
+ <data name="AssociationStoreRequired" xml:space="preserve">
+ <value>No association store has been given but is required for the current configuration.</value>
+ </data>
+ <data name="UnexpectedEnumPropertyValue" xml:space="preserve">
+ <value>The property {0} had unexpected value {1}.</value>
+ </data>
+ <data name="NoIdentifierSet" xml:space="preserve">
+ <value>No identifier has been set.</value>
+ </data>
+ <data name="PropertyValueNotSupported" xml:space="preserve">
+ <value>This property value is not supported by this control.</value>
+ </data>
+ <data name="ArgumentIsPpidIdentifier" xml:space="preserve">
+ <value>This is already a PPID Identifier.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OpenId/Protocol.cs b/src/DotNetOpenAuth/OpenId/Protocol.cs
index c3ac090..d84a923 100644
--- a/src/DotNetOpenAuth/OpenId/Protocol.cs
+++ b/src/DotNetOpenAuth/OpenId/Protocol.cs
@@ -12,6 +12,7 @@ namespace DotNetOpenAuth.OpenId {
using System.Globalization;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
+ using System.Diagnostics;
/// <summary>
/// An enumeration of the OpenID protocol versions supported by this library.
@@ -35,6 +36,7 @@ namespace DotNetOpenAuth.OpenId {
/// Tracks the several versions of OpenID this library supports and the unique
/// constants to each version used in the protocol.
/// </summary>
+ [DebuggerDisplay("OpenID {Version}")]
internal sealed class Protocol {
/// <summary>
/// The value of the openid.ns parameter in the OpenID 2.0 specification.
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs
index ac39356..430e1bc 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/AnonymousRequest.cs
@@ -14,6 +14,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// Provides access to a host Provider to read an incoming extension-only checkid request message,
/// and supply extension responses or a cancellation message to the RP.
/// </summary>
+ [Serializable]
internal class AnonymousRequest : HostProcessedRequest, IAnonymousRequest {
/// <summary>
/// The extension-response message to send, if the host site chooses to send it.
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
index 81beb01..2f5bab1 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/AuthenticationRequest.cs
@@ -199,6 +199,18 @@ namespace DotNetOpenAuth.OpenId.Provider {
this.positiveResponse.ClaimedIdentifier = builder.Uri;
}
+ /// <summary>
+ /// Sets the Claimed and Local identifiers even after they have been initially set.
+ /// </summary>
+ /// <param name="identifier">The value to set to the <see cref="ClaimedIdentifier"/> and <see cref="LocalIdentifier"/> properties.</param>
+ internal void ResetClaimedAndLocalIdentifiers(Identifier identifier) {
+ Contract.Requires(identifier != null);
+ ErrorUtilities.VerifyArgumentNotNull(identifier, "identifier");
+
+ this.positiveResponse.ClaimedIdentifier = identifier;
+ this.positiveResponse.LocalIdentifier = identifier;
+ }
+
#endregion
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs
index 67c0d99..e5988dd 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/AutoResponsiveRequest.cs
@@ -29,8 +29,9 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </summary>
/// <param name="request">The request message.</param>
/// <param name="response">The response that is ready for transmittal.</param>
- internal AutoResponsiveRequest(IDirectedProtocolMessage request, IProtocolMessage response)
- : base(request) {
+ /// <param name="securitySettings">The security settings.</param>
+ internal AutoResponsiveRequest(IDirectedProtocolMessage request, IProtocolMessage response, ProviderSecuritySettings securitySettings)
+ : base(request, securitySettings) {
Contract.Requires<ArgumentNullException>(response != null);
this.response = response;
@@ -41,8 +42,9 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// for a response to an unrecognizable request.
/// </summary>
/// <param name="response">The response that is ready for transmittal.</param>
- internal AutoResponsiveRequest(IProtocolMessage response)
- : base(IndirectResponseBase.GetVersion(response)) {
+ /// <param name="securitySettings">The security settings.</param>
+ internal AutoResponsiveRequest(IProtocolMessage response, ProviderSecuritySettings securitySettings)
+ : base(IndirectResponseBase.GetVersion(response), securitySettings) {
Contract.Requires<ArgumentNullException>(response != null);
this.response = response;
diff --git a/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
index 94b63df..bca7cc5 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/HostProcessedRequest.cs
@@ -17,6 +17,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// A base class from which identity and non-identity RP requests can derive.
/// </summary>
+ [Serializable]
internal abstract class HostProcessedRequest : Request, IHostProcessedRequest {
/// <summary>
/// The negative assertion to send, if the host site chooses to send it.
@@ -24,12 +25,17 @@ namespace DotNetOpenAuth.OpenId.Provider {
private readonly NegativeAssertionResponse negativeResponse;
/// <summary>
+ /// A cache of the result from discovery of the Realm URL.
+ /// </summary>
+ private RelyingPartyDiscoveryResult? realmDiscoveryResult;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="HostProcessedRequest"/> class.
/// </summary>
/// <param name="provider">The provider that received the request.</param>
/// <param name="request">The incoming request message.</param>
protected HostProcessedRequest(OpenIdProvider provider, SignedResponseRequest request)
- : base(request) {
+ : base(request, provider.SecuritySettings) {
Contract.Requires<ArgumentNullException>(provider != null);
this.negativeResponse = new NegativeAssertionResponse(request, provider.Channel);
@@ -63,6 +69,13 @@ namespace DotNetOpenAuth.OpenId.Provider {
#endregion
/// <summary>
+ /// Gets a value indicating whether realm discovery been performed.
+ /// </summary>
+ internal bool HasRealmDiscoveryBeenPerformed {
+ get { return this.realmDiscoveryResult.HasValue; }
+ }
+
+ /// <summary>
/// Gets the negative response.
/// </summary>
protected NegativeAssertionResponse NegativeResponse {
@@ -84,9 +97,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// succeeded.
/// </summary>
/// <param name="provider">The OpenIdProvider that is performing the RP discovery.</param>
- /// <returns>
- /// <c>true</c> if the Relying Party passed discovery verification; <c>false</c> otherwise.
- /// </returns>
+ /// <returns>Result of realm discovery.</returns>
/// <remarks>
/// Return URL verification is only attempted if this property is queried.
/// The result of the verification is cached per request so calling this
@@ -95,10 +106,26 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// </remarks>
public RelyingPartyDiscoveryResult IsReturnUrlDiscoverable(OpenIdProvider provider) {
ErrorUtilities.VerifyArgumentNotNull(provider, "provider");
+ if (!this.realmDiscoveryResult.HasValue) {
+ this.realmDiscoveryResult = this.IsReturnUrlDiscoverableCore(provider);
+ }
+
+ return this.realmDiscoveryResult.Value;
+ }
+
+ /// <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(provider != null);
+
ErrorUtilities.VerifyInternal(this.Realm != null, "Realm should have been read or derived by now.");
try {
- if (provider.SecuritySettings.RequireSsl && this.Realm.Scheme != Uri.UriSchemeHttps) {
+ 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;
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IDirectedIdentityIdentifierProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/IDirectedIdentityIdentifierProvider.cs
new file mode 100644
index 0000000..455f1bf
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Provider/IDirectedIdentityIdentifierProvider.cs
@@ -0,0 +1,82 @@
+//-----------------------------------------------------------------------
+// <copyright file="IDirectedIdentityIdentifierProvider.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using System;
+ using System.Diagnostics.Contracts;
+
+ /// <summary>
+ /// An interface to provide custom identifiers for users logging into specific relying parties.
+ /// </summary>
+ /// <remarks>
+ /// This interface would allow, for example, the Provider to offer PPIDs to their users,
+ /// allowing the users to log into RPs without leaving any clue as to their true identity,
+ /// and preventing multiple RPs from colluding to track user activity across realms.
+ /// </remarks>
+ [ContractClass(typeof(IDirectedIdentityIdentifierProviderContract))]
+ public interface IDirectedIdentityIdentifierProvider {
+ /// <summary>
+ /// Gets the Identifier to use for the Claimed Identifier and Local Identifier of
+ /// an outgoing positive assertion.
+ /// </summary>
+ /// <param name="localIdentifier">The OP local identifier for the authenticating user.</param>
+ /// <param name="relyingPartyRealm">The realm of the relying party receiving the assertion.</param>
+ /// <returns>
+ /// A valid, discoverable OpenID Identifier that should be used as the value for the
+ /// openid.claimed_id and openid.local_id parameters. Must not be null.
+ /// </returns>
+ Uri GetIdentifier(Identifier localIdentifier, Realm relyingPartyRealm);
+
+ /// <summary>
+ /// Determines whether a given identifier is the primary (non-PPID) local identifier for some user.
+ /// </summary>
+ /// <param name="identifier">The identifier in question.</param>
+ /// <returns>
+ /// <c>true</c> if the given identifier is the valid, unique identifier for some uesr (and NOT a PPID); otherwise, <c>false</c>.
+ /// </returns>
+ bool IsUserLocalIdentifier(Identifier identifier);
+ }
+
+ /// <summary>
+ /// Contract class for the <see cref="IDirectedIdentityIdentifierProvider"/> type.
+ /// </summary>
+ [ContractClassFor(typeof(IDirectedIdentityIdentifierProvider))]
+ internal abstract class IDirectedIdentityIdentifierProviderContract : IDirectedIdentityIdentifierProvider {
+ #region IDirectedIdentityIdentifierProvider Members
+
+ /// <summary>
+ /// Gets the Identifier to use for the Claimed Identifier and Local Identifier of
+ /// an outgoing positive assertion.
+ /// </summary>
+ /// <param name="localIdentifier">The OP local identifier for the authenticating user.</param>
+ /// <param name="relyingPartyRealm">The realm of the relying party receiving the assertion.</param>
+ /// <returns>
+ /// A valid, discoverable OpenID Identifier that should be used as the value for the
+ /// openid.claimed_id and openid.local_id parameters. Must not be null.
+ /// </returns>
+ Uri IDirectedIdentityIdentifierProvider.GetIdentifier(Identifier localIdentifier, Realm relyingPartyRealm) {
+ Contract.Requires(localIdentifier != null);
+ Contract.Requires(relyingPartyRealm != null);
+
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Determines whether a given identifier is the primary (non-PPID) local identifier for some user.
+ /// </summary>
+ /// <param name="identifier">The identifier in question.</param>
+ /// <returns>
+ /// <c>true</c> if the given identifier is the valid, unique identifier for some uesr (and NOT a PPID); otherwise, <c>false</c>.
+ /// </returns>
+ bool IDirectedIdentityIdentifierProvider.IsUserLocalIdentifier(Identifier identifier) {
+ Contract.Requires(identifier != null);
+
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs
index 458d256..f605f31 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/IHostProcessedRequest.cs
@@ -85,7 +85,18 @@ namespace DotNetOpenAuth.OpenId.Provider {
#endregion
- #region IRequest Properties
+ #region IRequest Members
+
+ /// <summary>
+ /// Gets or sets the security settings that apply to this request.
+ /// </summary>
+ /// <value>
+ /// Defaults to the <see cref="OpenIdProvider.SecuritySettings"/> on the <see cref="OpenIdProvider"/>.
+ /// </value>
+ ProviderSecuritySettings IRequest.SecuritySettings {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
/// <summary>
/// Gets a value indicating whether the response is ready to be sent to the user agent.
@@ -98,30 +109,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
get { throw new System.NotImplementedException(); }
}
- #endregion
-
- #region IHostProcessedRequest Methods
-
- /// <summary>
- /// Attempts to perform relying party discovery of the return URL claimed by the Relying Party.
- /// </summary>
- /// <param name="provider">The OpenIdProvider that is performing the RP discovery.</param>
- /// <returns>
- /// The details of how successful the relying party discovery was.
- /// </returns>
- /// <remarks>
- /// <para>Return URL verification is only attempted if this method is called.</para>
- /// <para>See OpenID Authentication 2.0 spec section 9.2.1.</para>
- /// </remarks>
- RelyingPartyDiscoveryResult IHostProcessedRequest.IsReturnUrlDiscoverable(OpenIdProvider provider) {
- Contract.Requires<ArgumentNullException>(provider != null);
- throw new System.NotImplementedException();
- }
-
- #endregion
-
- #region IRequest Methods
-
/// <summary>
/// Adds an extension to the response to send to the relying party.
/// </summary>
@@ -148,7 +135,27 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <returns>
/// An instance of the extension initialized with values passed in with the request.
/// </returns>
- IOpenIdMessageExtension IRequest.GetExtension(System.Type extensionType) {
+ DotNetOpenAuth.OpenId.Messages.IOpenIdMessageExtension IRequest.GetExtension(System.Type extensionType) {
+ throw new System.NotImplementedException();
+ }
+
+ #endregion
+
+ #region IHostProcessedRequest Methods
+
+ /// <summary>
+ /// Attempts to perform relying party discovery of the return URL claimed by the Relying Party.
+ /// </summary>
+ /// <param name="provider">The OpenIdProvider that is performing the RP discovery.</param>
+ /// <returns>
+ /// The details of how successful the relying party discovery was.
+ /// </returns>
+ /// <remarks>
+ /// <para>Return URL verification is only attempted if this method is called.</para>
+ /// <para>See OpenID Authentication 2.0 spec section 9.2.1.</para>
+ /// </remarks>
+ RelyingPartyDiscoveryResult IHostProcessedRequest.IsReturnUrlDiscoverable(OpenIdProvider provider) {
+ Contract.Requires(provider != null);
throw new System.NotImplementedException();
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs b/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs
new file mode 100644
index 0000000..48d40d4
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Provider/IProviderBehavior.cs
@@ -0,0 +1,54 @@
+//-----------------------------------------------------------------------
+// <copyright file="IProviderBehavior.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using DotNetOpenAuth.OpenId.ChannelElements;
+
+ /// <summary>
+ /// Applies a custom security policy to certain OpenID security settings and behaviors.
+ /// </summary>
+ /// <remarks>
+ /// BEFORE MARKING THIS INTERFACE PUBLIC: it's very important that we shift the methods to be channel-level
+ /// rather than facade class level and for the OpenIdChannel to be the one to invoke these methods.
+ /// </remarks>
+ internal interface IProviderBehavior {
+ /// <summary>
+ /// Applies a well known set of security requirements to a default set of security settings.
+ /// </summary>
+ /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param>
+ /// <remarks>
+ /// Care should be taken to never decrease security when applying a profile.
+ /// Profiles should only enhance security requirements to avoid being
+ /// incompatible with each other.
+ /// </remarks>
+ void ApplySecuritySettings(ProviderSecuritySettings securitySettings);
+
+ /// <summary>
+ /// Called when a request is received by the Provider.
+ /// </summary>
+ /// <param name="request">The incoming request.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ /// <remarks>
+ /// Implementations may set a new value to <see cref="IRequest.SecuritySettings"/> but
+ /// should not change the properties on the instance of <see cref="ProviderSecuritySettings"/>
+ /// itself as that instance may be shared across many requests.
+ /// </remarks>
+ bool OnIncomingRequest(IRequest request);
+
+ /// <summary>
+ /// Called when the Provider is preparing to send a response to an authentication request.
+ /// </summary>
+ /// <param name="request">The request that is configured to generate the outgoing response.</param>
+ /// <returns>
+ /// <c>true</c> if this behavior owns this request and wants to stop other behaviors
+ /// from handling it; <c>false</c> to allow other behaviors to process this request.
+ /// </returns>
+ bool OnOutgoingResponse(IAuthenticationRequest request);
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs b/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs
index d2a2e0c..0fcdc28 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/IRequest.cs
@@ -33,6 +33,12 @@ namespace DotNetOpenAuth.OpenId.Provider {
bool IsResponseReady { get; }
/// <summary>
+ /// Gets or sets the security settings that apply to this request.
+ /// </summary>
+ /// <value>Defaults to the <see cref="OpenIdProvider.SecuritySettings"/> on the <see cref="OpenIdProvider"/>.</value>
+ ProviderSecuritySettings SecuritySettings { get; set; }
+
+ /// <summary>
/// Adds an extension to the response to send to the relying party.
/// </summary>
/// <param name="extension">The extension to add to the response message.</param>
@@ -68,6 +74,17 @@ namespace DotNetOpenAuth.OpenId.Provider {
#region IRequest Members
/// <summary>
+ /// Gets or sets the security settings that apply to this request.
+ /// </summary>
+ /// <value>
+ /// Defaults to the <see cref="OpenIdProvider.SecuritySettings"/> on the <see cref="OpenIdProvider"/>.
+ /// </value>
+ ProviderSecuritySettings IRequest.SecuritySettings {
+ get { throw new NotImplementedException(); }
+ set { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
/// Gets a value indicating whether the response is ready to be sent to the user agent.
/// </summary>
/// <remarks>
diff --git a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
index 9645509..14a60a8 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/OpenIdProvider.cs
@@ -7,6 +7,8 @@
namespace DotNetOpenAuth.OpenId.Provider {
using System;
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
@@ -31,6 +33,11 @@ namespace DotNetOpenAuth.OpenId.Provider {
private const string ApplicationStoreKey = "DotNetOpenAuth.OpenId.Provider.OpenIdProvider.ApplicationStore";
/// <summary>
+ /// Backing store for the <see cref="Behaviors"/> property.
+ /// </summary>
+ private readonly ObservableCollection<IProviderBehavior> behaviors = new ObservableCollection<IProviderBehavior>();
+
+ /// <summary>
/// Backing field for the <see cref="SecuritySettings"/> property.
/// </summary>
private ProviderSecuritySettings securitySettings;
@@ -73,6 +80,11 @@ namespace DotNetOpenAuth.OpenId.Provider {
this.AssociationStore = associationStore;
this.SecuritySettings = DotNetOpenAuthSection.Configuration.OpenId.Provider.SecuritySettings.CreateSecuritySettings();
+ this.behaviors.CollectionChanged += this.OnBehaviorsChanged;
+ foreach (var behavior in DotNetOpenAuthSection.Configuration.OpenId.Provider.Behaviors.CreateInstances(false)) {
+ this.behaviors.Add(behavior);
+ }
+
this.Channel = new OpenIdChannel(this.AssociationStore, nonceStore, this.SecuritySettings);
}
@@ -137,6 +149,13 @@ namespace DotNetOpenAuth.OpenId.Provider {
public IErrorReporting ErrorReporting { get; set; }
/// <summary>
+ /// Gets a list of custom behaviors to apply to OpenID actions.
+ /// </summary>
+ internal ICollection<IProviderBehavior> Behaviors {
+ get { return this.behaviors; }
+ }
+
+ /// <summary>
/// Gets the association store.
/// </summary>
internal IAssociationStore<AssociationRelyingPartyType> AssociationStore { get; private set; }
@@ -191,31 +210,50 @@ namespace DotNetOpenAuth.OpenId.Provider {
// If the incoming request does not resemble an OpenID message at all,
// it's probably a user who just navigated to this URL, and we should
// just return null so the host can display a message to the user.
- if (httpRequestInfo.HttpMethod == "GET" && !httpRequestInfo.Url.QueryStringContainPrefixedParameters(Protocol.Default.openid.Prefix)) {
+ if (httpRequestInfo.HttpMethod == "GET" && !httpRequestInfo.UrlBeforeRewriting.QueryStringContainPrefixedParameters(Protocol.Default.openid.Prefix)) {
return null;
}
ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessageReceivedOfMany);
}
+ IRequest result = null;
+
var checkIdMessage = incomingMessage as CheckIdRequest;
if (checkIdMessage != null) {
- return new AuthenticationRequest(this, checkIdMessage);
+ result = new AuthenticationRequest(this, checkIdMessage);
}
- var extensionOnlyRequest = incomingMessage as SignedResponseRequest;
- if (extensionOnlyRequest != null) {
- return new AnonymousRequest(this, extensionOnlyRequest);
+ if (result == null) {
+ var extensionOnlyRequest = incomingMessage as SignedResponseRequest;
+ if (extensionOnlyRequest != null) {
+ result = new AnonymousRequest(this, extensionOnlyRequest);
+ }
}
- var checkAuthMessage = incomingMessage as CheckAuthenticationRequest;
- if (checkAuthMessage != null) {
- return new AutoResponsiveRequest(incomingMessage, new CheckAuthenticationResponse(checkAuthMessage, this));
+ if (result == null) {
+ var checkAuthMessage = incomingMessage as CheckAuthenticationRequest;
+ if (checkAuthMessage != null) {
+ result = new AutoResponsiveRequest(incomingMessage, new CheckAuthenticationResponse(checkAuthMessage, this), this.SecuritySettings);
+ }
}
- var associateMessage = incomingMessage as AssociateRequest;
- if (associateMessage != null) {
- return new AutoResponsiveRequest(incomingMessage, associateMessage.CreateResponse(this.AssociationStore, this.SecuritySettings));
+ if (result == null) {
+ var associateMessage = incomingMessage as AssociateRequest;
+ if (associateMessage != null) {
+ result = new AutoResponsiveRequest(incomingMessage, associateMessage.CreateResponse(this.AssociationStore, this.SecuritySettings), this.SecuritySettings);
+ }
+ }
+
+ if (result != null) {
+ foreach (var behavior in this.Behaviors) {
+ if (behavior.OnIncomingRequest(result)) {
+ // This behavior matched this request.
+ break;
+ }
+ }
+
+ return result;
}
throw ErrorUtilities.ThrowProtocol(MessagingStrings.UnexpectedMessageReceivedOfMany);
@@ -246,6 +284,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
Contract.Requires<ArgumentNullException>(request != null);
Contract.Requires(((Request)request).IsResponseReady);
+ this.ApplyBehaviorsToResponse(request);
Request requestInternal = (Request)request;
this.Channel.Send(requestInternal.Response);
}
@@ -261,6 +300,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
Contract.Requires<ArgumentNullException>(request != null);
Contract.Requires(((Request)request).IsResponseReady);
+ this.ApplyBehaviorsToResponse(request);
Request requestInternal = (Request)request;
return this.Channel.PrepareResponse(requestInternal.Response);
}
@@ -384,6 +424,22 @@ namespace DotNetOpenAuth.OpenId.Provider {
#endregion
/// <summary>
+ /// Applies all behaviors to the response message.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ private void ApplyBehaviorsToResponse(IRequest request) {
+ var authRequest = request as IAuthenticationRequest;
+ if (authRequest != null) {
+ foreach (var behavior in this.Behaviors) {
+ if (behavior.OnOutgoingResponse(authRequest)) {
+ // This behavior matched this request.
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
/// Prepares the return value for the GetRequest method in the event of an exception.
/// </summary>
/// <param name="ex">The exception that forms the basis of the error response. Must not be null.</param>
@@ -438,9 +494,20 @@ namespace DotNetOpenAuth.OpenId.Provider {
}
if (incomingMessage != null) {
- return new AutoResponsiveRequest(incomingMessage, errorMessage);
+ return new AutoResponsiveRequest(incomingMessage, errorMessage, this.SecuritySettings);
} else {
- return new AutoResponsiveRequest(errorMessage);
+ return new AutoResponsiveRequest(errorMessage, this.SecuritySettings);
+ }
+ }
+
+ /// <summary>
+ /// Called by derived classes when behaviors are added or removed.
+ /// </summary>
+ /// <param name="sender">The collection being modified.</param>
+ /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
+ private void OnBehaviorsChanged(object sender, NotifyCollectionChangedEventArgs e) {
+ foreach (IProviderBehavior profile in e.NewItems) {
+ profile.ApplySecuritySettings(this.SecuritySettings);
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs b/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs
new file mode 100644
index 0000000..64d2908
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/Provider/PrivatePersonalIdentifierProviderBase.cs
@@ -0,0 +1,228 @@
+//-----------------------------------------------------------------------
+// <copyright file="PrivatePersonalIdentifierProviderBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.Provider {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.Linq;
+ using System.Security.Cryptography;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// Provides standard PPID Identifiers to users to protect their identity from individual relying parties
+ /// and from colluding groups of relying parties.
+ /// </summary>
+ public abstract class PrivatePersonalIdentifierProviderBase : IDirectedIdentityIdentifierProvider {
+ /// <summary>
+ /// The type of hash function to use for the <see cref="Hasher"/> property.
+ /// </summary>
+ private const string HashAlgorithmName = "SHA256";
+
+ /// <summary>
+ /// The length of the salt to generate for first time PPID-users.
+ /// </summary>
+ private int newSaltLength = 20;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="PrivatePersonalIdentifierProviderBase"/> class.
+ /// </summary>
+ /// <param name="baseIdentifier">The base URI on which to append the anonymous part.</param>
+ public PrivatePersonalIdentifierProviderBase(Uri baseIdentifier) {
+ Contract.Requires(baseIdentifier != null);
+ ErrorUtilities.VerifyArgumentNotNull(baseIdentifier, "baseIdentifier");
+
+ this.Hasher = HashAlgorithm.Create(HashAlgorithmName);
+ this.Encoder = Encoding.UTF8;
+ this.BaseIdentifier = baseIdentifier;
+ this.PairwiseUnique = AudienceScope.Realm;
+ }
+
+ /// <summary>
+ /// A granularity description for who wide of an audience sees the same generated PPID.
+ /// </summary>
+ public enum AudienceScope {
+ /// <summary>
+ /// A unique Identifier is generated for every realm. This is the highest security setting.
+ /// </summary>
+ Realm,
+
+ /// <summary>
+ /// Only the host name in the realm is used in calculating the PPID,
+ /// allowing for some level of sharing of the PPID Identifiers between RPs
+ /// that are able to share the same realm host value.
+ /// </summary>
+ RealmHost,
+
+ /// <summary>
+ /// Although the user's Identifier is still opaque to the RP so they cannot determine
+ /// who the user is at the OP, the same Identifier is used at all RPs so collusion
+ /// between the RPs is possible.
+ /// </summary>
+ Global,
+ }
+
+ /// <summary>
+ /// Gets the base URI on which to append the anonymous part.
+ /// </summary>
+ public Uri BaseIdentifier { get; private set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether each Realm will get its own private identifier
+ /// for the authenticating uesr.
+ /// </summary>
+ /// <value>The default value is <see cref="AudienceScope.Realm"/>.</value>
+ public AudienceScope PairwiseUnique { get; set; }
+
+ /// <summary>
+ /// Gets the hash function to use to perform the one-way transform of a personal identifier
+ /// to an "anonymous" looking one.
+ /// </summary>
+ protected HashAlgorithm Hasher { get; private set; }
+
+ /// <summary>
+ /// Gets the encoder to use for transforming the personal identifier into bytes for hashing.
+ /// </summary>
+ protected Encoding Encoder { get; private set; }
+
+ /// <summary>
+ /// Gets or sets the new length of the salt.
+ /// </summary>
+ /// <value>The new length of the salt.</value>
+ protected int NewSaltLength {
+ get {
+ return this.newSaltLength;
+ }
+
+ set {
+ Contract.Requires(value > 0);
+ ErrorUtilities.VerifyArgumentInRange(value > 0, "value");
+ this.newSaltLength = value;
+ }
+ }
+
+ #region IDirectedIdentityIdentifierProvider Members
+
+ /// <summary>
+ /// Gets the Identifier to use for the Claimed Identifier and Local Identifier of
+ /// an outgoing positive assertion.
+ /// </summary>
+ /// <param name="localIdentifier">The OP local identifier for the authenticating user.</param>
+ /// <param name="relyingPartyRealm">The realm of the relying party receiving the assertion.</param>
+ /// <returns>
+ /// A valid, discoverable OpenID Identifier that should be used as the value for the
+ /// openid.claimed_id and openid.local_id parameters. Must not be null.
+ /// </returns>
+ public Uri GetIdentifier(Identifier localIdentifier, Realm relyingPartyRealm) {
+ ErrorUtilities.VerifyArgumentNotNull(localIdentifier, "localIdentifier");
+ ErrorUtilities.VerifyArgumentNotNull(relyingPartyRealm, "relyingPartyRealm");
+ ErrorUtilities.VerifyArgumentNamed(this.IsUserLocalIdentifier(localIdentifier), "localIdentifier", OpenIdStrings.ArgumentIsPpidIdentifier);
+
+ byte[] salt = this.GetHashSaltForLocalIdentifier(localIdentifier);
+ string valueToHash = localIdentifier + "#";
+ switch (this.PairwiseUnique) {
+ case AudienceScope.Realm:
+ valueToHash += relyingPartyRealm;
+ break;
+ case AudienceScope.RealmHost:
+ valueToHash += relyingPartyRealm.Host;
+ break;
+ case AudienceScope.Global:
+ break;
+ default:
+ throw new InvalidOperationException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ OpenIdStrings.UnexpectedEnumPropertyValue,
+ "PairwiseUnique",
+ this.PairwiseUnique));
+ }
+
+ byte[] valueAsBytes = this.Encoder.GetBytes(valueToHash);
+ byte[] bytesToHash = new byte[valueAsBytes.Length + salt.Length];
+ valueAsBytes.CopyTo(bytesToHash, 0);
+ salt.CopyTo(bytesToHash, valueAsBytes.Length);
+ byte[] hash = this.Hasher.ComputeHash(bytesToHash);
+ string base64Hash = Convert.ToBase64String(hash);
+ Uri anonymousIdentifier = this.AppendIdentifiers(base64Hash);
+ return anonymousIdentifier;
+ }
+
+ /// <summary>
+ /// Determines whether a given identifier is the primary (non-PPID) local identifier for some user.
+ /// </summary>
+ /// <param name="identifier">The identifier in question.</param>
+ /// <returns>
+ /// <c>true</c> if the given identifier is the valid, unique identifier for some uesr (and NOT a PPID); otherwise, <c>false</c>.
+ /// </returns>
+ public virtual bool IsUserLocalIdentifier(Identifier identifier)
+ {
+ return !identifier.ToString().StartsWith(this.BaseIdentifier.AbsoluteUri, StringComparison.Ordinal);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Creates a new salt to assign to a user.
+ /// </summary>
+ /// <returns>A non-null buffer of length <see cref="NewSaltLength"/> filled with a random salt.</returns>
+ protected virtual byte[] CreateSalt() {
+ // We COULD use a crypto random function, but for a salt it seems overkill.
+ return MessagingUtilities.GetNonCryptoRandomData(this.NewSaltLength);
+ }
+
+ /// <summary>
+ /// Creates a new PPID Identifier by appending a pseudonymous identifier suffix to
+ /// the <see cref="BaseIdentifier"/>.
+ /// </summary>
+ /// <param name="uriHash">The unique part of the Identifier to append to the common first part.</param>
+ /// <returns>The full PPID Identifier.</returns>
+ protected virtual Uri AppendIdentifiers(string uriHash) {
+ Contract.Requires(!String.IsNullOrEmpty(uriHash));
+ ErrorUtilities.VerifyNonZeroLength(uriHash, "uriHash");
+
+ if (string.IsNullOrEmpty(this.BaseIdentifier.Query)) {
+ // The uriHash will appear on the path itself.
+ string pathEncoded = Uri.EscapeUriString(uriHash.Replace('/', '_'));
+ return new Uri(this.BaseIdentifier, pathEncoded);
+ } else {
+ // The uriHash will appear on the query string.
+ string dataEncoded = Uri.EscapeDataString(uriHash);
+ return new Uri(this.BaseIdentifier + dataEncoded);
+ }
+ }
+
+ /// <summary>
+ /// Gets the salt to use for generating an anonymous identifier for a given OP local identifier.
+ /// </summary>
+ /// <param name="localIdentifier">The OP local identifier.</param>
+ /// <returns>The salt to use in the hash.</returns>
+ /// <remarks>
+ /// It is important that this method always return the same value for a given
+ /// <paramref name="localIdentifier"/>.
+ /// New salts can be generated for local identifiers without previously assigned salt
+ /// values by calling <see cref="CreateSalt"/> or by a custom method.
+ /// </remarks>
+ protected abstract byte[] GetHashSaltForLocalIdentifier(Identifier localIdentifier);
+
+#if CONTRACTS_FULL
+ /// <summary>
+ /// Verifies conditions that should be true for any valid state of this object.
+ /// </summary>
+ [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by code contracts.")]
+ [ContractInvariantMethod]
+ protected void ObjectInvariant() {
+ Contract.Invariant(this.Hasher != null);
+ Contract.Invariant(this.Encoder != null);
+ Contract.Invariant(this.BaseIdentifier != null);
+ Contract.Invariant(this.NewSaltLength > 0);
+ }
+#endif
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
index b2adafc..f778b76 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs
@@ -135,8 +135,9 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// Sends the response for the <see cref="PendingAuthenticationRequest"/> and clears the property.
/// </summary>
public static void SendResponse() {
- Provider.SendResponse(PendingRequest);
+ var pendingRequest = PendingRequest;
PendingRequest = null;
+ Provider.SendResponse(pendingRequest);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
index 4568dc2..876e412 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderSecuritySettings.cs
@@ -7,11 +7,15 @@
namespace DotNetOpenAuth.OpenId.Provider {
using System;
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Collections.Specialized;
+ using System.Linq;
using DotNetOpenAuth.Messaging;
/// <summary>
/// Security settings that are applicable to providers.
/// </summary>
+ [Serializable]
public sealed class ProviderSecuritySettings : SecuritySettings {
/// <summary>
/// The default value for the <see cref="ProtectDownlevelReplayAttacks"/> property.
@@ -81,5 +85,24 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// needed for testing the RP's rejection of unsigned extensions.
/// </remarks>
internal bool SignOutgoingExtensions { get; set; }
+
+ /// <summary>
+ /// Creates a deep clone of this instance.
+ /// </summary>
+ /// <returns>A new instance that is a deep clone of this instance.</returns>
+ internal ProviderSecuritySettings Clone() {
+ var securitySettings = new ProviderSecuritySettings();
+ foreach (var pair in this.AssociationLifetimes) {
+ securitySettings.AssociationLifetimes.Add(pair);
+ }
+
+ securitySettings.MaximumHashBitLength = this.MaximumHashBitLength;
+ securitySettings.MinimumHashBitLength = this.MinimumHashBitLength;
+ securitySettings.ProtectDownlevelReplayAttacks = this.ProtectDownlevelReplayAttacks;
+ securitySettings.RequireSsl = this.RequireSsl;
+ securitySettings.SignOutgoingExtensions = this.SignOutgoingExtensions;
+
+ return securitySettings;
+ }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/Request.cs b/src/DotNetOpenAuth/OpenId/Provider/Request.cs
index 50b9f54..d2c0398 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/Request.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/Request.cs
@@ -52,10 +52,14 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// Initializes a new instance of the <see cref="Request"/> class.
/// </summary>
/// <param name="request">The incoming request message.</param>
- protected Request(IDirectedProtocolMessage request) {
+ /// <param name="securitySettings">The security settings from the channel.</param>
+ protected Request(IDirectedProtocolMessage request, ProviderSecuritySettings securitySettings) {
Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires(securitySettings != null);
+ ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
this.request = request;
+ this.SecuritySettings = securitySettings;
this.protocolVersion = this.request.Version;
this.extensibleMessage = request as IProtocolMessageWithExtensions;
}
@@ -64,10 +68,14 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// Initializes a new instance of the <see cref="Request"/> class.
/// </summary>
/// <param name="version">The version.</param>
- protected Request(Version version) {
+ /// <param name="securitySettings">The security settings.</param>
+ protected Request(Version version, ProviderSecuritySettings securitySettings) {
Contract.Requires<ArgumentNullException>(version != null);
+ Contract.Requires(securitySettings != null);
+ ErrorUtilities.VerifyArgumentNotNull(securitySettings, "securitySettings");
this.protocolVersion = version;
+ this.SecuritySettings = securitySettings;
}
#region IRequest Members
@@ -75,7 +83,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// Gets a value indicating whether the response is ready to be sent to the user agent.
/// </summary>
- /// <value></value>
/// <remarks>
/// This property returns false if there are properties that must be set on this
/// request instance before the response can be sent.
@@ -83,6 +90,12 @@ namespace DotNetOpenAuth.OpenId.Provider {
public abstract bool IsResponseReady { get; }
/// <summary>
+ /// Gets or sets the security settings that apply to this request.
+ /// </summary>
+ /// <value>Defaults to the <see cref="OpenIdProvider.SecuritySettings"/> on the <see cref="OpenIdProvider"/>.</value>
+ public ProviderSecuritySettings SecuritySettings { get; set; }
+
+ /// <summary>
/// Gets the response to send to the user agent.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown if <see cref="IsResponseReady"/> is <c>false</c>.</exception>
@@ -117,7 +130,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// Gets the original request message.
/// </summary>
/// <value>This may be null in the case of an unrecognizable message.</value>
- protected IDirectedProtocolMessage RequestMessage {
+ protected internal IDirectedProtocolMessage RequestMessage {
get { return this.request; }
}
diff --git a/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs b/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs
index ab84289..b94b37d 100644
--- a/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs
+++ b/src/DotNetOpenAuth/OpenId/Provider/RequestContract.cs
@@ -20,7 +20,7 @@ namespace DotNetOpenAuth.OpenId.Provider {
/// <summary>
/// Prevents a default instance of the <see cref="RequestContract"/> class from being created.
/// </summary>
- private RequestContract() : base((Version)null) {
+ private RequestContract() : base((Version)null, null) {
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs b/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs
index fdf6b24..f7c72fe 100644
--- a/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs
+++ b/src/DotNetOpenAuth/OpenId/ProviderEndpointDescription.cs
@@ -11,6 +11,8 @@ namespace DotNetOpenAuth.OpenId {
using System.Diagnostics.Contracts;
using System.Linq;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Messages;
+ using DotNetOpenAuth.OpenId.RelyingParty;
/// <summary>
/// Describes some OpenID Provider endpoint and its capabilities.
@@ -18,7 +20,8 @@ namespace DotNetOpenAuth.OpenId {
/// <remarks>
/// This is an immutable type.
/// </remarks>
- internal class ProviderEndpointDescription {
+ [Serializable]
+ internal class ProviderEndpointDescription : IProviderEndpoint {
/// <summary>
/// Initializes a new instance of the <see cref="ProviderEndpointDescription"/> class.
/// </summary>
@@ -55,6 +58,24 @@ namespace DotNetOpenAuth.OpenId {
ErrorUtilities.VerifyProtocol(this.ProtocolVersion != null, OpenIdStrings.ProviderVersionUnrecognized, this.Endpoint);
}
+ #region IProviderEndpoint Properties
+
+ /// <summary>
+ /// Gets the detected version of OpenID implemented by the Provider.
+ /// </summary>
+ Version IProviderEndpoint.Version {
+ get { return this.ProtocolVersion; }
+ }
+
+ /// <summary>
+ /// Gets the URL that the OpenID Provider receives authentication requests at.
+ /// </summary>
+ Uri IProviderEndpoint.Uri {
+ get { return this.Endpoint; }
+ }
+
+ #endregion
+
/// <summary>
/// Gets the URL that the OpenID Provider listens for incoming OpenID messages on.
/// </summary>
@@ -72,6 +93,88 @@ namespace DotNetOpenAuth.OpenId {
/// <summary>
/// Gets the collection of service type URIs found in the XRDS document describing this Provider.
/// </summary>
- internal ReadOnlyCollection<string> Capabilities { get; private set; }
+ internal ReadOnlyCollection<string> Capabilities { get; private set; }
+
+ #region IProviderEndpoint Methods
+
+ /// <summary>
+ /// Checks whether the OpenId Identifier claims support for a given extension.
+ /// </summary>
+ /// <typeparam name="T">The extension whose support is being queried.</typeparam>
+ /// <returns>
+ /// True if support for the extension is advertised. False otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Note that a true or false return value is no guarantee of a Provider's
+ /// support for or lack of support for an extension. The return value is
+ /// determined by how the authenticating user filled out his/her XRDS document only.
+ /// The only way to be sure of support for a given extension is to include
+ /// the extension in the request and see if a response comes back for that extension.
+ /// </remarks>
+ public bool IsExtensionSupported<T>() where T : IOpenIdMessageExtension, new() {
+ T extension = new T();
+ return this.IsExtensionSupported(extension);
+ }
+
+ /// <summary>
+ /// Checks whether the OpenId Identifier claims support for a given extension.
+ /// </summary>
+ /// <param name="extensionType">The extension whose support is being queried.</param>
+ /// <returns>
+ /// True if support for the extension is advertised. False otherwise.
+ /// </returns>
+ /// <remarks>
+ /// Note that a true or false return value is no guarantee of a Provider's
+ /// support for or lack of support for an extension. The return value is
+ /// determined by how the authenticating user filled out his/her XRDS document only.
+ /// The only way to be sure of support for a given extension is to include
+ /// the extension in the request and see if a response comes back for that extension.
+ /// </remarks>
+ public bool IsExtensionSupported(Type extensionType) {
+ ErrorUtilities.VerifyArgumentNotNull(extensionType, "extensionType");
+ ErrorUtilities.VerifyArgument(typeof(IOpenIdMessageExtension).IsAssignableFrom(extensionType), OpenIdStrings.TypeMustImplementX, typeof(IOpenIdMessageExtension).FullName);
+ var extension = (IOpenIdMessageExtension)Activator.CreateInstance(extensionType);
+ return this.IsExtensionSupported(extension);
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Determines whether some extension is supported by the Provider.
+ /// </summary>
+ /// <param name="extensionUri">The extension URI.</param>
+ /// <returns>
+ /// <c>true</c> if the extension is supported; otherwise, <c>false</c>.
+ /// </returns>
+ protected internal bool IsExtensionSupported(string extensionUri) {
+ ErrorUtilities.VerifyNonZeroLength(extensionUri, "extensionUri");
+ ErrorUtilities.VerifyOperation(this.Capabilities != null, OpenIdStrings.ExtensionLookupSupportUnavailable);
+ return this.Capabilities.Contains(extensionUri);
+ }
+
+ /// <summary>
+ /// Determines whether a given extension is supported by this endpoint.
+ /// </summary>
+ /// <param name="extension">An instance of the extension to check support for.</param>
+ /// <returns>
+ /// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>.
+ /// </returns>
+ protected internal bool IsExtensionSupported(IOpenIdMessageExtension extension) {
+ ErrorUtilities.VerifyArgumentNotNull(extension, "extension");
+
+ // Consider the primary case.
+ if (this.IsExtensionSupported(extension.TypeUri)) {
+ return true;
+ }
+
+ // Consider the secondary cases.
+ if (extension.AdditionalSupportedTypeUris != null) {
+ if (extension.AdditionalSupportedTypeUris.Any(typeUri => this.IsExtensionSupported(typeUri))) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs
index 0bdc474..d3e0686 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AssociationManager.cs
@@ -9,6 +9,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
+ using System.Net;
using System.Text;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId.ChannelElements;
@@ -210,6 +211,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
throw new ProtocolException(MessagingStrings.UnexpectedMessageReceivedOfMany);
}
} catch (ProtocolException ex) {
+ // If the association failed because the remote server can't handle Expect: 100 Continue headers,
+ // then our web request handler should have already accomodated for future calls. Go ahead and
+ // immediately make one of those future calls now to try to get the association to succeed.
+ if (StandardWebRequestHandler.IsExceptionFrom417ExpectationFailed(ex)) {
+ return this.CreateNewAssociation(provider, associateRequest, retriesRemaining - 1);
+ }
+
// Since having associations with OPs is not totally critical, we'll log and eat
// the exception so that auth may continue in dumb mode.
Logger.OpenId.ErrorFormat("An error occurred while trying to create an association with {0}. {1}", provider.Endpoint, ex);
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
index c65e681..a32ca38 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationRequest.cs
@@ -89,7 +89,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// </summary>
/// <value></value>
public OutgoingWebResponse RedirectingResponse {
- get { return this.RelyingParty.Channel.PrepareResponse(this.CreateRequestMessage()); }
+ get {
+ foreach (var behavior in this.RelyingParty.Behaviors) {
+ behavior.OnOutgoingAuthenticationRequest(this);
+ }
+
+ return this.RelyingParty.Channel.PrepareResponse(this.CreateRequestMessage());
+ }
}
/// <summary>
@@ -147,7 +153,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// OpenId discovery documents found at the <see cref="ClaimedIdentifier"/>
/// location.
/// </summary>
- IProviderEndpoint IAuthenticationRequest.Provider {
+ public IProviderEndpoint Provider {
get { return this.endpoint; }
}
@@ -162,6 +168,20 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
set { this.associationPreference = value; }
}
+ /// <summary>
+ /// Gets the extensions that have been added to the request.
+ /// </summary>
+ internal IEnumerable<IOpenIdMessageExtension> AppliedExtensions {
+ get { return this.extensions; }
+ }
+
+ /// <summary>
+ /// Gets the list of extensions for this request.
+ /// </summary>
+ internal IList<IOpenIdMessageExtension> Extensions {
+ get { return this.extensions; }
+ }
+
#region IAuthenticationRequest methods
/// <summary>
@@ -262,7 +282,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
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 (!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) {
@@ -289,9 +312,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
// Filter disallowed endpoints.
- if (relyingParty.SecuritySettings.RejectDelegatingIdentifiers) {
- serviceEndpoints = serviceEndpoints.Where(se => se.ClaimedIdentifier == se.ProviderLocalIdentifier);
- }
+ serviceEndpoints = relyingParty.SecuritySettings.FilterEndpoints(serviceEndpoints);
// Call another method that defers request generation.
return CreateInternal(userSuppliedIdentifier, relyingParty, realm, returnToUrl, serviceEndpoints, createNewAssociationsAsNeeded);
@@ -321,6 +342,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
ErrorUtilities.VerifyArgumentNotNull(realm, "realm");
ErrorUtilities.VerifyArgumentNotNull(serviceEndpoints, "serviceEndpoints");
+ // If shared associations are required, then we had better have an association store.
+ ErrorUtilities.VerifyOperation(!relyingParty.SecuritySettings.RequireAssociation || relyingParty.AssociationManager.HasAssociationStore, OpenIdStrings.AssociationStoreRequired);
+
Logger.Yadis.InfoFormat("Performing discovery on user-supplied identifier: {0}", userSuppliedIdentifier);
IEnumerable<ServiceEndpoint> endpoints = FilterAndSortEndpoints(serviceEndpoints, relyingParty);
@@ -354,18 +378,23 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
// Now that we've run out of endpoints that respond to association requests,
// since we apparently are still running, the caller must want another request.
- // We'll go ahead and generate the requests to OPs that may be down.
+ // We'll go ahead and generate the requests to OPs that may be down --
+ // unless associations are set as required in our security settings.
if (failedAssociationEndpoints.Count > 0) {
- Logger.OpenId.WarnFormat("Now generating requests for Provider endpoints that failed initial association attempts.");
+ if (relyingParty.SecuritySettings.RequireAssociation) {
+ Logger.OpenId.Warn("Associations could not be formed with some Providers. Security settings require shared associations for authentication requests so these will be skipped.");
+ } else {
+ Logger.OpenId.WarnFormat("Now generating requests for Provider endpoints that failed initial association attempts.");
- foreach (var endpoint in failedAssociationEndpoints) {
- Logger.OpenId.WarnFormat("Creating authentication request for user supplied Identifier: {0}", userSuppliedIdentifier);
+ foreach (var endpoint in failedAssociationEndpoints) {
+ Logger.OpenId.WarnFormat("Creating authentication request for user supplied Identifier: {0}", userSuppliedIdentifier);
- // Create the auth request, but prevent it from attempting to create an association
- // because we've already tried. Let's not have it waste time trying again.
- var authRequest = new AuthenticationRequest(endpoint, realm, returnToUrl, relyingParty);
- authRequest.associationPreference = AssociationPreference.IfAlreadyEstablished;
- yield return authRequest;
+ // Create the auth request, but prevent it from attempting to create an association
+ // because we've already tried. Let's not have it waste time trying again.
+ var authRequest = new AuthenticationRequest(endpoint, realm, returnToUrl, relyingParty);
+ authRequest.associationPreference = AssociationPreference.IfAlreadyEstablished;
+ yield return authRequest;
+ }
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs
index 5ab7ec4..3fd7d20 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/AuthenticationResponseSnapshot.cs
@@ -32,6 +32,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
this.ClaimedIdentifier = copyFrom.ClaimedIdentifier;
this.FriendlyIdentifierForDisplay = copyFrom.FriendlyIdentifierForDisplay;
this.Status = copyFrom.Status;
+ this.Provider = copyFrom.Provider;
this.callbackArguments = copyFrom.GetCallbackArguments();
}
@@ -94,6 +95,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
public AuthenticationStatus Status { get; private set; }
/// <summary>
+ /// Gets information about the OpenId Provider, as advertised by the
+ /// OpenID discovery documents found at the <see cref="ClaimedIdentifier"/>
+ /// location.
+ /// </summary>
+ /// <value>
+ /// The Provider endpoint that issued the positive assertion;
+ /// or <c>null</c> if information about the Provider is unavailable.
+ /// </value>
+ public IProviderEndpoint Provider { get; private set; }
+
+ /// <summary>
/// Gets the details regarding a failed authentication attempt, if available.
/// This will be set if and only if <see cref="Status"/> is <see cref="AuthenticationStatus.Failed"/>.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs
index 0dc21bb..d94af14 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/FailedAuthenticationResponse.cs
@@ -97,6 +97,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets information about the OpenId Provider, as advertised by the
+ /// OpenID discovery documents found at the <see cref="ClaimedIdentifier"/>
+ /// location.
+ /// </summary>
+ /// <value>
+ /// The Provider endpoint that issued the positive assertion;
+ /// or <c>null</c> if information about the Provider is unavailable.
+ /// </value>
+ public IProviderEndpoint Provider {
+ get { return null; }
+ }
+
+ /// <summary>
/// Gets the details regarding a failed authentication attempt, if available.
/// This will be set if and only if <see cref="Status"/> is <see cref="AuthenticationStatus.Failed"/>.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs
index 65abf97..5e92645 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationRequest.cs
@@ -91,7 +91,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets information about the OpenId Provider, as advertised by the
- /// OpenId discovery documents found at the <see cref="ClaimedIdentifier"/>
+ /// OpenID discovery documents found at the <see cref="ClaimedIdentifier"/>
/// location.
/// </summary>
IProviderEndpoint Provider { get; }
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs
index afca13d..cc94de0 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IAuthenticationResponse.cs
@@ -79,6 +79,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
AuthenticationStatus Status { get; }
/// <summary>
+ /// Gets information about the OpenId Provider, as advertised by the
+ /// OpenID discovery documents found at the <see cref="ClaimedIdentifier"/>
+ /// location, if available.
+ /// </summary>
+ /// <value>
+ /// The Provider endpoint that issued the positive assertion;
+ /// or <c>null</c> if information about the Provider is unavailable.
+ /// </value>
+ IProviderEndpoint Provider { get; }
+
+ /// <summary>
/// Gets the details regarding a failed authentication attempt, if available.
/// This will be set if and only if <see cref="Status"/> is <see cref="AuthenticationStatus.Failed"/>.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs
new file mode 100644
index 0000000..e7c38db
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/IRelyingPartyBehavior.cs
@@ -0,0 +1,43 @@
+//-----------------------------------------------------------------------
+// <copyright file="IRelyingPartyBehavior.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ /// <summary>
+ /// Applies a custom security policy to certain OpenID security settings and behaviors.
+ /// </summary>
+ /// <remarks>
+ /// BEFORE MARKING THIS INTERFACE PUBLIC: it's very important that we shift the methods to be channel-level
+ /// rather than facade class level and for the OpenIdChannel to be the one to invoke these methods.
+ /// </remarks>
+ internal interface IRelyingPartyBehavior {
+ /// <summary>
+ /// Applies a well known set of security requirements to a default set of security settings.
+ /// </summary>
+ /// <param name="securitySettings">The security settings to enhance with the requirements of this profile.</param>
+ /// <remarks>
+ /// Care should be taken to never decrease security when applying a profile.
+ /// Profiles should only enhance security requirements to avoid being
+ /// incompatible with each other.
+ /// </remarks>
+ void ApplySecuritySettings(RelyingPartySecuritySettings securitySettings);
+
+ /// <summary>
+ /// Called when an authentication request is about to be sent.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <remarks>
+ /// Implementations should be prepared to be called multiple times on the same outgoing message
+ /// without malfunctioning.
+ /// </remarks>
+ void OnOutgoingAuthenticationRequest(IAuthenticationRequest request);
+
+ /// <summary>
+ /// Called when an incoming positive assertion is received.
+ /// </summary>
+ /// <param name="assertion">The positive assertion.</param>
+ void OnIncomingPositiveAssertion(IAuthenticationResponse assertion);
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs
index cd68a81..e66ac28 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/NegativeAuthenticationResponse.cs
@@ -96,6 +96,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets information about the OpenId Provider, as advertised by the
+ /// OpenID discovery documents found at the <see cref="ClaimedIdentifier"/>
+ /// location.
+ /// </summary>
+ /// <value>
+ /// The Provider endpoint that issued the positive assertion;
+ /// or <c>null</c> if information about the Provider is unavailable.
+ /// </value>
+ public IProviderEndpoint Provider {
+ get { return null; }
+ }
+
+ /// <summary>
/// Gets the details regarding a failed authentication attempt, if available.
/// This will be set if and only if <see cref="Status"/> is <see cref="AuthenticationStatus.Failed"/>.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
index 3c95770..5d7a785 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.cs
@@ -364,7 +364,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
Uri authUri = new Uri(formAuthData);
HttpRequestInfo clientResponseInfo = new HttpRequestInfo {
- Url = authUri,
+ UrlBeforeRewriting = authUri,
};
this.authenticationResponse = this.RelyingParty.GetResponse(clientResponseInfo);
@@ -908,12 +908,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
StringBuilder discoveryResultBuilder = new StringBuilder();
discoveryResultBuilder.Append("{");
try {
- List<IAuthenticationRequest> requests = this.CreateRequests(userSuppliedIdentifier, true);
+ List<IAuthenticationRequest> requests = this.CreateRequests(userSuppliedIdentifier, true).Where(req => this.OnLoggingIn(req)).ToList();
if (requests.Count > 0) {
discoveryResultBuilder.AppendFormat("claimedIdentifier: {0},", MessagingUtilities.GetSafeJavascriptValue(requests[0].ClaimedIdentifier));
discoveryResultBuilder.Append("requests: [");
foreach (IAuthenticationRequest request in requests) {
- this.OnLoggingIn(request);
discoveryResultBuilder.Append("{");
discoveryResultBuilder.AppendFormat("endpoint: {0},", MessagingUtilities.GetSafeJavascriptValue(request.Provider.Uri.AbsoluteUri));
request.Mode = AuthenticationRequestMode.Immediate;
@@ -996,6 +995,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
writer.WriteBeginTag("span");
writer.WriteAttribute("class", this.CssClass);
writer.Write(" style='");
+ writer.WriteStyleAttribute("display", "inline-block");
writer.WriteStyleAttribute("position", "relative");
writer.WriteStyleAttribute("font-size", "16px");
writer.Write("'>");
@@ -1089,11 +1089,16 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// Fires the <see cref="LoggingIn"/> event.
/// </summary>
/// <param name="request">The request.</param>
- private void OnLoggingIn(IAuthenticationRequest request) {
+ /// <returns><c>true</c> if the login should proceed; <c>false</c> otherwise.</returns>
+ private bool OnLoggingIn(IAuthenticationRequest request) {
var loggingIn = this.LoggingIn;
if (loggingIn != null) {
- loggingIn(this, new OpenIdEventArgs(request));
+ var args = new OpenIdEventArgs(request);
+ loggingIn(this, args);
+ return !args.Cancel;
}
+
+ return true;
}
/// <summary>
@@ -1233,7 +1238,7 @@ if (!openidbox.dnoi_internal.onSubmit()) {{ return false; }}
/// requests should be initialized for use in invisible iframes for background authentication.</param>
/// <returns>The list of authentication requests, any one of which may be
/// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.</returns>
- private List<IAuthenticationRequest> CreateRequests(string userSuppliedIdentifier, bool immediate) {
+ private IEnumerable<IAuthenticationRequest> CreateRequests(string userSuppliedIdentifier, bool immediate) {
var requests = new List<IAuthenticationRequest>();
// Approximate the returnTo (either based on the customize property or the page URL)
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js
index ee9bcdc..1078003 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdAjaxTextBox.js
@@ -126,9 +126,10 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
box.dnoi_internal.authenticationIFrames = new FrameManager(throttle);
box.dnoi_internal.constructButton = function(text, tooltip, onclick) {
- var button = document.createElement('button');
+ var button = document.createElement('input');
button.textContent = text; // Mozilla
button.value = text; // IE
+ button.type = 'button';
button.title = tooltip != null ? tooltip : '';
button.onclick = onclick;
button.style.visibility = 'hidden';
@@ -215,6 +216,7 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
});
box.dnoi_internal.openid_logo = box.dnoi_internal.constructIcon(openid_logo_url, null, false, true);
box.dnoi_internal.op_logo = box.dnoi_internal.constructIcon('', authenticatedByToolTip, false, false, "16px");
+ box.dnoi_internal.op_logo.style.maxWidth = '16px';
box.dnoi_internal.spinner = box.dnoi_internal.constructIcon(spinner_url, busyToolTip, true);
box.dnoi_internal.success_icon = box.dnoi_internal.constructIcon(success_icon_url, authenticatedAsToolTip, true);
//box.dnoi_internal.failure_icon = box.dnoi_internal.constructIcon(failure_icon_url, authenticationFailedToolTip, true);
@@ -247,7 +249,11 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
box.dnoi_internal.op_logo.src = opLogo;
box.dnoi_internal.op_logo.style.visibility = 'visible';
box.dnoi_internal.op_logo.title = box.dnoi_internal.op_logo.originalTitle.replace('{0}', authenticatedBy.getHost());
- } else {
+ }
+ trace("OP icon size: " + box.dnoi_internal.op_logo.fileSize);
+ if (opLogo == null || box.dnoi_internal.op_logo.fileSize == -1 /*IE*/ || box.dnoi_internal.op_logo.fileSize === undefined /* FF */) {
+ trace('recovering from missing OP icon');
+ box.dnoi_internal.op_logo.style.visibility = 'hidden';
box.dnoi_internal.openid_logo.style.visibility = 'visible';
box.dnoi_internal.openid_logo.title = box.dnoi_internal.op_logo.originalTitle.replace('{0}', authenticatedBy.getHost());
}
@@ -285,8 +291,9 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
}
box.dnoi_internal.isBusy = function() {
- return box.dnoi_internal.state == 'discovering' ||
- box.dnoi_internal.authenticationRequests[box.lastDiscoveredIdentifier].busy();
+ var lastDiscovery = box.dnoi_internal.authenticationRequests[box.lastDiscoveredIdentifier];
+ return box.dnoi_internal.state == 'discovering' ||
+ (lastDiscovery && lastDiscovery.busy());
};
box.dnoi_internal.canAttemptLogin = function() {
@@ -516,7 +523,7 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
trace('iframe hosting ' + self.endpoint + ' now OPENING.');
self.iframe = iframe;
//trace('initiating auth attempt with: ' + self.immediate);
- return self.immediate;
+ return self.immediate.toString();
};
this.trySetup = function() {
self.abort(); // ensure no concurrent attempts
@@ -528,13 +535,9 @@ function initAjaxOpenId(box, openid_logo_url, dotnetopenid_logo_url, spinner_url
height = 500;
}
- if (window.showModalDialog) {
- self.popup = window.showModalDialog(self.setup, 'opLogin', 'status:0;resizable:1;scroll:1;center:1;dialogWidth:' + width + 'px; dialogHeight:' + height + 'px');
- } else {
- var left = (screen.width - width) / 2;
- var top = (screen.height - height) / 2;
- self.popup = window.open(self.setup, 'opLogin', 'status=0,toolbar=0,location=1,resizable=1,scrollbars=1,left=' + left + ',top=' + top + ',width=' + width + ',height=' + height);
- }
+ var left = (screen.width - width) / 2;
+ var top = (screen.height - height) / 2;
+ self.popup = window.open(self.setup, 'opLogin', 'status=0,toolbar=0,location=1,resizable=1,scrollbars=1,left=' + left + ',top=' + top + ',width=' + width + ',height=' + height);
// If the OP supports the UI extension it MAY close its own window
// for a negative assertion. We must be able to recover from that scenario.
@@ -747,8 +750,10 @@ function Uri(url) {
var queryStringPairs = this.queryString.split('&');
for (var i = 0; i < queryStringPairs.length; i++) {
- var pair = queryStringPairs[i].split('=');
- this.Pairs.push(new KeyValuePair(unescape(pair[0]), unescape(pair[1])))
+ var equalsAt = queryStringPairs[i].indexOf('=');
+ left = (equalsAt >= 0) ? queryStringPairs[i].substring(0, equalsAt) : null;
+ right = (equalsAt >= 0) ? queryStringPairs[i].substring(equalsAt + 1) : queryStringPairs[i];
+ this.Pairs.push(new KeyValuePair(unescape(left), unescape(right)));
}
};
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs
new file mode 100644
index 0000000..b367944
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdButton.cs
@@ -0,0 +1,177 @@
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdButton.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Drawing.Design;
+ using System.Globalization;
+ using System.Linq;
+ using System.Text;
+ using System.Web.UI;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// An ASP.NET control that renders a button that initiates an
+ /// authentication when clicked.
+ /// </summary>
+ public class OpenIdButton : OpenIdRelyingPartyControlBase, IPostBackEventHandler {
+ #region Property defaults
+
+ /// <summary>
+ /// The default value for the <see cref="Text"/> property.
+ /// </summary>
+ private const string TextDefault = "Log in with [Provider]!";
+
+ /// <summary>
+ /// The default value for the <see cref="PrecreateRequest"/> property.
+ /// </summary>
+ private const bool PrecreateRequestDefault = false;
+
+ #endregion
+
+ #region View state keys
+
+ /// <summary>
+ /// The key under which the value for the <see cref="Text"/> property will be stored.
+ /// </summary>
+ private const string TextViewStateKey = "Text";
+
+ /// <summary>
+ /// The key under which the value for the <see cref="ImageUrl"/> property will be stored.
+ /// </summary>
+ private const string ImageUrlViewStateKey = "ImageUrl";
+
+ /// <summary>
+ /// The key under which the value for the <see cref="PrecreateRequest"/> property will be stored.
+ /// </summary>
+ private const string PrecreateRequestViewStateKey = "PrecreateRequest";
+
+ #endregion
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OpenIdButton"/> class.
+ /// </summary>
+ public OpenIdButton() {
+ }
+
+ /// <summary>
+ /// Gets or sets the text to display for the link.
+ /// </summary>
+ [Bindable(true), DefaultValue(TextDefault), Category(AppearanceCategory)]
+ [Description("The text to display for the link.")]
+ public string Text {
+ get { return (string)ViewState[TextViewStateKey] ?? TextDefault; }
+ set { ViewState[TextViewStateKey] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the image to display.
+ /// </summary>
+ [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Bindable property must be simple type")]
+ [Bindable(true), Category(AppearanceCategory)]
+ [Description("The image to display.")]
+ [UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
+ public string ImageUrl {
+ get {
+ return (string)ViewState[ImageUrlViewStateKey];
+ }
+
+ set {
+ UriUtil.ValidateResolvableUrl(Page, DesignMode, value);
+ ViewState[ImageUrlViewStateKey] = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to pre-discover the identifier so
+ /// the user agent has an immediate redirect.
+ /// </summary>
+ [Bindable(true), Category(OpenIdCategory), DefaultValue(PrecreateRequestDefault)]
+ [Description("Whether to pre-discover the identifier so the user agent has an immediate redirect.")]
+ public bool PrecreateRequest {
+ get { return (bool)(ViewState[PrecreateRequestViewStateKey] ?? PrecreateRequestDefault); }
+ set { ViewState[PrecreateRequestViewStateKey] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating when to use a popup window to complete the login experience.
+ /// </summary>
+ /// <value>The default value is <see cref="PopupBehavior.Never"/>.</value>
+ [Bindable(false), Browsable(false)]
+ public override PopupBehavior Popup {
+ get { return base.Popup; }
+ set { ErrorUtilities.VerifySupported(value == base.Popup, OpenIdStrings.PropertyValueNotSupported); }
+ }
+
+ #region IPostBackEventHandler Members
+
+ /// <summary>
+ /// When implemented by a class, enables a server control to process an event raised when a form is posted to the server.
+ /// </summary>
+ /// <param name="eventArgument">A <see cref="T:System.String"/> that represents an optional event argument to be passed to the event handler.</param>
+ public void RaisePostBackEvent(string eventArgument) {
+ if (!this.PrecreateRequest) {
+ IAuthenticationRequest request = this.CreateRequests().FirstOrDefault();
+ request.RedirectToProvider();
+ }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
+ /// </summary>
+ /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ protected override void OnPreRender(EventArgs e) {
+ base.OnPreRender(e);
+
+ if (!this.DesignMode) {
+ ErrorUtilities.VerifyOperation(this.Identifier != null, OpenIdStrings.NoIdentifierSet);
+ }
+ }
+
+ /// <summary>
+ /// Sends server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object, which writes the content to be rendered on the client.
+ /// </summary>
+ /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param>
+ protected override void Render(HtmlTextWriter writer) {
+ if (string.IsNullOrEmpty(this.Identifier)) {
+ writer.WriteEncodedText(string.Format(CultureInfo.CurrentCulture, "[{0}]", OpenIdStrings.NoIdentifierSet));
+ } else {
+ string tooltip = this.Text;
+ if (this.PrecreateRequest && !this.DesignMode) {
+ IAuthenticationRequest request = this.CreateRequests().FirstOrDefault();
+ if (request != null) {
+ RenderOpenIdMessageTransmissionAsAnchorAttributes(writer, request, tooltip);
+ } else {
+ tooltip = OpenIdStrings.OpenIdEndpointNotFound;
+ }
+ } else {
+ writer.AddAttribute(HtmlTextWriterAttribute.Href, this.Page.ClientScript.GetPostBackClientHyperlink(this, null));
+ }
+
+ writer.AddAttribute(HtmlTextWriterAttribute.Title, tooltip);
+ writer.RenderBeginTag(HtmlTextWriterTag.A);
+
+ if (!string.IsNullOrEmpty(this.ImageUrl)) {
+ writer.AddAttribute(HtmlTextWriterAttribute.Src, this.ResolveClientUrl(this.ImageUrl));
+ writer.AddAttribute(HtmlTextWriterAttribute.Border, "0");
+ writer.AddAttribute(HtmlTextWriterAttribute.Alt, this.Text);
+ writer.AddAttribute(HtmlTextWriterAttribute.Title, this.Text);
+ writer.RenderBeginTag(HtmlTextWriterTag.Img);
+ writer.RenderEndTag();
+ } else if (!string.IsNullOrEmpty(this.Text)) {
+ writer.WriteEncodedText(this.Text);
+ }
+
+ writer.RenderEndTag();
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
index 0327cba..28b6c34 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -7,6 +7,7 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
using System.Collections.Generic;
+ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
@@ -43,6 +44,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private const string ApplicationStoreKey = "DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingParty.ApplicationStore";
/// <summary>
+ /// Backing store for the <see cref="Behaviors"/> property.
+ /// </summary>
+ private readonly ObservableCollection<IRelyingPartyBehavior> behaviors = new ObservableCollection<IRelyingPartyBehavior>();
+
+ /// <summary>
/// Backing field for the <see cref="SecuritySettings"/> property.
/// </summary>
private RelyingPartySecuritySettings securitySettings;
@@ -85,6 +91,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
ErrorUtilities.VerifyArgument(associationStore == null || nonceStore != null, OpenIdStrings.AssociationStoreRequiresNonceStore);
this.securitySettings = DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.SecuritySettings.CreateSecuritySettings();
+ this.behaviors.CollectionChanged += this.OnBehaviorsChanged;
+ foreach (var behavior in DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.Behaviors.CreateInstances(false)) {
+ this.behaviors.Add(behavior);
+ }
// Without a nonce store, we must rely on the Provider to protect against
// replay attacks. But only 2.0+ Providers can be expected to provide
@@ -207,6 +217,13 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets a list of custom behaviors to apply to OpenID actions.
+ /// </summary>
+ internal ICollection<IRelyingPartyBehavior> Behaviors {
+ get { return this.behaviors; }
+ }
+
+ /// <summary>
/// Gets a value indicating whether this Relying Party can sign its return_to
/// parameter in outgoing authentication requests.
/// </summary>
@@ -400,7 +417,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
NameValueCollection queryParams = this.Channel.GetRequestFromContext().QueryStringBeforeRewriting;
var returnToParams = new Dictionary<string, string>(queryParams.Count);
foreach (string key in queryParams) {
- if (!IsOpenIdSupportingParameter(key)) {
+ if (!IsOpenIdSupportingParameter(key) && key != null) {
returnToParams.Add(key, queryParams[key]);
}
}
@@ -475,7 +492,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
NegativeAssertionResponse negativeAssertion;
IndirectSignedResponse positiveExtensionOnly;
if ((positiveAssertion = message as PositiveAssertionResponse) != null) {
- return new PositiveAuthenticationResponse(positiveAssertion, this);
+ var response = new PositiveAuthenticationResponse(positiveAssertion, this);
+ foreach (var behavior in this.Behaviors) {
+ behavior.OnIncomingPositiveAssertion(response);
+ }
+
+ return response;
} else if ((positiveExtensionOnly = message as IndirectSignedResponse) != null) {
return new PositiveAnonymousResponse(positiveExtensionOnly);
} else if ((negativeAssertion = message as NegativeAssertionResponse) != null) {
@@ -511,6 +533,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <c>true</c> if the named parameter is a library- or protocol-specific parameter; otherwise, <c>false</c>.
/// </returns>
internal static bool IsOpenIdSupportingParameter(string parameterName) {
+ // Yes, it is possible with some query strings to have a null or empty parameter name
+ if (string.IsNullOrEmpty(parameterName)) {
+ return false;
+ }
+
Protocol protocol = Protocol.Default;
return parameterName.StartsWith(protocol.openid.Prefix, StringComparison.OrdinalIgnoreCase)
|| parameterName.StartsWith(OpenIdUtilities.CustomParameterPrefix, StringComparison.Ordinal);
@@ -545,6 +572,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
}
+ /// <summary>
+ /// Called by derived classes when behaviors are added or removed.
+ /// </summary>
+ /// <param name="sender">The collection being modified.</param>
+ /// <param name="e">The <see cref="System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
+ private void OnBehaviorsChanged(object sender, NotifyCollectionChangedEventArgs e) {
+ foreach (IRelyingPartyBehavior profile in e.NewItems) {
+ profile.ApplySecuritySettings(this.SecuritySettings);
+ }
+ }
+
#if CONTRACTS_FULL
/// <summary>
/// Verifies conditions that should be true for any valid state of this object.
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
new file mode 100644
index 0000000..fc58eef
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.cs
@@ -0,0 +1,317 @@
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdRelyingPartyAjaxControlBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingPartyAjaxControlBase.EmbeddedAjaxJavascriptResource, "text/javascript")]
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Drawing.Design;
+ using System.Globalization;
+ using System.Linq;
+ using System.Text;
+ using System.Text.RegularExpressions;
+ using System.Web;
+ using System.Web.Security;
+ using System.Web.UI;
+ using DotNetOpenAuth.ComponentModel;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Extensions;
+ using DotNetOpenAuth.OpenId.Extensions.UI;
+
+ /// <summary>
+ /// A common base class for OpenID Relying Party controls.
+ /// </summary>
+ internal abstract class OpenIdRelyingPartyAjaxControlBase : OpenIdRelyingPartyControlBase, ICallbackEventHandler {
+ /// <summary>
+ /// The manifest resource name of the javascript file to include on the hosting page.
+ /// </summary>
+ internal const string EmbeddedAjaxJavascriptResource = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdRelyingPartyAjaxControlBase.js";
+
+ /// <summary>
+ /// The name of the javascript function that will initiate a synchronous callback.
+ /// </summary>
+ protected const string CallbackJsFunction = "window.dnoa_internal.callback";
+
+ /// <summary>
+ /// The name of the javascript function that will initiate an asynchronous callback.
+ /// </summary>
+ protected const string CallbackJsFunctionAsync = "window.dnoa_internal.callbackAsync";
+
+ /// <summary>
+ /// Stores the result of a AJAX callback discovery.
+ /// </summary>
+ private string discoveryResult;
+
+ /// <summary>
+ /// A dictionary of extension response types and the javascript member
+ /// name to map them to on the user agent.
+ /// </summary>
+ private Dictionary<Type, string> clientScriptExtensions = new Dictionary<Type, string>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OpenIdRelyingPartyAjaxControlBase"/> class.
+ /// </summary>
+ protected OpenIdRelyingPartyAjaxControlBase() {
+ // The AJAX login style always uses popups (or invisible iframes).
+ this.Popup = PopupBehavior.Always;
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating when to use a popup window to complete the login experience.
+ /// </summary>
+ /// <value>The default value is <see cref="PopupBehavior.Never"/>.</value>
+ [Bindable(false), Browsable(false)]
+ public override PopupBehavior Popup {
+ get { return base.Popup; }
+ set { ErrorUtilities.VerifySupported(value == base.Popup, OpenIdStrings.PropertyValueNotSupported); }
+ }
+
+ #region ICallbackEventHandler Members
+
+ /// <summary>
+ /// Returns the result of discovery on some Identifier passed to <see cref="ICallbackEventHandler.RaiseCallbackEvent"/>.
+ /// </summary>
+ /// <returns>The result of the callback.</returns>
+ /// <value>A whitespace delimited list of URLs that can be used to initiate authentication.</value>
+ string ICallbackEventHandler.GetCallbackResult() {
+ this.Page.Response.ContentType = "text/javascript";
+ return this.discoveryResult;
+ }
+
+ /// <summary>
+ /// Performs discovery on some OpenID Identifier. Called directly from the user agent via
+ /// AJAX callback mechanisms.
+ /// </summary>
+ /// <param name="eventArgument">The identifier to perform discovery on.</param>
+ void ICallbackEventHandler.RaiseCallbackEvent(string eventArgument) {
+ string userSuppliedIdentifier = eventArgument;
+
+ ErrorUtilities.VerifyNonZeroLength(userSuppliedIdentifier, "userSuppliedIdentifier");
+ Logger.OpenId.InfoFormat("AJAX discovery on {0} requested.", userSuppliedIdentifier);
+
+ // We prepare a JSON object with this interface:
+ // class jsonResponse {
+ // string claimedIdentifier;
+ // Array requests; // never null
+ // string error; // null if no error
+ // }
+ // Each element in the requests array looks like this:
+ // class jsonAuthRequest {
+ // string endpoint; // URL to the OP endpoint
+ // string immediate; // URL to initiate an immediate request
+ // string setup; // URL to initiate a setup request.
+ // }
+ StringBuilder discoveryResultBuilder = new StringBuilder();
+ discoveryResultBuilder.Append("{");
+ try {
+ this.Identifier = userSuppliedIdentifier;
+ IEnumerable<IAuthenticationRequest> requests = this.CreateRequests().CacheGeneratedResults();
+ if (requests.Any()) {
+ discoveryResultBuilder.AppendFormat("claimedIdentifier: {0},", MessagingUtilities.GetSafeJavascriptValue(requests.First().ClaimedIdentifier));
+ discoveryResultBuilder.Append("requests: [");
+ foreach (IAuthenticationRequest request in requests) {
+ this.OnLoggingIn(request);
+ discoveryResultBuilder.Append("{");
+ discoveryResultBuilder.AppendFormat("endpoint: {0},", MessagingUtilities.GetSafeJavascriptValue(request.Provider.Uri.AbsoluteUri));
+ request.Mode = AuthenticationRequestMode.Immediate;
+ OutgoingWebResponse response = request.RedirectingResponse;
+ discoveryResultBuilder.AppendFormat("immediate: {0},", MessagingUtilities.GetSafeJavascriptValue(response.GetDirectUriRequest(this.RelyingParty.Channel).AbsoluteUri));
+ request.Mode = AuthenticationRequestMode.Setup;
+ response = request.RedirectingResponse;
+ discoveryResultBuilder.AppendFormat("setup: {0}", MessagingUtilities.GetSafeJavascriptValue(response.GetDirectUriRequest(this.RelyingParty.Channel).AbsoluteUri));
+ discoveryResultBuilder.Append("},");
+ }
+ discoveryResultBuilder.Length -= 1; // trim off last comma
+ discoveryResultBuilder.Append("]");
+ } else {
+ discoveryResultBuilder.Append("requests: new Array(),");
+ discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(OpenIdStrings.OpenIdEndpointNotFound));
+ }
+ } catch (ProtocolException ex) {
+ discoveryResultBuilder.Append("requests: new Array(),");
+ discoveryResultBuilder.AppendFormat("error: {0}", MessagingUtilities.GetSafeJavascriptValue(ex.Message));
+ }
+ discoveryResultBuilder.Append("}");
+ this.discoveryResult = discoveryResultBuilder.ToString();
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Creates the authentication requests for a given user-supplied Identifier.
+ /// </summary>
+ /// <returns>A sequence of authentication requests, any one of which may be
+ /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.</returns>
+ protected override IEnumerable<IAuthenticationRequest> CreateRequests() {
+ // We delegate all our logic to another method, since invoking base. methods
+ // within an iterator method results in unverifiable code.
+ return this.CreateRequestsCore(base.CreateRequests());
+ }
+
+ /// <summary>
+ /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
+ /// </summary>
+ /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ protected override void OnPreRender(EventArgs e) {
+ base.OnPreRender(e);
+
+ this.Page.ClientScript.RegisterClientScriptResource(typeof(OpenIdRelyingPartyAjaxControlBase), EmbeddedAjaxJavascriptResource);
+
+ StringBuilder initScript = new StringBuilder();
+
+ initScript.AppendLine(CallbackJsFunctionAsync + " = " + this.GetJsCallbackConvenienceFunction(true));
+ initScript.AppendLine(CallbackJsFunction + " = " + this.GetJsCallbackConvenienceFunction(false));
+
+ this.Page.ClientScript.RegisterClientScriptBlock(typeof(OpenIdRelyingPartyControlBase), "initializer", initScript.ToString(), true);
+ }
+
+ /// <summary>
+ /// Creates the authentication requests for a given user-supplied Identifier.
+ /// </summary>
+ /// <param name="requests">The authentication requests to prepare.</param>
+ /// <returns>
+ /// A sequence of authentication requests, any one of which may be
+ /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.
+ /// </returns>
+ private IEnumerable<IAuthenticationRequest> CreateRequestsCore(IEnumerable<IAuthenticationRequest> requests) {
+ Contract.Requires(requests != null);
+
+ // Configure each generated request.
+ int reqIndex = 0;
+ foreach (var req in requests) {
+ req.AddCallbackArguments("index", (reqIndex++).ToString(CultureInfo.InvariantCulture));
+
+ if (req.Provider.IsExtensionSupported<UIRequest>()) {
+ // Provide a hint for the client javascript about whether the OP supports the UI extension.
+ // This is so the window can be made the correct size for the extension.
+ // If the OP doesn't advertise support for the extension, the javascript will use
+ // a bigger popup window.
+ req.AddCallbackArguments("dotnetopenid.popupUISupported", "1");
+ }
+
+ // If the ReturnToUrl was explicitly set, we'll need to reset our first parameter
+ if (string.IsNullOrEmpty(HttpUtility.ParseQueryString(req.ReturnToUrl.Query)["dotnetopenid.userSuppliedIdentifier"])) {
+ req.AddCallbackArguments("dotnetopenid.userSuppliedIdentifier", this.Identifier);
+ }
+
+ // Our javascript needs to let the user know which endpoint responded. So we force it here.
+ // This gives us the info even for 1.0 OPs and 2.0 setup_required responses.
+ req.AddCallbackArguments("dotnetopenid.op_endpoint", req.Provider.Uri.AbsoluteUri);
+ req.AddCallbackArguments("dotnetopenid.claimed_id", (string)req.ClaimedIdentifier ?? string.Empty);
+
+ // We append a # at the end so that if the OP happens to support it,
+ // the OpenID response "query string" is appended after the hash rather than before, resulting in the
+ // browser being super-speedy in closing the popup window since it doesn't try to pull a newer version
+ // of the static resource down from the server merely because of a changed URL.
+ // http://www.nabble.com/Re:-Defining-how-OpenID-should-behave-with-fragments-in-the-return_to-url-p22694227.html
+ ////TODO:
+
+ yield return req;
+ }
+ }
+
+ /// <summary>
+ /// Constructs a function that will initiate an AJAX callback.
+ /// </summary>
+ /// <param name="async">if set to <c>true</c> causes the AJAX callback to be a little more asynchronous. Note that <c>false</c> does not mean the call is absolutely synchronous.</param>
+ /// <returns>The string defining a javascript anonymous function that initiates a callback.</returns>
+ private string GetJsCallbackConvenienceFunction(bool async) {
+ string argumentParameterName = "argument";
+ string callbackResultParameterName = "resultFunction";
+ string callbackErrorCallbackParameterName = "errorCallback";
+ string callback = Page.ClientScript.GetCallbackEventReference(
+ this,
+ argumentParameterName,
+ callbackResultParameterName,
+ argumentParameterName,
+ callbackErrorCallbackParameterName,
+ async);
+ return string.Format(
+ CultureInfo.InvariantCulture,
+ "function({1}, {2}, {3}) {{{0}\treturn {4};{0}}};",
+ Environment.NewLine,
+ argumentParameterName,
+ callbackResultParameterName,
+ callbackErrorCallbackParameterName,
+ callback);
+ }
+
+ /// <summary>
+ /// Notifies the user agent via an AJAX response of a completed authentication attempt.
+ /// </summary>
+ private void ReportAuthenticationResult() {
+ Logger.OpenId.InfoFormat("AJAX (iframe) callback from OP: {0}", this.Page.Request.Url);
+ List<string> assignments = new List<string>();
+
+ var authResponse = this.RelyingParty.GetResponse();
+ if (authResponse.Status == AuthenticationStatus.Authenticated) {
+ this.OnLoggedIn(authResponse);
+ foreach (var pair in this.clientScriptExtensions) {
+ IClientScriptExtensionResponse extension = (IClientScriptExtensionResponse)authResponse.GetExtension(pair.Key);
+ if (extension == null) {
+ continue;
+ }
+ var positiveResponse = (PositiveAuthenticationResponse)authResponse;
+ string js = extension.InitializeJavaScriptData(positiveResponse.Response);
+ if (string.IsNullOrEmpty(js)) {
+ js = "null";
+ }
+ assignments.Add(pair.Value + " = " + js);
+ }
+ }
+
+ this.CallbackUserAgentMethod("dnoi_internal.processAuthorizationResult(document.URL)", assignments.ToArray());
+ }
+
+ /// <summary>
+ /// Invokes a method on a parent frame/window's OpenIdAjaxTextBox,
+ /// and closes the calling popup window if applicable.
+ /// </summary>
+ /// <param name="methodCall">The method to call on the OpenIdAjaxTextBox, including
+ /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param>
+ private void CallbackUserAgentMethod(string methodCall) {
+ this.CallbackUserAgentMethod(methodCall, null);
+ }
+
+ /// <summary>
+ /// Invokes a method on a parent frame/window's OpenIdAjaxTextBox,
+ /// and closes the calling popup window if applicable.
+ /// </summary>
+ /// <param name="methodCall">The method to call on the OpenIdAjaxTextBox, including
+ /// parameters. (i.e. "callback('arg1', 2)"). No escaping is done by this method.</param>
+ /// <param name="preAssignments">An optional list of assignments to make to the input box object before placing the method call.</param>
+ private void CallbackUserAgentMethod(string methodCall, string[] preAssignments) {
+ Logger.OpenId.InfoFormat("Sending Javascript callback: {0}", methodCall);
+ Page.Response.Write(@"<html><body><script language='javascript'>
+ var inPopup = !window.frameElement;
+ var objSrc = inPopup ? window.opener.waiting_openidBox : window.frameElement.openidBox;
+");
+ if (preAssignments != null) {
+ foreach (string assignment in preAssignments) {
+ Page.Response.Write(string.Format(CultureInfo.InvariantCulture, " objSrc.{0};\n", assignment));
+ }
+ }
+
+ // Something about calling objSrc.{0} can somehow cause FireFox to forget about the inPopup variable,
+ // so we have to actually put the test for it ABOVE the call to objSrc.{0} so that it already
+ // whether to call window.self.close() after the call.
+ string htmlFormat = @" if (inPopup) {{
+ objSrc.{0};
+ window.self.close();
+}} else {{
+ objSrc.{0};
+}}
+</script></body></html>";
+ Page.Response.Write(string.Format(CultureInfo.InvariantCulture, htmlFormat, methodCall));
+ Page.Response.End();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js
new file mode 100644
index 0000000..65b1b99
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyAjaxControlBase.js
@@ -0,0 +1,150 @@
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdRelyingPartyControlBase.js" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+if (window.dnoa_internal === undefined) {
+ window.dnoa_internal = new Object();
+};
+
+window.dnoa_internal.discoveryResults = new Array(); // user supplied identifiers and discovery results
+
+/// <summary>Instantiates an object that represents an OpenID Identifier.</summary>
+window.OpenId = function(identifier) {
+ /// <summary>Performs discovery on the identifier.</summary>
+ /// <param name="onCompleted">A function(DiscoveryResult) callback to be called when discovery has completed.</param>
+ this.discover = function(onCompleted) {
+ /// <summary>Instantiates an object that stores discovery results of some identifier.</summary>
+ function DiscoveryResult(identifier, discoveryInfo) {
+ /// <summary>
+ /// Instantiates an object that describes an OpenID service endpoint and facilitates
+ /// initiating and tracking an authentication request.
+ /// </summary>
+ function ServiceEndpoint(requestInfo, userSuppliedIdentifier) {
+ this.immediate = requestInfo.immediate ? new window.dnoa_internal.Uri(requestInfo.immediate) : null;
+ this.setup = requestInfo.setup ? new window.dnoa_internal.Uri(requestInfo.setup) : null;
+ this.endpoint = new window.dnoa_internal.Uri(requestInfo.endpoint);
+ this.userSuppliedIdentifier = userSuppliedIdentifier;
+ var self = this; // closure so that delegates have the right instance
+ this.loginPopup = function(onSuccess, onFailure) {
+ //self.abort(); // ensure no concurrent attempts
+ window.dnoa_internal.processAuthorizationResult = function(childLocation) {
+ window.dnoa_internal.processAuthorizationResult = null;
+ trace('Received event from child window: ' + childLocation);
+ var success = true; // TODO: discern between success and failure, and fire the correct event.
+
+ if (success) {
+ if (onSuccess) {
+ onSuccess();
+ }
+ } else {
+ if (onFailure) {
+ onFailure();
+ }
+ }
+ };
+ var width = 800;
+ var height = 600;
+ if (self.setup.getQueryArgValue("openid.return_to").indexOf("dotnetopenid.popupUISupported") >= 0) {
+ width = 450;
+ height = 500;
+ }
+
+ var left = (screen.width - width) / 2;
+ var top = (screen.height - height) / 2;
+ self.popup = window.open(self.setup, 'opLogin', 'status=0,toolbar=0,location=1,resizable=1,scrollbars=1,left=' + left + ',top=' + top + ',width=' + width + ',height=' + height);
+
+ // If the OP supports the UI extension it MAY close its own window
+ // for a negative assertion. We must be able to recover from that scenario.
+ var localSelf = self;
+ self.popupCloseChecker = window.setInterval(function() {
+ if (localSelf.popup && localSelf.popup.closed) {
+ // The window closed, either because the user closed it, canceled at the OP,
+ // or approved at the OP and the popup window closed itself due to our script.
+ // If we were graying out the entire page while the child window was up,
+ // we would probably revert that here.
+ window.clearInterval(localSelf.popupCloseChecker);
+ localSelf.popup = null;
+
+ // The popup may have managed to inform us of the result already,
+ // so check whether the callback method was cleared already, which
+ // would indicate we've already processed this.
+ if (window.dnoa_internal.processAuthorizationResult) {
+ trace('User or OP canceled by closing the window.');
+ if (onFailure) {
+ onFailure();
+ }
+ window.dnoa_internal.processAuthorizationResult = null;
+ }
+ }
+ }, 250);
+ };
+ };
+
+ this.userSuppliedIdentifier = identifier;
+ this.claimedIdentifier = discoveryInfo.claimedIdentifier; // The claimed identifier may be null if the user provided an OP Identifier.
+ trace('Discovered claimed identifier: ' + (this.claimedIdentifier ? this.claimedIdentifier : "(directed identity)"));
+
+ if (discoveryInfo) {
+ this.length = discoveryInfo.requests.length;
+ for (var i = 0; i < discoveryInfo.requests.length; i++) {
+ this[i] = new ServiceEndpoint(discoveryInfo.requests[i], identifier);
+ }
+ } else {
+ this.length = 0;
+ }
+ };
+
+ /// <summary>Receives the results of a successful discovery (even if it yielded 0 results).</summary>
+ function successCallback(discoveryResult, identifier) {
+ trace('Discovery completed for: ' + identifier);
+
+ // Deserialize the JSON object and store the result if it was a successful discovery.
+ discoveryResult = eval('(' + discoveryResult + ')');
+
+ // Add behavior for later use.
+ discoveryResult = new DiscoveryResult(identifier, discoveryResult);
+ window.dnoa_internal.discoveryResults[identifier] = discoveryResult;
+
+ if (onCompleted) {
+ onCompleted(discoveryResult);
+ }
+ };
+
+ /// <summary>Receives the discovery failure notification.</summary>
+ failureCallback = function(message, userSuppliedIdentifier) {
+ trace('Discovery failed for: ' + identifier);
+
+ if (onCompleted) {
+ onCompleted();
+ }
+ };
+
+ if (window.dnoa_internal.discoveryResults[identifier]) {
+ trace("We've already discovered " + identifier + " so we're skipping it this time.");
+ onCompleted(window.dnoa_internal.discoveryResults[identifier]);
+ }
+
+ trace('starting discovery on ' + identifier);
+ window.dnoa_internal.callbackAsync(identifier, successCallback, failureCallback);
+ };
+
+ /// <summary>Performs discovery and immediately begins checkid_setup to authenticate the user using a given identifier.</summary>
+ this.login = function(onSuccess, onFailure) {
+ this.discover(function(discoveryResult) {
+ if (discoveryResult) {
+ trace('Discovery succeeded and found ' + discoveryResult.length + ' OpenID service endpoints.');
+ if (discoveryResult.length > 0) {
+ discoveryResult[0].loginPopup(onSuccess, onFailure);
+ } else {
+ trace("This doesn't look like an OpenID Identifier. Aborting login.");
+ if (onFailure) {
+ onFailure();
+ }
+ }
+ }
+ });
+ };
+};
+
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
new file mode 100644
index 0000000..0b83412
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.cs
@@ -0,0 +1,753 @@
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdRelyingPartyControlBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+[assembly: System.Web.UI.WebResource(DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingPartyControlBase.EmbeddedJavascriptResource, "text/javascript")]
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ using System;
+ using System.Collections.Generic;
+ using System.ComponentModel;
+ using System.Diagnostics.CodeAnalysis;
+ using System.Diagnostics.Contracts;
+ using System.Drawing.Design;
+ using System.Globalization;
+ using System.Linq;
+ using System.Text;
+ using System.Text.RegularExpressions;
+ using System.Web;
+ using System.Web.Security;
+ using System.Web.UI;
+ using DotNetOpenAuth.ComponentModel;
+ using DotNetOpenAuth.Configuration;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId.Extensions;
+ using DotNetOpenAuth.OpenId.Extensions.UI;
+
+ /// <summary>
+ /// A common base class for OpenID Relying Party controls.
+ /// </summary>
+ [DefaultProperty("Identifier"), ValidationProperty("Identifier")]
+ public abstract class OpenIdRelyingPartyControlBase : Control {
+ /// <summary>
+ /// The manifest resource name of the javascript file to include on the hosting page.
+ /// </summary>
+ internal const string EmbeddedJavascriptResource = Util.DefaultNamespace + ".OpenId.RelyingParty.OpenIdRelyingPartyControlBase.js";
+
+ #region Property category constants
+
+ /// <summary>
+ /// The "Appearance" category for properties.
+ /// </summary>
+ protected const string AppearanceCategory = "Appearance";
+
+ /// <summary>
+ /// The "Behavior" category for properties.
+ /// </summary>
+ protected const string BehaviorCategory = "Behavior";
+
+ /// <summary>
+ /// The "OpenID" category for properties and events.
+ /// </summary>
+ protected const string OpenIdCategory = "OpenID";
+
+ #endregion
+
+ #region Property default values
+
+ /// <summary>
+ /// The default value for the <see cref="Stateless"/> property.
+ /// </summary>
+ private const bool StatelessDefault = false;
+
+ /// <summary>
+ /// The default value for the <see cref="ReturnToUrl"/> property.
+ /// </summary>
+ private const string ReturnToUrlDefault = "";
+
+ /// <summary>
+ /// Default value of <see cref="UsePersistentCookie"/>.
+ /// </summary>
+ private const bool UsePersistentCookieDefault = false;
+
+ /// <summary>
+ /// The default value for the <see cref="RealmUrl"/> property.
+ /// </summary>
+ private const string RealmUrlDefault = "~/";
+
+ /// <summary>
+ /// The default value for the <see cref="Popup"/> property.
+ /// </summary>
+ private const PopupBehavior PopupDefault = PopupBehavior.Never;
+
+ /// <summary>
+ /// The default value for the <see cref="RequireSsl"/> property.
+ /// </summary>
+ private const bool RequireSslDefault = false;
+
+ #endregion
+
+ #region Property view state keys
+
+ /// <summary>
+ /// The viewstate key to use for the <see cref="Stateless"/> property.
+ /// </summary>
+ private const string StatelessViewStateKey = "Stateless";
+
+ /// <summary>
+ /// The viewstate key to use for the <see cref="UsePersistentCookie"/> property.
+ /// </summary>
+ private const string UsePersistentCookieViewStateKey = "UsePersistentCookie";
+
+ /// <summary>
+ /// The viewstate key to use for the <see cref="RealmUrl"/> property.
+ /// </summary>
+ private const string RealmUrlViewStateKey = "RealmUrl";
+
+ /// <summary>
+ /// The viewstate key to use for the <see cref="ReturnToUrl"/> property.
+ /// </summary>
+ private const string ReturnToUrlViewStateKey = "ReturnToUrl";
+
+ /// <summary>
+ /// The key under which the value for the <see cref="Identifier"/> property will be stored.
+ /// </summary>
+ private const string IdentifierViewStateKey = "Identifier";
+
+ /// <summary>
+ /// The viewstate key to use for the <see cref="Popup"/> property.
+ /// </summary>
+ private const string PopupViewStateKey = "Popup";
+
+ /// <summary>
+ /// The viewstate key to use for the <see cref="RequireSsl"/> property.
+ /// </summary>
+ private const string RequireSslViewStateKey = "RequireSsl";
+
+ #endregion
+
+ #region Callback parameter names
+
+ /// <summary>
+ /// The callback parameter for use with persisting the <see cref="UsePersistentCookie"/> property.
+ /// </summary>
+ private const string UsePersistentCookieCallbackKey = OpenIdUtilities.CustomParameterPrefix + "UsePersistentCookie";
+
+ /// <summary>
+ /// The callback parameter to use for recognizing when the callback is in a popup window.
+ /// </summary>
+ private const string UIPopupCallbackKey = OpenIdUtilities.CustomParameterPrefix + "uipopup";
+
+ /// <summary>
+ /// The callback parameter to use for recognizing when the callback is in the parent window.
+ /// </summary>
+ private const string UIPopupCallbackParentKey = OpenIdUtilities.CustomParameterPrefix + "uipopupParent";
+
+ /// <summary>
+ /// The callback parameter name to use to store which control initiated the auth request.
+ /// </summary>
+ private const string ReturnToReceivingControlId = OpenIdUtilities.CustomParameterPrefix + "receiver";
+
+ /// <summary>
+ /// The parameter name to include in the formulated auth request so that javascript can know whether
+ /// the OP advertises support for the UI extension.
+ /// </summary>
+ private const string PopupUISupportedJsHint = "dotnetopenid.popupUISupported";
+
+ #endregion
+
+ /// <summary>
+ /// Backing field for the <see cref="RelyingParty"/> property.
+ /// </summary>
+ private OpenIdRelyingParty relyingParty;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OpenIdRelyingPartyControlBase"/> class.
+ /// </summary>
+ protected OpenIdRelyingPartyControlBase() {
+ }
+
+ #region Events
+
+ /// <summary>
+ /// Fired after the user clicks the log in button, but before the authentication
+ /// process begins. Offers a chance for the web application to disallow based on
+ /// OpenID URL before redirecting the user to the OpenID Provider.
+ /// </summary>
+ [Description("Fired after the user clicks the log in button, but before the authentication process begins. Offers a chance for the web application to disallow based on OpenID URL before redirecting the user to the OpenID Provider."), Category(OpenIdCategory)]
+ public event EventHandler<OpenIdEventArgs> LoggingIn;
+
+ /// <summary>
+ /// Fired upon completion of a successful login.
+ /// </summary>
+ [Description("Fired upon completion of a successful login."), Category(OpenIdCategory)]
+ public event EventHandler<OpenIdEventArgs> LoggedIn;
+
+ /// <summary>
+ /// Fired when a login attempt fails.
+ /// </summary>
+ [Description("Fired when a login attempt fails."), Category(OpenIdCategory)]
+ public event EventHandler<OpenIdEventArgs> Failed;
+
+ /// <summary>
+ /// Fired when an authentication attempt is canceled at the OpenID Provider.
+ /// </summary>
+ [Description("Fired when an authentication attempt is canceled at the OpenID Provider."), Category(OpenIdCategory)]
+ public event EventHandler<OpenIdEventArgs> Canceled;
+
+ #endregion
+
+ /// <summary>
+ /// Gets or sets the <see cref="OpenIdRelyingParty"/> instance to use.
+ /// </summary>
+ /// <value>The default value is an <see cref="OpenIdRelyingParty"/> instance initialized according to the web.config file.</value>
+ /// <remarks>
+ /// A performance optimization would be to store off the
+ /// instance as a static member in your web site and set it
+ /// to this property in your <see cref="Control.Load">Page.Load</see>
+ /// event since instantiating these instances can be expensive on
+ /// heavily trafficked web pages.
+ /// </remarks>
+ [Browsable(false)]
+ public OpenIdRelyingParty RelyingParty {
+ get {
+ if (this.relyingParty == null) {
+ this.relyingParty = this.CreateRelyingParty();
+ }
+ return this.relyingParty;
+ }
+
+ set {
+ this.relyingParty = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether stateless mode is used.
+ /// </summary>
+ [Bindable(true), DefaultValue(StatelessDefault), Category(OpenIdCategory)]
+ [Description("Controls whether stateless mode is used.")]
+ public bool Stateless {
+ get { return (bool)(ViewState[StatelessViewStateKey] ?? StatelessDefault); }
+ set { ViewState[StatelessViewStateKey] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the OpenID <see cref="Realm"/> of the relying party web site.
+ /// </summary>
+ [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "Using Uri.ctor for validation.")]
+ [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "DotNetOpenAuth.OpenId.Realm", Justification = "Using ctor for validation.")]
+ [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Bindable property must be simple type")]
+ [Bindable(true), DefaultValue(RealmUrlDefault), Category(OpenIdCategory)]
+ [Description("The OpenID Realm of the relying party web site.")]
+ [UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
+ public string RealmUrl {
+ get {
+ return (string)(ViewState[RealmUrlViewStateKey] ?? RealmUrlDefault);
+ }
+
+ set {
+ if (Page != null && !DesignMode) {
+ // Validate new value by trying to construct a Realm object based on it.
+ new Realm(OpenIdUtilities.GetResolvedRealm(this.Page, value, this.RelyingParty.Channel.GetRequestFromContext())); // throws an exception on failure.
+ } else {
+ // We can't fully test it, but it should start with either ~/ or a protocol.
+ if (Regex.IsMatch(value, @"^https?://")) {
+ new Uri(value.Replace("*.", string.Empty)); // make sure it's fully-qualified, but ignore wildcards
+ } else if (value.StartsWith("~/", StringComparison.Ordinal)) {
+ // this is valid too
+ } else {
+ throw new UriFormatException();
+ }
+ }
+ ViewState[RealmUrlViewStateKey] = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the OpenID ReturnTo of the relying party web site.
+ /// </summary>
+ [SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings", Justification = "Bindable property must be simple type")]
+ [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "System.Uri", Justification = "Using Uri.ctor for validation.")]
+ [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "Bindable property must be simple type")]
+ [Bindable(true), DefaultValue(ReturnToUrlDefault), Category(OpenIdCategory)]
+ [Description("The OpenID ReturnTo of the relying party web site.")]
+ [UrlProperty, Editor("System.Web.UI.Design.UrlEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
+ public string ReturnToUrl {
+ get {
+ return (string)(this.ViewState[ReturnToUrlViewStateKey] ?? ReturnToUrlDefault);
+ }
+
+ set {
+ if (this.Page != null && !this.DesignMode) {
+ // Validate new value by trying to construct a Uri based on it.
+ new Uri(this.RelyingParty.Channel.GetRequestFromContext().UrlBeforeRewriting, this.Page.ResolveUrl(value)); // throws an exception on failure.
+ } else {
+ // We can't fully test it, but it should start with either ~/ or a protocol.
+ if (Regex.IsMatch(value, @"^https?://")) {
+ new Uri(value); // make sure it's fully-qualified, but ignore wildcards
+ } else if (value.StartsWith("~/", StringComparison.Ordinal)) {
+ // this is valid too
+ } else {
+ throw new UriFormatException();
+ }
+ }
+
+ this.ViewState[ReturnToUrlViewStateKey] = value;
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to send a persistent cookie upon successful
+ /// login so the user does not have to log in upon returning to this site.
+ /// </summary>
+ [Bindable(true), DefaultValue(UsePersistentCookieDefault), Category(BehaviorCategory)]
+ [Description("Whether to send a persistent cookie upon successful " +
+ "login so the user does not have to log in upon returning to this site.")]
+ public virtual bool UsePersistentCookie {
+ get { return (bool)(this.ViewState[UsePersistentCookieViewStateKey] ?? UsePersistentCookieDefault); }
+ set { this.ViewState[UsePersistentCookieViewStateKey] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating when to use a popup window to complete the login experience.
+ /// </summary>
+ /// <value>The default value is <see cref="PopupBehavior.Never"/>.</value>
+ [Bindable(true), DefaultValue(PopupDefault), Category(BehaviorCategory)]
+ [Description("When to use a popup window to complete the login experience.")]
+ public virtual PopupBehavior Popup {
+ get { return (PopupBehavior)(ViewState[PopupViewStateKey] ?? PopupDefault); }
+ set { ViewState[PopupViewStateKey] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether to enforce on high security mode,
+ /// which requires the full authentication pipeline to be protected by SSL.
+ /// </summary>
+ [Bindable(true), DefaultValue(RequireSslDefault), Category(OpenIdCategory)]
+ [Description("Turns on high security mode, requiring the full authentication pipeline to be protected by SSL.")]
+ public bool RequireSsl {
+ get { return (bool)(ViewState[RequireSslViewStateKey] ?? RequireSslDefault); }
+ set { ViewState[RequireSslViewStateKey] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the URL to your privacy policy page that describes how
+ /// claims will be used and/or shared.
+ /// </summary>
+ [Bindable(true), Category(OpenIdCategory)]
+ [Description("The OpenID Identifier that this button will use to initiate login.")]
+ [TypeConverter(typeof(IdentifierConverter))]
+ public Identifier Identifier {
+ get { return (Identifier)ViewState[IdentifierViewStateKey]; }
+ set { ViewState[IdentifierViewStateKey] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the default association preference to set on authentication requests.
+ /// </summary>
+ internal AssociationPreference AssociationPreference { get; set; }
+
+ /// <summary>
+ /// Immediately redirects to the OpenID Provider to verify the Identifier
+ /// provided in the text box.
+ /// </summary>
+ public void LogOn() {
+ IAuthenticationRequest request = this.CreateRequests().FirstOrDefault();
+ if (this.IsPopupAppropriate(request)) {
+ this.ScriptPopupWindow(request);
+ } else {
+ request.RedirectToProvider();
+ }
+ }
+
+ /// <summary>
+ /// Creates the authentication requests for a given user-supplied Identifier.
+ /// </summary>
+ /// <returns>A sequence of authentication requests, any one of which may be
+ /// used to determine the user's control of the <see cref="IAuthenticationRequest.ClaimedIdentifier"/>.</returns>
+ protected virtual IEnumerable<IAuthenticationRequest> CreateRequests() {
+ Contract.Requires<InvalidOperationException>(this.Identifier != null, OpenIdStrings.NoIdentifierSet);
+ IEnumerable<IAuthenticationRequest> requests;
+
+ // Approximate the returnTo (either based on the customize property or the page URL)
+ // so we can use it to help with Realm resolution.
+ Uri returnToApproximation = this.ReturnToUrl != null ? new Uri(this.RelyingParty.Channel.GetRequestFromContext().UrlBeforeRewriting, this.ReturnToUrl) : this.Page.Request.Url;
+
+ // Resolve the trust root, and swap out the scheme and port if necessary to match the
+ // return_to URL, since this match is required by OpenId, and the consumer app
+ // may be using HTTP at some times and HTTPS at others.
+ UriBuilder realm = OpenIdUtilities.GetResolvedRealm(this.Page, this.RealmUrl, this.RelyingParty.Channel.GetRequestFromContext());
+ realm.Scheme = returnToApproximation.Scheme;
+ realm.Port = returnToApproximation.Port;
+
+ // Initiate openid request
+ // We use TryParse here to avoid throwing an exception which
+ // might slip through our validator control if it is disabled.
+ Realm typedRealm = new Realm(realm);
+ if (string.IsNullOrEmpty(this.ReturnToUrl)) {
+ requests = this.RelyingParty.CreateRequests(this.Identifier, typedRealm);
+ } else {
+ // Since the user actually gave us a return_to value,
+ // the "approximation" is exactly what we want.
+ requests = this.RelyingParty.CreateRequests(this.Identifier, typedRealm, returnToApproximation);
+ }
+
+ // Some OPs may be listed multiple times (one with HTTPS and the other with HTTP, for example).
+ // Since we're gathering OPs to try one after the other, just take the first choice of each OP
+ // and don't try it multiple times.
+ requests = requests.Distinct(DuplicateRequestedHostsComparer.Instance);
+
+ // Configure each generated request.
+ foreach (var req in requests) {
+ if (this.IsPopupAppropriate(req)) {
+ // Inform the OP that we'll be using a popup window.
+ req.AddExtension(new UIRequest());
+
+ // Inform ourselves in return_to that we're in a popup.
+ req.AddCallbackArguments(UIPopupCallbackKey, "1");
+
+ if (req.Provider.IsExtensionSupported<UIRequest>()) {
+ // Provide a hint for the client javascript about whether the OP supports the UI extension.
+ // This is so the window can be made the correct size for the extension.
+ // If the OP doesn't advertise support for the extension, the javascript will use
+ // a bigger popup window.
+ req.AddCallbackArguments(PopupUISupportedJsHint, "1");
+ }
+ }
+
+ // Add state that needs to survive across the redirect.
+ if (!this.Stateless) {
+ req.AddCallbackArguments(UsePersistentCookieCallbackKey, this.UsePersistentCookie.ToString(CultureInfo.InvariantCulture));
+ req.AddCallbackArguments(ReturnToReceivingControlId, this.ClientID);
+ }
+
+ ((AuthenticationRequest)req).AssociationPreference = this.AssociationPreference;
+ this.OnLoggingIn(req);
+
+ yield return req;
+ }
+ }
+
+ /// <summary>
+ /// Raises the <see cref="E:Load"/> event.
+ /// </summary>
+ /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
+ protected override void OnLoad(EventArgs e) {
+ base.OnLoad(e);
+
+ if (Page.IsPostBack) {
+ // OpenID responses NEVER come in the form of a postback.
+ return;
+ }
+
+ // Take an unreliable sneek peek to see if we're in a popup and an OpenID
+ // assertion is coming in. We shouldn't process assertions in a popup window.
+ if (this.Page.Request.QueryString[UIPopupCallbackKey] == "1" && this.Page.Request.QueryString[UIPopupCallbackParentKey] == null) {
+ // We're in a popup window. We need to close it and pass the
+ // message back to the parent window for processing.
+ this.ScriptClosingPopup();
+ return; // don't do any more processing on it now
+ }
+
+ // Only sniff for an OpenID response if it is targeted at this control. Note that
+ // Stateless mode causes no receiver to be indicated.
+ string receiver = this.Page.Request.QueryString[ReturnToReceivingControlId] ?? this.Page.Request.Form[ReturnToReceivingControlId];
+ if (receiver == null || receiver == this.ClientID) {
+ var response = this.RelyingParty.GetResponse();
+ if (response != null) {
+ string persistentString = response.GetCallbackArgument(UsePersistentCookieCallbackKey);
+ bool persistentBool;
+ if (persistentString != null && bool.TryParse(persistentString, out persistentBool)) {
+ this.UsePersistentCookie = persistentBool;
+ }
+
+ switch (response.Status) {
+ case AuthenticationStatus.Authenticated:
+ this.OnLoggedIn(response);
+ break;
+ case AuthenticationStatus.Canceled:
+ this.OnCanceled(response);
+ break;
+ case AuthenticationStatus.Failed:
+ this.OnFailed(response);
+ break;
+ case AuthenticationStatus.SetupRequired:
+ case AuthenticationStatus.ExtensionsOnly:
+ default:
+ // The NotApplicable (extension-only assertion) is NOT one that we support
+ // in this control because that scenario is primarily interesting to RPs
+ // that are asking a specific OP, and it is not user-initiated as this textbox
+ // is designed for.
+ throw new InvalidOperationException(MessagingStrings.UnexpectedMessageReceivedOfMany);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
+ /// </summary>
+ /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
+ protected override void OnPreRender(EventArgs e) {
+ base.OnPreRender(e);
+
+ this.Page.ClientScript.RegisterClientScriptResource(typeof(OpenIdRelyingPartyControlBase), EmbeddedJavascriptResource);
+ }
+
+ /// <summary>
+ /// Fires the <see cref="LoggedIn"/> event.
+ /// </summary>
+ /// <param name="response">The response.</param>
+ protected virtual void OnLoggedIn(IAuthenticationResponse response) {
+ Contract.Requires(response != null);
+ Contract.Requires(response.Status == AuthenticationStatus.Authenticated);
+ ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Authenticated, "Firing OnLoggedIn event without an authenticated response.");
+
+ var loggedIn = this.LoggedIn;
+ OpenIdEventArgs args = new OpenIdEventArgs(response);
+ if (loggedIn != null) {
+ loggedIn(this, args);
+ }
+
+ if (!args.Cancel) {
+ FormsAuthentication.RedirectFromLoginPage(response.ClaimedIdentifier, this.UsePersistentCookie);
+ }
+ }
+
+ /// <summary>
+ /// Fires the <see cref="LoggingIn"/> event.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>
+ /// Returns whether the login should proceed. False if some event handler canceled the request.
+ /// </returns>
+ protected virtual bool OnLoggingIn(IAuthenticationRequest request) {
+ Contract.Requires(request != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ EventHandler<OpenIdEventArgs> loggingIn = this.LoggingIn;
+
+ OpenIdEventArgs args = new OpenIdEventArgs(request);
+ if (loggingIn != null) {
+ loggingIn(this, args);
+ }
+
+ return !args.Cancel;
+ }
+
+ /// <summary>
+ /// Fires the <see cref="Canceled"/> event.
+ /// </summary>
+ /// <param name="response">The response.</param>
+ protected virtual void OnCanceled(IAuthenticationResponse response) {
+ Contract.Requires(response != null);
+ Contract.Requires(response.Status == AuthenticationStatus.Canceled);
+ ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Canceled, "Firing Canceled event for the wrong response type.");
+
+ var canceled = this.Canceled;
+ if (canceled != null) {
+ canceled(this, new OpenIdEventArgs(response));
+ }
+ }
+
+ /// <summary>
+ /// Fires the <see cref="Failed"/> event.
+ /// </summary>
+ /// <param name="response">The response.</param>
+ protected virtual void OnFailed(IAuthenticationResponse response) {
+ Contract.Requires(response != null);
+ Contract.Requires(response.Status == AuthenticationStatus.Failed);
+ ErrorUtilities.VerifyArgumentNotNull(response, "response");
+ ErrorUtilities.VerifyInternal(response.Status == AuthenticationStatus.Failed, "Firing Failed event for the wrong response type.");
+
+ var failed = this.Failed;
+ if (failed != null) {
+ failed(this, new OpenIdEventArgs(response));
+ }
+ }
+
+ /// <summary>
+ /// Creates the relying party instance used to generate authentication requests.
+ /// </summary>
+ /// <returns>The instantiated relying party.</returns>
+ protected virtual OpenIdRelyingParty CreateRelyingParty() {
+ IRelyingPartyApplicationStore store = this.Stateless ? null : DotNetOpenAuthSection.Configuration.OpenId.RelyingParty.ApplicationStore.CreateInstance(OpenIdRelyingParty.HttpApplicationStore);
+ var rp = new OpenIdRelyingParty(store);
+
+ // Only set RequireSsl to true, as we don't want to override
+ // a .config setting of true with false.
+ if (this.RequireSsl) {
+ rp.SecuritySettings.RequireSsl = true;
+ }
+
+ return rp;
+ }
+
+ /// <summary>
+ /// Detects whether a popup window should be used to show the Provider's UI.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ /// <returns>
+ /// <c>true</c> if a popup should be used; <c>false</c> otherwise.
+ /// </returns>
+ protected virtual bool IsPopupAppropriate(IAuthenticationRequest request) {
+ Contract.Requires(request != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ switch (this.Popup) {
+ case PopupBehavior.Never:
+ return false;
+ case PopupBehavior.Always:
+ return true;
+ case PopupBehavior.IfProviderSupported:
+ return request.Provider.IsExtensionSupported<UIRequest>();
+ default:
+ throw ErrorUtilities.ThrowInternal("Unexpected value for Popup property.");
+ }
+ }
+
+ /// <summary>
+ /// Adds attributes to an HTML &lt;A&gt; tag that will be written by the caller using
+ /// <see cref="HtmlTextWriter.RenderBeginTag(HtmlTextWriterTag)"/> after this method.
+ /// </summary>
+ /// <param name="writer">The HTML writer.</param>
+ /// <param name="request">The outgoing authentication request.</param>
+ /// <param name="windowStatus">The text to try to display in the status bar on mouse hover.</param>
+ protected void RenderOpenIdMessageTransmissionAsAnchorAttributes(HtmlTextWriter writer, IAuthenticationRequest request, string windowStatus) {
+ Contract.Requires(writer != null);
+ Contract.Requires(request != null);
+ ErrorUtilities.VerifyArgumentNotNull(writer, "writer");
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ // We render a standard HREF attribute for non-javascript browsers.
+ writer.AddAttribute(HtmlTextWriterAttribute.Href, request.RedirectingResponse.GetDirectUriRequest(this.RelyingParty.Channel).AbsoluteUri);
+
+ // And for the Javascript ones we do the extra work to use form POST where necessary.
+ writer.AddAttribute(HtmlTextWriterAttribute.Onclick, this.CreateGetOrPostAHrefValue(request) + " return false;");
+
+ writer.AddStyleAttribute(HtmlTextWriterStyle.Cursor, "pointer");
+ if (!string.IsNullOrEmpty(windowStatus)) {
+ writer.AddAttribute("onMouseOver", "window.status = " + MessagingUtilities.GetSafeJavascriptValue(windowStatus));
+ writer.AddAttribute("onMouseOut", "window.status = null");
+ }
+ }
+
+ /// <summary>
+ /// Gets the javascript to executee to redirect or POST an OpenID message to a remote party.
+ /// </summary>
+ /// <param name="request">The authentication request to send.</param>
+ /// <returns>The javascript that should execute.</returns>
+ private string CreateGetOrPostAHrefValue(IAuthenticationRequest request) {
+ Contract.Requires(request != null);
+ ErrorUtilities.VerifyArgumentNotNull(request, "request");
+
+ Uri directUri = request.RedirectingResponse.GetDirectUriRequest(this.RelyingParty.Channel);
+ return "window.dnoa_internal.GetOrPost(" + MessagingUtilities.GetSafeJavascriptValue(directUri.AbsoluteUri) + ");";
+ }
+
+ /// <summary>
+ /// Wires the return page to immediately display a popup window with the Provider in it.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ private void ScriptPopupWindow(IAuthenticationRequest request) {
+ Contract.Requires(request != null);
+ Contract.Requires(this.RelyingParty != null);
+
+ StringBuilder startupScript = new StringBuilder();
+
+ // Add a callback function that the popup window can call on this, the
+ // parent window, to pass back the authentication result.
+ startupScript.AppendLine("window.dnoa_internal = new Object();");
+ startupScript.AppendLine("window.dnoa_internal.processAuthorizationResult = function(uri) { window.location = uri; };");
+ startupScript.AppendLine("window.dnoa_internal.popupWindow = function() {");
+ startupScript.AppendFormat(
+ @"\tvar openidPopup = {0}",
+ UIUtilities.GetWindowPopupScript(this.RelyingParty, request, "openidPopup"));
+ startupScript.AppendLine("};");
+
+ this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "loginPopup", startupScript.ToString(), true);
+ }
+
+ /// <summary>
+ /// Wires the popup window to close itself and pass the authentication result to the parent window.
+ /// </summary>
+ private void ScriptClosingPopup() {
+ StringBuilder startupScript = new StringBuilder();
+ startupScript.AppendLine("window.opener.dnoa_internal.processAuthorizationResult(document.URL + '&" + UIPopupCallbackParentKey + "=1');");
+ startupScript.AppendLine("window.close();");
+
+ this.Page.ClientScript.RegisterStartupScript(typeof(OpenIdRelyingPartyControlBase), "loginPopupClose", startupScript.ToString(), true);
+
+ // TODO: alternately we should probably take over rendering this page here to avoid
+ // a lot of unnecessary work on the server and possible momentary display of the
+ // page in the popup window.
+ }
+
+ /// <summary>
+ /// An authentication request comparer that judges equality solely on the OP endpoint hostname.
+ /// </summary>
+ private class DuplicateRequestedHostsComparer : IEqualityComparer<IAuthenticationRequest> {
+ /// <summary>
+ /// The singleton instance of this comparer.
+ /// </summary>
+ private static IEqualityComparer<IAuthenticationRequest> instance = new DuplicateRequestedHostsComparer();
+
+ /// <summary>
+ /// Prevents a default instance of the <see cref="DuplicateRequestedHostsComparer"/> class from being created.
+ /// </summary>
+ private DuplicateRequestedHostsComparer() {
+ }
+
+ /// <summary>
+ /// Gets the singleton instance of this comparer.
+ /// </summary>
+ internal static IEqualityComparer<IAuthenticationRequest> Instance {
+ get { return instance; }
+ }
+
+ #region IEqualityComparer<IAuthenticationRequest> Members
+
+ /// <summary>
+ /// Determines whether the specified objects are equal.
+ /// </summary>
+ /// <param name="x">The first object of type <paramref name="T"/> to compare.</param>
+ /// <param name="y">The second object of type <paramref name="T"/> to compare.</param>
+ /// <returns>
+ /// true if the specified objects are equal; otherwise, false.
+ /// </returns>
+ public bool Equals(IAuthenticationRequest x, IAuthenticationRequest y) {
+ if (x == null && y == null) {
+ return true;
+ }
+
+ if (x == null || y == null) {
+ return false;
+ }
+
+ // We'll distinguish based on the host name only, which
+ // admittedly is only a heuristic, but if we remove one that really wasn't a duplicate, well,
+ // this multiple OP attempt thing was just a convenience feature anyway.
+ return string.Equals(x.Provider.Uri.Host, y.Provider.Uri.Host, StringComparison.OrdinalIgnoreCase);
+ }
+
+ /// <summary>
+ /// Returns a hash code for the specified object.
+ /// </summary>
+ /// <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param>
+ /// <returns>A hash code for the specified object.</returns>
+ /// <exception cref="T:System.ArgumentNullException">
+ /// The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.
+ /// </exception>
+ public int GetHashCode(IAuthenticationRequest obj) {
+ return obj.Provider.Uri.Host.GetHashCode();
+ }
+
+ #endregion
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js
new file mode 100644
index 0000000..3a17b7b
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdRelyingPartyControlBase.js
@@ -0,0 +1,174 @@
+//-----------------------------------------------------------------------
+// <copyright file="OpenIdRelyingPartyControlBase.js" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+// Options that can be set on the host page:
+//window.openid_visible_iframe = true; // causes the hidden iframe to show up
+//window.openid_trace = true; // causes lots of messages
+
+trace = function(msg) {
+ if (window.openid_trace) {
+ if (!window.openid_tracediv) {
+ window.openid_tracediv = document.createElement("ol");
+ document.body.appendChild(window.openid_tracediv);
+ }
+ var el = document.createElement("li");
+ el.appendChild(document.createTextNode(msg));
+ window.openid_tracediv.appendChild(el);
+ //alert(msg);
+ }
+};
+
+if (window.dnoa_internal === undefined) {
+ window.dnoa_internal = new Object();
+};
+
+// The possible authentication results
+window.dnoa_internal.authSuccess = new Object();
+window.dnoa_internal.authRefused = new Object();
+window.dnoa_internal.timedOut = new Object();
+
+/// <summary>Instantiates an object that provides string manipulation services for URIs.</summary>
+window.dnoa_internal.Uri = function(url) {
+ this.originalUri = url.toString();
+
+ this.toString = function() {
+ return this.originalUri;
+ };
+
+ this.getAuthority = function() {
+ var authority = this.getScheme() + "://" + this.getHost();
+ return authority;
+ }
+
+ this.getHost = function() {
+ var hostStartIdx = this.originalUri.indexOf("://") + 3;
+ var hostEndIndex = this.originalUri.indexOf("/", hostStartIdx);
+ if (hostEndIndex < 0) hostEndIndex = this.originalUri.length;
+ var host = this.originalUri.substr(hostStartIdx, hostEndIndex - hostStartIdx);
+ return host;
+ }
+
+ this.getScheme = function() {
+ var schemeStartIdx = this.indexOf("://");
+ return this.originalUri.substr(this.originalUri, schemeStartIdx);
+ }
+
+ this.trimFragment = function() {
+ var hashmark = this.originalUri.indexOf('#');
+ if (hashmark >= 0) {
+ return new window.dnoa_internal.Uri(this.originalUri.substr(0, hashmark));
+ }
+ return this;
+ };
+
+ this.appendQueryVariable = function(name, value) {
+ var pair = encodeURI(name) + "=" + encodeURI(value);
+ if (this.originalUri.indexOf('?') >= 0) {
+ this.originalUri = this.originalUri + "&" + pair;
+ } else {
+ this.originalUri = this.originalUri + "?" + pair;
+ }
+ };
+
+ function KeyValuePair(key, value) {
+ this.key = key;
+ this.value = value;
+ };
+
+ this.pairs = new Array();
+
+ var queryBeginsAt = this.originalUri.indexOf('?');
+ if (queryBeginsAt >= 0) {
+ this.queryString = url.substr(queryBeginsAt + 1);
+ var queryStringPairs = this.queryString.split('&');
+
+ for (var i = 0; i < queryStringPairs.length; i++) {
+ var equalsAt = queryStringPairs[i].indexOf('=');
+ left = (equalsAt >= 0) ? queryStringPairs[i].substring(0, equalsAt) : null;
+ right = (equalsAt >= 0) ? queryStringPairs[i].substring(equalsAt + 1) : queryStringPairs[i];
+ this.pairs.push(new KeyValuePair(unescape(left), unescape(right)));
+ }
+ };
+
+ this.getQueryArgValue = function(key) {
+ for (var i = 0; i < this.pairs.length; i++) {
+ if (this.pairs[i].key == key) {
+ return this.pairs[i].value;
+ }
+ }
+ };
+
+ this.getPairs = function() {
+ return this.pairs;
+ }
+
+ this.containsQueryArg = function(key) {
+ return this.getQueryArgValue(key);
+ };
+
+ this.getUriWithoutQueryOrFragement = function() {
+ var queryBeginsAt = this.originalUri.indexOf('?');
+ if (queryBeginsAt >= 0) {
+ return this.originalUri.substring(0, queryBeginsAt);
+ } else {
+ var fragmentBeginsAt = this.originalUri.indexOf('#');
+ if (fragmentBeginsAt >= 0) {
+ return this.originalUri.substring(0, fragmentBeginsAt);
+ } else {
+ return this.originalUri;
+ }
+ }
+ };
+
+ this.indexOf = function(args) {
+ return this.originalUri.indexOf(args);
+ };
+
+ return this;
+};
+
+/// <summary>Creates a hidden iframe.</summary>
+window.dnoa_internal.createHiddenIFrame = function() {
+ var iframe = document.createElement("iframe");
+ if (!window.openid_visible_iframe) {
+ iframe.setAttribute("width", 0);
+ iframe.setAttribute("height", 0);
+ iframe.setAttribute("style", "display: none");
+ iframe.setAttribute("border", 0);
+ }
+
+ return iframe;
+}
+
+/// <summary>Redirects the current window/frame to the given URI,
+/// either using a GET or a POST as required by the length of the URL.</summary>
+window.dnoa_internal.GetOrPost = function(uri) {
+ var maxGetLength = 2 * 1024; // keep in sync with DotNetOpenAuth.Messaging.Channel.IndirectMessageGetToPostThreshold
+ uri = new window.dnoa_internal.Uri(uri);
+
+ if (uri.toString().length <= maxGetLength) {
+ window.location = uri.toString();
+ } else {
+ trace("Preparing to POST: " + uri.toString());
+ var iframe = window.dnoa_internal.createHiddenIFrame();
+ document.body.appendChild(iframe);
+ var doc = iframe.ownerDocument;
+ var form = doc.createElement('form');
+ form.action = uri.getUriWithoutQueryOrFragement();
+ form.method = "POST";
+ form.target = "_top";
+ for (var i = 0; i < uri.getPairs().length; i++) {
+ var input = doc.createElement('input');
+ input.type = 'hidden';
+ input.name = uri.getPairs()[i].key;
+ input.value = uri.getPairs()[i].value;
+ trace(input.name + " = " + input.value);
+ form.appendChild(input);
+ }
+ doc.body.appendChild(form);
+ form.submit();
+ }
+};
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
index 22b740f..878e2b7 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/OpenIdTextBox.cs
@@ -1254,7 +1254,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
startupScript.AppendLine("window.opener.dnoa_internal.processAuthorizationResult(document.URL + '&" + UIPopupCallbackParentKey + "=1');");
startupScript.AppendLine("window.close();");
- this.Page.ClientScript.RegisterStartupScript(this.GetType(), "loginPopupClose", startupScript.ToString(), true);
+ // We're referencing the OpenIdRelyingPartyControlBase type here to avoid double-registering this script
+ // if the other control exists on the page.
+ this.Page.ClientScript.RegisterStartupScript(typeof(OpenIdRelyingPartyControlBase), "loginPopupClose", startupScript.ToString(), true);
}
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs
index cc81c99..1b8c94d 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/PositiveAnonymousResponse.cs
@@ -24,6 +24,11 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private readonly IndirectSignedResponse response;
/// <summary>
+ /// Information about the OP endpoint that issued this assertion.
+ /// </summary>
+ private readonly ProviderEndpointDescription provider;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="PositiveAnonymousResponse"/> class.
/// </summary>
/// <param name="response">The response message.</param>
@@ -32,6 +37,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
ErrorUtilities.VerifyArgumentNotNull(response, "response");
this.response = response;
+ if (response.ProviderEndpoint != null && response.Version != null) {
+ this.provider = new ProviderEndpointDescription(response.ProviderEndpoint, response.Version);
+ }
}
#region IAuthenticationResponse Properties
@@ -97,6 +105,19 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets information about the OpenId Provider, as advertised by the
+ /// OpenID discovery documents found at the <see cref="ClaimedIdentifier"/>
+ /// location.
+ /// </summary>
+ /// <value>
+ /// The Provider endpoint that issued the positive assertion;
+ /// or <c>null</c> if information about the Provider is unavailable.
+ /// </value>
+ public IProviderEndpoint Provider {
+ get { return this.provider; }
+ }
+
+ /// <summary>
/// Gets the details regarding a failed authentication attempt, if available.
/// This will be set if and only if <see cref="Status"/> is <see cref="AuthenticationStatus.Failed"/>.
/// </summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
index 2019d16..ff29498 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/RelyingPartySecuritySettings.cs
@@ -6,6 +6,9 @@
namespace DotNetOpenAuth.OpenId.RelyingParty {
using System;
+ using System.Collections.Generic;
+ using System.Collections.ObjectModel;
+ using System.Linq;
using DotNetOpenAuth.Messaging;
/// <summary>
@@ -51,6 +54,12 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
public bool RequireSsl { get; set; }
/// <summary>
+ /// Gets or sets a value indicating whether only OP Identifiers will be discoverable
+ /// when creating authentication requests.
+ /// </summary>
+ public bool RequireDirectedIdentity { get; set; }
+
+ /// <summary>
/// Gets or sets the oldest version of OpenID the remote party is allowed to implement.
/// </summary>
/// <value>Defaults to <see cref="ProtocolVersion.V10"/></value>
@@ -80,5 +89,36 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// directly issued by the Provider that is sending the assertion.
/// </remarks>
public bool RejectDelegatingIdentifiers { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether unsigned extensions in authentication responses should be ignored.
+ /// </summary>
+ /// <value>The default value is <c>false</c>.</value>
+ /// <remarks>
+ /// When set to true, the <see cref="IAuthenticationResponse.GetUntrustedExtension"/> methods
+ /// will not return any extension that was not signed by the Provider.
+ /// </remarks>
+ public bool IgnoreUnsignedExtensions { get; set; }
+
+ /// <summary>
+ /// Gets or sets a value indicating whether authentication requests will only be
+ /// sent to Providers with whom we can create a shared association.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> to immediately fail authentication if an association with the Provider cannot be established; otherwise, <c>false</c>.
+ /// The default value is <c>false</c>.
+ /// </value>
+ public bool RequireAssociation { get; set; }
+
+ /// <summary>
+ /// Filters out any disallowed endpoints.
+ /// </summary>
+ /// <param name="endpoints">The endpoints discovered on an Identifier.</param>
+ /// <returns>A sequence of endpoints that satisfy all security requirements.</returns>
+ internal IEnumerable<ServiceEndpoint> FilterEndpoints(IEnumerable<ServiceEndpoint> endpoints) {
+ return endpoints
+ .Where(se => !this.RejectDelegatingIdentifiers || se.ClaimedIdentifier == se.ProviderLocalIdentifier)
+ .Where(se => !this.RequireDirectedIdentity || se.ClaimedIdentifier == se.Protocol.ClaimedIdentifierForOPIdentifier);
+ }
}
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs
index 8ddcf27..2cfe1db 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/ServiceEndpoint.cs
@@ -95,26 +95,10 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets the URL that the OpenID Provider receives authentication requests at.
/// </summary>
- Uri IProviderEndpoint.Uri { get { return this.ProviderEndpoint; } }
-
- /// <summary>
- /// Gets the URL which accepts OpenID Authentication protocol messages.
- /// </summary>
- /// <remarks>
- /// Obtained by performing discovery on the User-Supplied Identifier.
- /// This value MUST be an absolute HTTP or HTTPS URL.
- /// </remarks>
- public Uri ProviderEndpoint {
+ Uri IProviderEndpoint.Uri {
get { return this.ProviderDescription.Endpoint; }
}
- /*
- /// <summary>
- /// An Identifier for an OpenID Provider.
- /// </summary>
- public Identifier ProviderIdentifier { get; private set; }
- */
-
/// <summary>
/// Gets the Identifier that was presented by the end user to the Relying Party,
/// or selected by the user at the OpenID Provider.
@@ -225,7 +209,9 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Gets the detected version of OpenID implemented by the Provider.
/// </summary>
- Version IProviderEndpoint.Version { get { return Protocol.Version; } }
+ Version IProviderEndpoint.Version {
+ get { return this.ProviderDescription.ProtocolVersion; }
+ }
/// <summary>
/// Gets an XRDS sorting routine that uses the XRDS Service/@Priority
@@ -280,6 +266,17 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
}
/// <summary>
+ /// Gets the URL which accepts OpenID Authentication protocol messages.
+ /// </summary>
+ /// <remarks>
+ /// Obtained by performing discovery on the User-Supplied Identifier.
+ /// This value MUST be an absolute HTTP or HTTPS URL.
+ /// </remarks>
+ internal Uri ProviderEndpoint {
+ get { return this.ProviderDescription.Endpoint; }
+ }
+
+ /// <summary>
/// Gets a value indicating whether the <see cref="ProviderEndpoint"/> is using an encrypted channel.
/// </summary>
internal bool IsSecure {
@@ -319,46 +316,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <c>true</c> if the service type uri is present; <c>false</c> otherwise.
/// </returns>
public bool IsTypeUriPresent(string typeUri) {
- return this.IsExtensionSupported(typeUri);
- }
-
- /// <summary>
- /// Determines whether some extension is supported by the Provider.
- /// </summary>
- /// <param name="extensionUri">The extension URI.</param>
- /// <returns>
- /// <c>true</c> if the extension is supported; otherwise, <c>false</c>.
- /// </returns>
- public bool IsExtensionSupported(string extensionUri) {
- Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(extensionUri));
-
- ErrorUtilities.VerifyOperation(this.ProviderSupportedServiceTypeUris != null, OpenIdStrings.ExtensionLookupSupportUnavailable);
- return this.ProviderSupportedServiceTypeUris.Contains(extensionUri);
- }
-
- /// <summary>
- /// Determines whether a given extension is supported by this endpoint.
- /// </summary>
- /// <param name="extension">An instance of the extension to check support for.</param>
- /// <returns>
- /// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>.
- /// </returns>
- public bool IsExtensionSupported(IOpenIdMessageExtension extension) {
- Contract.Requires<ArgumentNullException>(extension != null);
-
- // Consider the primary case.
- if (this.IsExtensionSupported(extension.TypeUri)) {
- return true;
- }
-
- // Consider the secondary cases.
- if (extension.AdditionalSupportedTypeUris != null) {
- if (extension.AdditionalSupportedTypeUris.Any(typeUri => this.IsExtensionSupported(typeUri))) {
- return true;
- }
- }
-
- return false;
+ return this.ProviderDescription.IsExtensionSupported(typeUri);
}
/// <summary>
@@ -369,8 +327,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>.
/// </returns>
public bool IsExtensionSupported<T>() where T : IOpenIdMessageExtension, new() {
- T extension = new T();
- return this.IsExtensionSupported(extension);
+ return this.ProviderDescription.IsExtensionSupported<T>();
}
/// <summary>
@@ -381,8 +338,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <c>true</c> if the extension is supported by this endpoint; otherwise, <c>false</c>.
/// </returns>
public bool IsExtensionSupported(Type extensionType) {
- var extension = (IOpenIdMessageExtension)Activator.CreateInstance(extensionType);
- return this.IsExtensionSupported(extension);
+ return this.ProviderDescription.IsExtensionSupported(extensionType);
}
/// <summary>
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
index 96dd8d8..3e0c8db 100644
--- a/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/StandardRelyingPartyApplicationStore.cs
@@ -14,7 +14,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// An in-memory store for Relying Parties, suitable for single server, single process
/// ASP.NET web sites.
/// </summary>
- internal class StandardRelyingPartyApplicationStore : IRelyingPartyApplicationStore {
+ public class StandardRelyingPartyApplicationStore : IRelyingPartyApplicationStore {
/// <summary>
/// The nonce store to use.
/// </summary>
@@ -28,7 +28,7 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
/// <summary>
/// Initializes a new instance of the <see cref="StandardRelyingPartyApplicationStore"/> class.
/// </summary>
- internal StandardRelyingPartyApplicationStore() {
+ public StandardRelyingPartyApplicationStore() {
this.nonceStore = new NonceMemoryStore(DotNetOpenAuthSection.Configuration.OpenId.MaxAuthenticationTime);
this.associationStore = new AssociationMemoryStore<Uri>();
}
diff --git a/src/DotNetOpenAuth/OpenId/RelyingParty/WellKnownProviders.cs b/src/DotNetOpenAuth/OpenId/RelyingParty/WellKnownProviders.cs
new file mode 100644
index 0000000..bd45842
--- /dev/null
+++ b/src/DotNetOpenAuth/OpenId/RelyingParty/WellKnownProviders.cs
@@ -0,0 +1,33 @@
+//-----------------------------------------------------------------------
+// <copyright file="WellKnownProviders.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OpenId.RelyingParty {
+ /// <summary>
+ /// Common OpenID Provider Identifiers.
+ /// </summary>
+ public sealed class WellKnownProviders {
+ /// <summary>
+ /// The Yahoo OP Identifier.
+ /// </summary>
+ public static readonly Identifier Yahoo = "https://me.yahoo.com/";
+
+ /// <summary>
+ /// The Google OP Identifier.
+ /// </summary>
+ public static readonly Identifier Google = "https://www.google.com/accounts/o8/id";
+
+ /// <summary>
+ /// The MyOpenID OP Identifier.
+ /// </summary>
+ public static readonly Identifier MyOpenId = "https://www.myopenid.com/";
+
+ /// <summary>
+ /// Prevents a default instance of the <see cref="WellKnownProviders"/> class from being created.
+ /// </summary>
+ private WellKnownProviders() {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/SecuritySettings.cs b/src/DotNetOpenAuth/OpenId/SecuritySettings.cs
index 422813b..d4df697 100644
--- a/src/DotNetOpenAuth/OpenId/SecuritySettings.cs
+++ b/src/DotNetOpenAuth/OpenId/SecuritySettings.cs
@@ -5,12 +5,16 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.OpenId {
+ using System;
+ using System.Collections.Generic;
+ using System.Collections.Specialized;
using DotNetOpenAuth.Messaging;
/// <summary>
/// Security settings that may be applicable to both relying parties and providers.
/// </summary>
- public class SecuritySettings {
+ [Serializable]
+ public abstract class SecuritySettings {
/// <summary>
/// Gets the default minimum hash bit length.
/// </summary>
@@ -30,7 +34,7 @@ namespace DotNetOpenAuth.OpenId {
/// Initializes a new instance of the <see cref="SecuritySettings"/> class.
/// </summary>
/// <param name="isProvider">A value indicating whether this class is being instantiated for a Provider.</param>
- internal SecuritySettings(bool isProvider) {
+ protected SecuritySettings(bool isProvider) {
this.MaximumHashBitLength = isProvider ? MaximumHashBitLengthOPDefault : MaximumHashBitLengthRPDefault;
this.MinimumHashBitLength = MinimumHashBitLengthDefault;
}
diff --git a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
index 5c6ec39..b165b33 100644
--- a/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
+++ b/src/DotNetOpenAuth/OpenId/UriIdentifier.cs
@@ -324,7 +324,7 @@ namespace DotNetOpenAuth.OpenId {
foreach (var protocol in Protocol.AllPracticalVersions) {
// rel attributes are supposed to be interpreted with case INsensitivity,
// and is a space-delimited list of values. (http://www.htmlhelp.com/reference/html40/values.html#linktypes)
- var serverLinkTag = linkTags.FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase));
+ var serverLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryProviderKey) + @"\b", RegexOptions.IgnoreCase));
if (serverLinkTag == null) {
continue;
}
@@ -333,7 +333,7 @@ namespace DotNetOpenAuth.OpenId {
if (Uri.TryCreate(serverLinkTag.Href, UriKind.Absolute, out providerEndpoint)) {
// See if a LocalId tag of the discovered version exists
Identifier providerLocalIdentifier = null;
- var delegateLinkTag = linkTags.FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase));
+ var delegateLinkTag = linkTags.WithAttribute("rel").FirstOrDefault(tag => Regex.IsMatch(tag.Attributes["rel"], @"\b" + Regex.Escape(protocol.HtmlDiscoveryLocalIdKey) + @"\b", RegexOptions.IgnoreCase));
if (delegateLinkTag != null) {
if (Identifier.IsValid(delegateLinkTag.Href)) {
providerLocalIdentifier = delegateLinkTag.Href;
diff --git a/src/DotNetOpenAuth/Strings.Designer.cs b/src/DotNetOpenAuth/Strings.Designer.cs
index eea4675..43fec22 100644
--- a/src/DotNetOpenAuth/Strings.Designer.cs
+++ b/src/DotNetOpenAuth/Strings.Designer.cs
@@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
-// Runtime Version:2.0.50727.3521
+// Runtime Version:2.0.50727.4918
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -68,5 +68,14 @@ namespace DotNetOpenAuth {
return ResourceManager.GetString("ConfigurationTypeMustBePublic", resourceCulture);
}
}
+
+ /// <summary>
+ /// Looks up a localized string similar to The configuration XAML reference to {0} requires a current HttpContext to resolve..
+ /// </summary>
+ internal static string ConfigurationXamlReferenceRequiresHttpContext {
+ get {
+ return ResourceManager.GetString("ConfigurationXamlReferenceRequiresHttpContext", resourceCulture);
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth/Strings.resx b/src/DotNetOpenAuth/Strings.resx
index c42347b..bbfa162 100644
--- a/src/DotNetOpenAuth/Strings.resx
+++ b/src/DotNetOpenAuth/Strings.resx
@@ -120,4 +120,7 @@
<data name="ConfigurationTypeMustBePublic" xml:space="preserve">
<value>The configuration-specified type {0} must be public, and is not.</value>
</data>
+ <data name="ConfigurationXamlReferenceRequiresHttpContext" xml:space="preserve">
+ <value>The configuration XAML reference to {0} requires a current HttpContext to resolve.</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/Yadis/HtmlParser.cs b/src/DotNetOpenAuth/Yadis/HtmlParser.cs
index 5a00da8..406cb4b 100644
--- a/src/DotNetOpenAuth/Yadis/HtmlParser.cs
+++ b/src/DotNetOpenAuth/Yadis/HtmlParser.cs
@@ -5,8 +5,11 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Yadis {
+ using System;
using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
using System.Globalization;
+ using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
@@ -88,6 +91,19 @@ namespace DotNetOpenAuth.Yadis {
}
/// <summary>
+ /// Filters a list of controls based on presence of an attribute.
+ /// </summary>
+ /// <typeparam name="T">The type of HTML controls being filtered.</typeparam>
+ /// <param name="sequence">The sequence.</param>
+ /// <param name="attribute">The attribute.</param>
+ /// <returns>A filtered sequence of attributes.</returns>
+ internal static IEnumerable<T> WithAttribute<T>(this IEnumerable<T> sequence, string attribute) where T : HtmlControl {
+ Contract.Requires(sequence != null);
+ Contract.Requires(!String.IsNullOrEmpty(attribute));
+ return sequence.Where(tag => tag.Attributes[attribute] != null);
+ }
+
+ /// <summary>
/// Generates a regular expression that will find a given HTML tag.
/// </summary>
/// <param name="tagName">Name of the tag.</param>
diff --git a/src/DotNetOpenAuth/Yadis/Yadis.cs b/src/DotNetOpenAuth/Yadis/Yadis.cs
index cce8836..a9a573b 100644
--- a/src/DotNetOpenAuth/Yadis/Yadis.cs
+++ b/src/DotNetOpenAuth/Yadis/Yadis.cs
@@ -12,6 +12,7 @@ namespace DotNetOpenAuth.Yadis {
using System.Net.Cache;
using System.Web.UI.HtmlControls;
using System.Xml;
+ using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.Xrds;
@@ -31,7 +32,7 @@ namespace DotNetOpenAuth.Yadis {
#if DEBUG
internal static readonly RequestCachePolicy IdentifierDiscoveryCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.BypassCache);
#else
- internal static readonly RequestCachePolicy IdentifierDiscoveryCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.CacheIfAvailable);
+ internal static readonly RequestCachePolicy IdentifierDiscoveryCachePolicy = new HttpRequestCachePolicy(DotNetOpenAuthSection.Configuration.OpenId.CacheDiscovery ? HttpRequestCacheLevel.CacheIfAvailable : HttpRequestCacheLevel.BypassCache);
#endif
/// <summary>
@@ -81,7 +82,7 @@ namespace DotNetOpenAuth.Yadis {
Logger.Yadis.DebugFormat("{0} found in HTTP header. Preparing to pull XRDS from {1}", HeaderName, url);
}
}
- if (url == null && response.ContentType != null && response.ContentType.MediaType == ContentTypes.Html) {
+ if (url == null && response.ContentType != null && (response.ContentType.MediaType == ContentTypes.Html || response.ContentType.MediaType == ContentTypes.XHtml)) {
url = FindYadisDocumentLocationInHtmlMetaTags(response.GetResponseString());
if (url != null) {
Logger.Yadis.DebugFormat("{0} found in HTML Http-Equiv tag. Preparing to pull XRDS from {1}", HeaderName, url);
diff --git a/src/official-build-key.pub b/src/official-build-key.pub
new file mode 100644
index 0000000..6e68f53
--- /dev/null
+++ b/src/official-build-key.pub
Binary files differ