diff options
Diffstat (limited to 'src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2')
3 files changed, 36 insertions, 10 deletions
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs index 9696402..c7a1a23 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.EndUserAuthorizationRequestErrorCodes.UnauthorizedClient); + ErrorUtilities.VerifyProtocol(client.HasNonEmptySecret, Protocol.EndUserAuthorizationRequestErrorCodes.UnauthorizedClient); } } diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs index be4f70d..23dcbf5 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs @@ -81,9 +81,8 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { var authenticatedClientRequest = message as AuthenticatedClientRequestBase; if (authenticatedClientRequest != null) { var client = this.AuthorizationServer.GetClientOrThrow(authenticatedClientRequest.ClientIdentifier); - string secret = client.Secret; - 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); + 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; diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ClientDescription.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ClientDescription.cs index 76c3ea6..1ec9789 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ClientDescription.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ClientDescription.cs @@ -9,6 +9,7 @@ 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. @@ -20,6 +21,11 @@ namespace DotNetOpenAuth.OAuth2 { private readonly Func<Uri, bool> isCallbackAllowed; /// <summary> + /// The client's secret, if any. + /// </summary> + private readonly string secret; + + /// <summary> /// Initializes a new instance of the <see cref="ClientDescription"/> class. /// </summary> /// <param name="secret">The secret.</param> @@ -27,18 +33,13 @@ namespace DotNetOpenAuth.OAuth2 { /// <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; + this.secret = secret; this.DefaultCallback = defaultCallback; this.ClientType = clientType; this.isCallbackAllowed = isCallbackAllowed; } /// <summary> - /// Gets the client secret. - /// </summary> - public string Secret { get; private set; } - - /// <summary> /// Gets the callback to use when an individual authorization request /// does not include an explicit callback URI. /// </summary> @@ -53,6 +54,13 @@ 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 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> @@ -67,5 +75,24 @@ namespace DotNetOpenAuth.OAuth2 { return EqualityComparer<Uri>.Default.Equals(this.DefaultCallback, callback); } + + #region IClientDescription Members + + /// <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="MessagingUtilites.EqualsConstantTime"/> to mitigate timing attacks. + /// </remarks> + public bool IsValidClientSecret(string secret) { + Requires.NotNullOrEmpty(secret, "secret"); + + return MessagingUtilities.EqualsConstantTime(secret, this.secret); + } + + #endregion } } |