diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2012-03-30 22:56:11 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2012-03-30 22:56:11 -0700 |
commit | 12ff0dc0fa007968813675a2e0d447389a5c1bd3 (patch) | |
tree | e4cc68fd05e24143bcbea3b4073fd289ffe62c01 | |
parent | 9ee48d7fa3d6d62807b854030b8303719e6f0a6f (diff) | |
download | DotNetOpenAuth-12ff0dc0fa007968813675a2e0d447389a5c1bd3.zip DotNetOpenAuth-12ff0dc0fa007968813675a2e0d447389a5c1bd3.tar.gz DotNetOpenAuth-12ff0dc0fa007968813675a2e0d447389a5c1bd3.tar.bz2 |
Fixed up an authorization server's token endpoint to generate more accurate error messages.
14 files changed, 551 insertions, 115 deletions
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj index 2861467..cffffa9 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj @@ -19,6 +19,11 @@ </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\AccessRequestBindingElement.cs" /> <Compile Include="OAuth2\ChannelElements\AccessTokenBindingElement.cs" /> @@ -50,6 +55,12 @@ <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..43a97f3 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.17611 +// +// 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 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); + } + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.resx b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.resx new file mode 100644 index 0000000..82b3e81 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerStrings.resx @@ -0,0 +1,129 @@ +<?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="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> +</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 index 75b21c8..a59eaa7 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerUtilities.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerUtilities.cs @@ -8,6 +8,7 @@ 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; @@ -36,5 +37,19 @@ namespace DotNetOpenAuth.OAuth2 { throw ErrorUtilities.Wrap(ex, OAuthStrings.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 19fc171..24868d2 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(!string.IsNullOrEmpty(client.Secret), Protocol.EndUserAuthorizationRequestErrorCodes.UnauthorizedClient); } } @@ -113,10 +113,10 @@ namespace DotNetOpenAuth.OAuth2 { // TODO: refreshToken should be set appropriately based on authorization server policy. responseMessage = this.PrepareAccessTokenResponse(requestMessage); } 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, diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AccessRequestBindingElement.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AccessRequestBindingElement.cs index 4c89b0b..1d85630 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AccessRequestBindingElement.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AccessRequestBindingElement.cs @@ -104,19 +104,15 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { if (this.AuthorizationServer.IsResourceOwnerCredentialValid(resourceOwnerPasswordCarrier.UserName, resourceOwnerPasswordCarrier.Password)) { resourceOwnerPasswordCarrier.CredentialsValidated = true; } else { - Logger.OAuth.WarnFormat( + Logger.OAuth.ErrorFormat( "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(); + throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.InvalidGrant, AuthServerStrings.InvalidResourceOwnerPasswordCredential); } } catch (NotSupportedException) { - // TODO: fix this to return the appropriate error code for not supporting resource owner password credentials - throw new ProtocolException(); + throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.UnsupportedGrantType); } catch (NotImplementedException) { - // TODO: fix this to return the appropriate error code for not supporting resource owner password credentials - throw new ProtocolException(); + throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.UnsupportedGrantType); } } else if (clientCredentialOnly != null) { // this method will throw later if the credentials are false. @@ -125,23 +121,29 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { throw ErrorUtilities.ThrowInternal("Unexpected message type: " + tokenRequest.GetType()); } } catch (ExpiredMessageException ex) { - throw ErrorUtilities.Wrap(ex, Protocol.authorization_expired); + throw new TokenEndpointProtocolException(ex); } 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); + AuthServerUtilities.TokenEndpointVerify(string.Equals(accessRequest.ClientIdentifier, tokenRequest.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. - ErrorUtilities.VerifyProtocol(scopedAccessRequest.Scope.IsSubsetOf(tokenRequest.AuthorizationDescription.Scope), OAuthStrings.AccessScopeExceedsGrantScope, scopedAccessRequest.Scope, tokenRequest.AuthorizationDescription.Scope); + if (!scopedAccessRequest.Scope.IsSubsetOf(tokenRequest.AuthorizationDescription.Scope)) { + Logger.OAuth.ErrorFormat("The requested access scope (\"{0}\") exceeds the grant scope (\"{1}\").", scopedAccessRequest.Scope, tokenRequest.AuthorizationDescription.Scope); + throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.InvalidScope, AuthServerStrings.AccessScopeExceedsGrantScope); + } } } // Make sure the authorization this token represents hasn't already been revoked. - ErrorUtilities.VerifyProtocol(this.AuthorizationServer.IsAuthorizationValid(tokenRequest.AuthorizationDescription), Protocol.authorization_expired); + if (!this.AuthorizationServer.IsAuthorizationValid(tokenRequest.AuthorizationDescription)) { + Logger.OAuth.Error("Rejecting access token request because the IAuthorizationServer.IsAuthorizationValid method returned false."); + throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.InvalidGrant); + } return MessageProtections.None; } diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs index 46a3de2..3d408ec 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs @@ -76,8 +76,8 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { if (authenticatedClientRequest != null) { var client = this.AuthorizationServer.GetClientOrThrow(authenticatedClientRequest.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, authenticatedClientRequest.ClientSecret), Protocol.incorrect_client_credentials); + AuthServerUtilities.TokenEndpointVerify(!string.IsNullOrEmpty(secret), Protocol.AccessTokenRequestErrorCodes.UnauthorizedClient); // an empty secret is not allowed for client authenticated calls. + AuthServerUtilities.TokenEndpointVerify(MessagingUtilities.EqualsConstantTime(secret, authenticatedClientRequest.ClientSecret), Protocol.AccessTokenRequestErrorCodes.InvalidClient, AuthServerStrings.ClientSecretMismatch); applied = true; } diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj index 2e43b7c..53ab1d0 100644 --- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/DotNetOpenAuth.OAuth2.ClientAuthorization.csproj @@ -22,6 +22,11 @@ <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" /> @@ -46,6 +51,7 @@ <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> @@ -58,7 +64,12 @@ <Name>DotNetOpenAuth.OAuth2</Name> </ProjectReference> </ItemGroup> - <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))' != '' " /> 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..7d33d3e --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ClientAuthorizationStrings.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30319.17611 +// +// 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 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..5ae922f --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/ClientAuthorizationStrings.resx @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="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.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/OAuth2/OAuthStrings.Designer.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.Designer.cs index 6ce3b53..d975330 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.17611 // // 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 { diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.resx b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.resx index af1a955..11c24f4 100644 --- a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.resx +++ b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthStrings.resx @@ -120,9 +120,6 @@ <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> 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"; } |