diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2012-04-18 19:55:50 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2012-04-18 19:55:50 -0700 |
commit | 2ddd19d9f037bebbbdc80d7de35ce4d899710859 (patch) | |
tree | baf8ad19d4799b1a1284b9fc668afd53e3b008a4 /src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements | |
parent | bd0de8217763d02759815b91588cd578becf496b (diff) | |
download | DotNetOpenAuth-2ddd19d9f037bebbbdc80d7de35ce4d899710859.zip DotNetOpenAuth-2ddd19d9f037bebbbdc80d7de35ce4d899710859.tar.gz DotNetOpenAuth-2ddd19d9f037bebbbdc80d7de35ce4d899710859.tar.bz2 |
We have HTTP Basic client authentication working now in OAuth 2.
Diffstat (limited to 'src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements')
6 files changed, 196 insertions, 7 deletions
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AggregatingClientCredentialReader.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AggregatingClientCredentialReader.cs new file mode 100644 index 0000000..4248c6f --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AggregatingClientCredentialReader.cs @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------- +// <copyright file="AggregatingClientCredentialReader.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.ChannelElements { + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Text; + using System.Web; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth2.Messages; + + /// <summary> + /// Applies OAuth 2 spec policy for supporting multiple methods of client authentication. + /// </summary> + internal class AggregatingClientCredentialReader : ClientAuthenticationModuleBase { + /// <summary> + /// The set of authenticators to apply to an incoming request. + /// </summary> + private readonly IEnumerable<IClientAuthenticationModule> authenticators; + + /// <summary> + /// Initializes a new instance of the <see cref="AggregatingClientCredentialReader"/> class. + /// </summary> + /// <param name="authenticators">The set of authentication modules to apply.</param> + internal AggregatingClientCredentialReader(IEnumerable<IClientAuthenticationModule> authenticators) { + Requires.NotNull(authenticators, "readers"); + this.authenticators = authenticators; + } + + public override ClientAuthenticationResult TryAuthenticateClient(AuthenticatedClientRequestBase requestMessage, out string clientIdentifier) { + Requires.NotNull(requestMessage, "requestMessage"); + + IClientAuthenticationModule authenticator = null; + ClientAuthenticationResult result = ClientAuthenticationResult.NoAuthenticationRecognized; + clientIdentifier = null; + + foreach (var candidateAuthenticator in this.authenticators) { + string candidateClientIdentifier; + var resultCandidate = candidateAuthenticator.TryAuthenticateClient(requestMessage, out candidateClientIdentifier); + + ErrorUtilities.VerifyProtocol( + result == ClientAuthenticationResult.NoAuthenticationRecognized || resultCandidate == ClientAuthenticationResult.NoAuthenticationRecognized, + "Message rejected because multiple forms of client authentication ({0} and {1}) were detected, which is forbidden by the OAuth 2 Protocol Framework specification.", + authenticator, + candidateAuthenticator); + + if (resultCandidate != ClientAuthenticationResult.NoAuthenticationRecognized) { + authenticator = candidateAuthenticator; + result = resultCandidate; + clientIdentifier = candidateClientIdentifier; + } + } + + return result; + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs new file mode 100644 index 0000000..da3f8ff --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------- +// <copyright file="ClientCredentialHttpBasicReader.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.ChannelElements { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Web; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth2.Messages; + + public class ClientCredentialHttpBasicReader : ClientAuthenticationModuleBase { + private readonly IAuthorizationServerHost authorizationServerHost; + + public ClientCredentialHttpBasicReader(IAuthorizationServerHost authorizationServerHost) { + Requires.NotNull(authorizationServerHost, "authorizationServerHost"); + this.authorizationServerHost = authorizationServerHost; + } + + public override ClientAuthenticationResult TryAuthenticateClient(AuthenticatedClientRequestBase requestMessage, out string clientIdentifier) { + Requires.NotNull(requestMessage, "requestMessage"); + + var credential = OAuthUtilities.ParseHttpBasicAuth(requestMessage.Headers); + if (credential != null) { + clientIdentifier = credential.UserName; + return TryAuthenticateClient(this.authorizationServerHost, credential.UserName, credential.Password); + } + + clientIdentifier = null; + return ClientAuthenticationResult.NoAuthenticationRecognized; + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialMessagePartReader.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialMessagePartReader.cs new file mode 100644 index 0000000..07ededf --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialMessagePartReader.cs @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------- +// <copyright file="ClientCredentialMessagePartReader.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.ChannelElements { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Web; + using DotNetOpenAuth.OAuth2.Messages; + + public class ClientCredentialMessagePartReader : ClientAuthenticationModuleBase { + private readonly IAuthorizationServerHost authorizationServerHost; + + public ClientCredentialMessagePartReader(IAuthorizationServerHost authorizationServerHost) { + Requires.NotNull(authorizationServerHost, "authorizationServerHost"); + this.authorizationServerHost = authorizationServerHost; + } + + public override ClientAuthenticationResult TryAuthenticateClient(AuthenticatedClientRequestBase requestMessage, out string clientIdentifier) { + Requires.NotNull(requestMessage, "requestMessage"); + clientIdentifier = requestMessage.ClientIdentifier; + return TryAuthenticateClient(this.authorizationServerHost, requestMessage.ClientIdentifier, requestMessage.ClientSecret); + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialReader.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialReader.cs new file mode 100644 index 0000000..085600a --- /dev/null +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialReader.cs @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------- +// <copyright file="ClientCredentialReader.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth2.ChannelElements { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading; + using System.Web; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth2.Messages; + + public abstract class ClientAuthenticationModuleBase : IClientAuthenticationModule { + protected ClientAuthenticationModuleBase() { + } + + public abstract ClientAuthenticationResult TryAuthenticateClient(AuthenticatedClientRequestBase requestMessage, out string clientIdentifier); + + public ClientAuthenticationResult TryAuthenticateClient(IDirectedProtocolMessage requestMessage, out string clientIdentifier) { + return this.TryAuthenticateClient((AuthenticatedClientRequestBase)requestMessage, out clientIdentifier); + } + + protected static ClientAuthenticationResult TryAuthenticateClient(IAuthorizationServerHost authorizationServerHost, string clientIdentifier, string clientSecret) { + Requires.NotNull(authorizationServerHost, "authorizationServerHost"); + + if (!string.IsNullOrEmpty(clientIdentifier)) { + var client = authorizationServerHost.GetClient(clientIdentifier); + if (client != null) { + if (!string.IsNullOrEmpty(clientSecret)) { + if (client.IsValidClientSecret(clientSecret)) { + return ClientAuthenticationResult.ClientAuthenticated; + } else { // invalid client secret + return ClientAuthenticationResult.ClientAuthenticationRejected; + } + } else { // no client secret provided + return ClientAuthenticationResult.ClientIdNotAuthenticated; + } + } else { // The client identifier is not recognized. + return ClientAuthenticationResult.ClientAuthenticationRejected; + } + } else { // no client id provided. + return ClientAuthenticationResult.NoAuthenticationRecognized; + } + } + } +} diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs index 7361fb9..fa21bdd 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs @@ -23,6 +23,13 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// not been revoked and that an access token has not expired. /// </remarks> internal class MessageValidationBindingElement : AuthServerBindingElementBase { + private readonly IClientAuthenticationModule clientAuthenticationModule; + + internal MessageValidationBindingElement(IClientAuthenticationModule clientAuthenticationModule) { + Requires.NotNull(clientAuthenticationModule, "clientAuthenticationModule"); + this.clientAuthenticationModule = clientAuthenticationModule; + } + /// <summary> /// Gets the protection commonly offered (if any) by this binding element. /// </summary> @@ -80,9 +87,11 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { 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); + string clientIdentifier; + var result = this.clientAuthenticationModule.TryAuthenticateClient(authenticatedClientRequest, out clientIdentifier); + AuthServerUtilities.TokenEndpointVerify(result != ClientAuthenticationResult.ClientIdNotAuthenticated, Protocol.AccessTokenRequestErrorCodes.UnauthorizedClient); // an empty secret is not allowed for client authenticated calls. + AuthServerUtilities.TokenEndpointVerify(result == ClientAuthenticationResult.ClientAuthenticated, Protocol.AccessTokenRequestErrorCodes.InvalidClient, AuthServerStrings.ClientSecretMismatch); + authenticatedClientRequest.ClientIdentifier = clientIdentifier; if (clientCredentialOnly != null) { clientCredentialOnly.CredentialsValidated = true; diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs index bd154bc..2521e5f 100644 --- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs +++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs @@ -35,8 +35,8 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// Initializes a new instance of the <see cref="OAuth2AuthorizationServerChannel"/> class. /// </summary> /// <param name="authorizationServer">The authorization server.</param> - protected internal OAuth2AuthorizationServerChannel(IAuthorizationServerHost authorizationServer) - : base(MessageTypes, InitializeBindingElements(authorizationServer)) { + protected internal OAuth2AuthorizationServerChannel(IAuthorizationServerHost authorizationServer, IClientAuthenticationModule clientAuthenticationModule) + : base(MessageTypes, InitializeBindingElements(authorizationServer, clientAuthenticationModule)) { Requires.NotNull(authorizationServer, "authorizationServer"); this.AuthorizationServer = authorizationServer; } @@ -109,12 +109,14 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// <returns> /// An array of binding elements used to initialize the channel. /// </returns> - private static IChannelBindingElement[] InitializeBindingElements(IAuthorizationServerHost authorizationServer) { + private static IChannelBindingElement[] InitializeBindingElements(IAuthorizationServerHost authorizationServer, IClientAuthenticationModule clientAuthenticationModule) { Requires.NotNull(authorizationServer, "authorizationServer"); + Requires.NotNull(clientAuthenticationModule, "clientAuthenticationModule"); + var bindingElements = new List<IChannelBindingElement>(); // The order they are provided is used for outgoing messgaes, and reversed for incoming messages. - bindingElements.Add(new MessageValidationBindingElement()); + bindingElements.Add(new MessageValidationBindingElement(clientAuthenticationModule)); bindingElements.Add(new TokenCodeSerializationBindingElement()); return bindingElements.ToArray(); |