summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Channel.cs10
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs5
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs5
-rw-r--r--src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs1
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj2
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerUtilities.cs14
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs2
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AggregatingClientCredentialReader.cs21
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientAuthenticationModule.cs (renamed from src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientAuthenticationModuleBase.cs)11
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs9
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialMessagePartReader.cs2
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs18
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj1
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs27
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs25
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs16
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs59
-rw-r--r--src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenFailedResponse.cs9
-rw-r--r--src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/TokenEndpointProtocolException.cs36
-rw-r--r--src/DotNetOpenAuth.OAuth2/Configuration/OAuth2SectionGroup.cs (renamed from src/DotNetOpenAuth.OAuth2/Configuration/OAuth2Element.cs)2
-rw-r--r--src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj2
-rw-r--r--src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj1
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2ClientChannel.cs33
-rw-r--r--src/DotNetOpenAuth.Test/OAuth2/OAuth2Coordinator.cs4
24 files changed, 281 insertions, 34 deletions
diff --git a/src/DotNetOpenAuth.Core/Messaging/Channel.cs b/src/DotNetOpenAuth.Core/Messaging/Channel.cs
index fbcb3d6..235c41b 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Channel.cs
@@ -1097,6 +1097,7 @@ namespace DotNetOpenAuth.Messaging {
UriBuilder builder = new UriBuilder(requestMessage.Recipient);
MessagingUtilities.AppendQueryArgs(builder, fields);
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(builder.Uri);
+ this.PrepareHttpWebRequest(httpRequest);
return httpRequest;
}
@@ -1137,6 +1138,7 @@ namespace DotNetOpenAuth.Messaging {
var fields = messageAccessor.Serialize();
var httpRequest = (HttpWebRequest)WebRequest.Create(requestMessage.Recipient);
+ this.PrepareHttpWebRequest(httpRequest);
httpRequest.CachePolicy = this.CachePolicy;
httpRequest.Method = "POST";
@@ -1314,6 +1316,14 @@ namespace DotNetOpenAuth.Messaging {
}
/// <summary>
+ /// Performs additional processing on an outgoing web request before it is sent to the remote server.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ protected virtual void PrepareHttpWebRequest(HttpWebRequest request) {
+ Requires.NotNull(request, "request");
+ }
+
+ /// <summary>
/// Customizes the binding element order for outgoing and incoming messages.
/// </summary>
/// <param name="outgoingOrder">The outgoing order.</param>
diff --git a/src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs b/src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs
index 9579a81..dad6bf6 100644
--- a/src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/HttpRequestHeaders.cs
@@ -20,6 +20,11 @@ namespace DotNetOpenAuth.Messaging {
internal const string Authorization = "Authorization";
/// <summary>
+ /// The WWW-Authenticate header, which is included in HTTP 401 Unauthorized responses to help the client know which authorization schemes are supported.
+ /// </summary>
+ internal const string WwwAuthenticate = "WWW-Authenticate";
+
+ /// <summary>
/// The Content-Type header, which specifies the MIME type of the accompanying body data.
/// </summary>
internal const string ContentType = "Content-Type";
diff --git a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs b/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs
index a5fe782..7691cc4 100644
--- a/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/OutgoingWebResponseActionResult.cs
@@ -35,6 +35,11 @@ namespace DotNetOpenAuth.Messaging {
/// <param name="context">The context in which to set the response.</param>
public override void ExecuteResult(ControllerContext context) {
this.response.Respond(context.HttpContext);
+
+ // MVC likes to muck with our response. For example, when returning contrived 401 Unauthorized responses
+ // MVC will rewrite our response and turn it into a redirect, which breaks OAuth 2 authorization server token endpoints.
+ // It turns out we can prevent this unwanted behavior by flushing the response before returning from this method.
+ context.HttpContext.Response.Flush();
}
}
}
diff --git a/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs
index b04c67e..db131a9 100644
--- a/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs
+++ b/src/DotNetOpenAuth.OAuth/OAuth/ChannelElements/OAuthChannel.cs
@@ -297,6 +297,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements {
MessagingUtilities.AppendQueryArgs(recipientBuilder, requestMessage.ExtraData);
}
httpRequest = (HttpWebRequest)WebRequest.Create(recipientBuilder.Uri);
+ this.PrepareHttpWebRequest(httpRequest);
httpRequest.Method = GetHttpMethod(requestMessage);
httpRequest.Headers.Add(HttpRequestHeader.Authorization, MessagingUtilities.AssembleAuthorizationHeader(Protocol.AuthorizationHeaderScheme, fields));
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj
index 628db32..c6371c0 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj
@@ -36,7 +36,7 @@
<Compile Include="OAuth2\ChannelElements\IOAuth2ChannelWithAuthorizationServer.cs" />
<Compile Include="OAuth2\ChannelElements\OAuth2AuthorizationServerChannel.cs" />
<Compile Include="OAuth2\ChannelElements\RefreshToken.cs" />
- <Compile Include="OAuth2\ChannelElements\ClientAuthenticationModuleBase.cs" />
+ <Compile Include="OAuth2\ChannelElements\ClientAuthenticationModule.cs" />
<Compile Include="OAuth2\ClientDescription.cs" />
<Compile Include="OAuth2\Messages\AccessTokenAuthorizationCodeRequestAS.cs" />
<Compile Include="OAuth2\Messages\AccessTokenRefreshRequestAS.cs" />
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerUtilities.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerUtilities.cs
index cd222e2..b8a1071 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerUtilities.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthServerUtilities.cs
@@ -12,6 +12,8 @@ namespace DotNetOpenAuth.OAuth2 {
using System.Linq;
using System.Text;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth2.ChannelElements;
+ using DotNetOpenAuth.OAuth2.Messages;
/// <summary>
/// Utility methods for authorization servers.
@@ -42,13 +44,21 @@ namespace DotNetOpenAuth.OAuth2 {
/// 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="requestMessage">The request message.</param>
/// <param name="error">A single error code from <see cref="Protocol.AccessTokenRequestErrorCodes"/>.</param>
+ /// <param name="authenticationModule">The authentication module from which to glean the WWW-Authenticate header when applicable.</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) {
+ internal static void TokenEndpointVerify(bool condition, AccessTokenRequestBase requestMessage, string error, ClientAuthenticationModule authenticationModule = null, string unformattedDescription = null, params object[] args) {
if (!condition) {
string description = unformattedDescription != null ? string.Format(CultureInfo.CurrentCulture, unformattedDescription, args) : null;
- throw new TokenEndpointProtocolException(error, description);
+
+ string wwwAuthenticateHeader = null;
+ if (authenticationModule != null) {
+ wwwAuthenticateHeader = authenticationModule.AuthenticateHeader;
+ }
+
+ throw new TokenEndpointProtocolException(requestMessage, error, description, authenticateHeader: wwwAuthenticateHeader);
}
}
}
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs
index fecc6be..59b75bf 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs
@@ -148,7 +148,7 @@ namespace DotNetOpenAuth.OAuth2 {
responseMessage = new AccessTokenFailedResponse() { Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest, };
}
} catch (TokenEndpointProtocolException ex) {
- responseMessage = new AccessTokenFailedResponse() { Error = ex.Error, ErrorDescription = ex.Description, ErrorUri = ex.MoreInformation };
+ responseMessage = ex.GetResponse();
} catch (ProtocolException) {
responseMessage = new AccessTokenFailedResponse() {
Error = Protocol.AccessTokenRequestErrorCodes.InvalidRequest,
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AggregatingClientCredentialReader.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AggregatingClientCredentialReader.cs
index 4f60303..ace95b3 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AggregatingClientCredentialReader.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AggregatingClientCredentialReader.cs
@@ -33,6 +33,27 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
}
/// <summary>
+ /// Gets this module's contribution to an HTTP 401 WWW-Authenticate header so the client knows what kind of authentication this module supports.
+ /// </summary>
+ public override string AuthenticateHeader {
+ get {
+ var builder = new StringBuilder();
+ foreach (var authenticator in this.authenticators) {
+ string scheme = authenticator.AuthenticateHeader;
+ if (scheme != null) {
+ if (builder.Length > 0) {
+ builder.Append(", ");
+ }
+
+ builder.Append(scheme);
+ }
+ }
+
+ return builder.Length > 0 ? builder.ToString() : null;
+ }
+ }
+
+ /// <summary>
/// Attempts to extract client identification/authentication information from a message.
/// </summary>
/// <param name="authorizationServerHost">The authorization server host.</param>
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientAuthenticationModuleBase.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientAuthenticationModule.cs
index e835e1e..027929a 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientAuthenticationModuleBase.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientAuthenticationModule.cs
@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------
-// <copyright file="ClientAuthenticationModuleBase.cs" company="Andrew Arnott">
+// <copyright file="ClientAuthenticationModule.cs" company="Andrew Arnott">
// Copyright (c) Andrew Arnott. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
@@ -26,6 +26,13 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
}
/// <summary>
+ /// Gets this module's contribution to an HTTP 401 WWW-Authenticate header so the client knows what kind of authentication this module supports.
+ /// </summary>
+ public virtual string AuthenticateHeader {
+ get { return null; }
+ }
+
+ /// <summary>
/// Attempts to extract client identification/authentication information from a message.
/// </summary>
/// <param name="authorizationServerHost">The authorization server host.</param>
@@ -41,7 +48,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
/// <param name="clientIdentifier">The alleged client identifier.</param>
/// <param name="clientSecret">The alleged client secret to be verified.</param>
/// <returns>An indication as to the outcome of the validation.</returns>
- protected static ClientAuthenticationResult TryAuthenticateClient(IAuthorizationServerHost authorizationServerHost, string clientIdentifier, string clientSecret) {
+ protected static ClientAuthenticationResult TryAuthenticateClientBySecret(IAuthorizationServerHost authorizationServerHost, string clientIdentifier, string clientSecret) {
Requires.NotNull(authorizationServerHost, "authorizationServerHost");
if (!string.IsNullOrEmpty(clientIdentifier)) {
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs
index 44af332..655d38f 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs
@@ -18,6 +18,13 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
/// </summary>
public class ClientCredentialHttpBasicReader : ClientAuthenticationModule {
/// <summary>
+ /// Gets this module's contribution to an HTTP 401 WWW-Authenticate header so the client knows what kind of authentication this module supports.
+ /// </summary>
+ public override string AuthenticateHeader {
+ get { return "Basic"; }
+ }
+
+ /// <summary>
/// Attempts to extract client identification/authentication information from a message.
/// </summary>
/// <param name="authorizationServerHost">The authorization server host.</param>
@@ -31,7 +38,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
var credential = OAuthUtilities.ParseHttpBasicAuth(requestMessage.Headers);
if (credential != null) {
clientIdentifier = credential.UserName;
- return TryAuthenticateClient(authorizationServerHost, credential.UserName, credential.Password);
+ return TryAuthenticateClientBySecret(authorizationServerHost, credential.UserName, credential.Password);
}
clientIdentifier = null;
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialMessagePartReader.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialMessagePartReader.cs
index 6579df2..2afd06e 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialMessagePartReader.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialMessagePartReader.cs
@@ -28,7 +28,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
Requires.NotNull(requestMessage, "requestMessage");
clientIdentifier = requestMessage.ClientIdentifier;
- return TryAuthenticateClient(authorizationServerHost, requestMessage.ClientIdentifier, requestMessage.ClientSecret);
+ return TryAuthenticateClientBySecret(authorizationServerHost, requestMessage.ClientIdentifier, requestMessage.ClientSecret);
}
}
}
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs
index e114208..ac23e24 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs
@@ -37,6 +37,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
/// Initializes a new instance of the <see cref="MessageValidationBindingElement"/> class.
/// </summary>
/// <param name="clientAuthenticationModule">The aggregating client authentication module.</param>
+ /// <param name="authorizationServer">The authorization server host.</param>
internal MessageValidationBindingElement(ClientAuthenticationModule clientAuthenticationModule, IAuthorizationServerHost authorizationServer) {
Requires.NotNull(clientAuthenticationModule, "clientAuthenticationModule");
Requires.NotNull(authorizationServer, "authorizationServer");
@@ -101,11 +102,12 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
// Check that the client secret is correct for client authenticated messages.
var clientCredentialOnly = message as AccessTokenClientCredentialsRequest;
var authenticatedClientRequest = message as AuthenticatedClientRequestBase;
+ var accessTokenRequest = authenticatedClientRequest as AccessTokenRequestBase; // currently the only type of message.
if (authenticatedClientRequest != null) {
string clientIdentifier;
var result = this.clientAuthenticationModule.TryAuthenticateClient(this.authorizationServer, 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);
+ AuthServerUtilities.TokenEndpointVerify(result != ClientAuthenticationResult.ClientIdNotAuthenticated, accessTokenRequest, Protocol.AccessTokenRequestErrorCodes.UnauthorizedClient); // an empty secret is not allowed for client authenticated calls.
+ AuthServerUtilities.TokenEndpointVerify(result == ClientAuthenticationResult.ClientAuthenticated, accessTokenRequest, Protocol.AccessTokenRequestErrorCodes.InvalidClient, this.clientAuthenticationModule, AuthServerStrings.ClientSecretMismatch);
authenticatedClientRequest.ClientIdentifier = clientIdentifier;
if (clientCredentialOnly != null) {
@@ -128,12 +130,12 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
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);
+ throw new TokenEndpointProtocolException(accessTokenRequest, Protocol.AccessTokenRequestErrorCodes.InvalidGrant, AuthServerStrings.InvalidResourceOwnerPasswordCredential);
}
} catch (NotSupportedException) {
- throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.UnsupportedGrantType);
+ throw new TokenEndpointProtocolException(accessTokenRequest, Protocol.AccessTokenRequestErrorCodes.UnsupportedGrantType);
} catch (NotImplementedException) {
- throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.UnsupportedGrantType);
+ throw new TokenEndpointProtocolException(accessTokenRequest, Protocol.AccessTokenRequestErrorCodes.UnsupportedGrantType);
}
}
@@ -159,14 +161,14 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
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);
+ AuthServerUtilities.TokenEndpointVerify(string.Equals(accessRequest.ClientIdentifier, authCarrier.AuthorizationDescription.ClientIdentifier, StringComparison.Ordinal), accessTokenRequest, 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);
+ throw new TokenEndpointProtocolException(accessTokenRequest, Protocol.AccessTokenRequestErrorCodes.InvalidScope, AuthServerStrings.AccessScopeExceedsGrantScope);
}
}
}
@@ -174,7 +176,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
// 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 IAuthorizationServerHost.IsAuthorizationValid method returned false.");
- throw new TokenEndpointProtocolException(Protocol.AccessTokenRequestErrorCodes.InvalidGrant);
+ throw new TokenEndpointProtocolException(accessTokenRequest, Protocol.AccessTokenRequestErrorCodes.InvalidGrant);
}
applied = true;
diff --git a/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj b/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj
index da76ecb..e72ee1a 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj
+++ b/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj
@@ -21,6 +21,7 @@
<Compile Include="Configuration\OAuth2ClientSection.cs" />
<Compile Include="OAuth2\AuthorizationServerDescription.cs" />
<Compile Include="OAuth2\AuthorizationState.cs" />
+ <Compile Include="OAuth2\ChannelElements\IOAuth2ChannelWithClient.cs" />
<Compile Include="OAuth2\ChannelElements\OAuth2ClientChannel.cs" />
<Compile Include="OAuth2\ClientCredentialApplicator.cs" />
<Compile Include="OAuth2\IAuthorizationState.cs" />
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs
new file mode 100644
index 0000000..c802be6
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs
@@ -0,0 +1,27 @@
+//-----------------------------------------------------------------------
+// <copyright file="IOAuth2ChannelWithClient.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;
+
+ /// <summary>
+ /// An interface that defines the OAuth2 client specific channel additions.
+ /// </summary>
+ internal interface IOAuth2ChannelWithClient {
+ /// <summary>
+ /// Gets or sets the identifier by which this client is known to the Authorization Server.
+ /// </summary>
+ string ClientIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the client credentials applicator extension to use.
+ /// </summary>
+ ClientCredentialApplicator ClientCredentialApplicator { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs
index 10a92e0..8ad2ed9 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs
@@ -18,7 +18,7 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
/// <summary>
/// The messaging channel used by OAuth 2.0 Clients.
/// </summary>
- internal class OAuth2ClientChannel : OAuth2ChannelBase {
+ internal class OAuth2ClientChannel : OAuth2ChannelBase, IOAuth2ChannelWithClient {
/// <summary>
/// The messages receivable by this channel.
/// </summary>
@@ -39,6 +39,17 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
}
/// <summary>
+ /// Gets or sets the identifier by which this client is known to the Authorization Server.
+ /// </summary>
+ public string ClientIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the tool to use to apply client credentials to authenticated requests to the Authorization Server.
+ /// </summary>
+ /// <value>May be <c>null</c> if this client has no client secret.</value>
+ public ClientCredentialApplicator ClientCredentialApplicator { get; set; }
+
+ /// <summary>
/// Prepares an HTTP request that carries a given message.
/// </summary>
/// <param name="request">The message to send.</param>
@@ -132,5 +143,17 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
// Clients don't ever send direct responses.
throw new NotImplementedException();
}
+
+ /// <summary>
+ /// Performs additional processing on an outgoing web request before it is sent to the remote server.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ protected override void PrepareHttpWebRequest(HttpWebRequest request) {
+ base.PrepareHttpWebRequest(request);
+
+ if (this.ClientCredentialApplicator != null) {
+ this.ClientCredentialApplicator.ApplyClientCredential(this.ClientIdentifier, request);
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
index dd34b12..77b0f92 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
@@ -53,13 +53,23 @@ namespace DotNetOpenAuth.OAuth2 {
/// <summary>
/// Gets or sets the identifier by which this client is known to the Authorization Server.
/// </summary>
- public string ClientIdentifier { get; set; }
+ public string ClientIdentifier {
+ get { return this.OAuthChannel.ClientIdentifier; }
+ set { this.OAuthChannel.ClientIdentifier = value; }
+ }
/// <summary>
/// Gets or sets the tool to use to apply client credentials to authenticated requests to the Authorization Server.
/// </summary>
/// <value>May be <c>null</c> if this client has no client secret.</value>
- public ClientCredentialApplicator ClientCredentialApplicator { get; set; }
+ public ClientCredentialApplicator ClientCredentialApplicator {
+ get { return this.OAuthChannel.ClientCredentialApplicator; }
+ set { this.OAuthChannel.ClientCredentialApplicator = value; }
+ }
+
+ internal IOAuth2ChannelWithClient OAuthChannel {
+ get { return (IOAuth2ChannelWithClient)this.Channel; }
+ }
/// <summary>
/// Adds the necessary HTTP Authorization header to an HTTP request for protected resources
@@ -278,7 +288,7 @@ namespace DotNetOpenAuth.OAuth2 {
/// <param name="secret">The client secret. May be <c>null</c></param>
/// <returns>The client credential applicator.</returns>
protected static ClientCredentialApplicator DefaultSecretApplicator(string secret) {
- return secret == null ? ClientCredentialApplicator.NoSecret() : ClientCredentialApplicator.HttpBasic(secret);
+ return secret == null ? ClientCredentialApplicator.NoSecret() : ClientCredentialApplicator.NetworkCredential(secret);
}
/// <summary>
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs
index a95f3d0..2a9a8a7 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs
@@ -36,11 +36,21 @@ namespace DotNetOpenAuth.OAuth2 {
/// <summary>
/// Transmits the client identifier and secret in the HTTP Authorization header via HTTP Basic authentication.
/// </summary>
+ /// <param name="credential">The client id and secret.</param>
+ /// <returns>The credential applicator to provide to the <see cref="ClientBase"/> instance.</returns>
+ public static ClientCredentialApplicator NetworkCredential(NetworkCredential credential) {
+ Requires.NotNull(credential, "credential");
+ return new NetworkCredentialApplicator(credential);
+ }
+
+ /// <summary>
+ /// Transmits the client identifier and secret in the HTTP Authorization header via HTTP Basic authentication.
+ /// </summary>
/// <param name="clientSecret">The secret the client shares with the authorization server.</param>
/// <returns>The credential applicator to provide to the <see cref="ClientBase"/> instance.</returns>
- public static ClientCredentialApplicator HttpBasic(string clientSecret) {
+ public static ClientCredentialApplicator NetworkCredential(string clientSecret) {
Requires.NotNullOrEmpty(clientSecret, "clientSecret");
- return new HttpBasicApplicator(clientSecret);
+ return new NetworkCredentialApplicator(clientSecret);
}
/// <summary>
@@ -56,27 +66,50 @@ namespace DotNetOpenAuth.OAuth2 {
/// </summary>
/// <param name="clientIdentifier">The identifier by which the authorization server should recognize this client.</param>
/// <param name="request">The outbound message to apply authentication information to.</param>
- public abstract void ApplyClientCredential(string clientIdentifier, AuthenticatedClientRequestBase request);
+ public virtual void ApplyClientCredential(string clientIdentifier, AuthenticatedClientRequestBase request) {
+ }
+
+ /// <summary>
+ /// Applies the client identifier and (when applicable) the client authentication to an outbound message.
+ /// </summary>
+ /// <param name="clientIdentifier">The identifier by which the authorization server should recognize this client.</param>
+ /// <param name="request">The outbound message to apply authentication information to.</param>
+ public virtual void ApplyClientCredential(string clientIdentifier, HttpWebRequest request) {
+ }
/// <summary>
/// Authenticates the client via HTTP Basic.
/// </summary>
- private class HttpBasicApplicator : ClientCredentialApplicator {
+ private class NetworkCredentialApplicator : ClientCredentialApplicator {
+ /// <summary>
+ /// The client identifier and secret.
+ /// </summary>
+ private readonly NetworkCredential credential;
+
/// <summary>
/// The client secret.
/// </summary>
private readonly string clientSecret;
/// <summary>
- /// Initializes a new instance of the <see cref="HttpBasicApplicator"/> class.
+ /// Initializes a new instance of the <see cref="NetworkCredentialApplicator"/> class.
/// </summary>
/// <param name="clientSecret">The client secret.</param>
- internal HttpBasicApplicator(string clientSecret) {
+ internal NetworkCredentialApplicator(string clientSecret) {
Requires.NotNullOrEmpty(clientSecret, "clientSecret");
this.clientSecret = clientSecret;
}
/// <summary>
+ /// Initializes a new instance of the <see cref="NetworkCredentialApplicator"/> class.
+ /// </summary>
+ /// <param name="credential">The client credential.</param>
+ internal NetworkCredentialApplicator(NetworkCredential credential) {
+ Requires.NotNull(credential, "credential");
+ this.credential = credential;
+ }
+
+ /// <summary>
/// Applies the client identifier and (when applicable) the client authentication to an outbound message.
/// </summary>
/// <param name="clientIdentifier">The identifier by which the authorization server should recognize this client.</param>
@@ -85,7 +118,19 @@ namespace DotNetOpenAuth.OAuth2 {
// When using network credentials, the client authentication is not done as standard message parts.
request.ClientIdentifier = null;
request.ClientSecret = null;
- OAuthUtilities.ApplyHttpBasicAuth(request.Headers, clientIdentifier, this.clientSecret);
+ }
+
+ /// <summary>
+ /// Applies the client identifier and (when applicable) the client authentication to an outbound message.
+ /// </summary>
+ /// <param name="clientIdentifier">The identifier by which the authorization server should recognize this client.</param>
+ /// <param name="request">The outbound message to apply authentication information to.</param>
+ public override void ApplyClientCredential(string clientIdentifier, HttpWebRequest request) {
+ if (this.credential != null && this.credential.UserName == clientIdentifier) {
+ ErrorUtilities.VerifyHost(false, "Client identifiers \"{0}\" and \"{1}\" do not match.", this.credential.UserName, clientIdentifier);
+ }
+
+ request.Credentials = this.credential ?? new NetworkCredential(clientIdentifier, this.clientSecret);
}
}
diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenFailedResponse.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenFailedResponse.cs
index 8c4b1c3..4aaf928 100644
--- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenFailedResponse.cs
+++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AccessTokenFailedResponse.cs
@@ -25,6 +25,11 @@ namespace DotNetOpenAuth.OAuth2.Messages {
private readonly bool invalidClientCredentialsInAuthorizationHeader;
/// <summary>
+ /// The headers to include in the response.
+ /// </summary>
+ private readonly WebHeaderCollection headers = new WebHeaderCollection();
+
+ /// <summary>
/// Initializes a new instance of the <see cref="AccessTokenFailedResponse"/> class.
/// </summary>
/// <param name="request">The faulty request.</param>
@@ -63,8 +68,8 @@ namespace DotNetOpenAuth.OAuth2.Messages {
/// Gets the HTTP headers to add to the response.
/// </summary>
/// <value>May be an empty collection, but must not be <c>null</c>.</value>
- WebHeaderCollection IHttpDirectResponse.Headers {
- get { return new WebHeaderCollection(); }
+ public WebHeaderCollection Headers {
+ get { return this.headers; }
}
#endregion
diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/TokenEndpointProtocolException.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/TokenEndpointProtocolException.cs
index 308bfe2..e86c27e 100644
--- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/TokenEndpointProtocolException.cs
+++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/TokenEndpointProtocolException.cs
@@ -12,24 +12,40 @@ namespace DotNetOpenAuth.OAuth2 {
using System.Text;
using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth2.Messages;
/// <summary>
/// Describes an error generated by an Authorization Server's token endpoint.
/// </summary>
public class TokenEndpointProtocolException : ProtocolException {
/// <summary>
+ /// The message being processed that caused this exception to be thrown.
+ /// </summary>
+ private readonly AccessTokenRequestBase requestMessage;
+
+ /// <summary>
+ /// The WWW-Authenticate header to add to the response message.
+ /// </summary>
+ private readonly string authenticateHeader;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="TokenEndpointProtocolException"/> class.
/// </summary>
+ /// <param name="requestMessage">The message whose processing resulted in this error.</param>
/// <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)
+ /// <param name="authenticateHeader">The WWW-Authenticate header to add to the response.</param>
+ public TokenEndpointProtocolException(AccessTokenRequestBase requestMessage, string error, string description = null, Uri moreInformation = null, string authenticateHeader = null)
: base(string.Format(CultureInfo.CurrentCulture, ClientAuthorizationStrings.TokenEndpointErrorFormat, error, description)) {
+ Requires.NotNull(requestMessage, "requestMessage");
Requires.NotNullOrEmpty(error, "error");
+ this.requestMessage = requestMessage;
this.Error = error;
this.Description = description;
this.MoreInformation = moreInformation;
+ this.authenticateHeader = authenticateHeader;
}
/// <summary>
@@ -55,5 +71,23 @@ namespace DotNetOpenAuth.OAuth2 {
/// 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; }
+
+ /// <summary>
+ /// Gets the response message to send to the client.
+ /// </summary>
+ /// <returns>A message.</returns>
+ public IDirectResponseProtocolMessage GetResponse() {
+ var response = this.requestMessage != null
+ ? new AccessTokenFailedResponse(this.requestMessage, this.authenticateHeader != null)
+ : new AccessTokenFailedResponse();
+ response.Error = this.Error;
+ response.ErrorDescription = this.Description;
+ response.ErrorUri = this.MoreInformation;
+ if (this.authenticateHeader != null) {
+ response.Headers.Add(HttpRequestHeaders.WwwAuthenticate, this.authenticateHeader);
+ }
+
+ return response;
+ }
}
}
diff --git a/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2Element.cs b/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2SectionGroup.cs
index 858d27b..112e756 100644
--- a/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2Element.cs
+++ b/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2SectionGroup.cs
@@ -1,5 +1,5 @@
//-----------------------------------------------------------------------
-// <copyright file="OAuth2Element.cs" company="Outercurve Foundation">
+// <copyright file="OAuth2SectionGroup.cs" company="Outercurve Foundation">
// Copyright (c) Outercurve Foundation. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
diff --git a/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj b/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj
index 4b1d534..b359508 100644
--- a/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj
+++ b/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj
@@ -18,7 +18,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
- <Compile Include="Configuration\OAuth2Element.cs" />
+ <Compile Include="Configuration\OAuth2SectionGroup.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="OAuth2\AccessToken.cs" />
<Compile Include="OAuth2\ChannelElements\AuthorizationDataBag.cs" />
diff --git a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
index a3edcf6..dae4a65 100644
--- a/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
+++ b/src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj
@@ -219,6 +219,7 @@
<Compile Include="Mocks\CoordinatingChannel.cs" />
<Compile Include="Mocks\CoordinatingHttpRequestInfo.cs" />
<Compile Include="Mocks\CoordinatingOAuth2AuthServerChannel.cs" />
+ <Compile Include="Mocks\CoordinatingOAuth2ClientChannel.cs" />
<Compile Include="Mocks\CoordinatingOutgoingWebResponse.cs" />
<Compile Include="Mocks\CoordinatingOAuthConsumerChannel.cs" />
<Compile Include="Mocks\IBaseMessageExplicitMembers.cs" />
diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2ClientChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2ClientChannel.cs
new file mode 100644
index 0000000..52f381d
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2ClientChannel.cs
@@ -0,0 +1,33 @@
+//-----------------------------------------------------------------------
+// <copyright file="CoordinatingOAuth2ClientChannel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Test.Mocks {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth2.ChannelElements;
+
+ internal class CoordinatingOAuth2ClientChannel : CoordinatingChannel, IOAuth2ChannelWithClient {
+ private OAuth2ClientChannel wrappedChannel;
+
+ internal CoordinatingOAuth2ClientChannel(Channel wrappedChannel, Action<IProtocolMessage> incomingMessageFilter, Action<IProtocolMessage> outgoingMessageFilter)
+ : base(wrappedChannel, incomingMessageFilter, outgoingMessageFilter) {
+ this.wrappedChannel = (OAuth2ClientChannel)wrappedChannel;
+ }
+
+ public string ClientIdentifier {
+ get { return this.wrappedChannel.ClientIdentifier; }
+ set { this.wrappedChannel.ClientIdentifier = value; }
+ }
+
+ public DotNetOpenAuth.OAuth2.ClientCredentialApplicator ClientCredentialApplicator {
+ get { return this.wrappedChannel.ClientCredentialApplicator; }
+ set { this.wrappedChannel.ClientCredentialApplicator = value; }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/DotNetOpenAuth.Test/OAuth2/OAuth2Coordinator.cs b/src/DotNetOpenAuth.Test/OAuth2/OAuth2Coordinator.cs
index afc1335..1223fe7 100644
--- a/src/DotNetOpenAuth.Test/OAuth2/OAuth2Coordinator.cs
+++ b/src/DotNetOpenAuth.Test/OAuth2/OAuth2Coordinator.cs
@@ -35,13 +35,13 @@ namespace DotNetOpenAuth.Test.OAuth2 {
this.client = client;
this.client.ClientIdentifier = OAuth2TestBase.ClientId;
- this.client.ClientCredentialApplicator = ClientCredentialApplicator.HttpBasic(OAuth2TestBase.ClientSecret);
+ this.client.ClientCredentialApplicator = ClientCredentialApplicator.NetworkCredential(OAuth2TestBase.ClientSecret);
}
internal override void Run() {
var authServer = new AuthorizationServer(this.authServerHost);
- var rpCoordinatingChannel = new CoordinatingChannel(this.client.Channel, this.IncomingMessageFilter, this.OutgoingMessageFilter);
+ var rpCoordinatingChannel = new CoordinatingOAuth2ClientChannel(this.client.Channel, this.IncomingMessageFilter, this.OutgoingMessageFilter);
var opCoordinatingChannel = new CoordinatingOAuth2AuthServerChannel(authServer.Channel, this.IncomingMessageFilter, this.OutgoingMessageFilter);
rpCoordinatingChannel.RemoteChannel = opCoordinatingChannel;
opCoordinatingChannel.RemoteChannel = rpCoordinatingChannel;