diff options
Diffstat (limited to 'src')
102 files changed, 2379 insertions, 1620 deletions
diff --git a/src/DotNetOpenAuth.Core/Messaging/Channel.cs b/src/DotNetOpenAuth.Core/Messaging/Channel.cs index 16e39d3..016a2b6 100644 --- a/src/DotNetOpenAuth.Core/Messaging/Channel.cs +++ b/src/DotNetOpenAuth.Core/Messaging/Channel.cs @@ -143,7 +143,10 @@ namespace DotNetOpenAuth.Messaging { /// A class prepared to analyze incoming messages and indicate what concrete /// message types can deserialize from it. /// </param> - /// <param name="bindingElements">The binding elements to use in sending and receiving messages.</param> + /// <param name="bindingElements"> + /// The binding elements to use in sending and receiving messages. + /// The order they are provided is used for outgoing messgaes, and reversed for incoming messages. + /// </param> protected Channel(IMessageFactory messageTypeProvider, params IChannelBindingElement[] bindingElements) { Requires.NotNull(messageTypeProvider, "messageTypeProvider"); diff --git a/src/DotNetOpenAuth.Core/Messaging/DataBag.cs b/src/DotNetOpenAuth.Core/Messaging/DataBag.cs index c9c3415..f09bcea 100644 --- a/src/DotNetOpenAuth.Core/Messaging/DataBag.cs +++ b/src/DotNetOpenAuth.Core/Messaging/DataBag.cs @@ -14,7 +14,7 @@ namespace DotNetOpenAuth.Messaging { /// A collection of message parts that will be serialized into a single string, /// to be set into a larger message. /// </summary> - internal abstract class DataBag : IMessage { + public abstract class DataBag : IMessage { /// <summary> /// The default version for DataBags. /// </summary> diff --git a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs index 9d4b93e..746efb1 100644 --- a/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs +++ b/src/DotNetOpenAuth.Core/Messaging/DataBagFormatterBase.cs @@ -146,6 +146,8 @@ namespace DotNetOpenAuth.Messaging { /// <returns>A non-null, non-empty value.</returns> [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "No apparent problem. False positive?")] public string Serialize(T message) { + Requires.NotNull(message, "message"); + message.UtcCreationDate = DateTime.UtcNow; if (this.decodeOnceOnly != null) { diff --git a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs index b26deeb..4e75145 100644 --- a/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth.Core/Messaging/MessagingUtilities.cs @@ -290,6 +290,56 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Compares to string values for ordinal equality in such a way that its execution time does not depend on how much of the value matches. + /// </summary> + /// <param name="value1">The first value.</param> + /// <param name="value2">The second value.</param> + /// <returns>A value indicating whether the two strings share ordinal equality.</returns> + /// <remarks> + /// In signature equality checks, a difference in execution time based on how many initial characters match MAY + /// be used as an attack to figure out the expected signature. It is therefore important to make a signature + /// equality check's execution time independent of how many characters match the expected value. + /// See http://codahale.com/a-lesson-in-timing-attacks/ for more information. + /// </remarks> + public static bool EqualsConstantTime(string value1, string value2) { + // If exactly one value is null, they don't match. + if (value1 == null ^ value2 == null) { + return false; + } + + // If both values are null (since if one is at this point then they both are), it's a match. + if (value1 == null) { + return true; + } + + if (value1.Length != value2.Length) { + return false; + } + + // This looks like a pretty crazy way to compare values, but it provides a constant time equality check, + // and is more resistant to compiler optimizations than simply setting a boolean flag and returning the boolean after the loop. + int result = 0; + for (int i = 0; i < value1.Length; i++) { + result |= value1[i] ^ value2[i]; + } + + return result == 0; + } + + /// <summary> + /// Gets the URL to the root of a web site, which may include a virtual directory path. + /// </summary> + /// <returns>An absolute URI.</returns> + internal static Uri GetWebRoot() { + HttpRequestBase requestInfo = new HttpRequestWrapper(HttpContext.Current.Request); + UriBuilder realmUrl = new UriBuilder(requestInfo.GetPublicFacingUrl()); + realmUrl.Path = HttpContext.Current.Request.ApplicationPath; + realmUrl.Query = null; + realmUrl.Fragment = null; + return realmUrl.Uri; + } + + /// <summary> /// Clears any existing elements in a collection and fills the collection with a given set of values. /// </summary> /// <typeparam name="T">The type of value kept in the collection.</typeparam> @@ -868,43 +918,6 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Compares to string values for ordinal equality in such a way that its execution time does not depend on how much of the value matches. - /// </summary> - /// <param name="value1">The first value.</param> - /// <param name="value2">The second value.</param> - /// <returns>A value indicating whether the two strings share ordinal equality.</returns> - /// <remarks> - /// In signature equality checks, a difference in execution time based on how many initial characters match MAY - /// be used as an attack to figure out the expected signature. It is therefore important to make a signature - /// equality check's execution time independent of how many characters match the expected value. - /// See http://codahale.com/a-lesson-in-timing-attacks/ for more information. - /// </remarks> - internal static bool EqualsConstantTime(string value1, string value2) { - // If exactly one value is null, they don't match. - if (value1 == null ^ value2 == null) { - return false; - } - - // If both values are null (since if one is at this point then they both are), it's a match. - if (value1 == null) { - return true; - } - - if (value1.Length != value2.Length) { - return false; - } - - // This looks like a pretty crazy way to compare values, but it provides a constant time equality check, - // and is more resistant to compiler optimizations than simply setting a boolean flag and returning the boolean after the loop. - int result = 0; - for (int i = 0; i < value1.Length; i++) { - result |= value1[i] ^ value2[i]; - } - - return result == 0; - } - - /// <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" /> diff --git a/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs b/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs index 7a1d194..7ca5d45 100644 --- a/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs +++ b/src/DotNetOpenAuth.Core/Messaging/StandardMessageFactoryChannel.cs @@ -31,7 +31,10 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="messageTypes">The message types that might be encountered.</param> /// <param name="versions">All the possible message versions that might be encountered.</param> - /// <param name="bindingElements">The binding elements to apply to the channel.</param> + /// <param name="bindingElements"> + /// The binding elements to use in sending and receiving messages. + /// The order they are provided is used for outgoing messgaes, and reversed for incoming messages. + /// </param> protected StandardMessageFactoryChannel(ICollection<Type> messageTypes, ICollection<Version> versions, params IChannelBindingElement[] bindingElements) : base(new StandardMessageFactory(), bindingElements) { Requires.NotNull(messageTypes, "messageTypes"); diff --git a/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs index e57b211..91d27f5 100644 --- a/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs +++ b/src/DotNetOpenAuth.Core/Properties/AssemblyInfo.cs @@ -64,6 +64,7 @@ using System.Web.UI; [assembly: InternalsVisibleTo("DotNetOpenAuth.OpenIdOAuth, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.AuthorizationServer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ClientAuthorization, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ResourceServer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client.UI, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] @@ -88,6 +89,7 @@ using System.Web.UI; [assembly: InternalsVisibleTo("DotNetOpenAuth.OpenIdOAuth")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.AuthorizationServer")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ClientAuthorization")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ResourceServer")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client")] [assembly: InternalsVisibleTo("DotNetOpenAuth.AspNet.Test")] diff --git a/src/DotNetOpenAuth.InfoCard/InfoCard/Token/TokenUtility.cs b/src/DotNetOpenAuth.InfoCard/InfoCard/Token/TokenUtility.cs index c424916..e50cafd 100644 --- a/src/DotNetOpenAuth.InfoCard/InfoCard/Token/TokenUtility.cs +++ b/src/DotNetOpenAuth.InfoCard/InfoCard/Token/TokenUtility.cs @@ -97,6 +97,10 @@ namespace DotNetOpenAuth.InfoCard { }), MaximumClockSkew); + if (audience != null) { + samlAuthenticator.AllowedAudienceUris.Add(audience.AbsoluteUri); + } + return AuthorizationContext.CreateDefaultAuthorizationContext(samlAuthenticator.ValidateToken(token)); } diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj index f6b1a50..c1f3124 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj @@ -19,6 +19,25 @@ </PropertyGroup> <ItemGroup> <Compile Include="OAuth2\AuthorizationServer.cs" /> + <Compile Include="OAuth2\AuthServerStrings.Designer.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>AuthServerStrings.resx</DependentUpon> + </Compile> + <Compile Include="OAuth2\AuthServerUtilities.cs" /> + <Compile Include="OAuth2\ChannelElements\TokenCodeSerializationBindingElement.cs" /> + <Compile Include="OAuth2\ChannelElements\AuthorizationCode.cs" /> + <Compile Include="OAuth2\ChannelElements\MessageValidationBindingElement.cs" /> + <Compile Include="OAuth2\ChannelElements\AuthServerBindingElementBase.cs" /> + <Compile Include="OAuth2\ChannelElements\IOAuth2ChannelWithAuthorizationServer.cs" /> + <Compile Include="OAuth2\ChannelElements\OAuth2AuthorizationServerChannel.cs" /> + <Compile Include="OAuth2\ChannelElements\RefreshToken.cs" /> + <Compile Include="OAuth2\ClientDescription.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenAuthorizationCodeRequestAS.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenRefreshRequestAS.cs" /> + <Compile Include="OAuth2\Messages\EndUserAuthorizationSuccessAuthCodeResponseAS.cs" /> + <Compile Include="OAuth2\Messages\IAuthorizationCodeCarryingRequest.cs" /> + <Compile Include="OAuth2\Messages\IRefreshTokenCarryingRequest.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <ItemGroup> @@ -26,11 +45,21 @@ <Project>{60426312-6AE5-4835-8667-37EDEA670222}</Project> <Name>DotNetOpenAuth.Core</Name> </ProjectReference> + <ProjectReference Include="..\DotNetOpenAuth.OAuth2.ClientAuthorization\DotNetOpenAuth.OAuth2.ClientAuthorization.csproj"> + <Project>{CCF3728A-B3D7-404A-9BC6-75197135F2D7}</Project> + <Name>DotNetOpenAuth.OAuth2.ClientAuthorization</Name> + </ProjectReference> <ProjectReference Include="..\DotNetOpenAuth.OAuth2\DotNetOpenAuth.OAuth2.csproj"> <Project>{56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}</Project> <Name>DotNetOpenAuth.OAuth2</Name> </ProjectReference> </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="OAuth2\AuthServerStrings.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>AuthServerStrings.Designer.cs</LastGenOutput> + </EmbeddedResource> + </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" /> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " /> diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.Designer.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.Designer.cs new file mode 100644 index 0000000..4b4f830 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.Designer.cs @@ -0,0 +1,117 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.17614 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace DotNetOpenAuth.OAuth2 { + 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", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class AuthServerStrings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal AuthServerStrings() { + } + + /// <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.OAuth2.AuthServerStrings", typeof(AuthServerStrings).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 The requested access scope exceeds the grant scope.. + /// </summary> + internal static string AccessScopeExceedsGrantScope { + get { + return ResourceManager.GetString("AccessScopeExceedsGrantScope", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The callback URL ({0}) is not allowed for this client.. + /// </summary> + internal static string ClientCallbackDisallowed { + get { + return ResourceManager.GetString("ClientCallbackDisallowed", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Failure looking up secret for client or token.. + /// </summary> + internal static string ClientOrTokenSecretNotFound { + get { + return ResourceManager.GetString("ClientOrTokenSecretNotFound", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The client secret was incorrect.. + /// </summary> + internal static string ClientSecretMismatch { + get { + return ResourceManager.GetString("ClientSecretMismatch", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Invalid resource owner password credential.. + /// </summary> + internal static string InvalidResourceOwnerPasswordCredential { + get { + return ResourceManager.GetString("InvalidResourceOwnerPasswordCredential", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to No callback URI was available for this request.. + /// </summary> + internal static string NoCallback { + get { + return ResourceManager.GetString("NoCallback", resourceCulture); + } + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.resx b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.resx new file mode 100644 index 0000000..29d841a --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.resx @@ -0,0 +1,138 @@ +<?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="AccessScopeExceedsGrantScope" xml:space="preserve"> + <value>The requested access scope exceeds the grant scope.</value> + </data> + <data name="ClientCallbackDisallowed" xml:space="preserve"> + <value>The callback URL ({0}) is not allowed for this client.</value> + </data> + <data name="ClientOrTokenSecretNotFound" xml:space="preserve"> + <value>Failure looking up secret for client or token.</value> + </data> + <data name="ClientSecretMismatch" xml:space="preserve"> + <value>The client secret was incorrect.</value> + </data> + <data name="InvalidResourceOwnerPasswordCredential" xml:space="preserve"> + <value>Invalid resource owner password credential.</value> + </data> + <data name="NoCallback" xml:space="preserve"> + <value>No callback URI was available for this request.</value> + </data> +</root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerUtilities.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerUtilities.cs new file mode 100644 index 0000000..dbb1279 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerUtilities.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------- +// <copyright file="AuthServerUtilities.cs" company="Outercurve Foundation"> +// Copyright (c) Outercurve Foundation. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2 { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Globalization; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging; + + /// <summary> + /// Utility methods for authorization servers. + /// </summary> + internal static class AuthServerUtilities { + /// <summary> + /// Gets information about the client with a given identifier. + /// </summary> + /// <param name="authorizationServer">The authorization server.</param> + /// <param name="clientIdentifier">The client identifier.</param> + /// <returns>The client information. Never null.</returns> + internal static IClientDescription GetClientOrThrow(this IAuthorizationServer authorizationServer, string clientIdentifier) { + Requires.NotNullOrEmpty(clientIdentifier, "clientIdentifier"); + Contract.Ensures(Contract.Result<IClientDescription>() != null); + + try { + var result = authorizationServer.GetClient(clientIdentifier); + ErrorUtilities.VerifyHost(result != null, OAuthStrings.ResultShouldNotBeNull, authorizationServer.GetType().FullName, "GetClient(string)"); + return result; + } catch (KeyNotFoundException ex) { + throw ErrorUtilities.Wrap(ex, AuthServerStrings.ClientOrTokenSecretNotFound); + } catch (ArgumentException ex) { + throw ErrorUtilities.Wrap(ex, AuthServerStrings.ClientOrTokenSecretNotFound); + } + } + + /// <summary> + /// Verifies a condition is true or throws an exception describing the problem. + /// </summary> + /// <param name="condition">The condition that evaluates to true to avoid an exception.</param> + /// <param name="error">A single error code from <see cref="Protocol.AccessTokenRequestErrorCodes"/>.</param> + /// <param name="unformattedDescription">A human-readable UTF-8 encoded text providing additional information, used to assist the client developer in understanding the error that occurred.</param> + /// <param name="args">The formatting arguments to generate the actual description.</param> + internal static void TokenEndpointVerify(bool condition, string error, string unformattedDescription = null, params object[] args) { + if (!condition) { + string description = unformattedDescription != null ? string.Format(CultureInfo.CurrentCulture, unformattedDescription, args) : null; + throw new TokenEndpointProtocolException(error, description); + } + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs index f555248..c3d1772 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs @@ -63,7 +63,7 @@ namespace DotNetOpenAuth.OAuth2 { if (message.ResponseType == EndUserAuthorizationResponseType.AuthorizationCode) { // Clients with no secrets can only request implicit grant types. var client = this.AuthorizationServerServices.GetClientOrThrow(message.ClientIdentifier); - ErrorUtilities.VerifyProtocol(!string.IsNullOrEmpty(client.Secret), Protocol.unauthorized_client); + ErrorUtilities.VerifyProtocol(client.HasNonEmptySecret, Protocol.EndUserAuthorizationRequestErrorCodes.UnauthorizedClient); } } @@ -110,13 +110,25 @@ namespace DotNetOpenAuth.OAuth2 { IProtocolMessage responseMessage; try { if (this.Channel.TryReadFromRequest(request, out requestMessage)) { - // TODO: refreshToken should be set appropriately based on authorization server policy. - responseMessage = this.PrepareAccessTokenResponse(requestMessage); + IAccessTokenRequestInternal accessRequestInternal = requestMessage; + accessRequestInternal.AccessTokenCreationParameters = this.AuthorizationServerServices.GetAccessTokenParameters(requestMessage); + ErrorUtilities.VerifyHost(accessRequestInternal.AccessTokenCreationParameters != null, "IAuthorizationServer.GetAccessTokenParameters must not return null."); + + var successResponseMessage = this.PrepareAccessTokenResponse(requestMessage, accessRequestInternal.AccessTokenCreationParameters.IncludeRefreshToken); + successResponseMessage.Lifetime = accessRequestInternal.AccessTokenCreationParameters.AccessTokenLifetime; + + var authCarryingRequest = requestMessage as IAuthorizationCarryingRequest; + if (authCarryingRequest != null) { + IAccessTokenIssuingResponse accessTokenIssuingResponse = successResponseMessage; + accessTokenIssuingResponse.AuthorizationDescription = new AccessToken(authCarryingRequest.AuthorizationDescription, successResponseMessage.Lifetime); + } + + responseMessage = successResponseMessage; } else { - responseMessage = new AccessTokenFailedResponse() { - Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest, - }; + responseMessage = new AccessTokenFailedResponse() { Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest, }; } + } catch (TokenEndpointProtocolException ex) { + responseMessage = new AccessTokenFailedResponse() { Error = ex.Error, ErrorDescription = ex.Description, ErrorUri = ex.MoreInformation }; } catch (ProtocolException) { responseMessage = new AccessTokenFailedResponse() { Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest, @@ -165,12 +177,28 @@ namespace DotNetOpenAuth.OAuth2 { EndUserAuthorizationSuccessResponseBase response; switch (authorizationRequest.ResponseType) { case EndUserAuthorizationResponseType.AccessToken: - var accessTokenResponse = new EndUserAuthorizationSuccessAccessTokenResponse(callback, authorizationRequest); - accessTokenResponse.Lifetime = this.AuthorizationServerServices.GetAccessTokenLifetime((EndUserAuthorizationImplicitRequest)authorizationRequest); - response = accessTokenResponse; + IAccessTokenRequestInternal accessRequestInternal = (EndUserAuthorizationImplicitRequest)authorizationRequest; + accessRequestInternal.AccessTokenCreationParameters = this.AuthorizationServerServices.GetAccessTokenParameters(accessRequestInternal); + + var implicitGrantResponse = new EndUserAuthorizationSuccessAccessTokenResponse(callback, authorizationRequest); + implicitGrantResponse.Lifetime = accessRequestInternal.AccessTokenCreationParameters.AccessTokenLifetime; + IAccessTokenCarryingRequest tokenCarryingResponse = implicitGrantResponse; + tokenCarryingResponse.AuthorizationDescription = new AccessToken( + implicitGrantResponse.Scope, + userName, + implicitGrantResponse.Lifetime); + + response = implicitGrantResponse; break; case EndUserAuthorizationResponseType.AuthorizationCode: - response = new EndUserAuthorizationSuccessAuthCodeResponse(callback, authorizationRequest); + var authCodeResponse = new EndUserAuthorizationSuccessAuthCodeResponseAS(callback, authorizationRequest); + IAuthorizationCodeCarryingRequest codeCarryingResponse = authCodeResponse; + codeCarryingResponse.AuthorizationDescription = new AuthorizationCode( + authorizationRequest.ClientIdentifier, + authorizationRequest.Callback, + authCodeResponse.Scope, + userName); + response = authCodeResponse; break; default: throw ErrorUtilities.ThrowInternal("Unexpected response type."); @@ -208,7 +236,7 @@ namespace DotNetOpenAuth.OAuth2 { // Since the request didn't include a callback URL, look up the callback from // the client's preregistration with this authorization server. Uri defaultCallback = client.DefaultCallback; - ErrorUtilities.VerifyProtocol(defaultCallback != null, OAuthStrings.NoCallback); + ErrorUtilities.VerifyProtocol(defaultCallback != null, AuthServerStrings.NoCallback); return defaultCallback; } @@ -218,7 +246,7 @@ namespace DotNetOpenAuth.OAuth2 { /// <param name="request">The request for an access token.</param> /// <param name="includeRefreshToken">If set to <c>true</c>, the response will include a long-lived refresh token.</param> /// <returns>The response message to send to the client.</returns> - private IDirectResponseProtocolMessage PrepareAccessTokenResponse(AccessTokenRequestBase request, bool includeRefreshToken = true) { + private AccessTokenSuccessResponse PrepareAccessTokenResponse(AccessTokenRequestBase request, bool includeRefreshToken = true) { Requires.NotNull(request, "request"); if (includeRefreshToken) { @@ -231,8 +259,8 @@ namespace DotNetOpenAuth.OAuth2 { } var tokenRequest = (IAuthorizationCarryingRequest)request; + var accessTokenRequest = (IAccessTokenRequestInternal)request; var response = new AccessTokenSuccessResponse(request) { - Lifetime = this.AuthorizationServerServices.GetAccessTokenLifetime(request), HasRefreshToken = includeRefreshToken, }; response.Scope.ResetContents(tokenRequest.AuthorizationDescription.Scope); diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthServerBindingElementBase.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AuthServerBindingElementBase.cs index 49f820d..49f820d 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthServerBindingElementBase.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AuthServerBindingElementBase.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthorizationCode.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AuthorizationCode.cs index 111c007..a62403b 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthorizationCode.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AuthorizationCode.cs @@ -47,6 +47,14 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { } /// <summary> + /// Gets the maximum message age from the standard expiration binding element. + /// </summary> + /// <value>This interval need not account for clock skew because it is only compared within a single authorization server or farm of servers.</value> + internal static TimeSpan MaximumMessageAge { + get { return Configuration.DotNetOpenAuthSection.Messaging.MaximumMessageLifetimeNoSkew; } + } + + /// <summary> /// Gets or sets the hash of the callback URL. /// </summary> [MessagePart("cb")] @@ -70,8 +78,8 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { signed: true, encrypted: true, compressed: false, - maximumAge: AuthorizationCodeBindingElement.MaximumMessageAge, - decodeOnceOnly: authorizationServer.VerificationCodeNonceStore); + maximumAge: MaximumMessageAge, + decodeOnceOnly: authorizationServer.NonceStore); } /// <summary> @@ -86,7 +94,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "redirecturimismatch", Justification = "Protocol requirement")] [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "DotNetOpenAuth.Messaging.ErrorUtilities.VerifyProtocol(System.Boolean,System.String,System.Object[])", Justification = "Protocol requirement")] internal void VerifyCallback(Uri callback) { - ErrorUtilities.VerifyProtocol(MessagingUtilities.AreEquivalent(this.CallbackHash, CalculateCallbackHash(callback)), Protocol.redirect_uri_mismatch); + ErrorUtilities.VerifyProtocol(MessagingUtilities.AreEquivalentConstantTime(this.CallbackHash, CalculateCallbackHash(callback)), Protocol.redirect_uri_mismatch); } /// <summary> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/IOAuth2ChannelWithAuthorizationServer.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/IOAuth2ChannelWithAuthorizationServer.cs index 5fc73ce..5fc73ce 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/IOAuth2ChannelWithAuthorizationServer.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/IOAuth2ChannelWithAuthorizationServer.cs diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs new file mode 100644 index 0000000..23dcbf5 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs @@ -0,0 +1,159 @@ +//----------------------------------------------------------------------- +// <copyright file="MessageValidationBindingElement.cs" company="Outercurve Foundation"> +// Copyright (c) Outercurve Foundation. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.ChannelElements { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuth2.Messages; + using Messaging; + + /// <summary> + /// A guard for all messages to or from an Authorization Server to ensure that they are well formed, + /// have valid secrets, callback URIs, etc. + /// </summary> + /// <remarks> + /// This binding element also ensures that the code/token coming in is issued to + /// the same client that is sending the code/token and that the authorization has + /// not been revoked and that an access token has not expired. + /// </remarks> + internal class MessageValidationBindingElement : AuthServerBindingElementBase { + /// <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 override 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 override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + var accessTokenResponse = message as AccessTokenSuccessResponse; + if (accessTokenResponse != null) { + var directResponseMessage = (IDirectResponseProtocolMessage)accessTokenResponse; + var accessTokenRequest = (AccessTokenRequestBase)directResponseMessage.OriginatingRequest; + ErrorUtilities.VerifyProtocol(accessTokenRequest.GrantType != GrantType.ClientCredentials || accessTokenResponse.RefreshToken == null, OAuthStrings.NoGrantNoRefreshToken); + } + + 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 override MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + bool applied = false; + + // Check that the client secret is correct for client authenticated messages. + var clientCredentialOnly = message as AccessTokenClientCredentialsRequest; + var authenticatedClientRequest = message as AuthenticatedClientRequestBase; + if (authenticatedClientRequest != null) { + var client = this.AuthorizationServer.GetClientOrThrow(authenticatedClientRequest.ClientIdentifier); + AuthServerUtilities.TokenEndpointVerify(client.HasNonEmptySecret, Protocol.AccessTokenRequestErrorCodes.UnauthorizedClient); // an empty secret is not allowed for client authenticated calls. + AuthServerUtilities.TokenEndpointVerify(client.IsValidClientSecret(authenticatedClientRequest.ClientSecret), Protocol.AccessTokenRequestErrorCodes.InvalidClient, AuthServerStrings.ClientSecretMismatch); + + if (clientCredentialOnly != null) { + clientCredentialOnly.CredentialsValidated = true; + } + + applied = true; + } + + // Check that any resource owner password credential is correct. + var resourceOwnerPasswordCarrier = message as AccessTokenResourceOwnerPasswordCredentialsRequest; + if (resourceOwnerPasswordCarrier != null) { + try { + if (this.AuthorizationServer.IsResourceOwnerCredentialValid(resourceOwnerPasswordCarrier.UserName, resourceOwnerPasswordCarrier.Password)) { + resourceOwnerPasswordCarrier.CredentialsValidated = true; + } else { + Logger.OAuth.ErrorFormat( + "Resource owner password credential for user \"{0}\" rejected by authorization server host.", + resourceOwnerPasswordCarrier.UserName); + throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.InvalidGrant, AuthServerStrings.InvalidResourceOwnerPasswordCredential); + } + } catch (NotSupportedException) { + throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.UnsupportedGrantType); + } catch (NotImplementedException) { + throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.UnsupportedGrantType); + } + } + + // Check that authorization requests come with an acceptable callback URI. + var authorizationRequest = message as EndUserAuthorizationRequest; + if (authorizationRequest != null) { + var client = this.AuthorizationServer.GetClientOrThrow(authorizationRequest.ClientIdentifier); + ErrorUtilities.VerifyProtocol(authorizationRequest.Callback == null || client.IsCallbackAllowed(authorizationRequest.Callback), AuthServerStrings.ClientCallbackDisallowed, authorizationRequest.Callback); + ErrorUtilities.VerifyProtocol(authorizationRequest.Callback != null || client.DefaultCallback != null, AuthServerStrings.NoCallback); + applied = true; + } + + // Check that the callback URI in a direct message from the client matches the one in the indirect message received earlier. + var request = message as AccessTokenAuthorizationCodeRequestAS; + if (request != null) { + IAuthorizationCodeCarryingRequest tokenRequest = request; + tokenRequest.AuthorizationDescription.VerifyCallback(request.Callback); + applied = true; + } + + var authCarrier = message as IAuthorizationCarryingRequest; + if (authCarrier != null) { + var accessRequest = authCarrier as AccessTokenRequestBase; + if (accessRequest != null) { + // Make sure the client sending us this token is the client we issued the token to. + AuthServerUtilities.TokenEndpointVerify(string.Equals(accessRequest.ClientIdentifier, authCarrier.AuthorizationDescription.ClientIdentifier, StringComparison.Ordinal), Protocol.AccessTokenRequestErrorCodes.InvalidClient); + + var scopedAccessRequest = accessRequest as ScopedAccessTokenRequest; + if (scopedAccessRequest != null) { + // Make sure the scope the client is requesting does not exceed the scope in the grant. + if (!scopedAccessRequest.Scope.IsSubsetOf(authCarrier.AuthorizationDescription.Scope)) { + Logger.OAuth.ErrorFormat("The requested access scope (\"{0}\") exceeds the grant scope (\"{1}\").", scopedAccessRequest.Scope, authCarrier.AuthorizationDescription.Scope); + throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.InvalidScope, AuthServerStrings.AccessScopeExceedsGrantScope); + } + } + } + + // Make sure the authorization this token represents hasn't already been revoked. + if (!this.AuthorizationServer.IsAuthorizationValid(authCarrier.AuthorizationDescription)) { + Logger.OAuth.Error("Rejecting access token request because the IAuthorizationServer.IsAuthorizationValid method returned false."); + throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.InvalidGrant); + } + + applied = true; + } + + return applied ? (MessageProtections?)MessageProtections.None : null; + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs index 6717717..00c34eb 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs @@ -11,17 +11,32 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { using System.Net.Mime; using System.Web; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth2.AuthServer.Messages; + using DotNetOpenAuth.OAuth2.Messages; /// <summary> /// The channel for the OAuth protocol. /// </summary> internal class OAuth2AuthorizationServerChannel : OAuth2ChannelBase, IOAuth2ChannelWithAuthorizationServer { /// <summary> + /// The messages receivable by this channel. + /// </summary> + private static readonly Type[] MessageTypes = new Type[] { + typeof(AccessTokenRefreshRequestAS), + typeof(AccessTokenAuthorizationCodeRequestAS), + typeof(AccessTokenResourceOwnerPasswordCredentialsRequest), + typeof(AccessTokenClientCredentialsRequest), + typeof(EndUserAuthorizationRequest), + typeof(EndUserAuthorizationImplicitRequest), + typeof(EndUserAuthorizationFailedResponse), + }; + + /// <summary> /// Initializes a new instance of the <see cref="OAuth2AuthorizationServerChannel"/> class. /// </summary> /// <param name="authorizationServer">The authorization server.</param> protected internal OAuth2AuthorizationServerChannel(IAuthorizationServer authorizationServer) - : base(InitializeBindingElements(authorizationServer)) { + : base(MessageTypes, InitializeBindingElements(authorizationServer)) { Requires.NotNull(authorizationServer, "authorizationServer"); this.AuthorizationServer = authorizationServer; } @@ -98,10 +113,9 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { Requires.NotNull(authorizationServer, "authorizationServer"); var bindingElements = new List<IChannelBindingElement>(); - bindingElements.Add(new AuthServerAllFlowsBindingElement()); - bindingElements.Add(new AuthorizationCodeBindingElement()); - bindingElements.Add(new AccessTokenBindingElement()); - bindingElements.Add(new AccessRequestBindingElement()); + // The order they are provided is used for outgoing messgaes, and reversed for incoming messages. + bindingElements.Add(new MessageValidationBindingElement()); + bindingElements.Add(new TokenCodeSerializationBindingElement()); return bindingElements.ToArray(); } diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/RefreshToken.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/RefreshToken.cs index 993583c..993583c 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/RefreshToken.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/RefreshToken.cs diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/TokenCodeSerializationBindingElement.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/TokenCodeSerializationBindingElement.cs new file mode 100644 index 0000000..41bc609 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/TokenCodeSerializationBindingElement.cs @@ -0,0 +1,122 @@ +//----------------------------------------------------------------------- +// <copyright file="TokenCodeSerializationBindingElement.cs" company="Outercurve Foundation"> +// Copyright (c) Outercurve Foundation. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.ChannelElements { + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Globalization; + using System.Linq; + using System.Security.Cryptography; + using System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.Messaging.Bindings; + using DotNetOpenAuth.OAuth2.AuthServer.ChannelElements; + using DotNetOpenAuth.OAuth2.Messages; + + /// <summary> + /// Serializes and deserializes authorization codes, refresh tokens and access tokens + /// on incoming and outgoing messages. + /// </summary> + internal class TokenCodeSerializationBindingElement : AuthServerBindingElementBase { + /// <summary> + /// Gets the protection commonly offered (if any) by this binding element. + /// </summary> + /// <value></value> + /// <remarks> + /// This value is used to assist in sorting binding elements in the channel stack. + /// </remarks> + public override 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 override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + var directResponse = message as IDirectResponseProtocolMessage; + var request = directResponse != null ? directResponse.OriginatingRequest as IAccessTokenRequestInternal : null; + + // Serialize the authorization code, if there is one. + var authCodeCarrier = message as IAuthorizationCodeCarryingRequest; + if (authCodeCarrier != null) { + var codeFormatter = AuthorizationCode.CreateFormatter(this.AuthorizationServer); + var code = authCodeCarrier.AuthorizationDescription; + authCodeCarrier.Code = codeFormatter.Serialize(code); + return MessageProtections.None; + } + + // Serialize the refresh token, if applicable. + var refreshTokenResponse = message as AccessTokenSuccessResponse; + if (refreshTokenResponse != null && refreshTokenResponse.HasRefreshToken) { + var refreshTokenCarrier = (IAuthorizationCarryingRequest)message; + var refreshToken = new RefreshToken(refreshTokenCarrier.AuthorizationDescription); + var refreshTokenFormatter = RefreshToken.CreateFormatter(this.AuthorizationServer.CryptoKeyStore); + refreshTokenResponse.RefreshToken = refreshTokenFormatter.Serialize(refreshToken); + } + + // Serialize the access token, if applicable. + var accessTokenResponse = message as IAccessTokenIssuingResponse; + if (accessTokenResponse != null && accessTokenResponse.AuthorizationDescription != null) { + ErrorUtilities.VerifyInternal(request != null, "We should always have a direct request message for this case."); + var accessTokenFormatter = AccessToken.CreateFormatter( + request.AccessTokenCreationParameters.AccessTokenSigningKey, + request.AccessTokenCreationParameters.ResourceServerEncryptionKey); + accessTokenResponse.AccessToken = accessTokenFormatter.Serialize(accessTokenResponse.AuthorizationDescription); + } + + 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> + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "unauthorizedclient", Justification = "Protocol requirement")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "incorrectclientcredentials", Justification = "Protocol requirement")] + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "authorizationexpired", Justification = "Protocol requirement")] + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "DotNetOpenAuth.Messaging.ErrorUtilities.VerifyProtocol(System.Boolean,System.String,System.Object[])", Justification = "Protocol requirement")] + public override MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + var authCodeCarrier = message as IAuthorizationCodeCarryingRequest; + if (authCodeCarrier != null) { + var authorizationCodeFormatter = AuthorizationCode.CreateFormatter(this.AuthorizationServer); + var authorizationCode = authorizationCodeFormatter.Deserialize(message, authCodeCarrier.Code, Protocol.code); + authCodeCarrier.AuthorizationDescription = authorizationCode; + } + + var refreshTokenCarrier = message as IRefreshTokenCarryingRequest; + if (refreshTokenCarrier != null) { + var refreshTokenFormatter = RefreshToken.CreateFormatter(this.AuthorizationServer.CryptoKeyStore); + var refreshToken = refreshTokenFormatter.Deserialize(message, refreshTokenCarrier.RefreshToken, Protocol.refresh_token); + refreshTokenCarrier.AuthorizationDescription = refreshToken; + } + + return null; + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ClientDescription.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ClientDescription.cs index 76c3ea6..3384183 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ClientDescription.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ClientDescription.cs @@ -9,15 +9,16 @@ namespace DotNetOpenAuth.OAuth2 { using System.Collections.Generic; using System.Linq; using System.Text; + using DotNetOpenAuth.Messaging; /// <summary> /// A default implementation of the <see cref="IClientDescription"/> interface. /// </summary> public class ClientDescription : IClientDescription { /// <summary> - /// A delegate that determines whether the callback is allowed. + /// The client's secret, if any. /// </summary> - private readonly Func<Uri, bool> isCallbackAllowed; + private readonly string secret; /// <summary> /// Initializes a new instance of the <see cref="ClientDescription"/> class. @@ -25,18 +26,13 @@ namespace DotNetOpenAuth.OAuth2 { /// <param name="secret">The secret.</param> /// <param name="defaultCallback">The default callback.</param> /// <param name="clientType">Type of the client.</param> - /// <param name="isCallbackAllowed">A delegate that determines whether the callback is allowed.</param> - public ClientDescription(string secret, Uri defaultCallback, ClientType clientType, Func<Uri, bool> isCallbackAllowed = null) { - this.Secret = secret; + public ClientDescription(string secret, Uri defaultCallback, ClientType clientType) { + this.secret = secret; this.DefaultCallback = defaultCallback; this.ClientType = clientType; - this.isCallbackAllowed = isCallbackAllowed; } - /// <summary> - /// Gets the client secret. - /// </summary> - public string Secret { get; private set; } + #region IClientDescription Members /// <summary> /// Gets the callback to use when an individual authorization request @@ -53,19 +49,42 @@ namespace DotNetOpenAuth.OAuth2 { public ClientType ClientType { get; private set; } /// <summary> + /// Gets a value indicating whether a non-empty secret is registered for this client. + /// </summary> + public virtual bool HasNonEmptySecret { + get { return !string.IsNullOrEmpty(this.secret); } + } + + /// <summary> /// Determines whether a callback URI included in a client's authorization request /// is among those allowed callbacks for the registered client. /// </summary> - /// <param name="callback">The absolute URI the client has requested the authorization result be received at.</param> + /// <param name="callback">The absolute URI the client has requested the authorization result be received at. Never null.</param> /// <returns> /// <c>true</c> if the callback URL is allowable for this client; otherwise, <c>false</c>. /// </returns> - public bool IsCallbackAllowed(Uri callback) { - if (this.isCallbackAllowed != null) { - return this.isCallbackAllowed(callback); - } - + /// <remarks> + /// This method may be overridden to allow for several callbacks to match. + /// </remarks> + public virtual bool IsCallbackAllowed(Uri callback) { return EqualityComparer<Uri>.Default.Equals(this.DefaultCallback, callback); } + + /// <summary> + /// Checks whether the specified client secret is correct. + /// </summary> + /// <param name="secret">The secret obtained from the client.</param> + /// <returns><c>true</c> if the secret matches the one in the authorization server's record for the client; <c>false</c> otherwise.</returns> + /// <remarks> + /// All string equality checks, whether checking secrets or their hashes, + /// should be done using <see cref="MessagingUtilities.EqualsConstantTime"/> to mitigate timing attacks. + /// </remarks> + public virtual bool IsValidClientSecret(string secret) { + Requires.NotNullOrEmpty(secret, "secret"); + + return MessagingUtilities.EqualsConstantTime(secret, this.secret); + } + + #endregion } } diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/AccessTokenAuthorizationCodeRequestAS.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/AccessTokenAuthorizationCodeRequestAS.cs new file mode 100644 index 0000000..ca14d0e --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/AccessTokenAuthorizationCodeRequestAS.cs @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessTokenAuthorizationCodeRequestAS.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.Messages { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuth2.ChannelElements; + + /// <summary> + /// A request from a Client to an Authorization Server to exchange an authorization code for an access token, + /// and (at the authorization server's option) a refresh token. + /// </summary> + internal class AccessTokenAuthorizationCodeRequestAS : AccessTokenAuthorizationCodeRequest, IAuthorizationCodeCarryingRequest { + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenAuthorizationCodeRequestAS"/> class. + /// </summary> + /// <param name="tokenEndpoint">The Authorization Server's access token endpoint URL.</param> + /// <param name="version">The version.</param> + internal AccessTokenAuthorizationCodeRequestAS(Uri tokenEndpoint, Version version) + : base(tokenEndpoint, version) { + } + + #region IAuthorizationCodeCarryingRequest Members + + /// <summary> + /// Gets or sets the verification code or refresh/access token. + /// </summary> + /// <value>The code or token.</value> + string IAuthorizationCodeCarryingRequest.Code { + get { return this.AuthorizationCode; } + set { this.AuthorizationCode = value; } + } + + /// <summary> + /// Gets or sets the authorization that the token describes. + /// </summary> + AuthorizationCode IAuthorizationCodeCarryingRequest.AuthorizationDescription { get; set; } + + /// <summary> + /// Gets the authorization that the code describes. + /// </summary> + IAuthorizationDescription IAuthorizationCarryingRequest.AuthorizationDescription { + get { return ((IAuthorizationCodeCarryingRequest)this).AuthorizationDescription; } + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/AccessTokenRefreshRequestAS.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/AccessTokenRefreshRequestAS.cs new file mode 100644 index 0000000..d9ca4c8 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/AccessTokenRefreshRequestAS.cs @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessTokenRefreshRequestAS.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.AuthServer.Messages { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuth2.AuthServer.ChannelElements; + using DotNetOpenAuth.OAuth2.ChannelElements; + using DotNetOpenAuth.OAuth2.Messages; + + /// <summary> + /// A request from the client to the token endpoint for a new access token + /// in exchange for a refresh token that the client has previously obtained. + /// </summary> + internal class AccessTokenRefreshRequestAS : AccessTokenRefreshRequest, IRefreshTokenCarryingRequest { + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenRefreshRequestAS"/> class. + /// </summary> + /// <param name="tokenEndpoint">The token endpoint.</param> + /// <param name="version">The version.</param> + internal AccessTokenRefreshRequestAS(Uri tokenEndpoint, Version version) + : base(tokenEndpoint, version) { + } + + #region IRefreshTokenCarryingRequest members + + /// <summary> + /// Gets or sets the verification code or refresh/access token. + /// </summary> + /// <value>The code or token.</value> + string IRefreshTokenCarryingRequest.RefreshToken { + get { return this.RefreshToken; } + set { this.RefreshToken = value; } + } + + /// <summary> + /// Gets or sets the authorization that the token describes. + /// </summary> + RefreshToken IRefreshTokenCarryingRequest.AuthorizationDescription { get; set; } + + /// <summary> + /// Gets the authorization that the token describes. + /// </summary> + IAuthorizationDescription IAuthorizationCarryingRequest.AuthorizationDescription { + get { return ((IRefreshTokenCarryingRequest)this).AuthorizationDescription; } + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponseAS.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponseAS.cs new file mode 100644 index 0000000..25f5dc8 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponseAS.cs @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------- +// <copyright file="EndUserAuthorizationSuccessAuthCodeResponseAS.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.Messages { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuth2.ChannelElements; + + /// <summary> + /// The message sent by the Authorization Server to the Client via the user agent + /// to indicate that user authorization was granted, carrying an authorization code and possibly an access token, + /// and to return the user to the Client where they started their experience. + /// </summary> + internal class EndUserAuthorizationSuccessAuthCodeResponseAS : EndUserAuthorizationSuccessAuthCodeResponse, IAuthorizationCodeCarryingRequest { + /// <summary> + /// Initializes a new instance of the <see cref="EndUserAuthorizationSuccessAuthCodeResponseAS"/> class. + /// </summary> + /// <param name="clientCallback">The URL to redirect to so the client receives the message. This may not be built into the request message if the client pre-registered the URL with the authorization server.</param> + /// <param name="version">The protocol version.</param> + internal EndUserAuthorizationSuccessAuthCodeResponseAS(Uri clientCallback, Version version) + : base(clientCallback, version) { + Requires.NotNull(version, "version"); + Requires.NotNull(clientCallback, "clientCallback"); + } + + /// <summary> + /// Initializes a new instance of the <see cref="EndUserAuthorizationSuccessAuthCodeResponseAS"/> class. + /// </summary> + /// <param name="clientCallback">The URL to redirect to so the client receives the message. This may not be built into the request message if the client pre-registered the URL with the authorization server.</param> + /// <param name="request">The authorization request from the user agent on behalf of the client.</param> + internal EndUserAuthorizationSuccessAuthCodeResponseAS(Uri clientCallback, EndUserAuthorizationRequest request) + : base(clientCallback, request) { + Requires.NotNull(clientCallback, "clientCallback"); + Requires.NotNull(request, "request"); + ((IMessageWithClientState)this).ClientState = request.ClientState; + } + + #region IAuthorizationCodeCarryingRequest Members + + /// <summary> + /// Gets or sets the authorization code. + /// </summary> + string IAuthorizationCodeCarryingRequest.Code { + get { return this.AuthorizationCode; } + set { this.AuthorizationCode = value; } + } + + /// <summary> + /// Gets or sets the authorization that the token describes. + /// </summary> + AuthorizationCode IAuthorizationCodeCarryingRequest.AuthorizationDescription { get; set; } + + /// <summary> + /// Gets the authorization that the code describes. + /// </summary> + IAuthorizationDescription IAuthorizationCarryingRequest.AuthorizationDescription { + get { return ((IAuthorizationCodeCarryingRequest)this).AuthorizationDescription; } + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/IAuthorizationCodeCarryingRequest.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/IAuthorizationCodeCarryingRequest.cs index 045cb80..045cb80 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/IAuthorizationCodeCarryingRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/IAuthorizationCodeCarryingRequest.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/IRefreshTokenCarryingRequest.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/IRefreshTokenCarryingRequest.cs index ce27538..9e6fc3c 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/IRefreshTokenCarryingRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/Messages/IRefreshTokenCarryingRequest.cs @@ -4,7 +4,9 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.OAuth2.ChannelElements { +namespace DotNetOpenAuth.OAuth2.AuthServer.ChannelElements { + using DotNetOpenAuth.OAuth2.ChannelElements; + /// <summary> /// A message that carries a refresh token between client and authorization server. /// </summary> diff --git a/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj b/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj index 3625e54..662bf86 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj +++ b/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj @@ -18,6 +18,15 @@ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> </PropertyGroup> <ItemGroup> + <Compile Include="OAuth2\AuthorizationServerDescription.cs" /> + <Compile Include="OAuth2\AuthorizationState.cs" /> + <Compile Include="OAuth2\ChannelElements\OAuth2ClientChannel.cs" /> + <Compile Include="OAuth2\IAuthorizationState.cs" /> + <Compile Include="OAuth2\IClientAuthorizationTracker.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenAuthorizationCodeRequestC.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenRefreshRequestC.cs" /> + <Compile Include="OAuth2\Messages\EndUserAuthorizationImplicitRequestC.cs" /> + <Compile Include="OAuth2\Messages\EndUserAuthorizationRequestC.cs" /> <Compile Include="OAuth2\ClientStrings.Designer.cs"> <AutoGen>True</AutoGen> <DesignTime>True</DesignTime> @@ -33,6 +42,10 @@ <Project>{60426312-6AE5-4835-8667-37EDEA670222}</Project> <Name>DotNetOpenAuth.Core</Name> </ProjectReference> + <ProjectReference Include="..\DotNetOpenAuth.OAuth2.ClientAuthorization\DotNetOpenAuth.OAuth2.ClientAuthorization.csproj"> + <Project>{CCF3728A-B3D7-404A-9BC6-75197135F2D7}</Project> + <Name>DotNetOpenAuth.OAuth2.ClientAuthorization</Name> + </ProjectReference> <ProjectReference Include="..\DotNetOpenAuth.OAuth2\DotNetOpenAuth.OAuth2.csproj"> <Project>{56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}</Project> <Name>DotNetOpenAuth.OAuth2</Name> @@ -47,4 +60,4 @@ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" /> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " /> -</Project>
\ No newline at end of file +</Project> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/AuthorizationServerDescription.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationServerDescription.cs index 38a9ff9..38a9ff9 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/AuthorizationServerDescription.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationServerDescription.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/AuthorizationState.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationState.cs index 4117b3c..4117b3c 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/AuthorizationState.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/AuthorizationState.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/OAuth2ClientChannel.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs index c9981d3..54fc110 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/OAuth2ClientChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs @@ -13,15 +13,28 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { using System.Web; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth2.Messages; /// <summary> /// The messaging channel used by OAuth 2.0 Clients. /// </summary> internal class OAuth2ClientChannel : OAuth2ChannelBase { /// <summary> + /// The messages receivable by this channel. + /// </summary> + private static readonly Type[] MessageTypes = new Type[] { + typeof(AccessTokenSuccessResponse), + typeof(AccessTokenFailedResponse), + typeof(EndUserAuthorizationSuccessAuthCodeResponse), + typeof(EndUserAuthorizationSuccessAccessTokenResponse), + typeof(EndUserAuthorizationFailedResponse), + typeof(UnauthorizedResponse), + }; + + /// <summary> /// Initializes a new instance of the <see cref="OAuth2ClientChannel"/> class. /// </summary> - internal OAuth2ClientChannel() { + internal OAuth2ClientChannel() : base(MessageTypes) { } /// <summary> @@ -65,7 +78,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { } else if (response.ContentType.MediaType == HttpFormUrlEncoded || response.ContentType.MediaType == PlainTextEncoded) { return HttpUtility.ParseQueryString(body).ToDictionary(); } else { - throw ErrorUtilities.ThrowProtocol(OAuthStrings.UnexpectedResponseContentType, response.ContentType.MediaType); + throw ErrorUtilities.ThrowProtocol(ClientStrings.UnexpectedResponseContentType, response.ContentType.MediaType); } } diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs index eda6bc1..77b9cc6 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs @@ -116,7 +116,7 @@ namespace DotNetOpenAuth.OAuth2 { } } - var request = new AccessTokenRefreshRequest(this.AuthorizationServer) { + var request = new AccessTokenRefreshRequestC(this.AuthorizationServer) { ClientIdentifier = this.ClientIdentifier, ClientSecret = this.ClientSecret, RefreshToken = authorization.RefreshToken, @@ -143,7 +143,7 @@ namespace DotNetOpenAuth.OAuth2 { Requires.NotNull(scope, "scope"); Contract.Ensures(Contract.Result<IAuthorizationState>() != null); - var request = new AccessTokenRefreshRequest(this.AuthorizationServer) { + var request = new AccessTokenRefreshRequestC(this.AuthorizationServer) { ClientIdentifier = this.ClientIdentifier, ClientSecret = this.ClientSecret, RefreshToken = refreshToken, @@ -248,7 +248,7 @@ namespace DotNetOpenAuth.OAuth2 { Requires.NotNull(authorizationState, "authorizationState"); Requires.NotNull(authorizationSuccess, "authorizationSuccess"); - var accessTokenRequest = new AccessTokenAuthorizationCodeRequest(this.AuthorizationServer) { + var accessTokenRequest = new AccessTokenAuthorizationCodeRequestC(this.AuthorizationServer) { ClientIdentifier = this.ClientIdentifier, ClientSecret = this.ClientSecret, Callback = authorizationState.Callback, @@ -262,7 +262,7 @@ namespace DotNetOpenAuth.OAuth2 { } else { authorizationState.Delete(); string error = failedAccessTokenResponse != null ? failedAccessTokenResponse.Error : "(unknown)"; - ErrorUtilities.ThrowProtocol(OAuthStrings.CannotObtainAccessTokenWithReason, error); + ErrorUtilities.ThrowProtocol(ClientStrings.CannotObtainAccessTokenWithReason, error); } } diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.Designer.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.Designer.cs index 9564704..7a5defd 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.Designer.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.Designer.cs @@ -88,6 +88,15 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> + /// Looks up a localized string similar to Failed to obtain access token. Authorization Server reports reason: {0}. + /// </summary> + internal static string CannotObtainAccessTokenWithReason { + get { + return ResourceManager.GetString("CannotObtainAccessTokenWithReason", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to The property {0} must be set before this operation is allowed.. /// </summary> internal static string RequiredPropertyNotYetPreset { @@ -95,5 +104,14 @@ namespace DotNetOpenAuth.OAuth2 { return ResourceManager.GetString("RequiredPropertyNotYetPreset", resourceCulture); } } + + /// <summary> + /// Looks up a localized string similar to Unexpected response Content-Type {0}. + /// </summary> + internal static string UnexpectedResponseContentType { + get { + return ResourceManager.GetString("UnexpectedResponseContentType", resourceCulture); + } + } } } diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.resx b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.resx index 0a41e42..a1ed7cd 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.resx +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientStrings.resx @@ -127,7 +127,13 @@ <value>Unexpected OAuth authorization response received with callback and client state that does not match an expected value.</value> <comment>The error message generated when detecting a mismatch between the state sent to the authorization server originally and what we got back with successful authorization, or that the user sessions were not identical between the two requests, suggesting XSRF or other attack on the user (victim).</comment> </data> + <data name="CannotObtainAccessTokenWithReason" xml:space="preserve"> + <value>Failed to obtain access token. Authorization Server reports reason: {0}</value> + </data> <data name="RequiredPropertyNotYetPreset" xml:space="preserve"> <value>The property {0} must be set before this operation is allowed.</value> </data> + <data name="UnexpectedResponseContentType" xml:space="preserve"> + <value>Unexpected response Content-Type {0}</value> + </data> </root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/IAuthorizationState.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/IAuthorizationState.cs index f38df9a..f38df9a 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/IAuthorizationState.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/IAuthorizationState.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/IClientAuthorizationTracker.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/IClientAuthorizationTracker.cs index 73b7a44..73b7a44 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/IClientAuthorizationTracker.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/IClientAuthorizationTracker.cs diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenAuthorizationCodeRequestC.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenAuthorizationCodeRequestC.cs new file mode 100644 index 0000000..ebfb2e8 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenAuthorizationCodeRequestC.cs @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessTokenAuthorizationCodeRequestC.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.Messages { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + /// <summary> + /// A request from a Client to an Authorization Server to exchange an authorization code for an access token, + /// and (at the authorization server's option) a refresh token. + /// </summary> + internal class AccessTokenAuthorizationCodeRequestC : AccessTokenAuthorizationCodeRequest { + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenAuthorizationCodeRequestC"/> class. + /// </summary> + /// <param name="authorizationServer">The authorization server.</param> + internal AccessTokenAuthorizationCodeRequestC(AuthorizationServerDescription authorizationServer) + : base(authorizationServer.TokenEndpoint, authorizationServer.Version) { + Requires.NotNull(authorizationServer, "authorizationServer"); + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenRefreshRequestC.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenRefreshRequestC.cs new file mode 100644 index 0000000..25da3dc --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/AccessTokenRefreshRequestC.cs @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessTokenRefreshRequestC.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.Messages { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + /// <summary> + /// A request from the client to the token endpoint for a new access token + /// in exchange for a refresh token that the client has previously obtained. + /// </summary> + internal class AccessTokenRefreshRequestC : AccessTokenRefreshRequest { + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenRefreshRequestC"/> class. + /// </summary> + /// <param name="authorizationServer">The authorization server.</param> + internal AccessTokenRefreshRequestC(AuthorizationServerDescription authorizationServer) + : base(authorizationServer.TokenEndpoint, authorizationServer.Version) { + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationImplicitRequestC.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationImplicitRequestC.cs new file mode 100644 index 0000000..78bf48e --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationImplicitRequestC.cs @@ -0,0 +1,28 @@ +//----------------------------------------------------------------------- +// <copyright file="EndUserAuthorizationImplicitRequestC.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.Messages { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + /// <summary> + /// A message sent by a web application Client to the AuthorizationServer + /// via the user agent to obtain authorization from the user and prepare + /// to issue an access token to the client if permission is granted. + /// </summary> + [Serializable] + internal class EndUserAuthorizationImplicitRequestC : EndUserAuthorizationImplicitRequest { + /// <summary> + /// Initializes a new instance of the <see cref="EndUserAuthorizationImplicitRequestC"/> class. + /// </summary> + /// <param name="authorizationServer">The authorization server.</param> + internal EndUserAuthorizationImplicitRequestC(AuthorizationServerDescription authorizationServer) + : base(authorizationServer.AuthorizationEndpoint, authorizationServer.Version) { + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationRequestC.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationRequestC.cs new file mode 100644 index 0000000..7c06897 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/Messages/EndUserAuthorizationRequestC.cs @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------- +// <copyright file="EndUserAuthorizationRequestC.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.Messages { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + /// <summary> + /// A message sent by a web application Client to the AuthorizationServer + /// via the user agent to obtain authorization from the user and prepare + /// to issue an access token to the client if permission is granted. + /// </summary> + [Serializable] + internal class EndUserAuthorizationRequestC : EndUserAuthorizationRequest { + /// <summary> + /// Initializes a new instance of the <see cref="EndUserAuthorizationRequestC"/> class. + /// </summary> + /// <param name="authorizationServer">The authorization server.</param> + internal EndUserAuthorizationRequestC(AuthorizationServerDescription authorizationServer) + : base(authorizationServer.AuthorizationEndpoint, authorizationServer.Version) { + Requires.NotNull(authorizationServer, "authorizationServer"); + Requires.True(authorizationServer.Version != null, "authorizationServer"); + Requires.True(authorizationServer.AuthorizationEndpoint != null, "authorizationServer"); + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs index c29d167..9834985 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs @@ -151,7 +151,7 @@ namespace DotNetOpenAuth.OAuth2 { authorization.Callback = new Uri("http://localhost/"); } - var request = implicitResponseType ? new EndUserAuthorizationImplicitRequest(this.AuthorizationServer) : new EndUserAuthorizationRequest(this.AuthorizationServer); + var request = implicitResponseType ? (EndUserAuthorizationRequest)new EndUserAuthorizationImplicitRequestC(this.AuthorizationServer) : new EndUserAuthorizationRequestC(this.AuthorizationServer); request.ClientIdentifier = this.ClientIdentifier; request.Callback = authorization.Callback; request.ClientState = state; diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs index 0429dcb..5a86bef 100644 --- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs +++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs @@ -78,7 +78,7 @@ namespace DotNetOpenAuth.OAuth2 { authorization.SaveChanges(); } - var request = new EndUserAuthorizationRequest(this.AuthorizationServer) { + var request = new EndUserAuthorizationRequestC(this.AuthorizationServer) { ClientIdentifier = this.ClientIdentifier, Callback = authorization.Callback, }; diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj new file mode 100644 index 0000000..53ab1d0 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.props))\EnlistmentInfo.props" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.props))' != '' " /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + </PropertyGroup> + <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.props" /> + <PropertyGroup> + <SchemaVersion>2.0</SchemaVersion> + <ProjectGuid>{CCF3728A-B3D7-404A-9BC6-75197135F2D7}</ProjectGuid> + <AppDesignerFolder>Properties</AppDesignerFolder> + <AssemblyName>DotNetOpenAuth.OAuth2.ClientAuthorization</AssemblyName> + </PropertyGroup> + <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.Product.props" /> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + </PropertyGroup> + <ItemGroup> + <Compile Include="OAuth2\AccessTokenParameters.cs" /> + <Compile Include="OAuth2\ChannelElements\EndUserAuthorizationResponseTypeEncoder.cs" /> + <Compile Include="OAuth2\ChannelElements\GrantTypeEncoder.cs" /> + <Compile Include="OAuth2\ChannelElements\OAuth2ChannelBase.cs" /> + <Compile Include="OAuth2\ClientAuthorizationStrings.Designer.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>ClientAuthorizationStrings.resx</DependentUpon> + </Compile> + <Compile Include="OAuth2\ClientType.cs" /> + <Compile Include="OAuth2\IAuthorizationServer.cs" /> + <Compile Include="OAuth2\IClientDescription.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenAuthorizationCodeRequest.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenClientCredentialsRequest.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenFailedResponse.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenRefreshRequest.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenRequestBase.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenResourceOwnerPasswordCredentialsRequest.cs" /> + <Compile Include="OAuth2\Messages\AccessTokenSuccessResponse.cs" /> + <Compile Include="OAuth2\Messages\AuthenticatedClientRequestBase.cs" /> + <Compile Include="OAuth2\Messages\EndUserAuthorizationFailedResponse.cs" /> + <Compile Include="OAuth2\Messages\EndUserAuthorizationImplicitRequest.cs" /> + <Compile Include="OAuth2\Messages\EndUserAuthorizationRequest.cs" /> + <Compile Include="OAuth2\Messages\EndUserAuthorizationResponseType.cs" /> + <Compile Include="OAuth2\Messages\EndUserAuthorizationSuccessAccessTokenResponse.cs" /> + <Compile Include="OAuth2\Messages\EndUserAuthorizationSuccessAuthCodeResponse.cs" /> + <Compile Include="OAuth2\Messages\EndUserAuthorizationSuccessResponseBase.cs" /> + <Compile Include="OAuth2\Messages\GrantType.cs" /> + <Compile Include="OAuth2\Messages\IAccessTokenIssuingResponse.cs" /> + <Compile Include="OAuth2\Messages\IAccessTokenRequest.cs" /> + <Compile Include="OAuth2\Messages\IAccessTokenRequestInternal.cs" /> + <Compile Include="OAuth2\Messages\IMessageWithClientState.cs" /> + <Compile Include="OAuth2\Messages\ScopedAccessTokenRequest.cs" /> + <Compile Include="OAuth2\TokenEndpointProtocolException.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\DotNetOpenAuth.Core\DotNetOpenAuth.Core.csproj"> + <Project>{60426312-6AE5-4835-8667-37EDEA670222}</Project> + <Name>DotNetOpenAuth.Core</Name> + </ProjectReference> + <ProjectReference Include="..\DotNetOpenAuth.OAuth2\DotNetOpenAuth.OAuth2.csproj"> + <Project>{56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}</Project> + <Name>DotNetOpenAuth.OAuth2</Name> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="OAuth2\ClientAuthorizationStrings.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>ClientAuthorizationStrings.Designer.cs</LastGenOutput> + </EmbeddedResource> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" /> + <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " /> +</Project>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/AccessTokenParameters.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/AccessTokenParameters.cs new file mode 100644 index 0000000..a6d6169 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/AccessTokenParameters.cs @@ -0,0 +1,96 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessTokenParameters.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2 { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Security.Cryptography; + using System.Text; + + /// <summary> + /// Describes the parameters to be fed into creating a response to an access token request. + /// </summary> + public class AccessTokenParameters : IDisposable { + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenParameters"/> class. + /// </summary> + public AccessTokenParameters() { + this.IncludeRefreshToken = true; + this.AccessTokenLifetime = TimeSpan.FromHours(1); + } + + /// <summary> + /// Gets or sets the access token lifetime. + /// </summary> + /// <value> + /// A positive timespan. + /// </value> + /// <remarks> + /// Note that within this lifetime, authorization <i>may</i> not be revokable. + /// Short lifetimes are recommended (e.g. one hour), particularly when the client is not authenticated or + /// the resources to which access is being granted are sensitive. + /// </remarks> + public TimeSpan AccessTokenLifetime { get; set; } + + /// <summary> + /// Gets or sets the crypto service provider with the asymmetric private key to use for signing access tokens. + /// </summary> + /// <returns>A crypto service provider instance that contains the private key.</returns> + /// <value>Must not be null, and must contain the private key.</value> + /// <remarks> + /// The public key in the private/public key pair will be used by the resource + /// servers to validate that the access token is minted by a trusted authorization server. + /// </remarks> + public RSACryptoServiceProvider AccessTokenSigningKey { get; set; } + + /// <summary> + /// Gets or sets the key to encrypt the access token. + /// </summary> + public RSACryptoServiceProvider ResourceServerEncryptionKey { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether to provide the client with a refresh token, when applicable. + /// </summary> + /// <value>The default value is <c>true</c>.</value> + /// <remarks>> + /// The refresh token will never be provided when this value is false. + /// The refresh token <em>may</em> be provided when this value is true. + /// </remarks> + public bool IncludeRefreshToken { get; set; } + + #region Implementation of IDisposable + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + /// <filterpriority>2</filterpriority> + public void Dispose() { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// <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) { + if (disposing) { + if (this.ResourceServerEncryptionKey != null) { + IDisposable value = this.ResourceServerEncryptionKey; + value.Dispose(); + } + + if (this.AccessTokenSigningKey != null) { + IDisposable value = this.AccessTokenSigningKey; + value.Dispose(); + } + } + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/EndUserAuthorizationResponseTypeEncoder.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ChannelElements/EndUserAuthorizationResponseTypeEncoder.cs index 2fba721..2fba721 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/EndUserAuthorizationResponseTypeEncoder.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ChannelElements/EndUserAuthorizationResponseTypeEncoder.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/GrantTypeEncoder.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ChannelElements/GrantTypeEncoder.cs index e0e8329..e0e8329 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/GrantTypeEncoder.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ChannelElements/GrantTypeEncoder.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/OAuth2ChannelBase.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ChannelElements/OAuth2ChannelBase.cs index 51ac58a..f2f674e 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/OAuth2ChannelBase.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ChannelElements/OAuth2ChannelBase.cs @@ -18,24 +18,6 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// </summary> internal abstract class OAuth2ChannelBase : StandardMessageFactoryChannel { /// <summary> - /// The messages receivable by this channel. - /// </summary> - private static readonly Type[] MessageTypes = new Type[] { - typeof(AccessTokenRefreshRequest), - typeof(AccessTokenAuthorizationCodeRequest), - typeof(AccessTokenResourceOwnerPasswordCredentialsRequest), - typeof(AccessTokenClientCredentialsRequest), - typeof(AccessTokenSuccessResponse), - typeof(AccessTokenFailedResponse), - typeof(EndUserAuthorizationRequest), - typeof(EndUserAuthorizationImplicitRequest), - typeof(EndUserAuthorizationSuccessAuthCodeResponse), - typeof(EndUserAuthorizationSuccessAccessTokenResponse), - typeof(EndUserAuthorizationFailedResponse), - typeof(UnauthorizedResponse), - }; - - /// <summary> /// The protocol versions supported by this channel. /// </summary> private static readonly Version[] Versions = Protocol.AllVersions.Select(v => v.Version).ToArray(); @@ -43,9 +25,13 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// <summary> /// Initializes a new instance of the <see cref="OAuth2ChannelBase"/> class. /// </summary> - /// <param name="channelBindingElements">The channel binding elements.</param> - internal OAuth2ChannelBase(params IChannelBindingElement[] channelBindingElements) - : base(MessageTypes, Versions, channelBindingElements) { + /// <param name="messageTypes">The message types that are received by this channel.</param> + /// <param name="channelBindingElements"> + /// The binding elements to use in sending and receiving messages. + /// The order they are provided is used for outgoing messgaes, and reversed for incoming messages. + /// </param> + internal OAuth2ChannelBase(Type[] messageTypes, params IChannelBindingElement[] channelBindingElements) + : base(Requires.NotNull(messageTypes, "messageTypes"), Versions, channelBindingElements) { } /// <summary> diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ClientAuthorizationStrings.Designer.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ClientAuthorizationStrings.Designer.cs new file mode 100644 index 0000000..e7e1b6b --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ClientAuthorizationStrings.Designer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.17614 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// </auto-generated> +//------------------------------------------------------------------------------ + +namespace DotNetOpenAuth.OAuth2 { + 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", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ClientAuthorizationStrings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ClientAuthorizationStrings() { + } + + /// <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.OAuth2.ClientAuthorizationStrings", typeof(ClientAuthorizationStrings).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 The request message type {0} should not be responded to with a refresh token.. + /// </summary> + internal static string RefreshTokenInappropriateForRequestType { + get { + return ResourceManager.GetString("RefreshTokenInappropriateForRequestType", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to The Authorization Server's token endpoint generated error {0}: '{1}'. + /// </summary> + internal static string TokenEndpointErrorFormat { + get { + return ResourceManager.GetString("TokenEndpointErrorFormat", resourceCulture); + } + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ClientAuthorizationStrings.resx b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ClientAuthorizationStrings.resx new file mode 100644 index 0000000..da2dd73 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ClientAuthorizationStrings.resx @@ -0,0 +1,126 @@ +<?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="RefreshTokenInappropriateForRequestType" xml:space="preserve"> + <value>The request message type {0} should not be responded to with a refresh token.</value> + </data> + <data name="TokenEndpointErrorFormat" xml:space="preserve"> + <value>The Authorization Server's token endpoint generated error {0}: '{1}'</value> + </data> +</root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ClientType.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ClientType.cs index 9e8ed2a..9e8ed2a 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ClientType.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ClientType.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/IAuthorizationServer.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/IAuthorizationServer.cs index a0a2ad9..605d007 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/IAuthorizationServer.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/IAuthorizationServer.cs @@ -35,52 +35,17 @@ namespace DotNetOpenAuth.OAuth2 { /// Gets the authorization code nonce store to use to ensure that authorization codes can only be used once. /// </summary> /// <value>The authorization code nonce store.</value> - INonceStore VerificationCodeNonceStore { get; } + INonceStore NonceStore { get; } /// <summary> - /// Gets the crypto service provider with the asymmetric private key to use for signing access tokens. + /// Obtains parameters to go into the formulation of an access token. /// </summary> - /// <returns>A crypto service provider instance that contains the private key.</returns> - /// <value>Must not be null, and must contain the private key.</value> - /// <remarks> - /// The public key in the private/public key pair will be used by the resource - /// servers to validate that the access token is minted by a trusted authorization server. - /// </remarks> - RSACryptoServiceProvider AccessTokenSigningKey { get; } - - /// <summary> - /// Obtains the lifetime for a new access token. - /// </summary> - /// <param name="accessTokenRequestMessage"> - /// Details regarding the resources that the access token will grant access to, and the identity of the client - /// that will receive that access. - /// Based on this information the receiving resource server can be determined and the lifetime of the access - /// token can be set based on the sensitivity of the resources. - /// </param> - /// <returns> - /// Receives the lifetime for this access token. Note that within this lifetime, authorization <i>may</i> not be revokable. - /// Short lifetimes are recommended (i.e. one hour), particularly when the client is not authenticated or - /// the resources to which access is being granted are sensitive. - /// </returns> - TimeSpan GetAccessTokenLifetime(IAccessTokenRequest accessTokenRequestMessage); - - /// <summary> - /// Obtains the encryption key for an access token being created. - /// </summary> - /// <param name="accessTokenRequestMessage"> - /// Details regarding the resources that the access token will grant access to, and the identity of the client + /// <param name="accessTokenRequestMessage">Details regarding the resources that the access token will grant access to, and the identity of the client /// that will receive that access. /// Based on this information the receiving resource server can be determined and the lifetime of the access - /// token can be set based on the sensitivity of the resources. - /// </param> - /// <returns> - /// The crypto service provider with the asymmetric public key to use for encrypting access tokens for a specific resource server. - /// The caller is responsible to dispose of this value. - /// </returns> - /// <remarks> - /// The caller is responsible to dispose of the returned value. - /// </remarks> - RSACryptoServiceProvider GetResourceServerEncryptionKey(IAccessTokenRequest accessTokenRequestMessage); + /// token can be set based on the sensitivity of the resources.</param> + /// <returns>A non-null parameters instance that DotNetOpenAuth will dispose after it has been used.</returns> + AccessTokenParameters GetAccessTokenParameters(IAccessTokenRequest accessTokenRequestMessage); /// <summary> /// Gets the client with a given identifier. @@ -151,7 +116,7 @@ namespace DotNetOpenAuth.OAuth2 { /// Gets the authorization code nonce store to use to ensure that authorization codes can only be used once. /// </summary> /// <value>The authorization code nonce store.</value> - INonceStore IAuthorizationServer.VerificationCodeNonceStore { + INonceStore IAuthorizationServer.NonceStore { get { Contract.Ensures(Contract.Result<INonceStore>() != null); throw new NotImplementedException(); @@ -159,55 +124,6 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> - /// Gets the crypto service provider with the asymmetric private key to use for signing access tokens. - /// </summary> - /// <value> - /// Must not be null, and must contain the private key. - /// </value> - /// <returns>A crypto service provider instance that contains the private key.</returns> - RSACryptoServiceProvider IAuthorizationServer.AccessTokenSigningKey { - get { - Contract.Ensures(Contract.Result<RSACryptoServiceProvider>() != null); - Contract.Ensures(!Contract.Result<RSACryptoServiceProvider>().PublicOnly); - throw new NotImplementedException(); - } - } - - /// <summary> - /// Obtains the lifetime for a new access token. - /// </summary> - /// <param name="accessTokenRequestMessage">Details regarding the resources that the access token will grant access to, and the identity of the client - /// that will receive that access. - /// Based on this information the receiving resource server can be determined and the lifetime of the access - /// token can be set based on the sensitivity of the resources.</param> - /// <returns> - /// Receives the lifetime for this access token. Note that within this lifetime, authorization <i>may</i> not be revokable. - /// Short lifetimes are recommended (i.e. one hour), particularly when the client is not authenticated or - /// the resources to which access is being granted are sensitive. - /// </returns> - TimeSpan IAuthorizationServer.GetAccessTokenLifetime(IAccessTokenRequest accessTokenRequestMessage) { - Requires.NotNull(accessTokenRequestMessage, "accessTokenRequestMessage"); - throw new NotImplementedException(); - } - - /// <summary> - /// Obtains the encryption key for an access token being created. - /// </summary> - /// <param name="accessTokenRequestMessage">Details regarding the resources that the access token will grant access to, and the identity of the client - /// that will receive that access. - /// Based on this information the receiving resource server can be determined and the lifetime of the access - /// token can be set based on the sensitivity of the resources.</param> - /// <returns> - /// The crypto service provider with the asymmetric public key to use for encrypting access tokens for a specific resource server. - /// The caller is responsible to dispose of this value. - /// </returns> - RSACryptoServiceProvider IAuthorizationServer.GetResourceServerEncryptionKey(IAccessTokenRequest accessTokenRequestMessage) { - Requires.NotNull(accessTokenRequestMessage, "accessTokenRequestMessage"); - Contract.Ensures(Contract.Result<RSACryptoServiceProvider>() != null); - throw new NotImplementedException(); - } - - /// <summary> /// Gets the client with a given identifier. /// </summary> /// <param name="clientIdentifier">The client identifier.</param> @@ -260,5 +176,21 @@ namespace DotNetOpenAuth.OAuth2 { Contract.Requires(password != null); throw new NotImplementedException(); } + + /// <summary> + /// Obtains parameters to go into the formulation of an access token. + /// </summary> + /// <param name="accessTokenRequestMessage">Details regarding the resources that the access token will grant access to, and the identity of the client + /// that will receive that access. + /// Based on this information the receiving resource server can be determined and the lifetime of the access + /// token can be set based on the sensitivity of the resources.</param> + /// <returns> + /// A non-null parameters instance that DotNetOpenAuth will dispose after it has been used. + /// </returns> + AccessTokenParameters IAuthorizationServer.GetAccessTokenParameters(IAccessTokenRequest accessTokenRequestMessage) { + Contract.Requires(accessTokenRequestMessage != null); + Contract.Ensures(Contract.Result<AccessTokenParameters>() != null); + throw new NotImplementedException(); + } } } diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/IClientDescription.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/IClientDescription.cs index d30151b..b4bc689 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/IClientDescription.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/IClientDescription.cs @@ -8,6 +8,7 @@ namespace DotNetOpenAuth.OAuth2 { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; + using DotNetOpenAuth.Messaging; /// <summary> /// A description of a client from an Authorization Server's point of view. @@ -15,11 +16,6 @@ namespace DotNetOpenAuth.OAuth2 { [ContractClass(typeof(IClientDescriptionContract))] public interface IClientDescription { /// <summary> - /// Gets the client secret. - /// </summary> - string Secret { get; } - - /// <summary> /// Gets the callback to use when an individual authorization request /// does not include an explicit callback URI. /// </summary> @@ -32,10 +28,15 @@ namespace DotNetOpenAuth.OAuth2 { ClientType ClientType { get; } /// <summary> + /// Gets a value indicating whether a non-empty secret is registered for this client. + /// </summary> + bool HasNonEmptySecret { get; } + + /// <summary> /// Determines whether a callback URI included in a client's authorization request /// is among those allowed callbacks for the registered client. /// </summary> - /// <param name="callback">The absolute URI the client has requested the authorization result be received at.</param> + /// <param name="callback">The absolute URI the client has requested the authorization result be received at. Never null.</param> /// <returns> /// <c>true</c> if the callback URL is allowable for this client; otherwise, <c>false</c>. /// </returns> @@ -56,6 +57,17 @@ namespace DotNetOpenAuth.OAuth2 { /// </para> /// </remarks> bool IsCallbackAllowed(Uri callback); + + /// <summary> + /// Checks whether the specified client secret is correct. + /// </summary> + /// <param name="secret">The secret obtained from the client.</param> + /// <returns><c>true</c> if the secret matches the one in the authorization server's record for the client; <c>false</c> otherwise.</returns> + /// <remarks> + /// All string equality checks, whether checking secrets or their hashes, + /// should be done using <see cref="MessagingUtilities.EqualsConstantTime"/> to mitigate timing attacks. + /// </remarks> + bool IsValidClientSecret(string secret); } /// <summary> @@ -66,14 +78,6 @@ namespace DotNetOpenAuth.OAuth2 { #region IClientDescription Members /// <summary> - /// Gets the client secret. - /// </summary> - /// <value></value> - string IClientDescription.Secret { - get { throw new NotImplementedException(); } - } - - /// <summary> /// Gets the type of the client. /// </summary> ClientType IClientDescription.ClientType { @@ -95,6 +99,13 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> + /// Gets a value indicating whether a non-empty secret is registered for this client. + /// </summary> + bool IClientDescription.HasNonEmptySecret { + get { throw new NotImplementedException(); } + } + + /// <summary> /// Determines whether a callback URI included in a client's authorization request /// is among those allowed callbacks for the registered client. /// </summary> @@ -108,6 +119,20 @@ namespace DotNetOpenAuth.OAuth2 { throw new NotImplementedException(); } + /// <summary> + /// Checks whether the specified client secret is correct. + /// </summary> + /// <param name="secret">The secret obtained from the client.</param> + /// <returns><c>true</c> if the secret matches the one in the authorization server's record for the client; <c>false</c> otherwise.</returns> + /// <remarks> + /// All string equality checks, whether checking secrets or their hashes, + /// should be done using <see cref="MessagingUtilities.EqualsConstantTime"/> to mitigate timing attacks. + /// </remarks> + bool IClientDescription.IsValidClientSecret(string secret) { + Requires.NotNullOrEmpty(secret, "secret"); + throw new NotImplementedException(); + } + #endregion } } diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs index 1f244f9..b8c9ede 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs @@ -18,51 +18,17 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// A request from a Client to an Authorization Server to exchange an authorization code for an access token, /// and (at the authorization server's option) a refresh token. /// </summary> - internal class AccessTokenAuthorizationCodeRequest : AccessTokenRequestBase, IAuthorizationCodeCarryingRequest { + internal class AccessTokenAuthorizationCodeRequest : AccessTokenRequestBase { /// <summary> /// Initializes a new instance of the <see cref="AccessTokenAuthorizationCodeRequest"/> class. /// </summary> /// <param name="tokenEndpoint">The Authorization Server's access token endpoint URL.</param> /// <param name="version">The version.</param> - internal AccessTokenAuthorizationCodeRequest(Uri tokenEndpoint, Version version) + protected AccessTokenAuthorizationCodeRequest(Uri tokenEndpoint, Version version) : base(tokenEndpoint, version) { } /// <summary> - /// Initializes a new instance of the <see cref="AccessTokenAuthorizationCodeRequest"/> class. - /// </summary> - /// <param name="authorizationServer">The authorization server.</param> - internal AccessTokenAuthorizationCodeRequest(AuthorizationServerDescription authorizationServer) - : this(authorizationServer.TokenEndpoint, authorizationServer.Version) { - Requires.NotNull(authorizationServer, "authorizationServer"); - } - - #region IAuthorizationCodeCarryingRequest Members - - /// <summary> - /// Gets or sets the verification code or refresh/access token. - /// </summary> - /// <value>The code or token.</value> - string IAuthorizationCodeCarryingRequest.Code { - get { return this.AuthorizationCode; } - set { this.AuthorizationCode = value; } - } - - /// <summary> - /// Gets or sets the authorization that the token describes. - /// </summary> - AuthorizationCode IAuthorizationCodeCarryingRequest.AuthorizationDescription { get; set; } - - /// <summary> - /// Gets the authorization that the code describes. - /// </summary> - IAuthorizationDescription IAuthorizationCarryingRequest.AuthorizationDescription { - get { return ((IAuthorizationCodeCarryingRequest)this).AuthorizationDescription; } - } - - #endregion - - /// <summary> /// Gets the type of the grant. /// </summary> /// <value>The type of the grant.</value> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenClientCredentialsRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenClientCredentialsRequest.cs index 48419eb..48419eb 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenClientCredentialsRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenClientCredentialsRequest.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenFailedResponse.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenFailedResponse.cs index 8c4b1c3..8c4b1c3 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenFailedResponse.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenFailedResponse.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenRefreshRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenRefreshRequest.cs index 2c3ab25..685f697 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenRefreshRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenRefreshRequest.cs @@ -14,50 +14,17 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// A request from the client to the token endpoint for a new access token /// in exchange for a refresh token that the client has previously obtained. /// </summary> - internal class AccessTokenRefreshRequest : ScopedAccessTokenRequest, IRefreshTokenCarryingRequest { + internal class AccessTokenRefreshRequest : ScopedAccessTokenRequest { /// <summary> /// Initializes a new instance of the <see cref="AccessTokenRefreshRequest"/> class. /// </summary> /// <param name="tokenEndpoint">The token endpoint.</param> /// <param name="version">The version.</param> - internal AccessTokenRefreshRequest(Uri tokenEndpoint, Version version) + protected AccessTokenRefreshRequest(Uri tokenEndpoint, Version version) : base(tokenEndpoint, version) { } /// <summary> - /// Initializes a new instance of the <see cref="AccessTokenRefreshRequest"/> class. - /// </summary> - /// <param name="authorizationServer">The authorization server.</param> - internal AccessTokenRefreshRequest(AuthorizationServerDescription authorizationServer) - : this(authorizationServer.TokenEndpoint, authorizationServer.Version) { - } - - #region IRefreshTokenCarryingRequest members - - /// <summary> - /// Gets or sets the verification code or refresh/access token. - /// </summary> - /// <value>The code or token.</value> - string IRefreshTokenCarryingRequest.RefreshToken { - get { return this.RefreshToken; } - set { this.RefreshToken = value; } - } - - /// <summary> - /// Gets or sets the authorization that the token describes. - /// </summary> - RefreshToken IRefreshTokenCarryingRequest.AuthorizationDescription { get; set; } - - /// <summary> - /// Gets the authorization that the token describes. - /// </summary> - IAuthorizationDescription IAuthorizationCarryingRequest.AuthorizationDescription { - get { return ((IRefreshTokenCarryingRequest)this).AuthorizationDescription; } - } - - #endregion - - /// <summary> /// Gets or sets the refresh token. /// </summary> /// <value>The refresh token.</value> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenRequestBase.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenRequestBase.cs index 7e41fcd..b9f4c56 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenRequestBase.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenRequestBase.cs @@ -16,7 +16,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// <summary> /// A message sent from the client to the authorization server to exchange a previously obtained grant for an access token. /// </summary> - public abstract class AccessTokenRequestBase : AuthenticatedClientRequestBase, IAccessTokenRequest { + public abstract class AccessTokenRequestBase : AuthenticatedClientRequestBase, IAccessTokenRequestInternal, IDisposable { /// <summary> /// Initializes a new instance of the <see cref="AccessTokenRequestBase"/> class. /// </summary> @@ -45,6 +45,14 @@ namespace DotNetOpenAuth.OAuth2.Messages { } /// <summary> + /// Gets or sets the access token creation parameters. + /// </summary> + /// <remarks> + /// This property's value is set by a binding element in the OAuth 2 channel. + /// </remarks> + AccessTokenParameters IAccessTokenRequestInternal.AccessTokenCreationParameters { get; set; } + + /// <summary> /// Gets the type of the grant. /// </summary> /// <value>The type of the grant.</value> @@ -57,6 +65,25 @@ namespace DotNetOpenAuth.OAuth2.Messages { protected abstract HashSet<string> RequestedScope { get; } /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// <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) { + IAccessTokenRequestInternal self = this; + if (self.AccessTokenCreationParameters != null) { + self.AccessTokenCreationParameters.Dispose(); + } + } + + /// <summary> /// Checks the message state for conformity to the protocol specification /// and throws an exception if the message is invalid. /// </summary> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs index 52e65be..52e65be 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenSuccessResponse.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenSuccessResponse.cs index 6d278c4..1de39a6 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AccessTokenSuccessResponse.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenSuccessResponse.cs @@ -18,7 +18,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// <remarks> /// This message type is shared by the Web App, Rich App, and Username/Password profiles. /// </remarks> - internal class AccessTokenSuccessResponse : MessageBase, IHttpDirectResponse { + internal class AccessTokenSuccessResponse : MessageBase, IHttpDirectResponse, IAccessTokenIssuingResponse { /// <summary> /// Initializes a new instance of the <see cref="AccessTokenSuccessResponse"/> class. /// </summary> @@ -92,6 +92,50 @@ namespace DotNetOpenAuth.OAuth2.Messages { [MessagePart(Protocol.scope, IsRequired = false, Encoder = typeof(ScopeEncoder))] public HashSet<string> Scope { get; private set; } + #region IAccessTokenIssuingResponse Members + + /// <summary> + /// Gets or sets the lifetime of the access token. + /// </summary> + /// <value> + /// The lifetime. + /// </value> + TimeSpan? IAccessTokenIssuingResponse.Lifetime { + get { return this.Lifetime; } + set { this.Lifetime = value; } + } + + #endregion + + #region IAuthorizationCarryingRequest + + /// <summary> + /// Gets the authorization that the token describes. + /// </summary> + IAuthorizationDescription IAuthorizationCarryingRequest.AuthorizationDescription { + get { return ((IAccessTokenCarryingRequest)this).AuthorizationDescription; } + } + + #endregion + + #region IAccessTokenCarryingRequest Members + + /// <summary> + /// Gets or sets the authorization that the token describes. + /// </summary> + /// <value></value> + AccessToken IAccessTokenCarryingRequest.AuthorizationDescription { get; set; } + + /// <summary> + /// Gets or sets the access token. + /// </summary> + string IAccessTokenCarryingRequest.AccessToken { + get { return this.AccessToken; } + set { this.AccessToken = value; } + } + + #endregion + /// <summary> /// Gets or sets a value indicating whether a refresh token is or should be included in the response. /// </summary> @@ -107,7 +151,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { // Per OAuth 2.0 section 4.4.3 (draft 23), refresh tokens should never be included // in a response to an access token request that used the client credential grant type. - ErrorUtilities.VerifyProtocol(!this.HasRefreshToken || !(this.OriginatingRequest is AccessTokenClientCredentialsRequest), OAuthStrings.RefreshTokenInappropriateForRequestType, this.OriginatingRequest.GetType().Name); + ErrorUtilities.VerifyProtocol(!this.HasRefreshToken || !(this.OriginatingRequest is AccessTokenClientCredentialsRequest), ClientAuthorizationStrings.RefreshTokenInappropriateForRequestType, this.OriginatingRequest.GetType().Name); } } } diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AuthenticatedClientRequestBase.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs index bc4d0ca..bc4d0ca 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/AuthenticatedClientRequestBase.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationFailedResponse.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationFailedResponse.cs index 7cc8e82..7cc8e82 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationFailedResponse.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationFailedResponse.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs index f5a5d67..661e2ae 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationImplicitRequest.cs @@ -18,7 +18,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// to issue an access token to the client if permission is granted. /// </summary> [Serializable] - public class EndUserAuthorizationImplicitRequest : EndUserAuthorizationRequest, IAccessTokenRequest { + public class EndUserAuthorizationImplicitRequest : EndUserAuthorizationRequest, IAccessTokenRequestInternal { /// <summary> /// Gets or sets the grant type that the client expects of the authorization server. /// </summary> @@ -31,19 +31,11 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// </summary> /// <param name="authorizationEndpoint">The Authorization Server's user authorization URL to direct the user to.</param> /// <param name="version">The protocol version.</param> - internal EndUserAuthorizationImplicitRequest(Uri authorizationEndpoint, Version version) + protected EndUserAuthorizationImplicitRequest(Uri authorizationEndpoint, Version version) : base(authorizationEndpoint, version) { } /// <summary> - /// Initializes a new instance of the <see cref="EndUserAuthorizationImplicitRequest"/> class. - /// </summary> - /// <param name="authorizationServer">The authorization server.</param> - internal EndUserAuthorizationImplicitRequest(AuthorizationServerDescription authorizationServer) - : this(authorizationServer.AuthorizationEndpoint, authorizationServer.Version) { - } - - /// <summary> /// Gets the grant type that the client expects of the authorization server. /// </summary> public override EndUserAuthorizationResponseType ResponseType { @@ -51,6 +43,14 @@ namespace DotNetOpenAuth.OAuth2.Messages { } /// <summary> + /// Gets or sets the access token creation parameters. + /// </summary> + /// <remarks> + /// This property's value is set by a binding element in the OAuth 2 channel. + /// </remarks> + AccessTokenParameters IAccessTokenRequestInternal.AccessTokenCreationParameters { get; set; } + + /// <summary> /// Gets a value indicating whether the client requesting the access token has authenticated itself. /// </summary> /// <value> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationRequest.cs index 45fa049..f229cf9 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationRequest.cs @@ -32,7 +32,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// </summary> /// <param name="authorizationEndpoint">The Authorization Server's user authorization URL to direct the user to.</param> /// <param name="version">The protocol version.</param> - internal EndUserAuthorizationRequest(Uri authorizationEndpoint, Version version) + protected EndUserAuthorizationRequest(Uri authorizationEndpoint, Version version) : base(version, MessageTransport.Indirect, authorizationEndpoint) { Requires.NotNull(authorizationEndpoint, "authorizationEndpoint"); Requires.NotNull(version, "version"); @@ -41,17 +41,6 @@ namespace DotNetOpenAuth.OAuth2.Messages { } /// <summary> - /// Initializes a new instance of the <see cref="EndUserAuthorizationRequest"/> class. - /// </summary> - /// <param name="authorizationServer">The authorization server.</param> - internal EndUserAuthorizationRequest(AuthorizationServerDescription authorizationServer) - : this(authorizationServer.AuthorizationEndpoint, authorizationServer.Version) { - Requires.NotNull(authorizationServer, "authorizationServer"); - Requires.True(authorizationServer.Version != null, "authorizationServer"); - Requires.True(authorizationServer.AuthorizationEndpoint != null, "authorizationServer"); - } - - /// <summary> /// Gets the grant type that the client expects of the authorization server. /// </summary> public virtual EndUserAuthorizationResponseType ResponseType { diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationResponseType.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationResponseType.cs index 75ece0f..75ece0f 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationResponseType.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationResponseType.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs index 5c03e7a..7a79e46 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs @@ -19,7 +19,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// to indicate that user authorization was granted, carrying only an access token, /// and to return the user to the Client where they started their experience. /// </summary> - internal class EndUserAuthorizationSuccessAccessTokenResponse : EndUserAuthorizationSuccessResponseBase, IAccessTokenCarryingRequest, IHttpIndirectResponse { + internal class EndUserAuthorizationSuccessAccessTokenResponse : EndUserAuthorizationSuccessResponseBase, IAccessTokenIssuingResponse, IHttpIndirectResponse { /// <summary> /// Initializes a new instance of the <see cref="EndUserAuthorizationSuccessAccessTokenResponse"/> class. /// </summary> @@ -75,6 +75,17 @@ namespace DotNetOpenAuth.OAuth2.Messages { #endregion /// <summary> + /// Gets or sets the lifetime of the access token. + /// </summary> + /// <value> + /// The lifetime. + /// </value> + TimeSpan? IAccessTokenIssuingResponse.Lifetime { + get { return this.Lifetime; } + set { this.Lifetime = value; } + } + + /// <summary> /// Gets or sets the token type. /// </summary> /// <value>Usually "bearer".</value> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs index dcacc14..9d6b015 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs @@ -16,7 +16,7 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// to indicate that user authorization was granted, carrying an authorization code and possibly an access token, /// and to return the user to the Client where they started their experience. /// </summary> - internal class EndUserAuthorizationSuccessAuthCodeResponse : EndUserAuthorizationSuccessResponseBase, IAuthorizationCodeCarryingRequest { + internal class EndUserAuthorizationSuccessAuthCodeResponse : EndUserAuthorizationSuccessResponseBase { /// <summary> /// Initializes a new instance of the <see cref="EndUserAuthorizationSuccessAuthCodeResponse"/> class. /// </summary> @@ -40,30 +40,6 @@ namespace DotNetOpenAuth.OAuth2.Messages { ((IMessageWithClientState)this).ClientState = request.ClientState; } - #region IAuthorizationCodeCarryingRequest Members - - /// <summary> - /// Gets or sets the authorization code. - /// </summary> - string IAuthorizationCodeCarryingRequest.Code { - get { return this.AuthorizationCode; } - set { this.AuthorizationCode = value; } - } - - /// <summary> - /// Gets or sets the authorization that the token describes. - /// </summary> - AuthorizationCode IAuthorizationCodeCarryingRequest.AuthorizationDescription { get; set; } - - /// <summary> - /// Gets the authorization that the code describes. - /// </summary> - IAuthorizationDescription IAuthorizationCarryingRequest.AuthorizationDescription { - get { return ((IAuthorizationCodeCarryingRequest)this).AuthorizationDescription; } - } - - #endregion - /// <summary> /// Gets or sets the authorization code. /// </summary> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs index ef0010e..ef0010e 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/GrantType.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/GrantType.cs index a26d405..a26d405 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/GrantType.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/GrantType.cs diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenIssuingResponse.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenIssuingResponse.cs new file mode 100644 index 0000000..1a54aca --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenIssuingResponse.cs @@ -0,0 +1,24 @@ +//----------------------------------------------------------------------- +// <copyright file="IAccessTokenIssuingResponse.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.Messages { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuth2.ChannelElements; + + /// <summary> + /// A message sent from the Authorization Server to the client carrying an access token. + /// </summary> + internal interface IAccessTokenIssuingResponse : IAccessTokenCarryingRequest { + /// <summary> + /// Gets or sets the lifetime of the access token. + /// </summary> + /// <value>The lifetime.</value> + TimeSpan? Lifetime { get; set; } + } +} diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/IAccessTokenRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequest.cs index 65378f9..65378f9 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/IAccessTokenRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequest.cs diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequestInternal.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequestInternal.cs new file mode 100644 index 0000000..e218462 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IAccessTokenRequestInternal.cs @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------- +// <copyright file="IAccessTokenRequestInternal.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.Messages { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + + /// <summary> + /// Implemented by all message types whose response may contain an access token. + /// </summary> + public interface IAccessTokenRequestInternal : IAccessTokenRequest { + /// <summary> + /// Gets or sets the access token creation parameters. + /// </summary> + /// <remarks> + /// This property's value is set by a binding element in the OAuth 2 channel. + /// </remarks> + AccessTokenParameters AccessTokenCreationParameters { get; set; } + } +} diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/IMessageWithClientState.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IMessageWithClientState.cs index 71476f2..71476f2 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/IMessageWithClientState.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/IMessageWithClientState.cs diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/ScopedAccessTokenRequest.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/ScopedAccessTokenRequest.cs index 0ea6efb..0ea6efb 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/ScopedAccessTokenRequest.cs +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/ScopedAccessTokenRequest.cs diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/TokenEndpointProtocolException.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/TokenEndpointProtocolException.cs new file mode 100644 index 0000000..308bfe2 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/TokenEndpointProtocolException.cs @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------- +// <copyright file="TokenEndpointProtocolException.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2 { + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Text; + + using DotNetOpenAuth.Messaging; + + /// <summary> + /// Describes an error generated by an Authorization Server's token endpoint. + /// </summary> + public class TokenEndpointProtocolException : ProtocolException { + /// <summary> + /// Initializes a new instance of the <see cref="TokenEndpointProtocolException"/> class. + /// </summary> + /// <param name="error">A single error code from <see cref="Protocol.AccessTokenRequestErrorCodes"/>.</param> + /// <param name="description">A human-readable UTF-8 encoded text providing additional information, used to assist the client developer in understanding the error that occurred.</param> + /// <param name="moreInformation">A URI identifying a human-readable web page with information about the error, used to provide the client developer with additional information about the error.</param> + public TokenEndpointProtocolException(string error, string description = null, Uri moreInformation = null) + : base(string.Format(CultureInfo.CurrentCulture, ClientAuthorizationStrings.TokenEndpointErrorFormat, error, description)) { + Requires.NotNullOrEmpty(error, "error"); + + this.Error = error; + this.Description = description; + this.MoreInformation = moreInformation; + } + + /// <summary> + /// Initializes a new instance of the <see cref="TokenEndpointProtocolException"/> class. + /// </summary> + /// <param name="innerException">The inner exception.</param> + public TokenEndpointProtocolException(Exception innerException) + : base(Protocol.AccessTokenRequestErrorCodes.InvalidRequest, innerException) { + this.Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest; + } + + /// <summary> + /// Gets a single error code from <see cref="Protocol.AccessTokenRequestErrorCodes"/>. + /// </summary> + public string Error { get; private set; } + + /// <summary> + /// Gets a human-readable UTF-8 encoded text providing additional information, used to assist the client developer in understanding the error that occurred. + /// </summary> + public string Description { get; private set; } + + /// <summary> + /// Gets a URI identifying a human-readable web page with information about the error, used to provide the client developer with additional information about the error. + /// </summary> + public Uri MoreInformation { get; private set; } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d536886 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/Properties/AssemblyInfo.cs @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------- +// <copyright file="AssemblyInfo.cs" company="Outercurve Foundation"> +// Copyright (c) Outercurve Foundation. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +// We DON'T put an AssemblyVersionAttribute in here because it is generated in the build. + +using System; +using System.Diagnostics.Contracts; +using System.Net; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; +using System.Security.Permissions; +using System.Web.UI; + +// 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("DotNetOpenAuth OAuth 2.0")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("DotNetOpenAuth")] +[assembly: AssemblyCopyright("Copyright © 2011 Outercurve Foundation")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: NeutralResourcesLanguage("en-US")] +[assembly: CLSCompliant(true)] + +// 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("7d73990c-47c0-4256-9f20-a893add9e289")] + +[assembly: ContractVerification(true)] + +#if StrongNameSigned +// See comment at top of this file. We need this so that strong-naming doesn't +// keep this assembly from being useful to shared host (medium trust) web sites. +[assembly: AllowPartiallyTrustedCallers] + +[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.AuthorizationServer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] +#else +[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.AuthorizationServer")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.Test")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] +#endif diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/DotNetOpenAuth.OAuth2.ResourceServer.csproj b/src/DotNetOpenAuth.OAuth2.ResourceServer/DotNetOpenAuth.OAuth2.ResourceServer.csproj index 63806b8..3aa92f7 100644 --- a/src/DotNetOpenAuth.OAuth2.ResourceServer/DotNetOpenAuth.OAuth2.ResourceServer.csproj +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/DotNetOpenAuth.OAuth2.ResourceServer.csproj @@ -18,12 +18,15 @@ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> </PropertyGroup> <ItemGroup> + <Compile Include="OAuth2\ChannelElements\OAuth2ResourceServerChannel.cs" /> + <Compile Include="OAuth2\IAccessTokenAnalyzer.cs" /> <Compile Include="OAuth2\ResourceServerStrings.Designer.cs"> <AutoGen>True</AutoGen> <DesignTime>True</DesignTime> <DependentUpon>ResourceServerStrings.resx</DependentUpon> </Compile> <Compile Include="OAuth2\ResourceServer.cs" /> + <Compile Include="OAuth2\StandardAccessTokenAnalyzer.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> </ItemGroup> <ItemGroup> @@ -49,4 +52,4 @@ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" /> <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " /> -</Project>
\ No newline at end of file +</Project> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs index 947c044..22514b4 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ChannelElements/OAuth2ResourceServerChannel.cs @@ -52,8 +52,8 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { var fields = new Dictionary<string, string>(); string accessToken; if ((accessToken = SearchForBearerAccessTokenInRequest(request)) != null) { - fields["token_type"] = Protocol.AccessTokenTypes.Bearer; - fields["access_token"] = accessToken; + fields[Protocol.token_type] = Protocol.AccessTokenTypes.Bearer; + fields[Protocol.access_token] = accessToken; } if (fields.Count > 0) { diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/IAccessTokenAnalyzer.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/IAccessTokenAnalyzer.cs index 5aa1bb6..c153bfa 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/IAccessTokenAnalyzer.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/IAccessTokenAnalyzer.cs @@ -23,13 +23,12 @@ namespace DotNetOpenAuth.OAuth2 { /// Reads an access token to find out what data it authorizes access to. /// </summary> /// <param name="message">The message carrying the access token.</param> - /// <param name="accessToken">The access token.</param> - /// <param name="user">The user whose data is accessible with this access token.</param> - /// <param name="scope">The scope of access authorized by this access token.</param> - /// <returns>A value indicating whether this access token is valid.</returns> + /// <param name="accessToken">The access token's serialized representation.</param> + /// <returns>The deserialized, validated token.</returns> + /// <exception cref="ProtocolException">Thrown if the access token is expired, invalid, or from an untrusted authorization server.</exception> [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Try pattern")] [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "Try pattern")] - bool TryValidateAccessToken(IDirectedProtocolMessage message, string accessToken, out string user, out HashSet<string> scope); + AccessToken DeserializeAccessToken(IDirectedProtocolMessage message, string accessToken); } /// <summary> @@ -47,17 +46,13 @@ namespace DotNetOpenAuth.OAuth2 { /// Reads an access token to find out what data it authorizes access to. /// </summary> /// <param name="message">The message carrying the access token.</param> - /// <param name="accessToken">The access token.</param> - /// <param name="user">The user whose data is accessible with this access token.</param> - /// <param name="scope">The scope of access authorized by this access token.</param> - /// <returns> - /// A value indicating whether this access token is valid. - /// </returns> - bool IAccessTokenAnalyzer.TryValidateAccessToken(IDirectedProtocolMessage message, string accessToken, out string user, out HashSet<string> scope) { + /// <param name="accessToken">The access token's serialized representation.</param> + /// <returns>The deserialized, validated token.</returns> + /// <exception cref="ProtocolException">Thrown if the access token is expired, invalid, or from an untrusted authorization server.</exception> + AccessToken IAccessTokenAnalyzer.DeserializeAccessToken(IDirectedProtocolMessage message, string accessToken) { Requires.NotNull(message, "message"); Requires.NotNullOrEmpty(accessToken, "accessToken"); - Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn<string>(out user) != null)); - + Contract.Ensures(Contract.Result<AccessToken>() != null); throw new NotImplementedException(); } } diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs index 2830ab8..9540d10 100644 --- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServer.cs @@ -34,6 +34,8 @@ namespace DotNetOpenAuth.OAuth2 { this.AccessTokenAnalyzer = accessTokenAnalyzer; this.Channel = new OAuth2ResourceServerChannel(); + this.ResourceOwnerPrincipalPrefix = string.Empty; + this.ClientPrincipalPrefix = "client:"; } /// <summary> @@ -43,6 +45,18 @@ namespace DotNetOpenAuth.OAuth2 { public IAccessTokenAnalyzer AccessTokenAnalyzer { get; private set; } /// <summary> + /// Gets or sets the prefix to apply to a resource owner's username when used as the username in an <see cref="IPrincipal"/>. + /// </summary> + /// <value>The default value is the empty string.</value> + public string ResourceOwnerPrincipalPrefix { get; set; } + + /// <summary> + /// Gets or sets the prefix to apply to a client identifier when used as the username in an <see cref="IPrincipal"/>. + /// </summary> + /// <value>The default value is "client:"</value> + public string ClientPrincipalPrefix { get; set; } + + /// <summary> /// Gets the channel. /// </summary> /// <value>The channel.</value> @@ -51,50 +65,48 @@ namespace DotNetOpenAuth.OAuth2 { /// <summary> /// Discovers what access the client should have considering the access token in the current request. /// </summary> - /// <param name="userName">The name on the account the client has access to.</param> - /// <param name="scope">The set of operations the client is authorized for.</param> + /// <param name="accessToken">Receives the access token describing the authorization the client has.</param> /// <returns>An error to return to the client if access is not authorized; <c>null</c> if access is granted.</returns> [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", Justification = "Try pattern")] [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Try pattern")] - public OutgoingWebResponse VerifyAccess(out string userName, out HashSet<string> scope) { - return this.VerifyAccess(this.Channel.GetRequestFromContext(), out userName, out scope); + public OutgoingWebResponse VerifyAccess(out AccessToken accessToken) { + return this.VerifyAccess(this.Channel.GetRequestFromContext(), out accessToken); } /// <summary> /// Discovers what access the client should have considering the access token in the current request. /// </summary> /// <param name="httpRequestInfo">The HTTP request info.</param> - /// <param name="userName">The name on the account the client has access to.</param> - /// <param name="scope">The set of operations the client is authorized for.</param> + /// <param name="accessToken">Receives the access token describing the authorization the client has.</param> /// <returns> /// An error to return to the client if access is not authorized; <c>null</c> if access is granted. /// </returns> [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Try pattern")] [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "Try pattern")] - public virtual OutgoingWebResponse VerifyAccess(HttpRequestBase httpRequestInfo, out string userName, out HashSet<string> scope) { + public virtual OutgoingWebResponse VerifyAccess(HttpRequestBase httpRequestInfo, out AccessToken accessToken) { Requires.NotNull(httpRequestInfo, "httpRequestInfo"); AccessProtectedResourceRequest request = null; try { if (this.Channel.TryReadFromRequest<AccessProtectedResourceRequest>(httpRequestInfo, out request)) { - if (this.AccessTokenAnalyzer.TryValidateAccessToken(request, request.AccessToken, out userName, out scope)) { - // No errors to return. - return null; + accessToken = this.AccessTokenAnalyzer.DeserializeAccessToken(request, request.AccessToken); + ErrorUtilities.VerifyHost(accessToken != null, "IAccessTokenAnalyzer.DeserializeAccessToken returned a null reslut."); + if (string.IsNullOrEmpty(accessToken.User) && string.IsNullOrEmpty(accessToken.ClientIdentifier)) { + Logger.OAuth.Error("Access token rejected because both the username and client id properties were null or empty."); + ErrorUtilities.ThrowProtocol(ResourceServerStrings.InvalidAccessToken); } - throw ErrorUtilities.ThrowProtocol(ResourceServerStrings.InvalidAccessToken); + return null; } else { var response = new UnauthorizedResponse(new ProtocolException(ResourceServerStrings.MissingAccessToken)); - userName = null; - scope = null; + accessToken = null; return this.Channel.PrepareResponse(response); } } catch (ProtocolException ex) { var response = request != null ? new UnauthorizedResponse(request, ex) : new UnauthorizedResponse(ex); - userName = null; - scope = null; + accessToken = null; return this.Channel.PrepareResponse(response); } } @@ -109,10 +121,23 @@ namespace DotNetOpenAuth.OAuth2 { /// </returns> [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Justification = "Try pattern")] public virtual OutgoingWebResponse VerifyAccess(HttpRequestBase httpRequestInfo, out IPrincipal principal) { - string username; - HashSet<string> scope; - var result = this.VerifyAccess(httpRequestInfo, out username, out scope); - principal = result == null ? new OAuthPrincipal(username, scope != null ? scope.ToArray() : new string[0]) : null; + AccessToken accessToken; + var result = this.VerifyAccess(httpRequestInfo, out accessToken); + if (result == null) { + // Mitigates attacks on this approach of differentiating clients from resource owners + // by checking that a username doesn't look suspiciously engineered to appear like the other type. + ErrorUtilities.VerifyProtocol(accessToken.User == null || string.IsNullOrEmpty(this.ClientPrincipalPrefix) || !accessToken.User.StartsWith(this.ClientPrincipalPrefix, StringComparison.OrdinalIgnoreCase), ResourceServerStrings.ResourceOwnerNameLooksLikeClientIdentifier); + ErrorUtilities.VerifyProtocol(accessToken.ClientIdentifier == null || string.IsNullOrEmpty(this.ResourceOwnerPrincipalPrefix) || !accessToken.ClientIdentifier.StartsWith(this.ResourceOwnerPrincipalPrefix, StringComparison.OrdinalIgnoreCase), ResourceServerStrings.ClientIdentifierLooksLikeResourceOwnerName); + + string principalUserName = !string.IsNullOrEmpty(accessToken.User) + ? this.ResourceOwnerPrincipalPrefix + accessToken.User + : this.ClientPrincipalPrefix + accessToken.ClientIdentifier; + string[] principalScope = accessToken.Scope != null ? accessToken.Scope.ToArray() : new string[0]; + principal = new OAuthPrincipal(principalUserName, principalScope); + } else { + principal = null; + } + return result; } diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServerStrings.Designer.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServerStrings.Designer.cs index 606b072..f97b41b 100644 --- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServerStrings.Designer.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServerStrings.Designer.cs @@ -61,6 +61,15 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> + /// Looks up a localized string similar to Client Identifier starts with a resource owner prefix. Authorization aborted.. + /// </summary> + internal static string ClientIdentifierLooksLikeResourceOwnerName { + get { + return ResourceManager.GetString("ClientIdentifierLooksLikeResourceOwnerName", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to Invalid access token.. /// </summary> internal static string InvalidAccessToken { @@ -77,5 +86,14 @@ namespace DotNetOpenAuth.OAuth2 { return ResourceManager.GetString("MissingAccessToken", resourceCulture); } } + + /// <summary> + /// Looks up a localized string similar to Resource owner username starts with a client prefix. Authorization aborted.. + /// </summary> + internal static string ResourceOwnerNameLooksLikeClientIdentifier { + get { + return ResourceManager.GetString("ResourceOwnerNameLooksLikeClientIdentifier", resourceCulture); + } + } } } diff --git a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServerStrings.resx b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServerStrings.resx index 175a386..46943c4 100644 --- a/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServerStrings.resx +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/ResourceServerStrings.resx @@ -117,10 +117,16 @@ <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> + <data name="ClientIdentifierLooksLikeResourceOwnerName" xml:space="preserve"> + <value>Client Identifier starts with a resource owner prefix. Authorization aborted.</value> + </data> <data name="InvalidAccessToken" xml:space="preserve"> <value>Invalid access token.</value> </data> <data name="MissingAccessToken" xml:space="preserve"> <value>Missing access token.</value> </data> + <data name="ResourceOwnerNameLooksLikeClientIdentifier" xml:space="preserve"> + <value>Resource owner username starts with a client prefix. Authorization aborted.</value> + </data> </root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/StandardAccessTokenAnalyzer.cs b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/StandardAccessTokenAnalyzer.cs index 636f490..992e93c 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/StandardAccessTokenAnalyzer.cs +++ b/src/DotNetOpenAuth.OAuth2.ResourceServer/OAuth2/StandardAccessTokenAnalyzer.cs @@ -45,22 +45,13 @@ namespace DotNetOpenAuth.OAuth2 { /// Reads an access token to find out what data it authorizes access to. /// </summary> /// <param name="message">The message carrying the access token.</param> - /// <param name="accessToken">The access token.</param> - /// <param name="user">The user whose data is accessible with this access token.</param> - /// <param name="scope">The scope of access authorized by this access token.</param> - /// <returns> - /// A value indicating whether this access token is valid. - /// </returns> - /// <remarks> - /// This method also responsible to throw a <see cref="ProtocolException"/> or return - /// <c>false</c> when the access token is expired, invalid, or from an untrusted authorization server. - /// </remarks> - public virtual bool TryValidateAccessToken(IDirectedProtocolMessage message, string accessToken, out string user, out HashSet<string> scope) { + /// <param name="accessToken">The access token's serialized representation.</param> + /// <returns>The deserialized, validated token.</returns> + /// <exception cref="ProtocolException">Thrown if the access token is expired, invalid, or from an untrusted authorization server.</exception> + public virtual AccessToken DeserializeAccessToken(IDirectedProtocolMessage message, string accessToken) { var accessTokenFormatter = AccessToken.CreateFormatter(this.AuthorizationServerPublicSigningKey, this.ResourceServerPrivateEncryptionKey); var token = accessTokenFormatter.Deserialize(message, accessToken, Protocol.access_token); - user = token.User; - scope = new HashSet<string>(token.Scope, OAuthUtilities.ScopeStringComparer); - return true; + return token; } } } diff --git a/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj b/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj index 438a21d..921cd84 100644 --- a/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj +++ b/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj @@ -19,71 +19,25 @@ </PropertyGroup> <ItemGroup> <Compile Include="GlobalSuppressions.cs" /> - <Compile Include="OAuth2\AuthorizationState.cs" /> - <Compile Include="OAuth2\ChannelElements\AccessRequestBindingElement.cs" /> - <Compile Include="OAuth2\ChannelElements\AccessToken.cs" /> - <Compile Include="OAuth2\ChannelElements\AccessTokenBindingElement.cs" /> + <Compile Include="OAuth2\AccessToken.cs" /> <Compile Include="OAuth2\ChannelElements\AuthorizationDataBag.cs" /> - <Compile Include="OAuth2\ChannelElements\AuthServerBindingElementBase.cs" /> - <Compile Include="OAuth2\ChannelElements\GrantTypeEncoder.cs" /> - <Compile Include="OAuth2\ChannelElements\EndUserAuthorizationResponseTypeEncoder.cs" /> <Compile Include="OAuth2\ChannelElements\IAccessTokenCarryingRequest.cs" /> - <Compile Include="OAuth2\ChannelElements\IAuthorizationCodeCarryingRequest.cs" /> - <Compile Include="OAuth2\ChannelElements\IOAuth2ChannelWithAuthorizationServer.cs" /> - <Compile Include="OAuth2\ChannelElements\IRefreshTokenCarryingRequest.cs" /> - <Compile Include="OAuth2\ChannelElements\OAuth2ChannelBase.cs" /> - <Compile Include="OAuth2\ChannelElements\OAuth2ClientChannel.cs" /> <Compile Include="OAuth2\ChannelElements\ScopeEncoder.cs" /> <Compile Include="OAuth2\ChannelElements\IAuthorizationDescription.cs" /> <Compile Include="OAuth2\ChannelElements\IAuthorizationCarryingRequest.cs" /> - <Compile Include="OAuth2\ChannelElements\OAuth2ResourceServerChannel.cs" /> - <Compile Include="OAuth2\ChannelElements\RefreshToken.cs" /> - <Compile Include="OAuth2\ChannelElements\AuthorizationCode.cs" /> - <Compile Include="OAuth2\ChannelElements\AuthorizationCodeBindingElement.cs" /> - <Compile Include="OAuth2\ChannelElements\AuthServerAllFlowsBindingElement.cs" /> - <Compile Include="OAuth2\ClientDescription.cs" /> - <Compile Include="OAuth2\ClientType.cs" /> - <Compile Include="OAuth2\IAccessTokenAnalyzer.cs" /> - <Compile Include="OAuth2\IAuthorizationServer.cs" /> - <Compile Include="OAuth2\IAuthorizationState.cs" /> - <Compile Include="OAuth2\IClientAuthorizationTracker.cs" /> - <Compile Include="OAuth2\IClientDescription.cs" /> <Compile Include="OAuth2\Messages\AccessProtectedResourceRequest.cs" /> - <Compile Include="OAuth2\Messages\AccessTokenAuthorizationCodeRequest.cs" /> - <Compile Include="OAuth2\Messages\AccessTokenResourceOwnerPasswordCredentialsRequest.cs" /> - <Compile Include="OAuth2\Messages\AccessTokenRequestBase.cs" /> - <Compile Include="OAuth2\Messages\AccessTokenClientCredentialsRequest.cs" /> - <Compile Include="OAuth2\Messages\AuthenticatedClientRequestBase.cs" /> - <Compile Include="OAuth2\Messages\EndUserAuthorizationImplicitRequest.cs" /> - <Compile Include="OAuth2\Messages\EndUserAuthorizationSuccessAccessTokenResponse.cs" /> - <Compile Include="OAuth2\Messages\EndUserAuthorizationFailedResponse.cs" /> - <Compile Include="OAuth2\Messages\EndUserAuthorizationSuccessAuthCodeResponse.cs" /> - <Compile Include="OAuth2\Messages\GrantType.cs" /> - <Compile Include="OAuth2\Messages\AccessTokenRefreshRequest.cs" /> - <Compile Include="OAuth2\Messages\EndUserAuthorizationResponseType.cs" /> - <Compile Include="OAuth2\Messages\IAccessTokenRequest.cs" /> - <Compile Include="OAuth2\Messages\IMessageWithClientState.cs" /> - <Compile Include="OAuth2\Messages\ScopedAccessTokenRequest.cs" /> <Compile Include="OAuth2\Messages\UnauthorizedResponse.cs" /> - <Compile Include="OAuth2\Messages\AccessTokenFailedResponse.cs" /> - <Compile Include="OAuth2\Messages\AccessTokenSuccessResponse.cs" /> - <Compile Include="OAuth2\Messages\EndUserAuthorizationSuccessResponseBase.cs" /> - <Compile Include="OAuth2\StandardAccessTokenAnalyzer.cs" /> <Compile Include="OAuth2\OAuthUtilities.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="OAuth2\ChannelElements\OAuth2AuthorizationServerChannel.cs" /> <Compile Include="OAuth2\Messages\MessageBase.cs" /> - <Compile Include="OAuth2\Messages\EndUserAuthorizationRequest.cs" /> <Compile Include="OAuth2\Protocol.cs" /> <Compile Include="OAuth2\OAuthStrings.Designer.cs"> <AutoGen>True</AutoGen> <DesignTime>True</DesignTime> <DependentUpon>OAuthStrings.resx</DependentUpon> </Compile> - <Compile Include="OAuth2\AuthorizationServerDescription.cs" /> </ItemGroup> <ItemGroup> - <None Include="OAuth2\Messages\OAuth 2 Messages.cd" /> <None Include="OAuth2\OAuth 2 client facades.cd" /> </ItemGroup> <ItemGroup> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AccessToken.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/AccessToken.cs index 84b17cc..3a12faa 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AccessToken.cs +++ b/src/DotNetOpenAuth.OAuth2/OAuth2/AccessToken.cs @@ -4,18 +4,19 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.OAuth2.ChannelElements { +namespace DotNetOpenAuth.OAuth2 { using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Security.Cryptography; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; + using DotNetOpenAuth.OAuth2.ChannelElements; /// <summary> /// A short-lived token that accompanies HTTP requests to protected data to authorize the request. /// </summary> - internal class AccessToken : AuthorizationDataBag { + public class AccessToken : AuthorizationDataBag { /// <summary> /// Initializes a new instance of the <see cref="AccessToken"/> class. /// </summary> @@ -40,14 +41,15 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// <summary> /// Initializes a new instance of the <see cref="AccessToken"/> class. /// </summary> - /// <param name="clientIdentifier">The client identifier.</param> /// <param name="scopes">The scopes.</param> /// <param name="username">The username of the account that authorized this token.</param> /// <param name="lifetime">The lifetime for this access token.</param> - internal AccessToken(string clientIdentifier, IEnumerable<string> scopes, string username, TimeSpan? lifetime) { - Requires.NotNullOrEmpty(clientIdentifier, "clientIdentifier"); - - this.ClientIdentifier = clientIdentifier; + /// <remarks> + /// The <see cref="AuthorizationDataBag.ClientIdentifier"/> is left <c>null</c> in this case because this constructor + /// is invoked in the case where the client is <em>not</em> authenticated, and therefore no + /// trust in the client_id is appropriate. + /// </remarks> + internal AccessToken(IEnumerable<string> scopes, string username, TimeSpan? lifetime) { this.Scope.ResetContents(scopes); this.User = username; this.Lifetime = lifetime; @@ -59,7 +61,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// </summary> /// <value>The lifetime.</value> [MessagePart(Encoder = typeof(TimespanSecondsEncoder))] - internal TimeSpan? Lifetime { get; set; } + public TimeSpan? Lifetime { get; set; } /// <summary> /// Creates a formatter capable of serializing/deserializing an access token. diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AccessRequestBindingElement.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AccessRequestBindingElement.cs deleted file mode 100644 index 7a68060..0000000 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AccessRequestBindingElement.cs +++ /dev/null @@ -1,183 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="AccessRequestBindingElement.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.OAuth2.ChannelElements { - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; - using System.Linq; - using System.Security.Cryptography; - using System.Text; - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.Messaging.Bindings; - using DotNetOpenAuth.OAuth2.Messages; - - /// <summary> - /// Decodes verification codes, refresh tokens and access tokens on incoming messages. - /// </summary> - /// <remarks> - /// This binding element also ensures that the code/token coming in is issued to - /// the same client that is sending the code/token and that the authorization has - /// not been revoked and that an access token has not expired. - /// </remarks> - internal class AccessRequestBindingElement : AuthServerBindingElementBase { - /// <summary> - /// Initializes a new instance of the <see cref="AccessRequestBindingElement"/> class. - /// </summary> - internal AccessRequestBindingElement() { - } - - /// <summary> - /// Gets the protection commonly offered (if any) by this binding element. - /// </summary> - /// <value></value> - /// <remarks> - /// This value is used to assist in sorting binding elements in the channel stack. - /// </remarks> - public override 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 override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { - var authCodeCarrier = message as IAuthorizationCodeCarryingRequest; - if (authCodeCarrier != null) { - var codeFormatter = AuthorizationCode.CreateFormatter(this.AuthorizationServer); - var code = authCodeCarrier.AuthorizationDescription; - authCodeCarrier.Code = codeFormatter.Serialize(code); - return MessageProtections.None; - } - - var accessTokenCarrier = message as IAccessTokenCarryingRequest; - if (accessTokenCarrier != null) { - var responseWithOriginatingRequest = (IDirectResponseProtocolMessage)message; - var request = (IAccessTokenRequest)responseWithOriginatingRequest.OriginatingRequest; - - using (var resourceServerKey = this.AuthorizationServer.GetResourceServerEncryptionKey(request)) { - var tokenFormatter = AccessToken.CreateFormatter(this.AuthorizationServer.AccessTokenSigningKey, resourceServerKey); - var token = accessTokenCarrier.AuthorizationDescription; - accessTokenCarrier.AccessToken = tokenFormatter.Serialize(token); - } - - return MessageProtections.None; - } - - var accessTokenResponse = message as AccessTokenSuccessResponse; - if (accessTokenResponse != null) { - var directResponseMessage = (IDirectResponseProtocolMessage)accessTokenResponse; - var accessTokenRequest = (AccessTokenRequestBase)directResponseMessage.OriginatingRequest; - ErrorUtilities.VerifyProtocol(accessTokenRequest.GrantType != GrantType.ClientCredentials || accessTokenResponse.RefreshToken == null, OAuthStrings.NoGrantNoRefreshToken); - } - - 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> - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "unauthorizedclient", Justification = "Protocol requirement")] - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "incorrectclientcredentials", Justification = "Protocol requirement")] - [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "authorizationexpired", Justification = "Protocol requirement")] - [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "DotNetOpenAuth.Messaging.ErrorUtilities.VerifyProtocol(System.Boolean,System.String,System.Object[])", Justification = "Protocol requirement")] - public override MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { - var tokenRequest = message as IAuthorizationCarryingRequest; - if (tokenRequest != null) { - try { - var authCodeCarrier = message as IAuthorizationCodeCarryingRequest; - var refreshTokenCarrier = message as IRefreshTokenCarryingRequest; - var resourceOwnerPasswordCarrier = message as AccessTokenResourceOwnerPasswordCredentialsRequest; - var clientCredentialOnly = message as AccessTokenClientCredentialsRequest; - if (authCodeCarrier != null) { - var authorizationCodeFormatter = AuthorizationCode.CreateFormatter(this.AuthorizationServer); - var authorizationCode = authorizationCodeFormatter.Deserialize(message, authCodeCarrier.Code, Protocol.code); - authCodeCarrier.AuthorizationDescription = authorizationCode; - } else if (refreshTokenCarrier != null) { - var refreshTokenFormatter = RefreshToken.CreateFormatter(this.AuthorizationServer.CryptoKeyStore); - var refreshToken = refreshTokenFormatter.Deserialize(message, refreshTokenCarrier.RefreshToken, Protocol.refresh_token); - refreshTokenCarrier.AuthorizationDescription = refreshToken; - } else if (resourceOwnerPasswordCarrier != null) { - try { - if (this.AuthorizationServer.IsResourceOwnerCredentialValid(resourceOwnerPasswordCarrier.UserName, resourceOwnerPasswordCarrier.Password)) { - resourceOwnerPasswordCarrier.CredentialsValidated = true; - } else { - Logger.OAuth.WarnFormat( - "Resource owner password credential for user \"{0}\" rejected by authorization server host.", - resourceOwnerPasswordCarrier.UserName); - - // TODO: fix this to report the appropriate error code for a bad credential. - throw new ProtocolException(); - } - } catch (NotSupportedException) { - // TODO: fix this to return the appropriate error code for not supporting resource owner password credentials - throw new ProtocolException(); - } catch (NotImplementedException) { - // TODO: fix this to return the appropriate error code for not supporting resource owner password credentials - throw new ProtocolException(); - } - } else if (clientCredentialOnly != null) { - // this method will throw later if the credentials are false. - clientCredentialOnly.CredentialsValidated = true; - } else { - throw ErrorUtilities.ThrowInternal("Unexpected message type: " + tokenRequest.GetType()); - } - } catch (ExpiredMessageException ex) { - throw ErrorUtilities.Wrap(ex, Protocol.authorization_expired); - } - - var accessRequest = tokenRequest as AccessTokenRequestBase; - if (accessRequest != null) { - // Make sure the client sending us this token is the client we issued the token to. - ErrorUtilities.VerifyProtocol(string.Equals(accessRequest.ClientIdentifier, tokenRequest.AuthorizationDescription.ClientIdentifier, StringComparison.Ordinal), Protocol.incorrect_client_credentials); - - // Check that the client secret is correct. - var client = this.AuthorizationServer.GetClientOrThrow(accessRequest.ClientIdentifier); - string secret = client.Secret; - ErrorUtilities.VerifyProtocol(!string.IsNullOrEmpty(secret), Protocol.unauthorized_client); // an empty secret is not allowed for client authenticated calls. - ErrorUtilities.VerifyProtocol(MessagingUtilities.EqualsConstantTime(secret, accessRequest.ClientSecret), Protocol.incorrect_client_credentials); - - var scopedAccessRequest = accessRequest as ScopedAccessTokenRequest; - if (scopedAccessRequest != null) { - // Make sure the scope the client is requesting does not exceed the scope in the grant. - ErrorUtilities.VerifyProtocol(scopedAccessRequest.Scope.IsSubsetOf(tokenRequest.AuthorizationDescription.Scope), OAuthStrings.AccessScopeExceedsGrantScope, scopedAccessRequest.Scope, tokenRequest.AuthorizationDescription.Scope); - } - } - - // Make sure the authorization this token represents hasn't already been revoked. - ErrorUtilities.VerifyProtocol(this.AuthorizationServer.IsAuthorizationValid(tokenRequest.AuthorizationDescription), Protocol.authorization_expired); - - return MessageProtections.None; - } - - return null; - } - } -} diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AccessTokenBindingElement.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AccessTokenBindingElement.cs deleted file mode 100644 index 4c63f29..0000000 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AccessTokenBindingElement.cs +++ /dev/null @@ -1,93 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="AccessTokenBindingElement.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.OAuth2.ChannelElements { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Security.Cryptography; - using System.Text; - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OAuth2.Messages; - - /// <summary> - /// Serializes access tokens inside an outgoing message. - /// </summary> - internal class AccessTokenBindingElement : AuthServerBindingElementBase { - /// <summary> - /// Initializes a new instance of the <see cref="AccessTokenBindingElement"/> class. - /// </summary> - internal AccessTokenBindingElement() { - } - - /// <summary> - /// Gets the protection commonly offered (if any) by this binding element. - /// </summary> - /// <value>Always <c>MessageProtections.None</c></value> - /// <remarks> - /// This value is used to assist in sorting binding elements in the channel stack. - /// </remarks> - public override 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> - public override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { - var directResponse = message as IDirectResponseProtocolMessage; - IAccessTokenRequest request = directResponse != null ? directResponse.OriginatingRequest as IAccessTokenRequest : null; - - var implicitGrantResponse = message as EndUserAuthorizationSuccessAccessTokenResponse; - if (implicitGrantResponse != null) { - IAccessTokenCarryingRequest tokenCarryingResponse = implicitGrantResponse; - tokenCarryingResponse.AuthorizationDescription = new AccessToken(request.ClientIdentifier, implicitGrantResponse.Scope, implicitGrantResponse.AuthorizingUsername, implicitGrantResponse.Lifetime); - - return MessageProtections.None; - } - - var accessTokenResponse = message as AccessTokenSuccessResponse; - if (accessTokenResponse != null) { - var authCarryingRequest = (IAuthorizationCarryingRequest)request; - var accessToken = new AccessToken(authCarryingRequest.AuthorizationDescription, accessTokenResponse.Lifetime); - using (var resourceServerEncryptionKey = this.AuthorizationServer.GetResourceServerEncryptionKey(request)) { - var accessTokenFormatter = AccessToken.CreateFormatter(this.AuthorizationServer.AccessTokenSigningKey, resourceServerEncryptionKey); - accessTokenResponse.AccessToken = accessTokenFormatter.Serialize(accessToken); - } - - if (accessTokenResponse.HasRefreshToken) { - var refreshToken = new RefreshToken(authCarryingRequest.AuthorizationDescription); - var refreshTokenFormatter = RefreshToken.CreateFormatter(this.AuthorizationServer.CryptoKeyStore); - accessTokenResponse.RefreshToken = refreshTokenFormatter.Serialize(refreshToken); - } - } - - 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> - public override MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { - return null; - } - } -} diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthServerAllFlowsBindingElement.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthServerAllFlowsBindingElement.cs deleted file mode 100644 index 24ac020..0000000 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthServerAllFlowsBindingElement.cs +++ /dev/null @@ -1,83 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="AuthServerAllFlowsBindingElement.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.OAuth2.ChannelElements { - using System; - using System.Collections.Generic; - using System.Diagnostics.Contracts; - using System.Linq; - using System.Text; - using DotNetOpenAuth.OAuth2.Messages; - using Messaging; - - /// <summary> - /// A binding element that should be applied for authorization server channels regardless of which flows - /// are supported. - /// </summary> - internal class AuthServerAllFlowsBindingElement : AuthServerBindingElementBase { - /// <summary> - /// Initializes a new instance of the <see cref="AuthServerAllFlowsBindingElement"/> class. - /// </summary> - internal AuthServerAllFlowsBindingElement() { - } - - /// <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 override 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 override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { - 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 override MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { - var authorizationRequest = message as EndUserAuthorizationRequest; - if (authorizationRequest != null) { - var client = this.AuthorizationServer.GetClientOrThrow(authorizationRequest.ClientIdentifier); - ErrorUtilities.VerifyProtocol(authorizationRequest.Callback == null || client.IsCallbackAllowed(authorizationRequest.Callback), OAuthStrings.ClientCallbackDisallowed, authorizationRequest.Callback); - ErrorUtilities.VerifyProtocol(authorizationRequest.Callback != null || client.DefaultCallback != null, OAuthStrings.NoCallback); - - return MessageProtections.None; - } - - return null; - } - } -} diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthorizationCodeBindingElement.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthorizationCodeBindingElement.cs deleted file mode 100644 index d602cae..0000000 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthorizationCodeBindingElement.cs +++ /dev/null @@ -1,101 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="AuthorizationCodeBindingElement.cs" company="Outercurve Foundation"> -// Copyright (c) Outercurve Foundation. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.OAuth2.ChannelElements { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using Messages; - using Messaging; - using Messaging.Bindings; - - /// <summary> - /// A binding element for OAuth 2.0 authorization servers that create/verify - /// issued authorization codes as part of obtaining access/refresh tokens. - /// </summary> - internal class AuthorizationCodeBindingElement : AuthServerBindingElementBase { - /// <summary> - /// Initializes a new instance of the <see cref="AuthorizationCodeBindingElement"/> class. - /// </summary> - internal AuthorizationCodeBindingElement() { - } - - /// <summary> - /// Gets the protection commonly offered (if any) by this binding element. - /// </summary> - /// <value>Always <c>MessageProtections.None</c></value> - /// <remarks> - /// This value is used to assist in sorting binding elements in the channel stack. - /// </remarks> - public override MessageProtections Protection { - get { return MessageProtections.None; } - } - - /// <summary> - /// Gets the maximum message age from the standard expiration binding element. - /// </summary> - /// <value>This interval need not account for clock skew because it is only compared within a single authorization server or farm of servers.</value> - internal static TimeSpan MaximumMessageAge { - get { return Configuration.DotNetOpenAuthSection.Messaging.MaximumMessageLifetimeNoSkew; } - } - - /// <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 override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { - var response = message as EndUserAuthorizationSuccessAuthCodeResponse; - if (response != null) { - var directResponse = (IDirectResponseProtocolMessage)response; - var request = (EndUserAuthorizationRequest)directResponse.OriginatingRequest; - IAuthorizationCodeCarryingRequest tokenCarryingResponse = response; - tokenCarryingResponse.AuthorizationDescription = new AuthorizationCode(request.ClientIdentifier, request.Callback, response.Scope, response.AuthorizingUsername); - - 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 override MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { - var request = message as AccessTokenAuthorizationCodeRequest; - if (request != null) { - IAuthorizationCarryingRequest tokenRequest = request; - ((AuthorizationCode)tokenRequest.AuthorizationDescription).VerifyCallback(request.Callback); - - return MessageProtections.None; - } - - return null; - } - } -} diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthorizationDataBag.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthorizationDataBag.cs index cee38db..a078d2f 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthorizationDataBag.cs +++ b/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/AuthorizationDataBag.cs @@ -13,7 +13,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// <summary> /// A data bag that stores authorization data. /// </summary> - internal abstract class AuthorizationDataBag : DataBag, IAuthorizationDescription { + public abstract class AuthorizationDataBag : DataBag, IAuthorizationDescription { /// <summary> /// Initializes a new instance of the <see cref="AuthorizationDataBag"/> class. /// </summary> diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/OAuth 2 Messages.cd b/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/OAuth 2 Messages.cd deleted file mode 100644 index 05e3ad9..0000000 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Messages/OAuth 2 Messages.cd +++ /dev/null @@ -1,164 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<ClassDiagram MajorVersion="1" MinorVersion="1"> - <Class Name="DotNetOpenAuth.OAuth2.Messages.MessageBase" Collapsed="true"> - <Position X="0.5" Y="0.5" Width="1.5" /> - <TypeIdentifier> - <HashCode>IAAMACQAQAAAgAkAAAAIAAYACgAAIAAAIACAACAAAIA=</HashCode> - <FileName>OAuth2\Messages\MessageBase.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessTokenAuthorizationCodeRequest" Collapsed="true"> - <Position X="8.5" Y="6.75" Width="3" /> - <TypeIdentifier> - <HashCode>ACAAEAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAgAAAATAA=</HashCode> - <FileName>OAuth2\Messages\AccessTokenAuthorizationCodeRequest.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessTokenClientCredentialsRequest" Collapsed="true"> - <Position X="8.5" Y="8.75" Width="2.75" /> - <TypeIdentifier> - <HashCode>AAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAA=</HashCode> - <FileName>OAuth2\Messages\AccessTokenClientCredentialsRequest.cs</FileName> - </TypeIdentifier> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessTokenFailedResponse" Collapsed="true"> - <Position X="3.25" Y="8.5" Width="2.25" /> - <TypeIdentifier> - <HashCode>AAAAAIAAAAAAAQAAAABAAAQAAAAAAAEQAAAAAAAAAAA=</HashCode> - <FileName>OAuth2\Messages\AccessTokenFailedResponse.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessTokenRefreshRequest" Collapsed="true"> - <Position X="8.5" Y="9.75" Width="2.25" /> - <TypeIdentifier> - <HashCode>AAAAEAAAQAAAAAAAAAAAAAAQAAAAAAAAAAAgAAAABAA=</HashCode> - <FileName>OAuth2\Messages\AccessTokenRefreshRequest.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessTokenRequestBase" Collapsed="true"> - <Position X="5.75" Y="5.75" Width="2" /> - <TypeIdentifier> - <HashCode>AAAAAAAAQABAAAAAAAAAAAAQAAAAAAAAAAAAAAAACAA=</HashCode> - <FileName>OAuth2\Messages\AccessTokenRequestBase.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessTokenResourceOwnerPasswordCredentialsRequest" Collapsed="true"> - <Position X="8.5" Y="10.5" Width="4" /> - <TypeIdentifier> - <HashCode>AAAAAAAAAAAAAAAAAAAAAAAQAAAAAAACAQAAAAAAAAA=</HashCode> - <FileName>OAuth2\Messages\AccessTokenResourceOwnerPasswordCredentialsRequest.cs</FileName> - </TypeIdentifier> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessTokenSuccessResponse" Collapsed="true"> - <Position X="3.25" Y="7.5" Width="2.25" /> - <TypeIdentifier> - <HashCode>AAAAAAAAQAAAACAAAAAAAAQAEAAAAAAQAEAAAAAAAgA=</HashCode> - <FileName>OAuth2\Messages\AccessTokenSuccessResponse.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.AuthenticatedClientRequestBase" Collapsed="true"> - <Position X="3.25" Y="5.25" Width="2.25" /> - <TypeIdentifier> - <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAA=</HashCode> - <FileName>OAuth2\Messages\AuthenticatedClientRequestBase.cs</FileName> - </TypeIdentifier> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.EndUserAuthorizationFailedResponse" Collapsed="true"> - <Position X="3.25" Y="4.5" Width="2.75" /> - <TypeIdentifier> - <HashCode>AAAAAIAAAAAAAQAAAAAAAAgAAAAAAAEAAAAAAAAAAAA=</HashCode> - <FileName>OAuth2\Messages\EndUserAuthorizationFailedResponse.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.EndUserAuthorizationRequest" Collapsed="true"> - <Position X="3.25" Y="0.5" Width="2.25" /> - <TypeIdentifier> - <HashCode>AAAAAAAAQABAACAAAAAAAACAAAQAAAQAAAAAAAAAQAA=</HashCode> - <FileName>OAuth2\Messages\EndUserAuthorizationRequest.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.EndUserAuthorizationSuccessAccessTokenResponse" Collapsed="true"> - <Position X="6.25" Y="3.75" Width="3.75" /> - <TypeIdentifier> - <HashCode>AAAAEAAAAAAAAAAAAAAAAAACEAAAAAAAAAAgAAAABgA=</HashCode> - <FileName>OAuth2\Messages\EndUserAuthorizationSuccessAccessTokenResponse.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.EndUserAuthorizationSuccessAuthCodeResponse" Collapsed="true"> - <Position X="6.25" Y="2.5" Width="3.5" /> - <TypeIdentifier> - <HashCode>ACAAEAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAgAAAABAA=</HashCode> - <FileName>OAuth2\Messages\EndUserAuthorizationSuccessAuthCodeResponse.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.EndUserAuthorizationSuccessResponseBase" Collapsed="true"> - <Position X="3.25" Y="1.5" Width="2.75" /> - <TypeIdentifier> - <HashCode>AAACAAAAAAAAACAAAAAAAAgAAAAAAAAAAEAAAAAAAAA=</HashCode> - <FileName>OAuth2\Messages\EndUserAuthorizationSuccessResponseBase.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.AccessProtectedResourceRequest" Collapsed="true"> - <Position X="3.25" Y="9.75" Width="2.5" /> - <TypeIdentifier> - <HashCode>AAAAEAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAgAAAABgA=</HashCode> - <FileName>OAuth2\Messages\AccessProtectedResourceRequest.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.UnauthorizedResponse" Collapsed="true"> - <Position X="3.25" Y="10.75" Width="2" /> - <TypeIdentifier> - <HashCode>AUABAAAAAAAAACAAAAAAAAQIAAAAAAAQAAAAAAAAABA=</HashCode> - <FileName>OAuth2\Messages\UnauthorizedResponse.cs</FileName> - </TypeIdentifier> - <Lollipop Position="0.2" /> - </Class> - <Class Name="DotNetOpenAuth.OAuth2.Messages.ScopedAccessTokenRequest" Collapsed="true"> - <Position X="6.75" Y="7.75" Width="2.25" /> - <TypeIdentifier> - <HashCode>AAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAACAA=</HashCode> - <FileName>OAuth2\Messages\ScopedAccessTokenRequest.cs</FileName> - </TypeIdentifier> - </Class> - <Interface Name="DotNetOpenAuth.OAuth2.Messages.IMessageWithClientState"> - <Position X="11.5" Y="0.5" Width="2" /> - <TypeIdentifier> - <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAA=</HashCode> - <FileName>OAuth2\Messages\IMessageWithClientState.cs</FileName> - </TypeIdentifier> - </Interface> - <Interface Name="DotNetOpenAuth.OAuth2.ChannelElements.IAuthorizationCarryingRequest"> - <Position X="11.75" Y="2" Width="2.5" /> - <TypeIdentifier> - <HashCode>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAEAAAAA=</HashCode> - <FileName>OAuth2\ChannelElements\IAuthorizationCarryingRequest.cs</FileName> - </TypeIdentifier> - </Interface> - <Enum Name="DotNetOpenAuth.OAuth2.Messages.EndUserAuthorizationResponseType"> - <Position X="8" Y="0.5" Width="3" /> - <TypeIdentifier> - <HashCode>ACAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAA=</HashCode> - <FileName>OAuth2\Messages\EndUserAuthorizationResponseType.cs</FileName> - </TypeIdentifier> - </Enum> - <Enum Name="DotNetOpenAuth.OAuth2.Messages.GrantType"> - <Position X="6.25" Y="0.5" Width="1.5" /> - <TypeIdentifier> - <HashCode>ACAAAAAAQAAAAAQAAgAAAAAAAAAAAAACAAAAAAAAAAA=</HashCode> - <FileName>OAuth2\Messages\GrantType.cs</FileName> - </TypeIdentifier> - </Enum> - <Font Name="Segoe UI" Size="9" /> -</ClassDiagram>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.Designer.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.Designer.cs index 6ce3b53..051d0d5 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.Designer.cs +++ b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:4.0.30319.239 +// Runtime Version:4.0.30319.17614 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -70,15 +70,6 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> - /// Looks up a localized string similar to The requested access scope ("{0}") exceeds the grant scope ("{1}").. - /// </summary> - internal static string AccessScopeExceedsGrantScope { - get { - return ResourceManager.GetString("AccessScopeExceedsGrantScope", resourceCulture); - } - } - - /// <summary> /// Looks up a localized string similar to The access token contains characters that must not appear in the HTTP Authorization header.. /// </summary> internal static string AccessTokenInvalidForHttpAuthorizationHeader { @@ -88,33 +79,6 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> - /// Looks up a localized string similar to Failed to obtain access token. Authorization Server reports reason: {0}. - /// </summary> - internal static string CannotObtainAccessTokenWithReason { - get { - return ResourceManager.GetString("CannotObtainAccessTokenWithReason", resourceCulture); - } - } - - /// <summary> - /// Looks up a localized string similar to The callback URL ({0}) is not allowed for this client.. - /// </summary> - internal static string ClientCallbackDisallowed { - get { - return ResourceManager.GetString("ClientCallbackDisallowed", resourceCulture); - } - } - - /// <summary> - /// Looks up a localized string similar to Failure looking up secret for client or token.. - /// </summary> - internal static string ClientOrTokenSecretNotFound { - get { - return ResourceManager.GetString("ClientOrTokenSecretNotFound", resourceCulture); - } - } - - /// <summary> /// Looks up a localized string similar to This message can only be sent over HTTPS.. /// </summary> internal static string HttpsRequired { @@ -124,15 +88,6 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> - /// Looks up a localized string similar to Failed to obtain access token due to invalid Client Identifier or Client Secret.. - /// </summary> - internal static string InvalidClientCredentials { - get { - return ResourceManager.GetString("InvalidClientCredentials", resourceCulture); - } - } - - /// <summary> /// Looks up a localized string similar to The scope token "{0}" contains illegal characters or is empty.. /// </summary> internal static string InvalidScopeToken { @@ -142,15 +97,6 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> - /// Looks up a localized string similar to No callback URI was available for this request.. - /// </summary> - internal static string NoCallback { - get { - return ResourceManager.GetString("NoCallback", resourceCulture); - } - } - - /// <summary> /// Looks up a localized string similar to Refresh tokens should not be granted without the request including an access grant.. /// </summary> internal static string NoGrantNoRefreshToken { @@ -160,15 +106,6 @@ namespace DotNetOpenAuth.OAuth2 { } /// <summary> - /// Looks up a localized string similar to The request message type {0} should not be responded to with a refresh token.. - /// </summary> - internal static string RefreshTokenInappropriateForRequestType { - get { - return ResourceManager.GetString("RefreshTokenInappropriateForRequestType", resourceCulture); - } - } - - /// <summary> /// Looks up a localized string similar to The return value of {0}.{1} should never be null.. /// </summary> internal static string ResultShouldNotBeNull { @@ -185,14 +122,5 @@ namespace DotNetOpenAuth.OAuth2 { return ResourceManager.GetString("ScopesMayNotContainSpaces", resourceCulture); } } - - /// <summary> - /// Looks up a localized string similar to Unexpected response Content-Type {0}. - /// </summary> - internal static string UnexpectedResponseContentType { - get { - return ResourceManager.GetString("UnexpectedResponseContentType", resourceCulture); - } - } } } diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.resx b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.resx index af1a955..4d9d248 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.resx +++ b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.resx @@ -120,46 +120,22 @@ <data name="AbsoluteUriRequired" xml:space="preserve"> <value>The value for message part "{0}" must be an absolute URI.</value> </data> - <data name="AccessScopeExceedsGrantScope" xml:space="preserve"> - <value>The requested access scope ("{0}") exceeds the grant scope ("{1}").</value> - </data> <data name="AccessTokenInvalidForHttpAuthorizationHeader" xml:space="preserve"> <value>The access token contains characters that must not appear in the HTTP Authorization header.</value> </data> - <data name="CannotObtainAccessTokenWithReason" xml:space="preserve"> - <value>Failed to obtain access token. Authorization Server reports reason: {0}</value> - </data> - <data name="ClientCallbackDisallowed" xml:space="preserve"> - <value>The callback URL ({0}) is not allowed for this client.</value> - </data> - <data name="ClientOrTokenSecretNotFound" xml:space="preserve"> - <value>Failure looking up secret for client or token.</value> - </data> <data name="HttpsRequired" xml:space="preserve"> <value>This message can only be sent over HTTPS.</value> </data> - <data name="InvalidClientCredentials" xml:space="preserve"> - <value>Failed to obtain access token due to invalid Client Identifier or Client Secret.</value> - </data> <data name="InvalidScopeToken" xml:space="preserve"> <value>The scope token "{0}" contains illegal characters or is empty.</value> </data> - <data name="NoCallback" xml:space="preserve"> - <value>No callback URI was available for this request.</value> - </data> <data name="NoGrantNoRefreshToken" xml:space="preserve"> <value>Refresh tokens should not be granted without the request including an access grant.</value> </data> - <data name="RefreshTokenInappropriateForRequestType" xml:space="preserve"> - <value>The request message type {0} should not be responded to with a refresh token.</value> - </data> <data name="ResultShouldNotBeNull" xml:space="preserve"> <value>The return value of {0}.{1} should never be null.</value> </data> <data name="ScopesMayNotContainSpaces" xml:space="preserve"> <value>Individual scopes may not contain spaces.</value> </data> - <data name="UnexpectedResponseContentType" xml:space="preserve"> - <value>Unexpected response Content-Type {0}</value> - </data> </root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs index dd7909b..eb5c8e4 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs +++ b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs @@ -129,26 +129,5 @@ namespace DotNetOpenAuth.OAuth2 { Protocol.BearerHttpAuthorizationHeaderFormat, accessToken); } - - /// <summary> - /// Gets information about the client with a given identifier. - /// </summary> - /// <param name="authorizationServer">The authorization server.</param> - /// <param name="clientIdentifier">The client identifier.</param> - /// <returns>The client information. Never null.</returns> - internal static IClientDescription GetClientOrThrow(this IAuthorizationServer authorizationServer, string clientIdentifier) { - Requires.NotNullOrEmpty(clientIdentifier, "clientIdentifier"); - Contract.Ensures(Contract.Result<IClientDescription>() != null); - - try { - var result = authorizationServer.GetClient(clientIdentifier); - ErrorUtilities.VerifyHost(result != null, OAuthStrings.ResultShouldNotBeNull, authorizationServer.GetType().FullName, "GetClient(string)"); - return result; - } catch (KeyNotFoundException ex) { - throw ErrorUtilities.Wrap(ex, OAuthStrings.ClientOrTokenSecretNotFound); - } catch (ArgumentException ex) { - throw ErrorUtilities.Wrap(ex, OAuthStrings.ClientOrTokenSecretNotFound); - } - } } } diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/Protocol.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/Protocol.cs index 19fe845..986af13 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/Protocol.cs +++ b/src/DotNetOpenAuth.OAuth2/OAuth2/Protocol.cs @@ -45,11 +45,6 @@ namespace DotNetOpenAuth.OAuth2 { internal const string BearerTokenEncodedUrlParameterName = "access_token"; /// <summary> - /// The "type" string. - /// </summary> - internal const string type = "type"; - - /// <summary> /// The "state" string. /// </summary> internal const string state = "state"; @@ -60,26 +55,6 @@ namespace DotNetOpenAuth.OAuth2 { internal const string redirect_uri_mismatch = "redirect_uri_mismatch"; /// <summary> - /// The "bad_verification_code" string. - /// </summary> - internal const string bad_verification_code = "bad_verification_code"; - - /// <summary> - /// The "incorrect_client_credentials" string. - /// </summary> - internal const string incorrect_client_credentials = "incorrect_client_credentials"; - - /// <summary> - /// The "unauthorized_client" string. - /// </summary> - internal const string unauthorized_client = "unauthorized_client"; - - /// <summary> - /// The "authorization_expired" string. - /// </summary> - internal const string authorization_expired = "authorization_expired"; - - /// <summary> /// The "redirect_uri" string. /// </summary> internal const string redirect_uri = "redirect_uri"; @@ -95,11 +70,6 @@ namespace DotNetOpenAuth.OAuth2 { internal const string scope = "scope"; /// <summary> - /// The "immediate" string. - /// </summary> - internal const string immediate = "immediate"; - - /// <summary> /// The "client_secret" string. /// </summary> internal const string client_secret = "client_secret"; @@ -110,21 +80,6 @@ namespace DotNetOpenAuth.OAuth2 { internal const string code = "code"; /// <summary> - /// The "user_code" string. - /// </summary> - internal const string user_code = "user_code"; - - /// <summary> - /// The "verification_uri" string. - /// </summary> - internal const string verification_uri = "verification_uri"; - - /// <summary> - /// The "interval" string. - /// </summary> - internal const string interval = "interval"; - - /// <summary> /// The "error" string. /// </summary> internal const string error = "error"; @@ -135,11 +90,6 @@ namespace DotNetOpenAuth.OAuth2 { internal const string access_token = "access_token"; /// <summary> - /// The "access_token_secret" string. - /// </summary> - internal const string access_token_secret = "access_token_secret"; - - /// <summary> /// The "token_type" string. /// </summary> internal const string token_type = "token_type"; @@ -155,11 +105,6 @@ namespace DotNetOpenAuth.OAuth2 { internal const string expires_in = "expires_in"; /// <summary> - /// The "expired_delegation_code" string. - /// </summary> - internal const string expired_delegation_code = "expired_delegation_code"; - - /// <summary> /// The "username" string. /// </summary> internal const string username = "username"; @@ -170,26 +115,6 @@ namespace DotNetOpenAuth.OAuth2 { internal const string password = "password"; /// <summary> - /// The "format" string. - /// </summary> - internal const string format = "format"; - - /// <summary> - /// The "assertion" string. - /// </summary> - internal const string assertion = "assertion"; - - /// <summary> - /// The "assertion_type" string. - /// </summary> - internal const string assertion_type = "assertion_type"; - - /// <summary> - /// The "user_denied" string. - /// </summary> - internal const string user_denied = "user_denied"; - - /// <summary> /// Gets the <see cref="Protocol"/> instance with values initialized for V1.0 of the protocol. /// </summary> internal static readonly Protocol V20 = new Protocol { @@ -286,27 +211,38 @@ namespace DotNetOpenAuth.OAuth2 { internal static class AccessTokenRequestErrorCodes { /// <summary> - /// The request is missing a required parameter, includes an unknown parameter or parameter value, repeats a parameter, includes multiple credentials, utilizes more than one mechanism for authenticating the client, or is otherwise malformed. + /// The request is missing a required parameter, includes an unknown parameter or parameter value, repeats a parameter, + /// includes multiple credentials, utilizes more than one mechanism for authenticating the client, or is otherwise malformed. /// </summary> internal const string InvalidRequest = "invalid_request"; /// <summary> - /// The client is not authorized to use the access grant type provided. + /// Client authentication failed (e.g. unknown client, no client authentication included, or unsupported authentication method). + /// The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported. + /// If the client attempted to authenticate via the Authorization request header field, the authorization server MUST respond with + /// an HTTP 401 (Unauthorized) status code, and include the WWW-Authenticate response header field matching the authentication scheme + /// used by the client. /// </summary> - internal const string UnauthorizedClient = "unauthorized_client"; + internal const string InvalidClient = "invalid_client"; /// <summary> - /// The resource owner or authorization server denied the request. + /// The provided authorization grant (e.g. authorization code, resource owner credentials) or refresh token is invalid, expired, + /// revoked, does not match the redirection URI used in the authorization request, or was issued to another client. /// </summary> - internal const string AccessDenied = "access_denied"; + internal const string InvalidGrant = "invalid_grant"; + + /// <summary> + /// The authenticated client is not authorized to use this authorization grant type. + /// </summary> + internal const string UnauthorizedClient = "unauthorized_client"; /// <summary> - /// The authorization server does not support obtaining an access token using this method. + /// The authorization grant type is not supported by the authorization server. /// </summary> - internal const string UnsupportedGrantType = "unsupported_response_type"; + internal const string UnsupportedGrantType = "unsupported_grant_type"; /// <summary> - /// The requested scope is invalid, unknown, malformed, or exceeds the previously granted scope. + /// The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the resource owner. /// </summary> internal const string InvalidScope = "invalid_scope"; } diff --git a/src/DotNetOpenAuth.OAuth2/Properties/AssemblyInfo.cs b/src/DotNetOpenAuth.OAuth2/Properties/AssemblyInfo.cs index 14fb526..26b1318 100644 --- a/src/DotNetOpenAuth.OAuth2/Properties/AssemblyInfo.cs +++ b/src/DotNetOpenAuth.OAuth2/Properties/AssemblyInfo.cs @@ -50,11 +50,13 @@ using System.Web.UI; [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.AuthorizationServer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ResourceServer, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ClientAuthorization, PublicKey=0024000004800000940000000602000000240000525341310004000001000100AD093C3765257C89A7010E853F2C7C741FF92FA8ACE06D7B8254702CAD5CF99104447F63AB05F8BB6F51CE0D81C8C93D2FCE8C20AAFF7042E721CBA16EAAE98778611DED11C0ABC8900DC5667F99B50A9DADEC24DBD8F2C91E3E8AD300EF64F1B4B9536CEB16FB440AF939F57624A9B486F867807C649AE4830EAB88C6C03998")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] #else [assembly: InternalsVisibleTo("DotNetOpenAuth.Test")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.AuthorizationServer")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ResourceServer")] [assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.Client")] +[assembly: InternalsVisibleTo("DotNetOpenAuth.OAuth2.ClientAuthorization")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] #endif diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Realm.cs b/src/DotNetOpenAuth.OpenId/OpenId/Realm.cs index 28e4df0..8f1baed 100644 --- a/src/DotNetOpenAuth.OpenId/OpenId/Realm.cs +++ b/src/DotNetOpenAuth.OpenId/OpenId/Realm.cs @@ -116,11 +116,7 @@ namespace DotNetOpenAuth.OpenId { Requires.ValidState(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); Contract.Ensures(Contract.Result<Realm>() != null); - HttpRequestBase requestInfo = new HttpRequestWrapper(HttpContext.Current.Request); - UriBuilder realmUrl = new UriBuilder(requestInfo.GetPublicFacingUrl()); - realmUrl.Path = HttpContext.Current.Request.ApplicationPath; - realmUrl.Query = null; - realmUrl.Fragment = null; + var realmUrl = new UriBuilder(MessagingUtilities.GetWebRoot()); // For RP discovery, the realm url MUST NOT redirect. To prevent this for // virtual directory hosted apps, we need to make sure that the realm path ends diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj index 00c1bb4..a3edcf6 100644 --- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj +++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj @@ -437,6 +437,10 @@ <Project>{ADC2CC8C-541E-4F86-ACB1-DD504A36FA4B}</Project> <Name>DotNetOpenAuth.OAuth2.Client.UI</Name> </ProjectReference> + <ProjectReference Include="..\DotNetOpenAuth.OAuth2.ClientAuthorization\DotNetOpenAuth.OAuth2.ClientAuthorization.csproj"> + <Project>{CCF3728A-B3D7-404A-9BC6-75197135F2D7}</Project> + <Name>DotNetOpenAuth.OAuth2.ClientAuthorization</Name> + </ProjectReference> <ProjectReference Include="..\DotNetOpenAuth.OAuth2.Client\DotNetOpenAuth.OAuth2.Client.csproj"> <Project>{CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}</Project> <Name>DotNetOpenAuth.OAuth2.Client</Name> diff --git a/src/DotNetOpenAuth.Test/OAuth2/AuthorizationServerTests.cs b/src/DotNetOpenAuth.Test/OAuth2/AuthorizationServerTests.cs index f3d8feb..3791e28 100644 --- a/src/DotNetOpenAuth.Test/OAuth2/AuthorizationServerTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth2/AuthorizationServerTests.cs @@ -28,7 +28,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { AuthorizationServerMock, new UserAgentClient(AuthorizationServerDescription), client => { - var request = new AccessTokenAuthorizationCodeRequest(AuthorizationServerDescription) + var request = new AccessTokenAuthorizationCodeRequestC(AuthorizationServerDescription) { ClientIdentifier = ClientId, ClientSecret = ClientSecret, AuthorizationCode = "foo" }; var response = client.Channel.Request<AccessTokenFailedResponse>(request); diff --git a/src/DotNetOpenAuth.Test/OAuth2/MessageFactoryTests.cs b/src/DotNetOpenAuth.Test/OAuth2/MessageFactoryTests.cs index bec85e2..b5ee767 100644 --- a/src/DotNetOpenAuth.Test/OAuth2/MessageFactoryTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth2/MessageFactoryTests.cs @@ -17,18 +17,22 @@ namespace DotNetOpenAuth.Test.OAuth2 { using NUnit.Framework; /// <summary> - /// Verifies that the WRAP message types are recognized. + /// Verifies that the OAuth 2 message types are recognized. /// </summary> public class MessageFactoryTests : OAuth2TestBase { private readonly MessageReceivingEndpoint recipient = new MessageReceivingEndpoint("http://who", HttpDeliveryMethods.PostRequest); - private OAuth2AuthorizationServerChannel channel; - private IMessageFactory messageFactory; + private IMessageFactory authServerMessageFactory; + + private IMessageFactory clientMessageFactory; public override void SetUp() { base.SetUp(); - this.channel = new OAuth2AuthorizationServerChannel(new Mock<IAuthorizationServer>().Object); - this.messageFactory = this.channel.MessageFactoryTestHook; + var authServerChannel = new OAuth2AuthorizationServerChannel(new Mock<IAuthorizationServer>().Object); + this.authServerMessageFactory = authServerChannel.MessageFactoryTestHook; + + var clientChannel = new OAuth2ClientChannel(); + this.clientMessageFactory = clientChannel.MessageFactoryTestHook; } #region End user authorization messages @@ -40,7 +44,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { { Protocol.client_id, "abc" }, { Protocol.redirect_uri, "abc" }, }; - IDirectedProtocolMessage request = this.messageFactory.GetNewRequestMessage(this.recipient, fields); + IDirectedProtocolMessage request = this.authServerMessageFactory.GetNewRequestMessage(this.recipient, fields); Assert.That(request, Is.InstanceOf(typeof(EndUserAuthorizationRequest))); } @@ -51,7 +55,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { { Protocol.client_id, "abc" }, { Protocol.redirect_uri, "abc" }, }; - IDirectedProtocolMessage request = this.messageFactory.GetNewRequestMessage(this.recipient, fields); + IDirectedProtocolMessage request = this.authServerMessageFactory.GetNewRequestMessage(this.recipient, fields); Assert.That(request, Is.InstanceOf(typeof(EndUserAuthorizationImplicitRequest))); } @@ -60,7 +64,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { var fields = new Dictionary<string, string> { { Protocol.code, "abc" }, }; - IDirectedProtocolMessage request = this.messageFactory.GetNewRequestMessage(this.recipient, fields); + IDirectedProtocolMessage request = this.clientMessageFactory.GetNewRequestMessage(this.recipient, fields); Assert.That(request, Is.InstanceOf(typeof(EndUserAuthorizationSuccessResponseBase))); } @@ -70,7 +74,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { { Protocol.access_token, "abc" }, { Protocol.token_type, "bearer" }, }; - IDirectedProtocolMessage request = this.messageFactory.GetNewRequestMessage(this.recipient, fields); + IDirectedProtocolMessage request = this.clientMessageFactory.GetNewRequestMessage(this.recipient, fields); Assert.That(request, Is.InstanceOf(typeof(EndUserAuthorizationSuccessResponseBase))); } @@ -79,7 +83,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { var fields = new Dictionary<string, string> { { Protocol.error, "access-denied" }, }; - IDirectedProtocolMessage request = this.messageFactory.GetNewRequestMessage(this.recipient, fields); + IDirectedProtocolMessage request = this.clientMessageFactory.GetNewRequestMessage(this.recipient, fields); Assert.That(request, Is.InstanceOf(typeof(EndUserAuthorizationFailedResponse))); } @@ -94,7 +98,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { { Protocol.refresh_token, "abc" }, { Protocol.grant_type, "refresh-token" }, }; - IDirectedProtocolMessage request = this.messageFactory.GetNewRequestMessage(this.recipient, fields); + IDirectedProtocolMessage request = this.authServerMessageFactory.GetNewRequestMessage(this.recipient, fields); Assert.That(request, Is.InstanceOf(typeof(AccessTokenRefreshRequest))); } @@ -106,7 +110,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { { Protocol.grant_type, "authorization-code" }, { Protocol.redirect_uri, "http://someUri" }, }; - IDirectedProtocolMessage request = this.messageFactory.GetNewRequestMessage(this.recipient, fields); + IDirectedProtocolMessage request = this.authServerMessageFactory.GetNewRequestMessage(this.recipient, fields); Assert.That(request, Is.InstanceOf(typeof(AccessTokenAuthorizationCodeRequest))); } @@ -119,7 +123,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { { Protocol.username, "abc" }, { Protocol.password, "abc" }, }; - IDirectedProtocolMessage request = this.messageFactory.GetNewRequestMessage(this.recipient, fields); + IDirectedProtocolMessage request = this.authServerMessageFactory.GetNewRequestMessage(this.recipient, fields); Assert.That(request, Is.InstanceOf(typeof(AccessTokenResourceOwnerPasswordCredentialsRequest))); } @@ -130,7 +134,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { { Protocol.client_secret, "abc" }, { Protocol.grant_type, "none" }, }; - IDirectedProtocolMessage request = this.messageFactory.GetNewRequestMessage(this.recipient, fields); + IDirectedProtocolMessage request = this.authServerMessageFactory.GetNewRequestMessage(this.recipient, fields); Assert.That(request, Is.InstanceOf(typeof(AccessTokenClientCredentialsRequest))); } diff --git a/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs b/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs index 87d91f7..ed6ce70 100644 --- a/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs +++ b/src/DotNetOpenAuth.Test/OAuth2/OAuth2TestBase.cs @@ -13,6 +13,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { using DotNetOpenAuth.Messaging.Bindings; using DotNetOpenAuth.OAuth2; using DotNetOpenAuth.OAuth2.ChannelElements; + using DotNetOpenAuth.OAuth2.Messages; using Moq; public class OAuth2TestBase : TestBase { @@ -53,6 +54,7 @@ namespace DotNetOpenAuth.Test.OAuth2 { d.ClientIdentifier == ClientId && d.User == ResourceOwnerUsername && MessagingUtilities.AreEquivalent(d.Scope, TestScopes)))).Returns(true); authHostMock.Setup(m => m.IsResourceOwnerCredentialValid(ResourceOwnerUsername, ResourceOwnerPassword)).Returns(true); + authHostMock.Setup(m => m.GetAccessTokenParameters(It.IsAny<IAccessTokenRequest>())).Returns(new AccessTokenParameters()); return authHostMock; } } diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/AnonymousRequestTests.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/AnonymousRequestTests.cs index 9b39522..7310eb3 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Provider/AnonymousRequestTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Provider/AnonymousRequestTests.cs @@ -5,6 +5,9 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.Test.OpenId.Provider { + using System.IO; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters.Binary; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; using DotNetOpenAuth.OpenId.Provider; @@ -33,5 +36,25 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { Assert.IsInstanceOf<IndirectSignedResponse>(anonReq.Response); Assert.IsNotInstanceOf<PositiveAssertionResponse>(anonReq.Response); } + + /// <summary> + /// Verifies that the AuthenticationRequest method is serializable. + /// </summary> + [Test] + public void Serializable() { + var op = CreateProvider(); + Protocol protocol = Protocol.V20; + var req = new SignedResponseRequest(protocol.Version, OPUri, AuthenticationRequestMode.Setup); + req.ReturnTo = RPUri; + var anonReq = new AnonymousRequest(op, req); + + MemoryStream ms = new MemoryStream(); + IFormatter formatter = new BinaryFormatter(); + formatter.Serialize(ms, anonReq); + + ms.Position = 0; + var req2 = (AnonymousRequest)formatter.Deserialize(ms); + Assert.That(req2, Is.Not.Null); + } } } diff --git a/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs b/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs index 8cc7116..baf5377 100644 --- a/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs +++ b/src/DotNetOpenAuth.Test/OpenId/Provider/AuthenticationRequestTest.cs @@ -6,6 +6,9 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { using System; + using System.IO; + using System.Runtime.Serialization; + using System.Runtime.Serialization.Formatters.Binary; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OpenId; using DotNetOpenAuth.OpenId.Messages; @@ -45,5 +48,26 @@ namespace DotNetOpenAuth.Test.OpenId.Provider { Assert.AreEqual(immediateRequest.LocalIdentifier, setupRequestMessage.LocalIdentifier); Assert.AreEqual(immediateRequest.Version, setupRequestMessage.Version); } + + /// <summary> + /// Verifies that the AuthenticationRequest method is serializable. + /// </summary> + [Test] + public void Serializable() { + OpenIdProvider provider = this.CreateProvider(); + CheckIdRequest immediateRequest = new CheckIdRequest(Protocol.Default.Version, OPUri, DotNetOpenAuth.OpenId.AuthenticationRequestMode.Immediate); + immediateRequest.Realm = RPRealmUri; + immediateRequest.ReturnTo = RPUri; + immediateRequest.LocalIdentifier = "http://somebody"; + AuthenticationRequest request = new AuthenticationRequest(provider, immediateRequest); + + MemoryStream ms = new MemoryStream(); + IFormatter formatter = new BinaryFormatter(); + formatter.Serialize(ms, request); + + ms.Position = 0; + var req2 = (AuthenticationRequest)formatter.Deserialize(ms); + Assert.That(req2, Is.Not.Null); + } } } diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln index e0f4ebf..f7364d7 100644 --- a/src/DotNetOpenAuth.sln +++ b/src/DotNetOpenAuth.sln @@ -1,25 +1,25 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 11 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{20B5E173-C3C4-49F8-BD25-E69044075B4D}" ProjectSection(SolutionItems) = preProject - ..\build.proj = ..\build.proj - ..\projecttemplates\DotNetOpenAuth Starter Kits.vscontent = ..\projecttemplates\DotNetOpenAuth Starter Kits.vscontent ..\LICENSE.txt = ..\LICENSE.txt + ..\build.proj = ..\build.proj ..\doc\README.Bin.html = ..\doc\README.Bin.html ..\doc\README.html = ..\doc\README.html + ..\projecttemplates\DotNetOpenAuth Starter Kits.vscontent = ..\projecttemplates\DotNetOpenAuth Starter Kits.vscontent ..\samples\README.html = ..\samples\README.html EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{CD57219F-24F4-4136-8741-6063D0D7A031}" ProjectSection(SolutionItems) = preProject - ..\doc\specs\draft-ietf-oauth-v2-23.txt = ..\doc\specs\draft-ietf-oauth-v2-23.txt - ..\doc\specs\draft-ietf-oauth-v2-bearer.htm = ..\doc\specs\draft-ietf-oauth-v2-bearer.htm - ..\doc\specs\draft-jones-json-web-token.htm = ..\doc\specs\draft-jones-json-web-token.htm ..\doc\specs\ICAM_OpenID20Profile.pdf = ..\doc\specs\ICAM_OpenID20Profile.pdf ..\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\draft-ietf-oauth-v2-23.txt = ..\doc\specs\draft-ietf-oauth-v2-23.txt + ..\doc\specs\draft-ietf-oauth-v2-bearer.htm = ..\doc\specs\draft-ietf-oauth-v2-bearer.htm + ..\doc\specs\draft-jones-json-web-token.htm = ..\doc\specs\draft-jones-json-web-token.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 ..\doc\specs\openid-authentication-2_0.html = ..\doc\specs\openid-authentication-2_0.html @@ -40,31 +40,45 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{E9ED920D EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Project Templates", "Project Templates", "{B9EB8729-4B54-4453-B089-FE6761BA3057}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Product", "Product", "{8D4236F7-C49B-49D3-BA71-6B86C9514BDE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OpenID", "OpenID", "{C7EF1823-3AA7-477E-8476-28929F5C05D2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OAuth", "OAuth", "{9AF74F53-10F5-49A2-B747-87B97CD559D3}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InfoCard", "InfoCard", "{529B4262-6B5A-4EF9-BD3B-1D29A2597B67}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OAuth2", "OAuth2", "{238B6BA8-AD99-43C9-B8E2-D2BCE6CE04DC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Combinations", "Combinations", "{57A7DD35-666C-4FA3-9A1B-38961E50CA27}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OAuth1", "OAuth1", "{2DA24D4F-6918-43CF-973C-BC9D818F8E90}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.Test", "DotNetOpenAuth.Test\DotNetOpenAuth.Test.csproj", "{4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.ApplicationBlock", "..\samples\DotNetOpenAuth.ApplicationBlock\DotNetOpenAuth.ApplicationBlock.csproj", "{AA78D112-D889-414B-A7D4-467B34C7B663}" EndProject Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "DotNetOpenAuth.TestWeb", "DotNetOpenAuth.TestWeb\", "{47A84EF7-68C3-4D47-926A-9CCEA6518531}" ProjectSection(WebsiteProperties) = preProject - TargetFrameworkMoniker = ".NETFramework,Version%3Dv3.5" - ProjectReferences = "{4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}|DotNetOpenAuth.Test.dll;{408D10B8-34BA-4CBD-B7AA-FEB1907ABA4C}|DotNetOpenAuth.InfoCard.dll;{60426312-6AE5-4835-8667-37EDEA670222}|DotNetOpenAuth.Core.dll;{A288FCC8-6FCF-46DA-A45E-5F9281556361}|DotNetOpenAuth.OAuth.dll;{3896A32A-E876-4C23-B9B8-78E17D134CD3}|DotNetOpenAuth.OpenId.dll;{56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}|DotNetOpenAuth.OAuth2.dll;{26DC877F-5987-48DD-9DDB-E62F2DE0E150}|Org.Mentalis.Security.Cryptography.dll;{F4CD3C04-6037-4946-B7A5-34BFC96A75D2}|Mono.Math.dll;{173E7B8D-E751-46E2-A133-F72297C0D2F4}|DotNetOpenAuth.Core.UI.dll;{E040EB58-B4D2-457B-A023-AE6EF3BD34DE}|DotNetOpenAuth.InfoCard.UI.dll;{B202E40D-4663-4A2B-ACDA-865F88FF7CAA}|DotNetOpenAuth.OAuth.Consumer.dll;{FED1923A-6D70-49B5-A37A-FB744FEC1C86}|DotNetOpenAuth.OAuth.ServiceProvider.dll;{99BB7543-EA16-43EE-A7BC-D7A25A3B22F6}|DotNetOpenAuth.OAuth2.AuthorizationServer.dll;{CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}|DotNetOpenAuth.OAuth2.Client.dll;{ADC2CC8C-541E-4F86-ACB1-DD504A36FA4B}|DotNetOpenAuth.OAuth2.Client.UI.dll;{A1A3150A-7B0E-4A34-8E35-045296CD3C76}|DotNetOpenAuth.OAuth2.ResourceServer.dll;{F8284738-3B5D-4733-A511-38C23F4A763F}|DotNetOpenAuth.OpenId.Provider.dll;{F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}|DotNetOpenAuth.OpenId.RelyingParty.dll;{9D0F8866-2131-4C2A-BC0E-16FEA5B50828}|DotNetOpenAuth.OpenId.Provider.UI.dll;{75E13AAE-7D51-4421-ABFD-3F3DC91F576E}|DotNetOpenAuth.OpenId.UI.dll;{1ED8D424-F8AB-4050-ACEB-F27F4F909484}|DotNetOpenAuth.OpenId.RelyingParty.UI.dll;{115217C5-22CD-415C-A292-0DD0238CDD89}|DotNetOpenAuth.OAuth.Common.dll;" - Debug.AspNetCompiler.VirtualPath = "/DotNetOpenAuth.TestWeb" + Debug.AspNetCompiler.Debug = "True" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.ForceOverwrite = "true" Debug.AspNetCompiler.PhysicalPath = "DotNetOpenAuth.TestWeb\" Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\DotNetOpenAuth.TestWeb\" Debug.AspNetCompiler.Updateable = "false" - Debug.AspNetCompiler.ForceOverwrite = "true" - Debug.AspNetCompiler.FixedNames = "false" - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.VirtualPath = "/DotNetOpenAuth.TestWeb" + Debug.AspNetCompiler.VirtualPath = "/DotNetOpenAuth.TestWeb" + DefaultWebSiteLanguage = "Visual C#" + ProjectReferences = "{4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}|DotNetOpenAuth.Test.dll;{408D10B8-34BA-4CBD-B7AA-FEB1907ABA4C}|DotNetOpenAuth.InfoCard.dll;{60426312-6AE5-4835-8667-37EDEA670222}|DotNetOpenAuth.Core.dll;{A288FCC8-6FCF-46DA-A45E-5F9281556361}|DotNetOpenAuth.OAuth.dll;{3896A32A-E876-4C23-B9B8-78E17D134CD3}|DotNetOpenAuth.OpenId.dll;{56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}|DotNetOpenAuth.OAuth2.dll;{26DC877F-5987-48DD-9DDB-E62F2DE0E150}|Org.Mentalis.Security.Cryptography.dll;{F4CD3C04-6037-4946-B7A5-34BFC96A75D2}|Mono.Math.dll;{173E7B8D-E751-46E2-A133-F72297C0D2F4}|DotNetOpenAuth.Core.UI.dll;{E040EB58-B4D2-457B-A023-AE6EF3BD34DE}|DotNetOpenAuth.InfoCard.UI.dll;{B202E40D-4663-4A2B-ACDA-865F88FF7CAA}|DotNetOpenAuth.OAuth.Consumer.dll;{FED1923A-6D70-49B5-A37A-FB744FEC1C86}|DotNetOpenAuth.OAuth.ServiceProvider.dll;{99BB7543-EA16-43EE-A7BC-D7A25A3B22F6}|DotNetOpenAuth.OAuth2.AuthorizationServer.dll;{CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}|DotNetOpenAuth.OAuth2.Client.dll;{ADC2CC8C-541E-4F86-ACB1-DD504A36FA4B}|DotNetOpenAuth.OAuth2.Client.UI.dll;{A1A3150A-7B0E-4A34-8E35-045296CD3C76}|DotNetOpenAuth.OAuth2.ResourceServer.dll;{F8284738-3B5D-4733-A511-38C23F4A763F}|DotNetOpenAuth.OpenId.Provider.dll;{F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}|DotNetOpenAuth.OpenId.RelyingParty.dll;{9D0F8866-2131-4C2A-BC0E-16FEA5B50828}|DotNetOpenAuth.OpenId.Provider.UI.dll;{75E13AAE-7D51-4421-ABFD-3F3DC91F576E}|DotNetOpenAuth.OpenId.UI.dll;{1ED8D424-F8AB-4050-ACEB-F27F4F909484}|DotNetOpenAuth.OpenId.RelyingParty.UI.dll;{115217C5-22CD-415C-A292-0DD0238CDD89}|DotNetOpenAuth.OAuth.Common.dll;{CCF3728A-B3D7-404A-9BC6-75197135F2D7}|DotNetOpenAuth.OAuth2.ClientAuthorization.dll;" + Release.AspNetCompiler.Debug = "False" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.ForceOverwrite = "true" Release.AspNetCompiler.PhysicalPath = "DotNetOpenAuth.TestWeb\" Release.AspNetCompiler.TargetPath = "PrecompiledWeb\DotNetOpenAuth.TestWeb\" Release.AspNetCompiler.Updateable = "false" - Release.AspNetCompiler.ForceOverwrite = "true" - Release.AspNetCompiler.FixedNames = "false" - Release.AspNetCompiler.Debug = "False" - VWDPort = "5073" - DefaultWebSiteLanguage = "Visual C#" + Release.AspNetCompiler.VirtualPath = "/DotNetOpenAuth.TestWeb" StartServerOnDebug = "false" + TargetFrameworkMoniker = ".NETFramework,Version%3Dv3.5" + VWDPort = "5073" EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdProviderWebForms", "..\samples\OpenIdProviderWebForms\OpenIdProviderWebForms.csproj", "{2A59DE0A-B76A-4B42-9A33-04D34548353D}" @@ -73,22 +87,24 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdProviderMvc", "..\sam EndProject Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "InfoCardRelyingParty", "..\samples\InfoCardRelyingParty\", "{6EB90284-BD15-461C-BBF2-131CF55F7C8B}" ProjectSection(WebsiteProperties) = preProject - TargetFrameworkMoniker = ".NETFramework,Version%3Dv3.5" - ProjectReferences = "{408D10B8-34BA-4CBD-B7AA-FEB1907ABA4C}|DotNetOpenAuth.InfoCard.dll;{60426312-6AE5-4835-8667-37EDEA670222}|DotNetOpenAuth.Core.dll;{173E7B8D-E751-46E2-A133-F72297C0D2F4}|DotNetOpenAuth.Core.UI.dll;{26DC877F-5987-48DD-9DDB-E62F2DE0E150}|Org.Mentalis.Security.Cryptography.dll;{F4CD3C04-6037-4946-B7A5-34BFC96A75D2}|Mono.Math.dll;{E040EB58-B4D2-457B-A023-AE6EF3BD34DE}|DotNetOpenAuth.InfoCard.UI.dll;" - Debug.AspNetCompiler.VirtualPath = "/InfoCardRelyingParty" + Debug.AspNetCompiler.Debug = "True" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.ForceOverwrite = "true" Debug.AspNetCompiler.PhysicalPath = "..\samples\InfoCardRelyingParty\" Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\InfoCardRelyingParty\" Debug.AspNetCompiler.Updateable = "true" - Debug.AspNetCompiler.ForceOverwrite = "true" - Debug.AspNetCompiler.FixedNames = "false" - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.VirtualPath = "/InfoCardRelyingParty" + Debug.AspNetCompiler.VirtualPath = "/InfoCardRelyingParty" + DefaultWebSiteLanguage = "Visual Basic" + ProjectReferences = "{408D10B8-34BA-4CBD-B7AA-FEB1907ABA4C}|DotNetOpenAuth.InfoCard.dll;{60426312-6AE5-4835-8667-37EDEA670222}|DotNetOpenAuth.Core.dll;{173E7B8D-E751-46E2-A133-F72297C0D2F4}|DotNetOpenAuth.Core.UI.dll;{E040EB58-B4D2-457B-A023-AE6EF3BD34DE}|DotNetOpenAuth.InfoCard.UI.dll;" + Release.AspNetCompiler.Debug = "False" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.ForceOverwrite = "true" Release.AspNetCompiler.PhysicalPath = "..\samples\InfoCardRelyingParty\" Release.AspNetCompiler.TargetPath = "PrecompiledWeb\InfoCardRelyingParty\" Release.AspNetCompiler.Updateable = "true" - Release.AspNetCompiler.ForceOverwrite = "true" - Release.AspNetCompiler.FixedNames = "false" - Release.AspNetCompiler.Debug = "False" + Release.AspNetCompiler.VirtualPath = "/InfoCardRelyingParty" + StartServerOnDebug = "false" + TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0" VWDPort = "59719" EndProjectSection EndProject @@ -98,21 +114,22 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenIdRelyingPartyWebForms" EndProject Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "OpenIdRelyingPartyClassicAsp", "..\samples\OpenIdRelyingPartyClassicAsp\", "{BBACD972-014D-478F-9B07-56B9E1D4CC73}" ProjectSection(WebsiteProperties) = preProject - TargetFrameworkMoniker = ".NETFramework,Version%3Dv3.5" - Debug.AspNetCompiler.VirtualPath = "/OpenIdRelyingPartyClassicAsp" + Debug.AspNetCompiler.Debug = "True" + Debug.AspNetCompiler.FixedNames = "false" + Debug.AspNetCompiler.ForceOverwrite = "true" Debug.AspNetCompiler.PhysicalPath = "..\samples\OpenIdRelyingPartyClassicAsp\" Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\OpenIdRelyingPartyClassicAsp\" Debug.AspNetCompiler.Updateable = "true" - Debug.AspNetCompiler.ForceOverwrite = "true" - Debug.AspNetCompiler.FixedNames = "false" - Debug.AspNetCompiler.Debug = "True" - Release.AspNetCompiler.VirtualPath = "/OpenIdRelyingPartyClassicAsp" + Debug.AspNetCompiler.VirtualPath = "/OpenIdRelyingPartyClassicAsp" + Release.AspNetCompiler.Debug = "False" + Release.AspNetCompiler.FixedNames = "false" + Release.AspNetCompiler.ForceOverwrite = "true" Release.AspNetCompiler.PhysicalPath = "..\samples\OpenIdRelyingPartyClassicAsp\" Release.AspNetCompiler.TargetPath = "PrecompiledWeb\OpenIdRelyingPartyClassicAsp\" Release.AspNetCompiler.Updateable = "true" - Release.AspNetCompiler.ForceOverwrite = "true" - Release.AspNetCompiler.FixedNames = "false" - Release.AspNetCompiler.Debug = "False" + Release.AspNetCompiler.VirtualPath = "/OpenIdRelyingPartyClassicAsp" + StartServerOnDebug = "false" + TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0" VWDPort = "10318" EndProjectSection EndProject @@ -124,10 +141,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebFormsRelyingParty", "..\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RelyingPartyLogic", "..\projecttemplates\RelyingPartyLogic\RelyingPartyLogic.csproj", "{17932639-1F50-48AF-B0A5-E2BF832F82CC}" ProjectSection(ProjectDependencies) = postProject - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D} = {2B4261AC-25AC-4B8D-B459-1C42B6B1401D} + {08A938B6-EBBD-4036-880E-CE7BA2D14510} = {08A938B6-EBBD-4036-880E-CE7BA2D14510} EndProjectSection EndProject -Project("{C8D11400-126E-41CD-887F-60BD40844F9E}") = "RelyingPartyDatabase", "..\projecttemplates\RelyingPartyDatabase\RelyingPartyDatabase.dbproj", "{2B4261AC-25AC-4B8D-B459-1C42B6B1401D}" +Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "RelyingPartyDatabase", "..\projecttemplates\RelyingPartyDatabase\RelyingPartyDatabase.sqlproj", "{08A938B6-EBBD-4036-880E-CE7BA2D14510}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MvcRelyingParty", "..\projecttemplates\MvcRelyingParty\MvcRelyingParty.csproj", "{152B7BAB-E884-4A59-8067-440971A682B3}" EndProject @@ -172,18 +189,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.OpenId.UI", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.Core.UI", "DotNetOpenAuth.Core.UI\DotNetOpenAuth.Core.UI.csproj", "{173E7B8D-E751-46E2-A133-F72297C0D2F4}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Product", "Product", "{8D4236F7-C49B-49D3-BA71-6B86C9514BDE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OpenID", "OpenID", "{C7EF1823-3AA7-477E-8476-28929F5C05D2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OAuth", "OAuth", "{9AF74F53-10F5-49A2-B747-87B97CD559D3}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "InfoCard", "InfoCard", "{529B4262-6B5A-4EF9-BD3B-1D29A2597B67}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.InfoCard.UI", "DotNetOpenAuth.InfoCard.UI\DotNetOpenAuth.InfoCard.UI.csproj", "{E040EB58-B4D2-457B-A023-AE6EF3BD34DE}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OAuth2", "OAuth2", "{238B6BA8-AD99-43C9-B8E2-D2BCE6CE04DC}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.OAuth.Consumer", "DotNetOpenAuth.OAuth.Consumer\DotNetOpenAuth.OAuth.Consumer.csproj", "{B202E40D-4663-4A2B-ACDA-865F88FF7CAA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.OAuth.ServiceProvider", "DotNetOpenAuth.OAuth.ServiceProvider\DotNetOpenAuth.OAuth.ServiceProvider.csproj", "{FED1923A-6D70-49B5-A37A-FB744FEC1C86}" @@ -196,12 +203,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.OAuth2.Resou EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.OAuth2.Client.UI", "DotNetOpenAuth.OAuth2.Client.UI\DotNetOpenAuth.OAuth2.Client.UI.csproj", "{ADC2CC8C-541E-4F86-ACB1-DD504A36FA4B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Combinations", "Combinations", "{57A7DD35-666C-4FA3-9A1B-38961E50CA27}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.OpenIdInfoCard.UI", "DotNetOpenAuth.OpenIdInfoCard.UI\DotNetOpenAuth.OpenIdInfoCard.UI.csproj", "{3A8347E8-59A5-4092-8842-95C75D7D2F36}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OAuth1", "OAuth1", "{2DA24D4F-6918-43CF-973C-BC9D818F8E90}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OAuthConsumer", "..\samples\OAuthConsumer\OAuthConsumer.csproj", "{2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OAuthServiceProvider", "..\samples\OAuthServiceProvider\OAuthServiceProvider.csproj", "{CAA2408C-6918-4902-A512-58BCD62216C3}" @@ -214,6 +217,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.AspNet.Test" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.OAuth.Common", "DotNetOpenAuth.OAuth.Common\DotNetOpenAuth.OAuth.Common.csproj", "{115217C5-22CD-415C-A292-0DD0238CDD89}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DotNetOpenAuth.OAuth2.ClientAuthorization", "DotNetOpenAuth.OAuth2.ClientAuthorization\DotNetOpenAuth.OAuth2.ClientAuthorization.csproj", "{CCF3728A-B3D7-404A-9BC6-75197135F2D7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution CodeAnalysis|Any CPU = CodeAnalysis|Any CPU @@ -221,356 +226,343 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.Release|Any CPU.Build.0 = Release|Any CPU - {AA78D112-D889-414B-A7D4-467B34C7B663}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {AA78D112-D889-414B-A7D4-467B34C7B663}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {AA78D112-D889-414B-A7D4-467B34C7B663}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AA78D112-D889-414B-A7D4-467B34C7B663}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AA78D112-D889-414B-A7D4-467B34C7B663}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AA78D112-D889-414B-A7D4-467B34C7B663}.Release|Any CPU.Build.0 = Release|Any CPU - {47A84EF7-68C3-4D47-926A-9CCEA6518531}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {47A84EF7-68C3-4D47-926A-9CCEA6518531}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU - {47A84EF7-68C3-4D47-926A-9CCEA6518531}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {47A84EF7-68C3-4D47-926A-9CCEA6518531}.Debug|Any CPU.Build.0 = Debug|Any CPU - {47A84EF7-68C3-4D47-926A-9CCEA6518531}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {47A84EF7-68C3-4D47-926A-9CCEA6518531}.Release|Any CPU.Build.0 = Debug|Any CPU - {2A59DE0A-B76A-4B42-9A33-04D34548353D}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {2A59DE0A-B76A-4B42-9A33-04D34548353D}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {2A59DE0A-B76A-4B42-9A33-04D34548353D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2A59DE0A-B76A-4B42-9A33-04D34548353D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2A59DE0A-B76A-4B42-9A33-04D34548353D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2A59DE0A-B76A-4B42-9A33-04D34548353D}.Release|Any CPU.Build.0 = Release|Any CPU - {AEA29D4D-396F-47F6-BC81-B58D4B855245}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {AEA29D4D-396F-47F6-BC81-B58D4B855245}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU - {AEA29D4D-396F-47F6-BC81-B58D4B855245}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AEA29D4D-396F-47F6-BC81-B58D4B855245}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AEA29D4D-396F-47F6-BC81-B58D4B855245}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AEA29D4D-396F-47F6-BC81-B58D4B855245}.Release|Any CPU.Build.0 = Release|Any CPU - {6EB90284-BD15-461C-BBF2-131CF55F7C8B}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {6EB90284-BD15-461C-BBF2-131CF55F7C8B}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU - {6EB90284-BD15-461C-BBF2-131CF55F7C8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6EB90284-BD15-461C-BBF2-131CF55F7C8B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6EB90284-BD15-461C-BBF2-131CF55F7C8B}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {6EB90284-BD15-461C-BBF2-131CF55F7C8B}.Release|Any CPU.Build.0 = Debug|Any CPU - {07B193F1-68AD-4E9C-98AF-BEFB5E9403CB}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {07B193F1-68AD-4E9C-98AF-BEFB5E9403CB}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU + {07B193F1-68AD-4E9C-98AF-BEFB5E9403CB}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU {07B193F1-68AD-4E9C-98AF-BEFB5E9403CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {07B193F1-68AD-4E9C-98AF-BEFB5E9403CB}.Debug|Any CPU.Build.0 = Debug|Any CPU {07B193F1-68AD-4E9C-98AF-BEFB5E9403CB}.Release|Any CPU.ActiveCfg = Release|Any CPU {07B193F1-68AD-4E9C-98AF-BEFB5E9403CB}.Release|Any CPU.Build.0 = Release|Any CPU + {08A938B6-EBBD-4036-880E-CE7BA2D14510}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {08A938B6-EBBD-4036-880E-CE7BA2D14510}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08A938B6-EBBD-4036-880E-CE7BA2D14510}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08A938B6-EBBD-4036-880E-CE7BA2D14510}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {08A938B6-EBBD-4036-880E-CE7BA2D14510}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08A938B6-EBBD-4036-880E-CE7BA2D14510}.Release|Any CPU.Build.0 = Release|Any CPU + {08A938B6-EBBD-4036-880E-CE7BA2D14510}.Release|Any CPU.Deploy.0 = Release|Any CPU + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.Build.0 = Release|Any CPU + {115217C5-22CD-415C-A292-0DD0238CDD89}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {115217C5-22CD-415C-A292-0DD0238CDD89}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {115217C5-22CD-415C-A292-0DD0238CDD89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {115217C5-22CD-415C-A292-0DD0238CDD89}.Debug|Any CPU.Build.0 = Debug|Any CPU + {115217C5-22CD-415C-A292-0DD0238CDD89}.Release|Any CPU.ActiveCfg = Release|Any CPU + {115217C5-22CD-415C-A292-0DD0238CDD89}.Release|Any CPU.Build.0 = Release|Any CPU + {152B7BAB-E884-4A59-8067-440971A682B3}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {152B7BAB-E884-4A59-8067-440971A682B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {152B7BAB-E884-4A59-8067-440971A682B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {152B7BAB-E884-4A59-8067-440971A682B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {152B7BAB-E884-4A59-8067-440971A682B3}.Release|Any CPU.Build.0 = Release|Any CPU + {173E7B8D-E751-46E2-A133-F72297C0D2F4}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {173E7B8D-E751-46E2-A133-F72297C0D2F4}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {173E7B8D-E751-46E2-A133-F72297C0D2F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {173E7B8D-E751-46E2-A133-F72297C0D2F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {173E7B8D-E751-46E2-A133-F72297C0D2F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {173E7B8D-E751-46E2-A133-F72297C0D2F4}.Release|Any CPU.Build.0 = Release|Any CPU + {17932639-1F50-48AF-B0A5-E2BF832F82CC}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Release|Any CPU.Build.0 = Release|Any CPU {1E8AEA89-BF69-47A1-B290-E8B0FE588700}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {1E8AEA89-BF69-47A1-B290-E8B0FE588700}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {1E8AEA89-BF69-47A1-B290-E8B0FE588700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1E8AEA89-BF69-47A1-B290-E8B0FE588700}.Debug|Any CPU.Build.0 = Debug|Any CPU {1E8AEA89-BF69-47A1-B290-E8B0FE588700}.Release|Any CPU.ActiveCfg = Release|Any CPU {1E8AEA89-BF69-47A1-B290-E8B0FE588700}.Release|Any CPU.Build.0 = Release|Any CPU - {BBACD972-014D-478F-9B07-56B9E1D4CC73}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {BBACD972-014D-478F-9B07-56B9E1D4CC73}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU - {BBACD972-014D-478F-9B07-56B9E1D4CC73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BBACD972-014D-478F-9B07-56B9E1D4CC73}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BBACD972-014D-478F-9B07-56B9E1D4CC73}.Release|Any CPU.ActiveCfg = Debug|Any CPU - {BBACD972-014D-478F-9B07-56B9E1D4CC73}.Release|Any CPU.Build.0 = Debug|Any CPU - {6EC36418-DBC5-4AD1-A402-413604AA7A08}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {6EC36418-DBC5-4AD1-A402-413604AA7A08}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {6EC36418-DBC5-4AD1-A402-413604AA7A08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6EC36418-DBC5-4AD1-A402-413604AA7A08}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6EC36418-DBC5-4AD1-A402-413604AA7A08}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6EC36418-DBC5-4AD1-A402-413604AA7A08}.Release|Any CPU.Build.0 = Release|Any CPU - {5C65603B-235F-47E6-B536-06385C60DE7F}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {5C65603B-235F-47E6-B536-06385C60DE7F}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU - {5C65603B-235F-47E6-B536-06385C60DE7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5C65603B-235F-47E6-B536-06385C60DE7F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5C65603B-235F-47E6-B536-06385C60DE7F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5C65603B-235F-47E6-B536-06385C60DE7F}.Release|Any CPU.Build.0 = Release|Any CPU - {A78F8FC6-7B03-4230-BE41-761E400D6810}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {A78F8FC6-7B03-4230-BE41-761E400D6810}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU - {A78F8FC6-7B03-4230-BE41-761E400D6810}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A78F8FC6-7B03-4230-BE41-761E400D6810}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A78F8FC6-7B03-4230-BE41-761E400D6810}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A78F8FC6-7B03-4230-BE41-761E400D6810}.Release|Any CPU.Build.0 = Release|Any CPU - {17932639-1F50-48AF-B0A5-E2BF832F82CC}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {17932639-1F50-48AF-B0A5-E2BF832F82CC}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU - {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {17932639-1F50-48AF-B0A5-E2BF832F82CC}.Release|Any CPU.Build.0 = Release|Any CPU - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.CodeAnalysis|Any CPU.Deploy.0 = Debug|Any CPU - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Release|Any CPU.Build.0 = Release|Any CPU - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D}.Release|Any CPU.Deploy.0 = Release|Any CPU - {152B7BAB-E884-4A59-8067-440971A682B3}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {152B7BAB-E884-4A59-8067-440971A682B3}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU - {152B7BAB-E884-4A59-8067-440971A682B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {152B7BAB-E884-4A59-8067-440971A682B3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {152B7BAB-E884-4A59-8067-440971A682B3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {152B7BAB-E884-4A59-8067-440971A682B3}.Release|Any CPU.Build.0 = Release|Any CPU - {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU - {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Release|Any CPU.Build.0 = Release|Any CPU - {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU - {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4}.Release|Any CPU.Build.0 = Release|Any CPU - {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU - {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.Build.0 = Release|Any CPU - {9529606E-AF76-4387-BFB7-3D10A5B399AA}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {9529606E-AF76-4387-BFB7-3D10A5B399AA}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU - {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Release|Any CPU.Build.0 = Release|Any CPU - {E135F455-0669-49F8-9207-07FCA8C8FC79}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {E135F455-0669-49F8-9207-07FCA8C8FC79}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU - {E135F455-0669-49F8-9207-07FCA8C8FC79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E135F455-0669-49F8-9207-07FCA8C8FC79}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E135F455-0669-49F8-9207-07FCA8C8FC79}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E135F455-0669-49F8-9207-07FCA8C8FC79}.Release|Any CPU.Build.0 = Release|Any CPU - {C78E8235-1D46-43EB-A912-80B522C4E9AE}.CodeAnalysis|Any CPU.ActiveCfg = Release|Any CPU - {C78E8235-1D46-43EB-A912-80B522C4E9AE}.CodeAnalysis|Any CPU.Build.0 = Release|Any CPU - {C78E8235-1D46-43EB-A912-80B522C4E9AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C78E8235-1D46-43EB-A912-80B522C4E9AE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C78E8235-1D46-43EB-A912-80B522C4E9AE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C78E8235-1D46-43EB-A912-80B522C4E9AE}.Release|Any CPU.Build.0 = Release|Any CPU - {60426312-6AE5-4835-8667-37EDEA670222}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {60426312-6AE5-4835-8667-37EDEA670222}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {60426312-6AE5-4835-8667-37EDEA670222}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {60426312-6AE5-4835-8667-37EDEA670222}.Debug|Any CPU.Build.0 = Debug|Any CPU - {60426312-6AE5-4835-8667-37EDEA670222}.Release|Any CPU.ActiveCfg = Release|Any CPU - {60426312-6AE5-4835-8667-37EDEA670222}.Release|Any CPU.Build.0 = Release|Any CPU - {3896A32A-E876-4C23-B9B8-78E17D134CD3}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {3896A32A-E876-4C23-B9B8-78E17D134CD3}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU + {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.Release|Any CPU.Build.0 = Release|Any CPU + {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.Debug|Any CPU.Build.0 = Debug|Any CPU + {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.Release|Any CPU.ActiveCfg = Release|Any CPU + {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.Release|Any CPU.Build.0 = Release|Any CPU + {2A59DE0A-B76A-4B42-9A33-04D34548353D}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {2A59DE0A-B76A-4B42-9A33-04D34548353D}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {2A59DE0A-B76A-4B42-9A33-04D34548353D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A59DE0A-B76A-4B42-9A33-04D34548353D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A59DE0A-B76A-4B42-9A33-04D34548353D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A59DE0A-B76A-4B42-9A33-04D34548353D}.Release|Any CPU.Build.0 = Release|Any CPU + {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}.Release|Any CPU.Build.0 = Release|Any CPU + {3896A32A-E876-4C23-B9B8-78E17D134CD3}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {3896A32A-E876-4C23-B9B8-78E17D134CD3}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {3896A32A-E876-4C23-B9B8-78E17D134CD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3896A32A-E876-4C23-B9B8-78E17D134CD3}.Debug|Any CPU.Build.0 = Debug|Any CPU {3896A32A-E876-4C23-B9B8-78E17D134CD3}.Release|Any CPU.ActiveCfg = Release|Any CPU {3896A32A-E876-4C23-B9B8-78E17D134CD3}.Release|Any CPU.Build.0 = Release|Any CPU - {A288FCC8-6FCF-46DA-A45E-5F9281556361}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {A288FCC8-6FCF-46DA-A45E-5F9281556361}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {A288FCC8-6FCF-46DA-A45E-5F9281556361}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A288FCC8-6FCF-46DA-A45E-5F9281556361}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A288FCC8-6FCF-46DA-A45E-5F9281556361}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A288FCC8-6FCF-46DA-A45E-5F9281556361}.Release|Any CPU.Build.0 = Release|Any CPU + {3A8347E8-59A5-4092-8842-95C75D7D2F36}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {3A8347E8-59A5-4092-8842-95C75D7D2F36}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {3A8347E8-59A5-4092-8842-95C75D7D2F36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3A8347E8-59A5-4092-8842-95C75D7D2F36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3A8347E8-59A5-4092-8842-95C75D7D2F36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3A8347E8-59A5-4092-8842-95C75D7D2F36}.Release|Any CPU.Build.0 = Release|Any CPU {408D10B8-34BA-4CBD-B7AA-FEB1907ABA4C}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {408D10B8-34BA-4CBD-B7AA-FEB1907ABA4C}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {408D10B8-34BA-4CBD-B7AA-FEB1907ABA4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {408D10B8-34BA-4CBD-B7AA-FEB1907ABA4C}.Debug|Any CPU.Build.0 = Debug|Any CPU {408D10B8-34BA-4CBD-B7AA-FEB1907ABA4C}.Release|Any CPU.ActiveCfg = Release|Any CPU {408D10B8-34BA-4CBD-B7AA-FEB1907ABA4C}.Release|Any CPU.Build.0 = Release|Any CPU + {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4376ECC9-C346-4A99-B13C-FA93C0FBD2C9}.Release|Any CPU.Build.0 = Release|Any CPU + {47A84EF7-68C3-4D47-926A-9CCEA6518531}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {47A84EF7-68C3-4D47-926A-9CCEA6518531}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47A84EF7-68C3-4D47-926A-9CCEA6518531}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47A84EF7-68C3-4D47-926A-9CCEA6518531}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {47A84EF7-68C3-4D47-926A-9CCEA6518531}.Release|Any CPU.Build.0 = Debug|Any CPU + {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.Release|Any CPU.Build.0 = Release|Any CPU + {51835086-9611-4C53-819B-F2D5C9320873}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {51835086-9611-4C53-819B-F2D5C9320873}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {51835086-9611-4C53-819B-F2D5C9320873}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {51835086-9611-4C53-819B-F2D5C9320873}.Debug|Any CPU.Build.0 = Debug|Any CPU + {51835086-9611-4C53-819B-F2D5C9320873}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51835086-9611-4C53-819B-F2D5C9320873}.Release|Any CPU.Build.0 = Release|Any CPU {56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}.Debug|Any CPU.Build.0 = Debug|Any CPU {56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}.Release|Any CPU.ActiveCfg = Release|Any CPU {56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6}.Release|Any CPU.Build.0 = Release|Any CPU - {F8284738-3B5D-4733-A511-38C23F4A763F}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {F8284738-3B5D-4733-A511-38C23F4A763F}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU - {F8284738-3B5D-4733-A511-38C23F4A763F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F8284738-3B5D-4733-A511-38C23F4A763F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F8284738-3B5D-4733-A511-38C23F4A763F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F8284738-3B5D-4733-A511-38C23F4A763F}.Release|Any CPU.Build.0 = Release|Any CPU - {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU - {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.Release|Any CPU.Build.0 = Release|Any CPU - {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.Release|Any CPU.Build.0 = Release|Any CPU - {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.Debug|Any CPU.Build.0 = Debug|Any CPU - {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.Release|Any CPU.ActiveCfg = Release|Any CPU - {26DC877F-5987-48DD-9DDB-E62F2DE0E150}.Release|Any CPU.Build.0 = Release|Any CPU - {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1ED8D424-F8AB-4050-ACEB-F27F4F909484}.Release|Any CPU.Build.0 = Release|Any CPU - {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.Release|Any CPU.Build.0 = Release|Any CPU + {5C65603B-235F-47E6-B536-06385C60DE7F}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {5C65603B-235F-47E6-B536-06385C60DE7F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5C65603B-235F-47E6-B536-06385C60DE7F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5C65603B-235F-47E6-B536-06385C60DE7F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5C65603B-235F-47E6-B536-06385C60DE7F}.Release|Any CPU.Build.0 = Release|Any CPU + {60426312-6AE5-4835-8667-37EDEA670222}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {60426312-6AE5-4835-8667-37EDEA670222}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {60426312-6AE5-4835-8667-37EDEA670222}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {60426312-6AE5-4835-8667-37EDEA670222}.Debug|Any CPU.Build.0 = Debug|Any CPU + {60426312-6AE5-4835-8667-37EDEA670222}.Release|Any CPU.ActiveCfg = Release|Any CPU + {60426312-6AE5-4835-8667-37EDEA670222}.Release|Any CPU.Build.0 = Release|Any CPU + {6EB90284-BD15-461C-BBF2-131CF55F7C8B}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {6EB90284-BD15-461C-BBF2-131CF55F7C8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6EB90284-BD15-461C-BBF2-131CF55F7C8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6EB90284-BD15-461C-BBF2-131CF55F7C8B}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {6EB90284-BD15-461C-BBF2-131CF55F7C8B}.Release|Any CPU.Build.0 = Debug|Any CPU + {6EC36418-DBC5-4AD1-A402-413604AA7A08}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {6EC36418-DBC5-4AD1-A402-413604AA7A08}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {6EC36418-DBC5-4AD1-A402-413604AA7A08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6EC36418-DBC5-4AD1-A402-413604AA7A08}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6EC36418-DBC5-4AD1-A402-413604AA7A08}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6EC36418-DBC5-4AD1-A402-413604AA7A08}.Release|Any CPU.Build.0 = Release|Any CPU {75E13AAE-7D51-4421-ABFD-3F3DC91F576E}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {75E13AAE-7D51-4421-ABFD-3F3DC91F576E}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {75E13AAE-7D51-4421-ABFD-3F3DC91F576E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {75E13AAE-7D51-4421-ABFD-3F3DC91F576E}.Debug|Any CPU.Build.0 = Debug|Any CPU {75E13AAE-7D51-4421-ABFD-3F3DC91F576E}.Release|Any CPU.ActiveCfg = Release|Any CPU {75E13AAE-7D51-4421-ABFD-3F3DC91F576E}.Release|Any CPU.Build.0 = Release|Any CPU - {173E7B8D-E751-46E2-A133-F72297C0D2F4}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {173E7B8D-E751-46E2-A133-F72297C0D2F4}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {173E7B8D-E751-46E2-A133-F72297C0D2F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {173E7B8D-E751-46E2-A133-F72297C0D2F4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {173E7B8D-E751-46E2-A133-F72297C0D2F4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {173E7B8D-E751-46E2-A133-F72297C0D2F4}.Release|Any CPU.Build.0 = Release|Any CPU - {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.Release|Any CPU.Build.0 = Release|Any CPU - {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.Release|Any CPU.Build.0 = Release|Any CPU - {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.Release|Any CPU.Build.0 = Release|Any CPU + {9529606E-AF76-4387-BFB7-3D10A5B399AA}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9529606E-AF76-4387-BFB7-3D10A5B399AA}.Release|Any CPU.Build.0 = Release|Any CPU {99BB7543-EA16-43EE-A7BC-D7A25A3B22F6}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {99BB7543-EA16-43EE-A7BC-D7A25A3B22F6}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {99BB7543-EA16-43EE-A7BC-D7A25A3B22F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {99BB7543-EA16-43EE-A7BC-D7A25A3B22F6}.Debug|Any CPU.Build.0 = Debug|Any CPU {99BB7543-EA16-43EE-A7BC-D7A25A3B22F6}.Release|Any CPU.ActiveCfg = Release|Any CPU {99BB7543-EA16-43EE-A7BC-D7A25A3B22F6}.Release|Any CPU.Build.0 = Release|Any CPU - {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.Release|Any CPU.Build.0 = Release|Any CPU + {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D0F8866-2131-4C2A-BC0E-16FEA5B50828}.Release|Any CPU.Build.0 = Release|Any CPU {A1A3150A-7B0E-4A34-8E35-045296CD3C76}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {A1A3150A-7B0E-4A34-8E35-045296CD3C76}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {A1A3150A-7B0E-4A34-8E35-045296CD3C76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A1A3150A-7B0E-4A34-8E35-045296CD3C76}.Debug|Any CPU.Build.0 = Debug|Any CPU {A1A3150A-7B0E-4A34-8E35-045296CD3C76}.Release|Any CPU.ActiveCfg = Release|Any CPU {A1A3150A-7B0E-4A34-8E35-045296CD3C76}.Release|Any CPU.Build.0 = Release|Any CPU + {A288FCC8-6FCF-46DA-A45E-5F9281556361}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {A288FCC8-6FCF-46DA-A45E-5F9281556361}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {A288FCC8-6FCF-46DA-A45E-5F9281556361}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A288FCC8-6FCF-46DA-A45E-5F9281556361}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A288FCC8-6FCF-46DA-A45E-5F9281556361}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A288FCC8-6FCF-46DA-A45E-5F9281556361}.Release|Any CPU.Build.0 = Release|Any CPU + {A78F8FC6-7B03-4230-BE41-761E400D6810}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {A78F8FC6-7B03-4230-BE41-761E400D6810}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A78F8FC6-7B03-4230-BE41-761E400D6810}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A78F8FC6-7B03-4230-BE41-761E400D6810}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A78F8FC6-7B03-4230-BE41-761E400D6810}.Release|Any CPU.Build.0 = Release|Any CPU + {AA78D112-D889-414B-A7D4-467B34C7B663}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {AA78D112-D889-414B-A7D4-467B34C7B663}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {AA78D112-D889-414B-A7D4-467B34C7B663}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA78D112-D889-414B-A7D4-467B34C7B663}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA78D112-D889-414B-A7D4-467B34C7B663}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA78D112-D889-414B-A7D4-467B34C7B663}.Release|Any CPU.Build.0 = Release|Any CPU {ADC2CC8C-541E-4F86-ACB1-DD504A36FA4B}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU {ADC2CC8C-541E-4F86-ACB1-DD504A36FA4B}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU {ADC2CC8C-541E-4F86-ACB1-DD504A36FA4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ADC2CC8C-541E-4F86-ACB1-DD504A36FA4B}.Debug|Any CPU.Build.0 = Debug|Any CPU {ADC2CC8C-541E-4F86-ACB1-DD504A36FA4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADC2CC8C-541E-4F86-ACB1-DD504A36FA4B}.Release|Any CPU.Build.0 = Release|Any CPU - {3A8347E8-59A5-4092-8842-95C75D7D2F36}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {3A8347E8-59A5-4092-8842-95C75D7D2F36}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {3A8347E8-59A5-4092-8842-95C75D7D2F36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3A8347E8-59A5-4092-8842-95C75D7D2F36}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3A8347E8-59A5-4092-8842-95C75D7D2F36}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3A8347E8-59A5-4092-8842-95C75D7D2F36}.Release|Any CPU.Build.0 = Release|Any CPU - {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU - {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D}.Release|Any CPU.Build.0 = Release|Any CPU - {CAA2408C-6918-4902-A512-58BCD62216C3}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {CAA2408C-6918-4902-A512-58BCD62216C3}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU - {CAA2408C-6918-4902-A512-58BCD62216C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CAA2408C-6918-4902-A512-58BCD62216C3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CAA2408C-6918-4902-A512-58BCD62216C3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CAA2408C-6918-4902-A512-58BCD62216C3}.Release|Any CPU.Build.0 = Release|Any CPU - {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4BFAA336-5DF3-4F27-82D3-06D13240E8AB}.Release|Any CPU.Build.0 = Release|Any CPU - {51835086-9611-4C53-819B-F2D5C9320873}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {51835086-9611-4C53-819B-F2D5C9320873}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {51835086-9611-4C53-819B-F2D5C9320873}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {51835086-9611-4C53-819B-F2D5C9320873}.Debug|Any CPU.Build.0 = Debug|Any CPU - {51835086-9611-4C53-819B-F2D5C9320873}.Release|Any CPU.ActiveCfg = Release|Any CPU - {51835086-9611-4C53-819B-F2D5C9320873}.Release|Any CPU.Build.0 = Release|Any CPU + {AEA29D4D-396F-47F6-BC81-B58D4B855245}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {AEA29D4D-396F-47F6-BC81-B58D4B855245}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEA29D4D-396F-47F6-BC81-B58D4B855245}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEA29D4D-396F-47F6-BC81-B58D4B855245}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEA29D4D-396F-47F6-BC81-B58D4B855245}.Release|Any CPU.Build.0 = Release|Any CPU + {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B202E40D-4663-4A2B-ACDA-865F88FF7CAA}.Release|Any CPU.Build.0 = Release|Any CPU + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942}.Release|Any CPU.Build.0 = Release|Any CPU + {BBACD972-014D-478F-9B07-56B9E1D4CC73}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {BBACD972-014D-478F-9B07-56B9E1D4CC73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BBACD972-014D-478F-9B07-56B9E1D4CC73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BBACD972-014D-478F-9B07-56B9E1D4CC73}.Release|Any CPU.ActiveCfg = Debug|Any CPU + {BBACD972-014D-478F-9B07-56B9E1D4CC73}.Release|Any CPU.Build.0 = Debug|Any CPU {C23B217B-4D35-4A72-A1F7-FAEB4F39CB91}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU - {C23B217B-4D35-4A72-A1F7-FAEB4F39CB91}.CodeAnalysis|Any CPU.Build.0 = Debug|Any CPU {C23B217B-4D35-4A72-A1F7-FAEB4F39CB91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C23B217B-4D35-4A72-A1F7-FAEB4F39CB91}.Debug|Any CPU.Build.0 = Debug|Any CPU {C23B217B-4D35-4A72-A1F7-FAEB4F39CB91}.Release|Any CPU.ActiveCfg = Release|Any CPU {C23B217B-4D35-4A72-A1F7-FAEB4F39CB91}.Release|Any CPU.Build.0 = Release|Any CPU - {115217C5-22CD-415C-A292-0DD0238CDD89}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU - {115217C5-22CD-415C-A292-0DD0238CDD89}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU - {115217C5-22CD-415C-A292-0DD0238CDD89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {115217C5-22CD-415C-A292-0DD0238CDD89}.Debug|Any CPU.Build.0 = Debug|Any CPU - {115217C5-22CD-415C-A292-0DD0238CDD89}.Release|Any CPU.ActiveCfg = Release|Any CPU - {115217C5-22CD-415C-A292-0DD0238CDD89}.Release|Any CPU.Build.0 = Release|Any CPU + {C78E8235-1D46-43EB-A912-80B522C4E9AE}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {C78E8235-1D46-43EB-A912-80B522C4E9AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C78E8235-1D46-43EB-A912-80B522C4E9AE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C78E8235-1D46-43EB-A912-80B522C4E9AE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C78E8235-1D46-43EB-A912-80B522C4E9AE}.Release|Any CPU.Build.0 = Release|Any CPU + {CAA2408C-6918-4902-A512-58BCD62216C3}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {CAA2408C-6918-4902-A512-58BCD62216C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CAA2408C-6918-4902-A512-58BCD62216C3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CAA2408C-6918-4902-A512-58BCD62216C3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CAA2408C-6918-4902-A512-58BCD62216C3}.Release|Any CPU.Build.0 = Release|Any CPU + {CCF3728A-B3D7-404A-9BC6-75197135F2D7}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {CCF3728A-B3D7-404A-9BC6-75197135F2D7}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {CCF3728A-B3D7-404A-9BC6-75197135F2D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CCF3728A-B3D7-404A-9BC6-75197135F2D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CCF3728A-B3D7-404A-9BC6-75197135F2D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CCF3728A-B3D7-404A-9BC6-75197135F2D7}.Release|Any CPU.Build.0 = Release|Any CPU + {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99}.Release|Any CPU.Build.0 = Release|Any CPU + {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E040EB58-B4D2-457B-A023-AE6EF3BD34DE}.Release|Any CPU.Build.0 = Release|Any CPU + {E135F455-0669-49F8-9207-07FCA8C8FC79}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {E135F455-0669-49F8-9207-07FCA8C8FC79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E135F455-0669-49F8-9207-07FCA8C8FC79}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E135F455-0669-49F8-9207-07FCA8C8FC79}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E135F455-0669-49F8-9207-07FCA8C8FC79}.Release|Any CPU.Build.0 = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.CodeAnalysis|Any CPU.ActiveCfg = Debug|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F289B925-4307-4BEC-B411-885CE70E3379}.Release|Any CPU.Build.0 = Release|Any CPU + {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B}.Release|Any CPU.Build.0 = Release|Any CPU + {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4CD3C04-6037-4946-B7A5-34BFC96A75D2}.Release|Any CPU.Build.0 = Release|Any CPU + {F8284738-3B5D-4733-A511-38C23F4A763F}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {F8284738-3B5D-4733-A511-38C23F4A763F}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {F8284738-3B5D-4733-A511-38C23F4A763F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F8284738-3B5D-4733-A511-38C23F4A763F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F8284738-3B5D-4733-A511-38C23F4A763F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F8284738-3B5D-4733-A511-38C23F4A763F}.Release|Any CPU.Build.0 = Release|Any CPU + {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.CodeAnalysis|Any CPU.ActiveCfg = CodeAnalysis|Any CPU + {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.CodeAnalysis|Any CPU.Build.0 = CodeAnalysis|Any CPU + {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FED1923A-6D70-49B5-A37A-FB744FEC1C86}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {CD57219F-24F4-4136-8741-6063D0D7A031} = {20B5E173-C3C4-49F8-BD25-E69044075B4D} {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} - {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} - {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} - {AA78D112-D889-414B-A7D4-467B34C7B663} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} - {2DA24D4F-6918-43CF-973C-BC9D818F8E90} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} - {2A59DE0A-B76A-4B42-9A33-04D34548353D} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} - {AEA29D4D-396F-47F6-BC81-B58D4B855245} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} {07B193F1-68AD-4E9C-98AF-BEFB5E9403CB} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} - {1E8AEA89-BF69-47A1-B290-E8B0FE588700} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} - {BBACD972-014D-478F-9B07-56B9E1D4CC73} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} - {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} + {08A938B6-EBBD-4036-880E-CE7BA2D14510} = {B9EB8729-4B54-4453-B089-FE6761BA3057} {0B4EB2A8-283D-48FB-BCD0-85B8DFFE05E4} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} - {F289B925-4307-4BEC-B411-885CE70E3379} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} - {6EC36418-DBC5-4AD1-A402-413604AA7A08} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} - {9529606E-AF76-4387-BFB7-3D10A5B399AA} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} - {E135F455-0669-49F8-9207-07FCA8C8FC79} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} - {C78E8235-1D46-43EB-A912-80B522C4E9AE} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} - {6EB90284-BD15-461C-BBF2-131CF55F7C8B} = {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277} - {5C65603B-235F-47E6-B536-06385C60DE7F} = {E9ED920D-1F83-48C0-9A4B-09CCE505FE6D} - {A78F8FC6-7B03-4230-BE41-761E400D6810} = {B9EB8729-4B54-4453-B089-FE6761BA3057} - {17932639-1F50-48AF-B0A5-E2BF832F82CC} = {B9EB8729-4B54-4453-B089-FE6761BA3057} - {2B4261AC-25AC-4B8D-B459-1C42B6B1401D} = {B9EB8729-4B54-4453-B089-FE6761BA3057} + {115217C5-22CD-415C-A292-0DD0238CDD89} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} {152B7BAB-E884-4A59-8067-440971A682B3} = {B9EB8729-4B54-4453-B089-FE6761BA3057} - {C7EF1823-3AA7-477E-8476-28929F5C05D2} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} - {9AF74F53-10F5-49A2-B747-87B97CD559D3} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} - {529B4262-6B5A-4EF9-BD3B-1D29A2597B67} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} - {238B6BA8-AD99-43C9-B8E2-D2BCE6CE04DC} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} {173E7B8D-E751-46E2-A133-F72297C0D2F4} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} - {60426312-6AE5-4835-8667-37EDEA670222} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} - {57A7DD35-666C-4FA3-9A1B-38961E50CA27} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} - {51835086-9611-4C53-819B-F2D5C9320873} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} - {115217C5-22CD-415C-A292-0DD0238CDD89} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} - {F8284738-3B5D-4733-A511-38C23F4A763F} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} - {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} - {F4CD3C04-6037-4946-B7A5-34BFC96A75D2} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} - {26DC877F-5987-48DD-9DDB-E62F2DE0E150} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} + {17932639-1F50-48AF-B0A5-E2BF832F82CC} = {B9EB8729-4B54-4453-B089-FE6761BA3057} + {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} + {1E8AEA89-BF69-47A1-B290-E8B0FE588700} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} {1ED8D424-F8AB-4050-ACEB-F27F4F909484} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} - {9D0F8866-2131-4C2A-BC0E-16FEA5B50828} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} - {75E13AAE-7D51-4421-ABFD-3F3DC91F576E} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} + {238B6BA8-AD99-43C9-B8E2-D2BCE6CE04DC} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} + {26DC877F-5987-48DD-9DDB-E62F2DE0E150} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} + {2A59DE0A-B76A-4B42-9A33-04D34548353D} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} + {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D} = {2DA24D4F-6918-43CF-973C-BC9D818F8E90} + {2DA24D4F-6918-43CF-973C-BC9D818F8E90} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} {3896A32A-E876-4C23-B9B8-78E17D134CD3} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} - {A288FCC8-6FCF-46DA-A45E-5F9281556361} = {9AF74F53-10F5-49A2-B747-87B97CD559D3} - {B202E40D-4663-4A2B-ACDA-865F88FF7CAA} = {9AF74F53-10F5-49A2-B747-87B97CD559D3} - {FED1923A-6D70-49B5-A37A-FB744FEC1C86} = {9AF74F53-10F5-49A2-B747-87B97CD559D3} + {3A8347E8-59A5-4092-8842-95C75D7D2F36} = {57A7DD35-666C-4FA3-9A1B-38961E50CA27} {408D10B8-34BA-4CBD-B7AA-FEB1907ABA4C} = {529B4262-6B5A-4EF9-BD3B-1D29A2597B67} - {E040EB58-B4D2-457B-A023-AE6EF3BD34DE} = {529B4262-6B5A-4EF9-BD3B-1D29A2597B67} + {4BFAA336-5DF3-4F27-82D3-06D13240E8AB} = {57A7DD35-666C-4FA3-9A1B-38961E50CA27} + {51835086-9611-4C53-819B-F2D5C9320873} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} + {529B4262-6B5A-4EF9-BD3B-1D29A2597B67} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} {56459A6C-6BA2-4BAC-A9C0-27E3BD961FA6} = {238B6BA8-AD99-43C9-B8E2-D2BCE6CE04DC} + {57A7DD35-666C-4FA3-9A1B-38961E50CA27} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} + {5C65603B-235F-47E6-B536-06385C60DE7F} = {E9ED920D-1F83-48C0-9A4B-09CCE505FE6D} + {60426312-6AE5-4835-8667-37EDEA670222} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} + {6EB90284-BD15-461C-BBF2-131CF55F7C8B} = {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277} + {6EC36418-DBC5-4AD1-A402-413604AA7A08} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} + {75E13AAE-7D51-4421-ABFD-3F3DC91F576E} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} + {8A5CEDB9-7F8A-4BE2-A1B9-97130F453277} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} + {9529606E-AF76-4387-BFB7-3D10A5B399AA} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} {99BB7543-EA16-43EE-A7BC-D7A25A3B22F6} = {238B6BA8-AD99-43C9-B8E2-D2BCE6CE04DC} - {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99} = {238B6BA8-AD99-43C9-B8E2-D2BCE6CE04DC} + {9AF74F53-10F5-49A2-B747-87B97CD559D3} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} + {9D0F8866-2131-4C2A-BC0E-16FEA5B50828} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} {A1A3150A-7B0E-4A34-8E35-045296CD3C76} = {238B6BA8-AD99-43C9-B8E2-D2BCE6CE04DC} + {A288FCC8-6FCF-46DA-A45E-5F9281556361} = {9AF74F53-10F5-49A2-B747-87B97CD559D3} + {A78F8FC6-7B03-4230-BE41-761E400D6810} = {B9EB8729-4B54-4453-B089-FE6761BA3057} + {AA78D112-D889-414B-A7D4-467B34C7B663} = {B4C6F647-C046-4B54-BE12-7701C4119EE7} {ADC2CC8C-541E-4F86-ACB1-DD504A36FA4B} = {238B6BA8-AD99-43C9-B8E2-D2BCE6CE04DC} - {3A8347E8-59A5-4092-8842-95C75D7D2F36} = {57A7DD35-666C-4FA3-9A1B-38961E50CA27} - {4BFAA336-5DF3-4F27-82D3-06D13240E8AB} = {57A7DD35-666C-4FA3-9A1B-38961E50CA27} - {2BF1FFD1-607E-40D0-8AB5-EDA677EF932D} = {2DA24D4F-6918-43CF-973C-BC9D818F8E90} + {AEA29D4D-396F-47F6-BC81-B58D4B855245} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} + {B202E40D-4663-4A2B-ACDA-865F88FF7CAA} = {9AF74F53-10F5-49A2-B747-87B97CD559D3} + {B64A1E7E-6A15-4B91-AF13-7D48F7DA5942} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} + {BBACD972-014D-478F-9B07-56B9E1D4CC73} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} + {C78E8235-1D46-43EB-A912-80B522C4E9AE} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} + {C7EF1823-3AA7-477E-8476-28929F5C05D2} = {8D4236F7-C49B-49D3-BA71-6B86C9514BDE} {CAA2408C-6918-4902-A512-58BCD62216C3} = {2DA24D4F-6918-43CF-973C-BC9D818F8E90} + {CCF3728A-B3D7-404A-9BC6-75197135F2D7} = {238B6BA8-AD99-43C9-B8E2-D2BCE6CE04DC} + {CD57219F-24F4-4136-8741-6063D0D7A031} = {20B5E173-C3C4-49F8-BD25-E69044075B4D} + {CDEDD439-7F35-4E6E-8605-4E70BDC4CC99} = {238B6BA8-AD99-43C9-B8E2-D2BCE6CE04DC} + {E040EB58-B4D2-457B-A023-AE6EF3BD34DE} = {529B4262-6B5A-4EF9-BD3B-1D29A2597B67} + {E135F455-0669-49F8-9207-07FCA8C8FC79} = {1E2CBAA5-60A3-4AED-912E-541F5753CDC6} + {F289B925-4307-4BEC-B411-885CE70E3379} = {034D5B5B-7D00-4A9D-8AFE-4A476E0575B1} + {F458AB60-BA1C-43D9-8CEF-EC01B50BE87B} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} + {F4CD3C04-6037-4946-B7A5-34BFC96A75D2} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} + {F8284738-3B5D-4733-A511-38C23F4A763F} = {C7EF1823-3AA7-477E-8476-28929F5C05D2} + {FED1923A-6D70-49B5-A37A-FB744FEC1C86} = {9AF74F53-10F5-49A2-B747-87B97CD559D3} EndGlobalSection EndGlobal diff --git a/src/version.txt b/src/version.txt index 13b681f..684edf4 100644 --- a/src/version.txt +++ b/src/version.txt @@ -1,3 +1,3 @@ -4.0.2 +4.1.0 -0.23.0-draft4 +0.25.0-draft1 |