summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Arnott <andrewarnott@gmail.com>2012-04-18 19:55:50 -0700
committerAndrew Arnott <andrewarnott@gmail.com>2012-04-18 19:55:50 -0700
commit2ddd19d9f037bebbbdc80d7de35ce4d899710859 (patch)
treebaf8ad19d4799b1a1284b9fc668afd53e3b008a4
parentbd0de8217763d02759815b91588cd578becf496b (diff)
downloadDotNetOpenAuth-2ddd19d9f037bebbbdc80d7de35ce4d899710859.zip
DotNetOpenAuth-2ddd19d9f037bebbbdc80d7de35ce4d899710859.tar.gz
DotNetOpenAuth-2ddd19d9f037bebbbdc80d7de35ce4d899710859.tar.bz2
We have HTTP Basic client authentication working now in OAuth 2.
-rw-r--r--nuget/DotNetOpenAuth.OAuth2.Core.nuspec2
-rw-r--r--nuget/content/OAuth2.Core/web.config.transform7
-rw-r--r--projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj2
-rw-r--r--projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj11
-rw-r--r--samples/OAuthAuthorizationServer/Web.config6
-rw-r--r--samples/OAuthClient/Facebook.aspx.cs2
-rw-r--r--samples/OAuthClient/WindowsLive.aspx.cs2
-rw-r--r--samples/OAuthServiceProvider/OAuthServiceProvider.csproj4
-rw-r--r--src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuth.xsd65
-rw-r--r--src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj2
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Channel.cs15
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs16
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs2
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs22
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequestContract.cs51
-rw-r--r--src/DotNetOpenAuth.Core/Requires.cs4
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj4
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs30
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/AggregatingClientCredentialReader.cs62
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialHttpBasicReader.cs37
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialMessagePartReader.cs29
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/ClientCredentialReader.cs50
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/MessageValidationBindingElement.cs15
-rw-r--r--src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/ChannelElements/OAuth2AuthorizationServerChannel.cs10
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj4
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs15
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/OAuth2ClientChannel.cs20
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs52
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs75
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs31
-rw-r--r--src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs17
-rw-r--r--src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs15
-rw-r--r--src/DotNetOpenAuth.OAuth2/Configuration/OAuth2AuthorizationServerElement.cs55
-rw-r--r--src/DotNetOpenAuth.OAuth2/Configuration/OAuth2ClientElement.cs20
-rw-r--r--src/DotNetOpenAuth.OAuth2/Configuration/OAuth2Element.cs78
-rw-r--r--src/DotNetOpenAuth.OAuth2/Configuration/OAuth2ResourceServerElement.cs20
-rw-r--r--src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj5
-rw-r--r--src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/IClientAuthenticationModule.cs22
-rw-r--r--src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs41
-rw-r--r--src/DotNetOpenAuth.OpenId/Configuration/OpenIdRelyingPartyElement.cs3
-rw-r--r--src/DotNetOpenAuth.Test/DotNetOpenAuth.Test.csproj1
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs37
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs7
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2ClientChannel.cs27
-rw-r--r--src/DotNetOpenAuth.Test/OAuth2/MessageFactoryTests.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OAuth2/OAuth2Coordinator.cs5
-rw-r--r--src/DotNetOpenAuth.Test/OAuth2/UserAgentClientAuthorizeTests.cs2
-rw-r--r--src/DotNetOpenAuth.Test/OAuth2/WebServerClientAuthorizeTests.cs1
-rw-r--r--src/DotNetOpenAuth.Test/TestUtilities.cs21
49 files changed, 950 insertions, 76 deletions
diff --git a/nuget/DotNetOpenAuth.OAuth2.Core.nuspec b/nuget/DotNetOpenAuth.OAuth2.Core.nuspec
index f957069..28c2614 100644
--- a/nuget/DotNetOpenAuth.OAuth2.Core.nuspec
+++ b/nuget/DotNetOpenAuth.OAuth2.Core.nuspec
@@ -26,6 +26,8 @@
<file src="$OutputPath35$DotNetOpenAuth.OAuth2.xml" target="lib\net35-full" />
<file src="$OutputPath40$DotNetOpenAuth.OAuth2.xml" target="lib\net40-full" />
+ <file src="content\OAuth2.Core\web.config.transform" target="content\web.config.transform" />
+
<file src="..\src\DotNetOpenAuth.OAuth2\**\*.cs" target="src" />
</files>
</package> \ No newline at end of file
diff --git a/nuget/content/OAuth2.Core/web.config.transform b/nuget/content/OAuth2.Core/web.config.transform
new file mode 100644
index 0000000..cbb42e1
--- /dev/null
+++ b/nuget/content/OAuth2.Core/web.config.transform
@@ -0,0 +1,7 @@
+<configuration>
+ <configSections>
+ <sectionGroup name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection, DotNetOpenAuth.Core">
+ <section name="oauth2" type="DotNetOpenAuth.Configuration.OAuth2Element, DotNetOpenAuth.OAuth2" requirePermission="false" allowLocation="true" />
+ </sectionGroup>
+ </configSections>
+</configuration> \ No newline at end of file
diff --git a/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj b/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj
index 4db4969..4ef10f6 100644
--- a/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj
+++ b/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj
@@ -81,7 +81,6 @@
<Compile Include="Controllers\HomeController.cs" />
<Compile Include="Default.aspx.cs">
<DependentUpon>Default.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
@@ -94,7 +93,6 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Setup.aspx.cs">
<DependentUpon>Setup.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Setup.aspx.designer.cs">
<DependentUpon>Setup.aspx</DependentUpon>
diff --git a/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj b/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj
index 46236ab..92a0b5f 100644
--- a/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj
+++ b/projecttemplates/WebFormsRelyingParty/WebFormsRelyingParty.csproj
@@ -99,42 +99,36 @@
<Compile Include="Code\SiteUtilities.cs" />
<Compile Include="Members\OAuthAuthorize.aspx.cs">
<DependentUpon>OAuthAuthorize.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Members\OAuthAuthorize.aspx.designer.cs">
<DependentUpon>OAuthAuthorize.aspx</DependentUpon>
</Compile>
<Compile Include="LoginFrame.aspx.cs">
<DependentUpon>LoginFrame.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="LoginFrame.aspx.designer.cs">
<DependentUpon>LoginFrame.aspx</DependentUpon>
</Compile>
<Compile Include="Members\AccountInfo.aspx.cs">
<DependentUpon>AccountInfo.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Members\AccountInfo.aspx.designer.cs">
<DependentUpon>AccountInfo.aspx</DependentUpon>
</Compile>
<Compile Include="Admin\Admin.Master.cs">
<DependentUpon>Admin.Master</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Admin\Admin.Master.designer.cs">
<DependentUpon>Admin.Master</DependentUpon>
</Compile>
<Compile Include="Admin\Default.aspx.cs">
<DependentUpon>Default.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Admin\Default.aspx.designer.cs">
<DependentUpon>Default.aspx</DependentUpon>
</Compile>
<Compile Include="Default.aspx.cs">
<DependentUpon>Default.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Default.aspx.designer.cs">
<DependentUpon>Default.aspx</DependentUpon>
@@ -144,21 +138,18 @@
</Compile>
<Compile Include="Login.aspx.cs">
<DependentUpon>Login.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Login.aspx.designer.cs">
<DependentUpon>Login.aspx</DependentUpon>
</Compile>
<Compile Include="Logout.aspx.cs">
<DependentUpon>Logout.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Logout.aspx.designer.cs">
<DependentUpon>Logout.aspx</DependentUpon>
</Compile>
<Compile Include="Members\Default.aspx.cs">
<DependentUpon>Default.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Members\Default.aspx.designer.cs">
<DependentUpon>Default.aspx</DependentUpon>
@@ -169,14 +160,12 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Setup.aspx.cs">
<DependentUpon>Setup.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Setup.aspx.designer.cs">
<DependentUpon>Setup.aspx</DependentUpon>
</Compile>
<Compile Include="Site.Master.cs">
<DependentUpon>Site.Master</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Site.Master.designer.cs">
<DependentUpon>Site.Master</DependentUpon>
diff --git a/samples/OAuthAuthorizationServer/Web.config b/samples/OAuthAuthorizationServer/Web.config
index b68bb88..e98d63b 100644
--- a/samples/OAuthAuthorizationServer/Web.config
+++ b/samples/OAuthAuthorizationServer/Web.config
@@ -38,6 +38,12 @@
<dotNetOpenAuth>
<!-- Allow DotNetOpenAuth to publish usage statistics to library authors to improve the library. -->
<reporting enabled="true" />
+ <oauth2>
+ <authorizationServer>
+ <clientAuthenticationModules>
+ </clientAuthenticationModules>
+ </authorizationServer>
+ </oauth2>
<!-- Relaxing SSL requirements is useful for simple samples, but NOT a good idea in production. -->
<messaging relaxSslRequirements="true">
diff --git a/samples/OAuthClient/Facebook.aspx.cs b/samples/OAuthClient/Facebook.aspx.cs
index 0f71712..6202651 100644
--- a/samples/OAuthClient/Facebook.aspx.cs
+++ b/samples/OAuthClient/Facebook.aspx.cs
@@ -10,7 +10,7 @@
public partial class Facebook : System.Web.UI.Page {
private static readonly FacebookClient client = new FacebookClient {
ClientIdentifier = ConfigurationManager.AppSettings["facebookAppID"],
- ClientSecret = ConfigurationManager.AppSettings["facebookAppSecret"],
+ ClientCredentialApplicator = ClientCredentialApplicator.SecretParameter(ConfigurationManager.AppSettings["facebookAppSecret"]),
};
protected void Page_Load(object sender, EventArgs e) {
diff --git a/samples/OAuthClient/WindowsLive.aspx.cs b/samples/OAuthClient/WindowsLive.aspx.cs
index b550e17..e5eb5d6 100644
--- a/samples/OAuthClient/WindowsLive.aspx.cs
+++ b/samples/OAuthClient/WindowsLive.aspx.cs
@@ -14,7 +14,7 @@
public partial class WindowsLive : System.Web.UI.Page {
private static readonly WindowsLiveClient client = new WindowsLiveClient {
ClientIdentifier = ConfigurationManager.AppSettings["windowsLiveAppID"],
- ClientSecret = ConfigurationManager.AppSettings["WindowsLiveAppSecret"],
+ ClientCredentialApplicator = ClientCredentialApplicator.SecretParameter(ConfigurationManager.AppSettings["WindowsLiveAppSecret"]),
};
protected void Page_Load(object sender, EventArgs e) {
diff --git a/samples/OAuthServiceProvider/OAuthServiceProvider.csproj b/samples/OAuthServiceProvider/OAuthServiceProvider.csproj
index c96721e..fd2a5bb 100644
--- a/samples/OAuthServiceProvider/OAuthServiceProvider.csproj
+++ b/samples/OAuthServiceProvider/OAuthServiceProvider.csproj
@@ -114,19 +114,15 @@
</Compile>
<Compile Include="Default.aspx.cs">
<DependentUpon>Default.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Members\Authorize.aspx.cs">
<DependentUpon>Authorize.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="Members\AuthorizedConsumers.aspx.cs">
<DependentUpon>AuthorizedConsumers.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="TracePage.aspx.cs">
<DependentUpon>TracePage.aspx</DependentUpon>
- <SubType>ASPXCodeBehind</SubType>
</Compile>
<Compile Include="TracePage.aspx.designer.cs">
<DependentUpon>TracePage.aspx</DependentUpon>
diff --git a/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuth.xsd b/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuth.xsd
index d193776..3caadde 100644
--- a/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuth.xsd
+++ b/src/DotNetOpenAuth.Core/Configuration/DotNetOpenAuth.xsd
@@ -898,6 +898,71 @@
</xs:choice>
</xs:complexType>
</xs:element>
+ <xs:element name="oauth2">
+ <xs:annotation>
+ <xs:documentation>
+ Settings OAuth 2 clients, authorization servers and resource servers.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="client">
+ <xs:annotation>
+ <xs:documentation>
+ Settings applicable to OAuth 2 Clients.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="authorizationServer">
+ <xs:annotation>
+ <xs:documentation>
+ Settings applicable to OAuth 2 Authorization Servers.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="clientAuthenticationModules">
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="add">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="remove">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="clear">
+ <xs:complexType>
+ <!--tag is empty-->
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="resourceServer">
+ <xs:annotation>
+ <xs:documentation>
+ Settings applicable to OAuth 2 Resource Servers.
+ </xs:documentation>
+ </xs:annotation>
+ <xs:complexType>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:complexType>
+ </xs:element>
<xs:element name="reporting">
<xs:annotation>
<xs:documentation>
diff --git a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
index 447a3c5..65dee44 100644
--- a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
+++ b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
@@ -29,6 +29,8 @@
<Compile Include="Messaging\ChannelContract.cs" />
<Compile Include="Messaging\DataBagFormatterBase.cs" />
<Compile Include="Messaging\HttpRequestHeaders.cs" />
+ <Compile Include="Messaging\IHttpDirectRequest.cs" />
+ <Compile Include="Messaging\IHttpDirectRequestContract.cs" />
<Compile Include="Messaging\IHttpIndirectResponse.cs" />
<Compile Include="Messaging\IMessageOriginalPayload.cs" />
<Compile Include="Messaging\DirectWebRequestOptions.cs" />
diff --git a/src/DotNetOpenAuth.Core/Messaging/Channel.cs b/src/DotNetOpenAuth.Core/Messaging/Channel.cs
index 016a2b6..fbcb3d6 100644
--- a/src/DotNetOpenAuth.Core/Messaging/Channel.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/Channel.cs
@@ -478,6 +478,14 @@ namespace DotNetOpenAuth.Messaging {
IDirectedProtocolMessage requestMessage = this.ReadFromRequestCore(httpRequest);
if (requestMessage != null) {
Logger.Channel.DebugFormat("Incoming request received: {0}", requestMessage.GetType().Name);
+
+ var directRequest = requestMessage as IHttpDirectRequest;
+ if (directRequest != null) {
+ foreach (string header in httpRequest.Headers) {
+ directRequest.Headers[header] = httpRequest.Headers[header];
+ }
+ }
+
this.ProcessIncomingMessage(requestMessage);
}
@@ -717,6 +725,13 @@ namespace DotNetOpenAuth.Messaging {
Requires.True(request.Recipient != null, "request", MessagingStrings.DirectedMessageMissingRecipient);
HttpWebRequest webRequest = this.CreateHttpRequest(request);
+ var directRequest = request as IHttpDirectRequest;
+ if (directRequest != null) {
+ foreach (string header in directRequest.Headers) {
+ webRequest.Headers[header] = directRequest.Headers[header];
+ }
+ }
+
IDictionary<string, string> responseFields;
IDirectResponseProtocolMessage responseMessage;
diff --git a/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs b/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs
index f499d67..2237cc7 100644
--- a/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/ErrorUtilities.cs
@@ -193,17 +193,17 @@ namespace DotNetOpenAuth.Messaging {
/// Throws a <see cref="ProtocolException"/> if some <paramref name="condition"/> evaluates to false.
/// </summary>
/// <param name="condition">True to do nothing; false to throw the exception.</param>
- /// <param name="message">The error message for the exception.</param>
+ /// <param name="unformattedMessage">The error message for the exception.</param>
/// <param name="args">The string formatting arguments, if any.</param>
/// <exception cref="ProtocolException">Thrown if <paramref name="condition"/> evaluates to <c>false</c>.</exception>
[Pure]
- internal static void VerifyProtocol(bool condition, string message, params object[] args) {
+ internal static void VerifyProtocol(bool condition, string unformattedMessage, params object[] args) {
Requires.NotNull(args, "args");
Contract.Ensures(condition);
Contract.EnsuresOnThrow<ProtocolException>(!condition);
- Contract.Assume(message != null);
+ Contract.Assume(unformattedMessage != null);
if (!condition) {
- var exception = new ProtocolException(string.Format(CultureInfo.CurrentCulture, message, args));
+ var exception = new ProtocolException(string.Format(CultureInfo.CurrentCulture, unformattedMessage, args));
if (Logger.Messaging.IsErrorEnabled) {
Logger.Messaging.Error(
string.Format(
@@ -220,7 +220,7 @@ namespace DotNetOpenAuth.Messaging {
/// <summary>
/// Throws a <see cref="ProtocolException"/>.
/// </summary>
- /// <param name="message">The message to set in the exception.</param>
+ /// <param name="unformattedMessage">The message to set in the exception.</param>
/// <param name="args">The formatting arguments of the message.</param>
/// <returns>
/// An InternalErrorException, which may be "thrown" by the caller in order
@@ -229,10 +229,10 @@ namespace DotNetOpenAuth.Messaging {
/// </returns>
/// <exception cref="ProtocolException">Always thrown.</exception>
[Pure]
- internal static Exception ThrowProtocol(string message, params object[] args) {
+ internal static Exception ThrowProtocol(string unformattedMessage, params object[] args) {
Requires.NotNull(args, "args");
- Contract.Assume(message != null);
- VerifyProtocol(false, message, args);
+ Contract.Assume(unformattedMessage != null);
+ VerifyProtocol(false, unformattedMessage, args);
// we never reach here, but this allows callers to "throw" this method.
return new InternalErrorException();
diff --git a/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs b/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs
index 0f60e04..f613dc5 100644
--- a/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs
+++ b/src/DotNetOpenAuth.Core/Messaging/HttpRequestInfo.cs
@@ -90,7 +90,7 @@ namespace DotNetOpenAuth.Messaging {
this.requestUri = requestUri;
this.form = form ?? new NameValueCollection();
this.queryString = HttpUtility.ParseQueryString(requestUri.Query);
- this.headers = headers ?? new NameValueCollection();
+ this.headers = headers ?? new WebHeaderCollection();
this.serverVariables = new NameValueCollection();
}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs
new file mode 100644
index 0000000..7153334
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequest.cs
@@ -0,0 +1,22 @@
+//-----------------------------------------------------------------------
+// <copyright file="IHttpDirectRequest.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System.Diagnostics.Contracts;
+ using System.Net;
+
+ /// <summary>
+ /// An interface that allows direct request messages to capture the details of the HTTP request they arrived on.
+ /// </summary>
+ [ContractClass(typeof(IHttpDirectRequestContract))]
+ public interface IHttpDirectRequest : IMessage {
+ /// <summary>
+ /// Gets the HTTP headers of the request.
+ /// </summary>
+ /// <value>May be an empty collection, but must not be <c>null</c>.</value>
+ WebHeaderCollection Headers { get; }
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequestContract.cs b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequestContract.cs
new file mode 100644
index 0000000..c17a4c2
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/Messaging/IHttpDirectRequestContract.cs
@@ -0,0 +1,51 @@
+//-----------------------------------------------------------------------
+// <copyright file="IHttpDirectRequestContract.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+
+ /// <summary>
+ /// Contract class for the <see cref="IHttpDirectRequest"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(IHttpDirectRequest))]
+ public abstract class IHttpDirectRequestContract : IHttpDirectRequest {
+ #region IHttpDirectRequest Members
+
+ /// <summary>
+ /// Gets the HTTP headers of the request.
+ /// </summary>
+ /// <value>May be an empty collection, but must not be <c>null</c>.</value>
+ WebHeaderCollection IHttpDirectRequest.Headers {
+ get {
+ Contract.Ensures(Contract.Result<WebHeaderCollection>() != null);
+ throw new NotImplementedException();
+ }
+ }
+
+ #endregion
+
+ #region IMessage Members
+
+ Version IMessage.Version {
+ get { throw new NotImplementedException(); }
+ }
+
+ IDictionary<string, string> IMessage.ExtraData {
+ get { throw new NotImplementedException(); }
+ }
+
+ void IMessage.EnsureValidMessage() {
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth.Core/Requires.cs b/src/DotNetOpenAuth.Core/Requires.cs
index 7a196a3..5cd8b6a 100644
--- a/src/DotNetOpenAuth.Core/Requires.cs
+++ b/src/DotNetOpenAuth.Core/Requires.cs
@@ -47,10 +47,12 @@ namespace DotNetOpenAuth {
[ContractArgumentValidator]
#endif
[Pure, DebuggerStepThrough]
- internal static void NotNullOrEmpty(string value, string parameterName) {
+ internal static string NotNullOrEmpty(string value, string parameterName) {
NotNull(value, parameterName);
True(value.Length > 0, parameterName, Strings.EmptyStringNotAllowed);
+ Contract.Ensures(Contract.Result<string>() == value);
Contract.EndContractBlock();
+ return value;
}
/// <summary>
diff --git a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj
index c1f3124..ad21f21 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/DotNetOpenAuth.OAuth2.AuthorizationServer.csproj
@@ -25,6 +25,9 @@
<DependentUpon>AuthServerStrings.resx</DependentUpon>
</Compile>
<Compile Include="OAuth2\AuthServerUtilities.cs" />
+ <Compile Include="OAuth2\ChannelElements\AggregatingClientCredentialReader.cs" />
+ <Compile Include="OAuth2\ChannelElements\ClientCredentialHttpBasicReader.cs" />
+ <Compile Include="OAuth2\ChannelElements\ClientCredentialMessagePartReader.cs" />
<Compile Include="OAuth2\ChannelElements\TokenCodeSerializationBindingElement.cs" />
<Compile Include="OAuth2\ChannelElements\AuthorizationCode.cs" />
<Compile Include="OAuth2\ChannelElements\MessageValidationBindingElement.cs" />
@@ -32,6 +35,7 @@
<Compile Include="OAuth2\ChannelElements\IOAuth2ChannelWithAuthorizationServer.cs" />
<Compile Include="OAuth2\ChannelElements\OAuth2AuthorizationServerChannel.cs" />
<Compile Include="OAuth2\ChannelElements\RefreshToken.cs" />
+ <Compile Include="OAuth2\ChannelElements\ClientCredentialReader.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/AuthorizationServer.cs b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs
index 3809c3d..dc8245d 100644
--- a/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs
+++ b/src/DotNetOpenAuth.OAuth2.AuthorizationServer/OAuth2/AuthorizationServer.cs
@@ -13,7 +13,7 @@ namespace DotNetOpenAuth.OAuth2 {
using System.Security.Cryptography;
using System.Text;
using System.Web;
-
+ using DotNetOpenAuth.Configuration;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth2.ChannelElements;
using DotNetOpenAuth.OAuth2.Messages;
@@ -23,12 +23,34 @@ namespace DotNetOpenAuth.OAuth2 {
/// </summary>
public class AuthorizationServer {
/// <summary>
+ /// The built-in set of client authentication modules.
+ /// </summary>
+ private static readonly TypeConfigurationCollection<IClientAuthenticationModule> defaultClientAuthenticationModules =
+ new TypeConfigurationCollection<IClientAuthenticationModule>(new Type[] { typeof(ClientCredentialHttpBasicReader), typeof(ClientCredentialMessagePartReader) });
+
+ private readonly List<IClientAuthenticationModule> clientAuthenticationModules = new List<IClientAuthenticationModule>();
+
+ private readonly ClientAuthenticationModuleBase aggregatingClientAuthenticationModule;
+
+ /// <summary>
/// Initializes a new instance of the <see cref="AuthorizationServer"/> class.
/// </summary>
/// <param name="authorizationServer">The authorization server.</param>
public AuthorizationServer(IAuthorizationServerHost authorizationServer) {
Requires.NotNull(authorizationServer, "authorizationServer");
- this.Channel = new OAuth2AuthorizationServerChannel(authorizationServer);
+ this.aggregatingClientAuthenticationModule = new AggregatingClientCredentialReader(this.clientAuthenticationModules);
+ this.Channel = new OAuth2AuthorizationServerChannel(authorizationServer, this.aggregatingClientAuthenticationModule);
+
+ var modules = OAuth2Element.Configuration.AuthorizationServer.ClientAuthenticationModules;
+ if (modules.Count == 0) {
+ modules = defaultClientAuthenticationModules;
+ }
+
+ // TODO: work this out once we move configurations into the oauth2 authorization server.
+ ////this.clientAuthenticationModules.AddRange(modules.CreateInstances(true));
+ this.clientAuthenticationModules.Add(new ClientCredentialMessagePartReader(authorizationServer));
+ this.clientAuthenticationModules.Add(new ClientCredentialHttpBasicReader(authorizationServer));
+
}
/// <summary>
@@ -45,6 +67,10 @@ namespace DotNetOpenAuth.OAuth2 {
get { return ((IOAuth2ChannelWithAuthorizationServer)this.Channel).AuthorizationServer; }
}
+ public IList<IClientAuthenticationModule> ClientAuthenticationModules {
+ get { return this.clientAuthenticationModules; }
+ }
+
/// <summary>
/// Reads in a client's request for the Authorization Server to obtain permission from
/// the user to authorize the Client's access of some protected resource(s).
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();
diff --git a/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj b/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj
index 662bf86..cfe7d6d 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj
+++ b/src/DotNetOpenAuth.OAuth2.Client/DotNetOpenAuth.OAuth2.Client.csproj
@@ -20,7 +20,9 @@
<ItemGroup>
<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" />
<Compile Include="OAuth2\IClientAuthorizationTracker.cs" />
<Compile Include="OAuth2\Messages\AccessTokenAuthorizationCodeRequestC.cs" />
@@ -60,4 +62,4 @@
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" />
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))\EnlistmentInfo.targets" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), EnlistmentInfo.targets))' != '' " />
-</Project>
+</Project> \ No newline at end of file
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..85c3242
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ChannelElements/IOAuth2ChannelWithClient.cs
@@ -0,0 +1,15 @@
+namespace DotNetOpenAuth.OAuth2.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ internal interface IOAuth2ChannelWithClient {
+ /// <summary>
+ /// Gets or sets the identifier by which this client is known to the Authorization Server.
+ /// </summary>
+ string ClientIdentifier { get; set; }
+
+ 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 54fc110..51b1646 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>
@@ -34,10 +34,18 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
/// <summary>
/// Initializes a new instance of the <see cref="OAuth2ClientChannel"/> class.
/// </summary>
- internal OAuth2ClientChannel() : base(MessageTypes) {
+ internal OAuth2ClientChannel()
+ : base(MessageTypes) {
}
/// <summary>
+ /// Gets or sets the identifier by which this client is known to the Authorization Server.
+ /// </summary>
+ public string ClientIdentifier { get; set; }
+
+ public ClientCredentialApplicator ClientCredentialApplicator { get; set; }
+
+ /// <summary>
/// Prepares an HTTP request that carries a given message.
/// </summary>
/// <param name="request">The message to send.</param>
@@ -131,5 +139,13 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements {
// Clients don't ever send direct responses.
throw new NotImplementedException();
}
+
+ protected override IncomingWebResponse GetDirectResponse(HttpWebRequest webRequest) {
+ if (this.ClientCredentialApplicator != null) {
+ this.ClientCredentialApplicator.ApplyClientCredential(this.ClientIdentifier, webRequest);
+ }
+
+ return base.GetDirectResponse(webRequest);
+ }
}
}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
index 77b9cc6..271a87e 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientBase.cs
@@ -26,13 +26,16 @@ namespace DotNetOpenAuth.OAuth2 {
/// </summary>
/// <param name="authorizationServer">The token issuer.</param>
/// <param name="clientIdentifier">The client identifier.</param>
- /// <param name="clientSecret">The client secret.</param>
- protected ClientBase(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null) {
+ /// <param name="clientCredentialApplicator">
+ /// The tool to use to apply client credentials to authenticated requests to the Authorization Server.
+ /// May be <c>null</c> for clients with no secret or other means of authentication.
+ /// </param>
+ protected ClientBase(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, ClientCredentialApplicator clientCredentialApplicator = null) {
Requires.NotNull(authorizationServer, "authorizationServer");
this.AuthorizationServer = authorizationServer;
this.Channel = new OAuth2ClientChannel();
this.ClientIdentifier = clientIdentifier;
- this.ClientSecret = clientSecret;
+ this.ClientCredentialApplicator = clientCredentialApplicator;
}
/// <summary>
@@ -50,12 +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 client secret shared with the Authorization Server.
+ /// Gets or sets the tool to use to apply client credentials to authenticated requests to the Authorization Server.
/// </summary>
- public string ClientSecret { get; set; }
+ /// <value>May be <c>null</c> if this client has no client secret.</value>
+ 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
@@ -118,10 +132,11 @@ namespace DotNetOpenAuth.OAuth2 {
var request = new AccessTokenRefreshRequestC(this.AuthorizationServer) {
ClientIdentifier = this.ClientIdentifier,
- ClientSecret = this.ClientSecret,
RefreshToken = authorization.RefreshToken,
};
+ this.ApplyClientCredential(request);
+
var response = this.Channel.Request<AccessTokenSuccessResponse>(request);
UpdateAuthorizationWithResponse(authorization, response);
return true;
@@ -145,10 +160,11 @@ namespace DotNetOpenAuth.OAuth2 {
var request = new AccessTokenRefreshRequestC(this.AuthorizationServer) {
ClientIdentifier = this.ClientIdentifier,
- ClientSecret = this.ClientSecret,
RefreshToken = refreshToken,
};
+ this.ApplyClientCredential(request);
+
var response = this.Channel.Request<AccessTokenSuccessResponse>(request);
var authorization = new AuthorizationState();
UpdateAuthorizationWithResponse(authorization, response);
@@ -250,10 +266,10 @@ namespace DotNetOpenAuth.OAuth2 {
var accessTokenRequest = new AccessTokenAuthorizationCodeRequestC(this.AuthorizationServer) {
ClientIdentifier = this.ClientIdentifier,
- ClientSecret = this.ClientSecret,
Callback = authorizationState.Callback,
AuthorizationCode = authorizationSuccess.AuthorizationCode,
};
+ this.ApplyClientCredential(accessTokenRequest);
IProtocolMessage accessTokenResponse = this.Channel.Request(accessTokenRequest);
var accessTokenSuccess = accessTokenResponse as AccessTokenSuccessResponse;
var failedAccessTokenResponse = accessTokenResponse as AccessTokenFailedResponse;
@@ -267,6 +283,22 @@ namespace DotNetOpenAuth.OAuth2 {
}
/// <summary>
+ /// Applies any applicable client credential to an authenticated outbound request to the authorization server.
+ /// </summary>
+ /// <param name="request"></param>
+ protected void ApplyClientCredential(AuthenticatedClientRequestBase request) {
+ Requires.NotNull(request, "request");
+
+ if (this.ClientCredentialApplicator != null) {
+ this.ClientCredentialApplicator.ApplyClientCredential(this.ClientIdentifier, request);
+ }
+ }
+
+ protected static ClientCredentialApplicator DefaultSecretApplicator(string secret) {
+ return secret == null ? ClientCredentialApplicator.NoSecret() : ClientCredentialApplicator.SecretParameter(secret);
+ }
+
+ /// <summary>
/// Calculates the fraction of life remaining in an access token.
/// </summary>
/// <param name="authorization">The authorization to measure.</param>
@@ -295,7 +327,7 @@ namespace DotNetOpenAuth.OAuth2 {
var authorizationState = new AuthorizationState(scopes);
request.ClientIdentifier = this.ClientIdentifier;
- request.ClientSecret = this.ClientSecret;
+ this.ApplyClientCredential(request);
request.Scope.UnionWith(authorizationState.Scope);
var response = this.Channel.Request(request);
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs
new file mode 100644
index 0000000..ac78a90
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/ClientCredentialApplicator.cs
@@ -0,0 +1,75 @@
+//-----------------------------------------------------------------------
+// <copyright file="ClientCredentialApplicator.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2 {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuth2.Messages;
+
+ public abstract class ClientCredentialApplicator {
+ protected ClientCredentialApplicator() {
+ }
+
+ public static ClientCredentialApplicator SecretParameter(string clientSecret) {
+ Requires.NotNullOrEmpty(clientSecret, "clientSecret");
+ return new SecretParameterApplicator(clientSecret);
+ }
+
+ public static ClientCredentialApplicator NetworkCredential(NetworkCredential credential) {
+ Requires.NotNull(credential, "credential");
+ return new NetworkCredentialApplicator(credential);
+ }
+
+ public static ClientCredentialApplicator NoSecret() {
+ return null;
+ }
+
+ public virtual void ApplyClientCredential(string clientIdentifier, AuthenticatedClientRequestBase request) {
+ }
+
+ public virtual void ApplyClientCredential(string clientIdentifier, HttpWebRequest request) {
+ }
+
+ private class NetworkCredentialApplicator : ClientCredentialApplicator {
+ private readonly NetworkCredential credential;
+
+ internal NetworkCredentialApplicator(NetworkCredential credential) {
+ Requires.NotNull(credential, "credential");
+ this.credential = credential;
+ }
+
+ public override void ApplyClientCredential(string clientIdentifier, AuthenticatedClientRequestBase request) {
+ // 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.credential.Password);
+ }
+
+ public override void ApplyClientCredential(string clientIdentifier, HttpWebRequest request) {
+ }
+ }
+
+ private class SecretParameterApplicator : ClientCredentialApplicator {
+ private readonly string secret;
+
+ internal SecretParameterApplicator(string clientSecret) {
+ Requires.NotNullOrEmpty(clientSecret, "clientSecret");
+ this.secret = clientSecret;
+ }
+
+ public override void ApplyClientCredential(string clientIdentifier, AuthenticatedClientRequestBase request) {
+ request.ClientSecret = this.secret;
+ }
+
+ public override void ApplyClientCredential(string clientIdentifier, HttpWebRequest request) {
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs
index 9834985..edde2a9 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/UserAgentClient.cs
@@ -27,7 +27,7 @@ namespace DotNetOpenAuth.OAuth2 {
/// <param name="clientIdentifier">The client identifier.</param>
/// <param name="clientSecret">The client secret.</param>
public UserAgentClient(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null)
- : base(authorizationServer, clientIdentifier, clientSecret) {
+ : this(authorizationServer, clientIdentifier, DefaultSecretApplicator(clientSecret)) {
}
/// <summary>
@@ -38,12 +38,39 @@ namespace DotNetOpenAuth.OAuth2 {
/// <param name="clientIdentifier">The client identifier.</param>
/// <param name="clientSecret">The client secret.</param>
public UserAgentClient(Uri authorizationEndpoint, Uri tokenEndpoint, string clientIdentifier = null, string clientSecret = null)
- : this(new AuthorizationServerDescription { AuthorizationEndpoint = authorizationEndpoint, TokenEndpoint = tokenEndpoint }, clientIdentifier, clientSecret) {
+ : this(authorizationEndpoint, tokenEndpoint, clientIdentifier, DefaultSecretApplicator(clientSecret)) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserAgentClient"/> class.
+ /// </summary>
+ /// <param name="authorizationEndpoint">The authorization endpoint.</param>
+ /// <param name="tokenEndpoint">The token endpoint.</param>
+ /// <param name="clientIdentifier">The client identifier.</param>
+ /// <param name="clientCredentialApplicator">
+ /// The tool to use to apply client credentials to authenticated requests to the Authorization Server.
+ /// May be <c>null</c> for clients with no secret or other means of authentication.
+ /// </param>
+ public UserAgentClient(Uri authorizationEndpoint, Uri tokenEndpoint, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator)
+ : this(new AuthorizationServerDescription { AuthorizationEndpoint = authorizationEndpoint, TokenEndpoint = tokenEndpoint }, clientIdentifier, clientCredentialApplicator) {
Requires.NotNull(authorizationEndpoint, "authorizationEndpoint");
Requires.NotNull(tokenEndpoint, "tokenEndpoint");
}
/// <summary>
+ /// Initializes a new instance of the <see cref="UserAgentClient"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The token issuer.</param>
+ /// <param name="clientIdentifier">The client identifier.</param>
+ /// <param name="clientCredentialApplicator">
+ /// The tool to use to apply client credentials to authenticated requests to the Authorization Server.
+ /// May be <c>null</c> for clients with no secret or other means of authentication.
+ /// </param>
+ public UserAgentClient(AuthorizationServerDescription authorizationServer, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator)
+ : base(authorizationServer, clientIdentifier, clientCredentialApplicator) {
+ }
+
+ /// <summary>
/// Generates a URL that the user's browser can be directed to in order to authorize
/// this client to access protected data at some resource server.
/// </summary>
diff --git a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs
index 5a86bef..c19757f 100644
--- a/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs
+++ b/src/DotNetOpenAuth.OAuth2.Client/OAuth2/WebServerClient.cs
@@ -26,7 +26,20 @@ namespace DotNetOpenAuth.OAuth2 {
/// <param name="clientIdentifier">The client identifier.</param>
/// <param name="clientSecret">The client secret.</param>
public WebServerClient(AuthorizationServerDescription authorizationServer, string clientIdentifier = null, string clientSecret = null)
- : base(authorizationServer, clientIdentifier, clientSecret) {
+ : this(authorizationServer, clientIdentifier, DefaultSecretApplicator(clientSecret)) {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebServerClient"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ /// <param name="clientIdentifier">The client identifier.</param>
+ /// <param name="clientCredentialApplicator">
+ /// The tool to use to apply client credentials to authenticated requests to the Authorization Server.
+ /// May be <c>null</c> for clients with no secret or other means of authentication.
+ /// </param>
+ public WebServerClient(AuthorizationServerDescription authorizationServer, string clientIdentifier, ClientCredentialApplicator clientCredentialApplicator)
+ : base(authorizationServer, clientIdentifier, clientCredentialApplicator) {
}
/// <summary>
@@ -106,7 +119,7 @@ namespace DotNetOpenAuth.OAuth2 {
/// <returns>The authorization state that contains the details of the authorization.</returns>
public IAuthorizationState ProcessUserAuthorization(HttpRequestBase request = null) {
Requires.ValidState(!string.IsNullOrEmpty(this.ClientIdentifier), ClientStrings.RequiredPropertyNotYetPreset, "ClientIdentifier");
- Requires.ValidState(!string.IsNullOrEmpty(this.ClientSecret), ClientStrings.RequiredPropertyNotYetPreset, "ClientSecret");
+ Requires.ValidState(this.ClientCredentialApplicator != null, ClientStrings.RequiredPropertyNotYetPreset, "ClientCredentialApplicator");
if (request == null) {
request = this.Channel.GetRequestFromContext();
diff --git a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs
index bc4d0ca..03d5c8a 100644
--- a/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs
+++ b/src/DotNetOpenAuth.OAuth2.ClientAuthorization/OAuth2/Messages/AuthenticatedClientRequestBase.cs
@@ -6,13 +6,15 @@
namespace DotNetOpenAuth.OAuth2.Messages {
using System;
-
+ using System.Net;
using DotNetOpenAuth.Messaging;
/// <summary>
/// A direct message from the client to the authorization server that includes the client's credentials.
/// </summary>
- public abstract class AuthenticatedClientRequestBase : MessageBase {
+ public abstract class AuthenticatedClientRequestBase : MessageBase, IHttpDirectRequest {
+ private readonly WebHeaderCollection headers = new WebHeaderCollection();
+
/// <summary>
/// Initializes a new instance of the <see cref="AuthenticatedClientRequestBase"/> class.
/// </summary>
@@ -26,7 +28,10 @@ namespace DotNetOpenAuth.OAuth2.Messages {
/// Gets the client identifier previously obtained from the Authorization Server.
/// </summary>
/// <value>The client identifier.</value>
- [MessagePart(Protocol.client_id, IsRequired = true)]
+ /// <remarks>
+ /// Not required, because the client id may be communicate through alternate means like HTTP Basic authentication (the OAuth 2 spec allows a lot of freedom here).
+ /// </remarks>
+ [MessagePart(Protocol.client_id, IsRequired = false)]
public string ClientIdentifier { get; internal set; }
/// <summary>
@@ -38,5 +43,9 @@ namespace DotNetOpenAuth.OAuth2.Messages {
/// </remarks>
[MessagePart(Protocol.client_secret, IsRequired = false)]
public string ClientSecret { get; internal set; }
+
+ public WebHeaderCollection Headers {
+ get { return this.headers; }
+ }
}
} \ No newline at end of file
diff --git a/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2AuthorizationServerElement.cs b/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2AuthorizationServerElement.cs
new file mode 100644
index 0000000..fa7b52e
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2AuthorizationServerElement.cs
@@ -0,0 +1,55 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuth2AuthorizationServerElement.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Configuration {
+ using System;
+ using System.Configuration;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.OAuth2.ChannelElements;
+
+ /// <summary>
+ /// Represents the &lt;oauth2/authorizationServer&gt; element in the host's .config file.
+ /// </summary>
+ internal class OAuth2AuthorizationServerElement : ConfigurationElement {
+
+ /// <summary>
+ /// The name of the &lt;clientAuthenticationModules&gt; sub-element.
+ /// </summary>
+ private const string ClientAuthenticationModulesElementName = "clientAuthenticationModules";
+
+ /// <summary>
+ /// The built-in set of identifier discovery services.
+ /// </summary>
+ private static readonly TypeConfigurationCollection<IClientAuthenticationModule> defaultClientAuthenticationModules =
+ new TypeConfigurationCollection<IClientAuthenticationModule>();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuth2AuthorizationServerElement"/> class.
+ /// </summary>
+ internal OAuth2AuthorizationServerElement() {
+ }
+
+ /// <summary>
+ /// Gets or sets the services to use for discovering service endpoints for identifiers.
+ /// </summary>
+ /// <remarks>
+ /// If no discovery services are defined in the (web) application's .config file,
+ /// the default set of discovery services built into the library are used.
+ /// </remarks>
+ [ConfigurationProperty(ClientAuthenticationModulesElementName, IsDefaultCollection = false)]
+ [ConfigurationCollection(typeof(TypeConfigurationCollection<IClientAuthenticationModule>))]
+ internal TypeConfigurationCollection<IClientAuthenticationModule> ClientAuthenticationModules {
+ get {
+ var configResult = (TypeConfigurationCollection<IClientAuthenticationModule>)this[ClientAuthenticationModulesElementName];
+ return configResult != null && configResult.Count > 0 ? configResult : defaultClientAuthenticationModules;
+ }
+
+ set {
+ this[ClientAuthenticationModulesElementName] = value;
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2ClientElement.cs b/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2ClientElement.cs
new file mode 100644
index 0000000..95a7a36
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2ClientElement.cs
@@ -0,0 +1,20 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuth2ClientElement.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Configuration {
+ using System.Configuration;
+
+ /// <summary>
+ /// Represents the &lt;oauth2/client&gt; element in the host's .config file.
+ /// </summary>
+ internal class OAuth2ClientElement : ConfigurationElement {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuth2ClientElement"/> class.
+ /// </summary>
+ internal OAuth2ClientElement() {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2Element.cs b/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2Element.cs
new file mode 100644
index 0000000..6ba7e23
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2Element.cs
@@ -0,0 +1,78 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuth2Element.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Configuration {
+ using System.Configuration;
+ using System.Diagnostics.Contracts;
+
+ /// <summary>
+ /// Represents the &lt;oauth&gt; element in the host's .config file.
+ /// </summary>
+ internal class OAuth2Element : ConfigurationSection {
+ /// <summary>
+ /// The name of the oauth section.
+ /// </summary>
+ private const string SectionName = DotNetOpenAuthSection.SectionName + "/oauth2";
+
+ /// <summary>
+ /// The name of the &lt;client&gt; sub-element.
+ /// </summary>
+ private const string ClientElementName = "client";
+
+ /// <summary>
+ /// The name of the &lt;authorizationServer&gt; sub-element.
+ /// </summary>
+ private const string AuthorizationServerElementName = "authorizationServer";
+
+ /// <summary>
+ /// The name of the &lt;resourceServer&gt; sub-element.
+ /// </summary>
+ private const string ResourceServerElementName = "resourceServer";
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuth2Element"/> class.
+ /// </summary>
+ internal OAuth2Element() {
+ }
+
+ /// <summary>
+ /// Gets the configuration section from the .config file.
+ /// </summary>
+ public static OAuth2Element Configuration {
+ get {
+ Contract.Ensures(Contract.Result<OAuth2Element>() != null);
+ return (OAuth2Element)ConfigurationManager.GetSection(SectionName) ?? new OAuth2Element();
+ }
+ }
+
+ /// <summary>
+ /// Gets or sets the configuration specific for Clients.
+ /// </summary>
+ [ConfigurationProperty(ClientElementName)]
+ internal OAuth2ClientElement Client {
+ get { return (OAuth2ClientElement)this[ClientElementName] ?? new OAuth2ClientElement(); }
+ set { this[ClientElementName] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the configuration specific for Authorization Servers.
+ /// </summary>
+ [ConfigurationProperty(AuthorizationServerElementName)]
+ internal OAuth2AuthorizationServerElement AuthorizationServer {
+ get { return (OAuth2AuthorizationServerElement)this[AuthorizationServerElementName] ?? new OAuth2AuthorizationServerElement(); }
+ set { this[AuthorizationServerElementName] = value; }
+ }
+
+ /// <summary>
+ /// Gets or sets the configuration specific for Resource Servers.
+ /// </summary>
+ [ConfigurationProperty(ResourceServerElementName)]
+ internal OAuth2ResourceServerElement ResourceServer {
+ get { return (OAuth2ResourceServerElement)this[ResourceServerElementName] ?? new OAuth2ResourceServerElement(); }
+ set { this[ResourceServerElementName] = value; }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2ResourceServerElement.cs b/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2ResourceServerElement.cs
new file mode 100644
index 0000000..a07e973
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2/Configuration/OAuth2ResourceServerElement.cs
@@ -0,0 +1,20 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuth2ResourceServerElement.cs" company="Outercurve Foundation">
+// Copyright (c) Outercurve Foundation. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Configuration {
+ using System.Configuration;
+
+ /// <summary>
+ /// Represents the &lt;oauth2/resourceServer&gt; element in the host's .config file.
+ /// </summary>
+ internal class OAuth2ResourceServerElement : ConfigurationElement {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuth2ResourceServerElement"/> class.
+ /// </summary>
+ internal OAuth2ResourceServerElement() {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj b/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj
index 921cd84..a3eda22 100644
--- a/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj
+++ b/src/DotNetOpenAuth.OAuth2/DotNetOpenAuth.OAuth2.csproj
@@ -18,10 +18,15 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
+ <Compile Include="Configuration\OAuth2ResourceServerElement.cs" />
+ <Compile Include="Configuration\OAuth2Element.cs" />
+ <Compile Include="Configuration\OAuth2ClientElement.cs" />
+ <Compile Include="Configuration\OAuth2AuthorizationServerElement.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="OAuth2\AccessToken.cs" />
<Compile Include="OAuth2\ChannelElements\AuthorizationDataBag.cs" />
<Compile Include="OAuth2\ChannelElements\IAccessTokenCarryingRequest.cs" />
+ <Compile Include="OAuth2\ChannelElements\IClientAuthenticationModule.cs" />
<Compile Include="OAuth2\ChannelElements\ScopeEncoder.cs" />
<Compile Include="OAuth2\ChannelElements\IAuthorizationDescription.cs" />
<Compile Include="OAuth2\ChannelElements\IAuthorizationCarryingRequest.cs" />
diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/IClientAuthenticationModule.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/IClientAuthenticationModule.cs
new file mode 100644
index 0000000..b7c4792
--- /dev/null
+++ b/src/DotNetOpenAuth.OAuth2/OAuth2/ChannelElements/IClientAuthenticationModule.cs
@@ -0,0 +1,22 @@
+namespace DotNetOpenAuth.OAuth2.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Web;
+ using DotNetOpenAuth.Messaging;
+
+ public enum ClientAuthenticationResult {
+ NoAuthenticationRecognized,
+
+ ClientIdNotAuthenticated,
+
+ ClientAuthenticated,
+
+ ClientAuthenticationRejected,
+ }
+
+ public interface IClientAuthenticationModule {
+ ClientAuthenticationResult TryAuthenticateClient(IDirectedProtocolMessage requestMessage, out string clientIdentifier);
+ }
+}
diff --git a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs
index eb5c8e4..2e83482 100644
--- a/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs
+++ b/src/DotNetOpenAuth.OAuth2/OAuth2/OAuthUtilities.cs
@@ -18,6 +18,8 @@ namespace DotNetOpenAuth.OAuth2 {
/// Some common utility methods for OAuth 2.0.
/// </summary>
public static class OAuthUtilities {
+ private const string HttpBasicAuthScheme = "Basic ";
+
/// <summary>
/// The <see cref="StringComparer"/> instance to use when comparing scope equivalence.
/// </summary>
@@ -28,6 +30,8 @@ namespace DotNetOpenAuth.OAuth2 {
/// </summary>
private static char[] scopeDelimiter = new char[] { ' ' };
+ private static readonly char[] ColonSeparator = new char[] { ':' };
+
/// <summary>
/// The characters that may appear in an access token that is included in an HTTP Authorization header.
/// </summary>
@@ -35,9 +39,9 @@ namespace DotNetOpenAuth.OAuth2 {
/// This is defined in OAuth 2.0 DRAFT 10, section 5.1.1. (http://tools.ietf.org/id/draft-ietf-oauth-v2-10.html#authz-header)
/// </remarks>
private static string accessTokenAuthorizationHeaderAllowedCharacters = MessagingUtilities.UppercaseLetters +
- MessagingUtilities.LowercaseLetters +
- MessagingUtilities.Digits +
- @"!#$%&'()*+-./:<=>?@[]^_`{|}~\,;";
+ MessagingUtilities.LowercaseLetters +
+ MessagingUtilities.Digits +
+ @"!#$%&'()*+-./:<=>?@[]^_`{|}~\,;";
/// <summary>
/// Determines whether one given scope is a subset of another scope.
@@ -129,5 +133,36 @@ namespace DotNetOpenAuth.OAuth2 {
Protocol.BearerHttpAuthorizationHeaderFormat,
accessToken);
}
+
+ private static readonly Encoding HttpBasicEncoding = Encoding.UTF8;
+
+ internal static void ApplyHttpBasicAuth(WebHeaderCollection headers, string userName, string password) {
+ Requires.NotNull(headers, "headers");
+ Requires.NotNullOrEmpty(userName, "userName");
+ Requires.NotNull(password, "password");
+
+ string concat = userName + ":" + password;
+ byte[] bits = HttpBasicEncoding.GetBytes(concat);
+ string base64 = Convert.ToBase64String(bits);
+ string header = HttpBasicAuthScheme + base64;
+ headers[HttpRequestHeader.Authorization] = header;
+ }
+
+ internal static NetworkCredential ParseHttpBasicAuth(WebHeaderCollection headers) {
+ Requires.NotNull(headers, "headers");
+
+ string authorizationHeader = headers[HttpRequestHeaders.Authorization];
+ if (authorizationHeader != null && authorizationHeader.StartsWith(HttpBasicAuthScheme, StringComparison.Ordinal)) {
+ string base64 = authorizationHeader.Substring(HttpBasicAuthScheme.Length);
+ byte[] bits = Convert.FromBase64String(base64);
+ string usernameColonPassword = HttpBasicEncoding.GetString(bits);
+ string[] usernameAndPassword = usernameColonPassword.Split(ColonSeparator, 2);
+ if (usernameAndPassword.Length == 2) {
+ return new NetworkCredential(usernameAndPassword[0], usernameAndPassword[1]);
+ }
+ }
+
+ return null;
+ }
}
}
diff --git a/src/DotNetOpenAuth.OpenId/Configuration/OpenIdRelyingPartyElement.cs b/src/DotNetOpenAuth.OpenId/Configuration/OpenIdRelyingPartyElement.cs
index 749659e..7d8c050 100644
--- a/src/DotNetOpenAuth.OpenId/Configuration/OpenIdRelyingPartyElement.cs
+++ b/src/DotNetOpenAuth.OpenId/Configuration/OpenIdRelyingPartyElement.cs
@@ -49,7 +49,8 @@ namespace DotNetOpenAuth.Configuration {
/// <summary>
/// The built-in set of identifier discovery services.
/// </summary>
- private static readonly TypeConfigurationCollection<IIdentifierDiscoveryService> defaultDiscoveryServices = new TypeConfigurationCollection<IIdentifierDiscoveryService>(new Type[] { typeof(UriDiscoveryService), typeof(XriDiscoveryProxyService) });
+ private static readonly TypeConfigurationCollection<IIdentifierDiscoveryService> defaultDiscoveryServices =
+ new TypeConfigurationCollection<IIdentifierDiscoveryService>(new Type[] { typeof(UriDiscoveryService), typeof(XriDiscoveryProxyService) });
/// <summary>
/// Initializes a new instance of the <see cref="OpenIdRelyingPartyElement"/> class.
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/CoordinatingChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs
index d7205d6..2e09943 100644
--- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingChannel.cs
@@ -9,10 +9,10 @@ namespace DotNetOpenAuth.Test.Mocks {
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
+ using System.Net;
using System.Text;
using System.Threading;
using System.Web;
-
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.Messaging.Reflection;
using DotNetOpenAuth.Test.OpenId;
@@ -65,9 +65,17 @@ namespace DotNetOpenAuth.Test.Mocks {
/// </summary>
private IDictionary<string, string> incomingMessage;
+ /// <summary>
+ /// The recipient URL of the <see cref="incomingMessage"/>, where applicable.
+ /// </summary>
private MessageReceivingEndpoint incomingMessageRecipient;
/// <summary>
+ /// The headers of the <see cref="incomingMessage"/>, where applicable.
+ /// </summary>
+ private WebHeaderCollection incomingMessageHttpHeaders;
+
+ /// <summary>
/// A delegate that gets a chance to peak at and fiddle with all
/// incoming messages.
/// </summary>
@@ -145,17 +153,27 @@ namespace DotNetOpenAuth.Test.Mocks {
this.incomingMessage = this.MessageDescriptions.GetAccessor(message).Serialize();
var directedMessage = message as IDirectedProtocolMessage;
this.incomingMessageRecipient = (directedMessage != null && directedMessage.Recipient != null) ? new MessageReceivingEndpoint(directedMessage.Recipient, directedMessage.HttpMethods) : null;
+ var httpMessage = message as IHttpDirectRequest;
+ this.incomingMessageHttpHeaders = (httpMessage != null) ? httpMessage.Headers.Clone() : null;
this.incomingMessageSignal.Set();
}
protected internal override HttpRequestBase GetRequestFromContext() {
MessageReceivingEndpoint recipient;
- var messageData = this.AwaitIncomingMessage(out recipient);
+ WebHeaderCollection headers;
+ var messageData = this.AwaitIncomingMessage(out recipient, out headers);
+ CoordinatingHttpRequestInfo result;
if (messageData != null) {
- return new CoordinatingHttpRequestInfo(this, this.MessageFactory, messageData, recipient);
+ result = new CoordinatingHttpRequestInfo(this, this.MessageFactory, messageData, recipient);
} else {
- return new CoordinatingHttpRequestInfo(recipient);
+ result = new CoordinatingHttpRequestInfo(recipient);
+ }
+
+ if (headers != null) {
+ headers.ApplyTo(result.Headers);
}
+
+ return result;
}
protected override IProtocolMessage RequestCore(IDirectedProtocolMessage request) {
@@ -166,7 +184,8 @@ namespace DotNetOpenAuth.Test.Mocks {
// Now wait for a response...
MessageReceivingEndpoint recipient;
- IDictionary<string, string> responseData = this.AwaitIncomingMessage(out recipient);
+ WebHeaderCollection headers;
+ IDictionary<string, string> responseData = this.AwaitIncomingMessage(out recipient, out headers);
ErrorUtilities.VerifyInternal(recipient == null, "The recipient is expected to be null for direct responses.");
// And deserialize it.
@@ -177,6 +196,10 @@ namespace DotNetOpenAuth.Test.Mocks {
var responseAccessor = this.MessageDescriptions.GetAccessor(responseMessage);
responseAccessor.Deserialize(responseData);
+ var responseMessageHttpRequest = responseMessage as IHttpDirectRequest;
+ if (headers != null && responseMessageHttpRequest != null) {
+ headers.ApplyTo(responseMessageHttpRequest.Headers);
+ }
this.ProcessMessageFilter(responseMessage, false);
return responseMessage;
@@ -258,7 +281,7 @@ namespace DotNetOpenAuth.Test.Mocks {
return channel.MessageFactoryTestHook;
}
- private IDictionary<string, string> AwaitIncomingMessage(out MessageReceivingEndpoint recipient) {
+ private IDictionary<string, string> AwaitIncomingMessage(out MessageReceivingEndpoint recipient, out WebHeaderCollection headers) {
// Special care should be taken so that we don't indefinitely
// wait for a message that may never come due to a bug in the product
// or the test.
@@ -284,8 +307,10 @@ namespace DotNetOpenAuth.Test.Mocks {
this.waitingForMessage = false;
var response = this.incomingMessage;
recipient = this.incomingMessageRecipient;
+ headers = this.incomingMessageHttpHeaders;
this.incomingMessage = null;
this.incomingMessageRecipient = null;
+ this.incomingMessageHttpHeaders = null;
// Briefly signal to another thread that might be waiting for our inbox to be empty
this.messageReceivedSignal.Set();
diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs
index 9f139f3..a1f5cf5 100644
--- a/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingHttpRequestInfo.cs
@@ -6,9 +6,10 @@
namespace DotNetOpenAuth.Test.Mocks {
using System;
- using System.Collections.Generic;
- using System.Diagnostics.Contracts;
- using DotNetOpenAuth.Messaging;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Net;
+using DotNetOpenAuth.Messaging;
internal class CoordinatingHttpRequestInfo : HttpRequestInfo {
private readonly Channel channel;
diff --git a/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2ClientChannel.cs b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2ClientChannel.cs
new file mode 100644
index 0000000..1e70f80
--- /dev/null
+++ b/src/DotNetOpenAuth.Test/Mocks/CoordinatingOAuth2ClientChannel.cs
@@ -0,0 +1,27 @@
+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; }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth.Test/OAuth2/MessageFactoryTests.cs b/src/DotNetOpenAuth.Test/OAuth2/MessageFactoryTests.cs
index 89271ae..dff831b 100644
--- a/src/DotNetOpenAuth.Test/OAuth2/MessageFactoryTests.cs
+++ b/src/DotNetOpenAuth.Test/OAuth2/MessageFactoryTests.cs
@@ -28,7 +28,7 @@ namespace DotNetOpenAuth.Test.OAuth2 {
public override void SetUp() {
base.SetUp();
- var authServerChannel = new OAuth2AuthorizationServerChannel(new Mock<IAuthorizationServerHost>().Object);
+ var authServerChannel = new OAuth2AuthorizationServerChannel(new Mock<IAuthorizationServerHost>().Object, new Mock<IClientAuthenticationModule>().Object);
this.authServerMessageFactory = authServerChannel.MessageFactoryTestHook;
var clientChannel = new OAuth2ClientChannel();
diff --git a/src/DotNetOpenAuth.Test/OAuth2/OAuth2Coordinator.cs b/src/DotNetOpenAuth.Test/OAuth2/OAuth2Coordinator.cs
index ede3258..fa4ffef 100644
--- a/src/DotNetOpenAuth.Test/OAuth2/OAuth2Coordinator.cs
+++ b/src/DotNetOpenAuth.Test/OAuth2/OAuth2Coordinator.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.Test.OAuth2 {
using System;
using System.Collections.Generic;
using System.Linq;
+ using System.Net;
using System.Text;
using DotNetOpenAuth.OAuth2;
using DotNetOpenAuth.Test.Mocks;
@@ -34,13 +35,13 @@ namespace DotNetOpenAuth.Test.OAuth2 {
this.client = client;
this.client.ClientIdentifier = OAuth2TestBase.ClientId;
- this.client.ClientSecret = OAuth2TestBase.ClientSecret;
+ this.client.ClientCredentialApplicator = ClientCredentialApplicator.NetworkCredential(new NetworkCredential(OAuth2TestBase.ClientId, 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;
diff --git a/src/DotNetOpenAuth.Test/OAuth2/UserAgentClientAuthorizeTests.cs b/src/DotNetOpenAuth.Test/OAuth2/UserAgentClientAuthorizeTests.cs
index 97c0f56..ae03b0c 100644
--- a/src/DotNetOpenAuth.Test/OAuth2/UserAgentClientAuthorizeTests.cs
+++ b/src/DotNetOpenAuth.Test/OAuth2/UserAgentClientAuthorizeTests.cs
@@ -73,7 +73,7 @@ namespace DotNetOpenAuth.Test.OAuth2 {
server.ApproveAuthorizationRequest(request, ResourceOwnerUsername);
});
- coordinatorClient.ClientSecret = null; // implicit grant clients don't need a secret.
+ coordinatorClient.ClientCredentialApplicator = null; // implicit grant clients don't need a secret.
coordinator.Run();
}
}
diff --git a/src/DotNetOpenAuth.Test/OAuth2/WebServerClientAuthorizeTests.cs b/src/DotNetOpenAuth.Test/OAuth2/WebServerClientAuthorizeTests.cs
index fe0abd2..6f46271 100644
--- a/src/DotNetOpenAuth.Test/OAuth2/WebServerClientAuthorizeTests.cs
+++ b/src/DotNetOpenAuth.Test/OAuth2/WebServerClientAuthorizeTests.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.Test.OAuth2 {
using System;
using System.Collections.Generic;
using System.Linq;
+ using System.Net;
using System.Text;
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OAuth2;
diff --git a/src/DotNetOpenAuth.Test/TestUtilities.cs b/src/DotNetOpenAuth.Test/TestUtilities.cs
index cf9b5a3..a526f7f 100644
--- a/src/DotNetOpenAuth.Test/TestUtilities.cs
+++ b/src/DotNetOpenAuth.Test/TestUtilities.cs
@@ -7,16 +7,35 @@
namespace DotNetOpenAuth.Test {
using System;
using System.Collections.Generic;
+ using System.Collections.Specialized;
using System.Linq;
+ using System.Net;
using log4net;
/// <summary>
/// An assortment of methods useful for testing.
/// </summary>
- internal class TestUtilities {
+ internal static class TestUtilities {
/// <summary>
/// The logger that tests should use.
/// </summary>
internal static readonly ILog TestLogger = LogManager.GetLogger("DotNetOpenAuth.Test");
+
+ internal static void ApplyTo(this NameValueCollection source, NameValueCollection target) {
+ Requires.NotNull(source, "source");
+ Requires.NotNull(target, "target");
+
+ foreach (string header in source) {
+ target[header] = source[header];
+ }
+ }
+
+ internal static T Clone<T>(this T source) where T : NameValueCollection, new() {
+ Requires.NotNull(source, "source");
+
+ var result = new T();
+ ApplyTo(source, result);
+ return result;
+ }
}
}