summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.Test/Mocks/TestDirectResponseMessageWithHttpStatus.cs12
-rw-r--r--src/DotNetOpenAuth.sln1
-rw-r--r--src/DotNetOpenAuth/DotNetOpenAuth.csproj53
-rw-r--r--src/DotNetOpenAuth/Logger.cs10
-rw-r--r--src/DotNetOpenAuth/Messaging/IHttpDirectResponse.cs10
-rw-r--r--src/DotNetOpenAuth/Messaging/IHttpDirectResponseContract.cs43
-rw-r--r--src/DotNetOpenAuth/Messaging/TimespanSecondsEncoder.cs (renamed from src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/TimespanSecondsEncoder.cs)9
-rw-r--r--src/DotNetOpenAuth/OAuth/Protocol.cs2
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/AuthorizationServerDescription.cs64
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapChannel.cs52
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapMessageFactory.cs99
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/ClientBase.cs68
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/IClientTokenManager.cs48
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionFailedResponse.cs21
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionRequest.cs65
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionSuccessResponse.cs40
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordFailedResponse.cs22
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordRequest.cs69
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordSuccessResponse.cs47
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/IMessageWithClientState.cs13
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs205
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenFailedResponse.cs25
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs57
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenSuccessResponse.cs44
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenFailedResponse.cs21
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenRequest.cs61
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenSuccessResponse.cs45
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppRequest.cs68
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppResponse.cs60
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/UnauthorizedResponse.cs47
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordCaptchaResponse.cs54
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordFailedResponse.cs22
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordRequest.cs93
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordSuccessResponse.cs51
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordVerificationResponse.cs55
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenBadClientResponse.cs22
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenFailedResponse.cs56
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenRequest.cs90
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenSuccessResponse.cs45
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppFailedResponse.cs47
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppRequest.cs80
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppSuccessResponse.cs49
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs90
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx129
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/Protocol.cs188
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs113
-rw-r--r--src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs34
-rw-r--r--src/DotNetOpenAuth/OpenId/Messages/DirectErrorResponse.cs8
48 files changed, 2599 insertions, 8 deletions
diff --git a/src/DotNetOpenAuth.Test/Mocks/TestDirectResponseMessageWithHttpStatus.cs b/src/DotNetOpenAuth.Test/Mocks/TestDirectResponseMessageWithHttpStatus.cs
index d692320..20fd6c4 100644
--- a/src/DotNetOpenAuth.Test/Mocks/TestDirectResponseMessageWithHttpStatus.cs
+++ b/src/DotNetOpenAuth.Test/Mocks/TestDirectResponseMessageWithHttpStatus.cs
@@ -8,6 +8,7 @@ namespace DotNetOpenAuth.Test.Mocks {
using System;
using System.Collections.Generic;
using System.Linq;
+ using System.Net;
using System.Text;
using DotNetOpenAuth.Messaging;
@@ -23,8 +24,15 @@ namespace DotNetOpenAuth.Test.Mocks {
/// <summary>
/// Gets or sets the HTTP status code that the direct respones should be sent with.
/// </summary>
- /// <value></value>
- public System.Net.HttpStatusCode HttpStatusCode { get; set; }
+ public HttpStatusCode HttpStatusCode { get; set; }
+
+ /// <summary>
+ /// Gets the HTTP headers to add to the response.
+ /// </summary>
+ /// <value>May be an empty collection, but must not be <c>null</c>.</value>
+ public WebHeaderCollection Headers {
+ get { return new WebHeaderCollection(); }
+ }
#endregion
}
diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln
index 55d43e8..135d51f 100644
--- a/src/DotNetOpenAuth.sln
+++ b/src/DotNetOpenAuth.sln
@@ -13,6 +13,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{CD57219F-24F4-4136-8741-6063D0D7A031}"
ProjectSection(SolutionItems) = preProject
+ ..\doc\specs\draft-hardt-oauth-01.htm = ..\doc\specs\draft-hardt-oauth-01.htm
..\doc\specs\ICAM_OpenID20Profile.pdf = ..\doc\specs\ICAM_OpenID20Profile.pdf
..\doc\specs\OAuth Core 1.0.htm = ..\doc\specs\OAuth Core 1.0.htm
..\doc\specs\OAuth Core 1.0a (Draft 3).htm = ..\doc\specs\OAuth Core 1.0a (Draft 3).htm
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
index d622a4a..669863c 100644
--- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj
+++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj
@@ -224,13 +224,13 @@ http://opensource.org/licenses/ms-pl.html
</Reference>
</ItemGroup>
<ItemGroup Condition=" '$(ClrVersion)' == '4' ">
- <Reference Include="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"/>
+ <Reference Include="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
</ItemGroup>
<ItemGroup Condition=" '$(ClrVersion)' != '4' ">
<!-- MVC 2 can run on CLR 2 (it doesn't require CLR 4) but since MVC 2 apps tend to use type forwarding,
it's a more broadly consumable idea to bind against MVC 1 for the library unless we're building on CLR 4,
which will definitely have MVC 2 available. -->
- <Reference Include="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"/>
+ <Reference Include="System.Web.Mvc, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />
</ItemGroup>
<ItemGroup>
<Compile Include="ComponentModel\ClaimTypeSuggestions.cs" />
@@ -284,6 +284,7 @@ http://opensource.org/licenses/ms-pl.html
<Compile Include="Messaging\HostErrorException.cs" />
<Compile Include="Messaging\IHttpDirectResponse.cs" />
<Compile Include="Messaging\IExtensionMessage.cs" />
+ <Compile Include="Messaging\IHttpDirectResponseContract.cs" />
<Compile Include="Messaging\IMessage.cs" />
<Compile Include="Messaging\IncomingWebResponse.cs" />
<Compile Include="Messaging\IDirectResponseProtocolMessage.cs" />
@@ -303,6 +304,28 @@ http://opensource.org/licenses/ms-pl.html
<Compile Include="Messaging\Reflection\IMessagePartNullEncoder.cs" />
<Compile Include="Messaging\Reflection\MessageDescriptionCollection.cs" />
<Compile Include="Messaging\StandardMessageFactory.cs" />
+ <Compile Include="OAuthWrap\IClientTokenManager.cs" />
+ <Compile Include="OAuthWrap\Messages\Assertion\AssertionRequest.cs" />
+ <Compile Include="OAuthWrap\Messages\Assertion\AssertionFailedResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\ClientAccountAndPassword\ClientAccountUsernamePasswordFailedResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\ClientAccountAndPassword\ClientAccountUsernamePasswordRequest.cs" />
+ <Compile Include="OAuthWrap\Messages\ClientAccountAndPassword\ClientAccountUsernamePasswordSuccessResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\Assertion\AssertionSuccessResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\IMessageWithClientState.cs" />
+ <Compile Include="OAuthWrap\Messages\RichApp\RichAppAccessTokenRequest.cs" />
+ <Compile Include="OAuthWrap\Messages\RichApp\RichAppRequest.cs" />
+ <Compile Include="OAuthWrap\Messages\RichApp\RichAppResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\RichApp\RichAppAccessTokenSuccessResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\RichApp\RichAppAccessTokenFailedResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\UnauthorizedResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\RefreshAccessTokenFailedResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\RefreshAccessTokenSuccessResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\RefreshAccessTokenRequest.cs" />
+ <Compile Include="OAuthWrap\Messages\UsernameAndPassword\UserNamePasswordCaptchaResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\UsernameAndPassword\UserNamePasswordVerificationResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\WebApp\WebAppAccessTokenBadClientResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\WebApp\WebAppAccessTokenFailedResponse.cs" />
+ <Compile Include="OAuthWrap\WrapUtilities.cs" />
<Compile Include="OAuth\ChannelElements\ICombinedOpenIdProviderTokenManager.cs" />
<Compile Include="OAuth\ChannelElements\IConsumerDescription.cs" />
<Compile Include="OAuth\ChannelElements\IConsumerTokenManager.cs" />
@@ -446,7 +469,7 @@ http://opensource.org/licenses/ms-pl.html
<Compile Include="OpenId\Extensions\ProviderAuthenticationPolicy\PapeUtilities.cs" />
<Compile Include="OpenId\Extensions\ProviderAuthenticationPolicy\PolicyRequest.cs" />
<Compile Include="OpenId\Extensions\ProviderAuthenticationPolicy\PolicyResponse.cs" />
- <Compile Include="OpenId\Extensions\ProviderAuthenticationPolicy\TimespanSecondsEncoder.cs" />
+ <Compile Include="Messaging\TimespanSecondsEncoder.cs" />
<Compile Include="OpenId\Extensions\SimpleRegistration\ClaimsRequest.cs" />
<Compile Include="OpenId\Extensions\SimpleRegistration\ClaimsResponse.cs" />
<Compile Include="OpenId\Extensions\SimpleRegistration\Constants.cs" />
@@ -587,6 +610,26 @@ http://opensource.org/licenses/ms-pl.html
<Compile Include="Messaging\StandardWebRequestHandler.cs" />
<Compile Include="Messaging\MessageReceivingEndpoint.cs" />
<Compile Include="Reporting.cs" />
+ <Compile Include="OAuthWrap\ChannelElements\OAuthWrapChannel.cs" />
+ <Compile Include="OAuthWrap\ChannelElements\OAuthWrapMessageFactory.cs" />
+ <Compile Include="OAuthWrap\ClientBase.cs" />
+ <Compile Include="OAuthWrap\Messages\WebApp\WebAppAccessTokenSuccessResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\MessageBase.cs" />
+ <Compile Include="OAuthWrap\Messages\WebApp\WebAppAccessTokenRequest.cs" />
+ <Compile Include="OAuthWrap\Messages\WebApp\WebAppFailedResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\WebApp\WebAppRequest.cs" />
+ <Compile Include="OAuthWrap\Messages\WebApp\WebAppSuccessResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\UsernameAndPassword\UserNamePasswordFailedResponse.cs" />
+ <Compile Include="OAuthWrap\Messages\UsernameAndPassword\UserNamePasswordRequest.cs" />
+ <Compile Include="OAuthWrap\Messages\UsernameAndPassword\UserNamePasswordSuccessResponse.cs" />
+ <Compile Include="OAuthWrap\Protocol.cs" />
+ <Compile Include="OAuthWrap\OAuthWrapStrings.Designer.cs">
+ <AutoGen>True</AutoGen>
+ <DesignTime>True</DesignTime>
+ <DependentUpon>OAuthWrapStrings.resx</DependentUpon>
+ </Compile>
+ <Compile Include="OAuthWrap\AuthorizationServerDescription.cs" />
+ <Compile Include="OAuthWrap\WebAppClient.cs" />
<Compile Include="Util.cs" />
<Compile Include="OAuth\Protocol.cs" />
<Compile Include="OAuth\ServiceProvider.cs" />
@@ -699,6 +742,10 @@ http://opensource.org/licenses/ms-pl.html
<EmbeddedResource Include="OpenId\RelyingParty\OpenIdRelyingPartyAjaxControlBase.js">
<Copyright>$(StandardCopyright)</Copyright>
</EmbeddedResource>
+ <EmbeddedResource Include="OAuthWrap\OAuthWrapStrings.resx">
+ <Generator>ResXFileCodeGenerator</Generator>
+ <LastGenOutput>OAuthWrapStrings.Designer.cs</LastGenOutput>
+ </EmbeddedResource>
<EmbeddedResource Include="Strings.sr.resx" />
<EmbeddedResource Include="Xrds\XrdsStrings.sr.resx" />
</ItemGroup>
diff --git a/src/DotNetOpenAuth/Logger.cs b/src/DotNetOpenAuth/Logger.cs
index 48007ed..7c8691c 100644
--- a/src/DotNetOpenAuth/Logger.cs
+++ b/src/DotNetOpenAuth/Logger.cs
@@ -75,6 +75,11 @@ namespace DotNetOpenAuth {
private static readonly ILog oauth = Create("DotNetOpenAuth.OAuth");
/// <summary>
+ /// Backing field for the <see cref="Wrap"/> property.
+ /// </summary>
+ private static readonly ILog wrap = Create("DotNetOpenAuth.WRAP");
+
+ /// <summary>
/// Backing field for the <see cref="InfoCard"/> property.
/// </summary>
private static readonly ILog infocard = Create("DotNetOpenAuth.InfoCard");
@@ -130,6 +135,11 @@ namespace DotNetOpenAuth {
internal static ILog OAuth { get { return oauth; } }
/// <summary>
+ /// Gets the logger for the high-level OAuth WRAP events.
+ /// </summary>
+ internal static ILog Wrap { get { return wrap; } }
+
+ /// <summary>
/// Gets the logger for high-level InfoCard events.
/// </summary>
internal static ILog InfoCard { get { return infocard; } }
diff --git a/src/DotNetOpenAuth/Messaging/IHttpDirectResponse.cs b/src/DotNetOpenAuth/Messaging/IHttpDirectResponse.cs
index c0e7803..20c3d6f 100644
--- a/src/DotNetOpenAuth/Messaging/IHttpDirectResponse.cs
+++ b/src/DotNetOpenAuth/Messaging/IHttpDirectResponse.cs
@@ -5,16 +5,24 @@
//-----------------------------------------------------------------------
namespace DotNetOpenAuth.Messaging {
+ using System.Diagnostics.Contracts;
using System.Net;
/// <summary>
/// An interface that allows direct response messages to specify
/// HTTP transport specific properties.
/// </summary>
+ [ContractClass(typeof(IHttpDirectResponseContract))]
public interface IHttpDirectResponse {
/// <summary>
- /// Gets the HTTP status code that the direct respones should be sent with.
+ /// Gets the HTTP status code that the direct response should be sent with.
/// </summary>
HttpStatusCode HttpStatusCode { get; }
+
+ /// <summary>
+ /// 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 Headers { get; }
}
}
diff --git a/src/DotNetOpenAuth/Messaging/IHttpDirectResponseContract.cs b/src/DotNetOpenAuth/Messaging/IHttpDirectResponseContract.cs
new file mode 100644
index 0000000..b1ddba2
--- /dev/null
+++ b/src/DotNetOpenAuth/Messaging/IHttpDirectResponseContract.cs
@@ -0,0 +1,43 @@
+//-----------------------------------------------------------------------
+// <copyright file="IHttpDirectResponseContract.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. 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="IHttpDirectResponse"/> interface.
+ /// </summary>
+ [ContractClassFor(typeof(IHttpDirectResponse))]
+ public abstract class IHttpDirectResponseContract : IHttpDirectResponse {
+ #region IHttpDirectResponse Members
+
+ /// <summary>
+ /// Gets the HTTP status code that the direct response should be sent with.
+ /// </summary>
+ /// <value></value>
+ HttpStatusCode IHttpDirectResponse.HttpStatusCode {
+ get { throw new NotImplementedException(); }
+ }
+
+ /// <summary>
+ /// 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 {
+ Contract.Ensures(Contract.Result<WebHeaderCollection>() != null);
+ throw new NotImplementedException();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/TimespanSecondsEncoder.cs b/src/DotNetOpenAuth/Messaging/TimespanSecondsEncoder.cs
index cc3f7cc..b28e5a8 100644
--- a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/TimespanSecondsEncoder.cs
+++ b/src/DotNetOpenAuth/Messaging/TimespanSecondsEncoder.cs
@@ -4,7 +4,7 @@
// </copyright>
//-----------------------------------------------------------------------
-namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
+namespace DotNetOpenAuth.Messaging {
using System;
using System.Globalization;
using DotNetOpenAuth.Messaging.Reflection;
@@ -13,6 +13,13 @@ namespace DotNetOpenAuth.OpenId.Extensions.ProviderAuthenticationPolicy {
/// Encodes and decodes the <see cref="TimeSpan"/> as an integer of total seconds.
/// </summary>
internal class TimespanSecondsEncoder : IMessagePartEncoder {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="TimespanSecondsEncoder"/> class.
+ /// </summary>
+ public TimespanSecondsEncoder() {
+ // Note that this constructor is public so it can be instantiated via Activator.
+ }
+
#region IMessagePartEncoder Members
/// <summary>
diff --git a/src/DotNetOpenAuth/OAuth/Protocol.cs b/src/DotNetOpenAuth/OAuth/Protocol.cs
index a524ba7..71a25f8 100644
--- a/src/DotNetOpenAuth/OAuth/Protocol.cs
+++ b/src/DotNetOpenAuth/OAuth/Protocol.cs
@@ -100,7 +100,7 @@ namespace DotNetOpenAuth.OAuth {
}
/// <summary>
- /// Gets the version used to represent OAuth 1.0a.
+ /// Gets the OAuth version this instance represents.
/// </summary>
internal Version Version { get; private set; }
diff --git a/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerDescription.cs b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerDescription.cs
new file mode 100644
index 0000000..c04cec0
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerDescription.cs
@@ -0,0 +1,64 @@
+//-----------------------------------------------------------------------
+// <copyright file="AuthorizationServerDescription.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// A description of an OAuth WRAP Authorization Server.
+ /// </summary>
+ public class AuthorizationServerDescription {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthorizationServerDescription"/> class.
+ /// </summary>
+ public AuthorizationServerDescription() {
+ this.ProtocolVersion = Protocol.Default.ProtocolVersion;
+ }
+
+ /// <summary>
+ /// Gets or sets the Authorization Server URL at which an Access Token is requested by the Client.
+ /// A refresh token may also be returned to the Client.
+ /// </summary>
+ /// <value>An HTTPS URL.</value>
+ /// <remarks>
+ /// Messages sent to this URL must always be sent by the POST HTTP method.
+ /// </remarks>
+ public Uri AccessTokenEndpoint { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Authorization Server URL at which a Refresh Token is presented in exchange
+ /// for a new Access Token.
+ /// </summary>
+ /// <value>An HTTPS URL.</value>
+ /// <remarks>
+ /// Messages sent to this URL must always be sent by the POST HTTP method.
+ /// </remarks>
+ public Uri RefreshTokenEndpoint { get; set; }
+
+ /// <summary>
+ /// Gets or sets the Authorization Server URL where the Client (re)directs the User
+ /// to make an authorization request.
+ /// </summary>
+ /// <value>An HTTP or HTTPS URL.</value>
+ public Uri UserAuthorizationEndpoint { get; set; }
+
+ /// <summary>
+ /// Gets or sets the OAuth WRAP version supported by the Authorization Server.
+ /// </summary>
+ public ProtocolVersion ProtocolVersion { get; set; }
+
+ /// <summary>
+ /// Gets the version of the OAuth WRAP protocol to use with this Authorization Server.
+ /// </summary>
+ /// <value>The version.</value>
+ internal Version Version {
+ get { return Protocol.Lookup(this.ProtocolVersion).Version; }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapChannel.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapChannel.cs
new file mode 100644
index 0000000..3c2065f
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapChannel.cs
@@ -0,0 +1,52 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthWrapChannel.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The channel for the OAuth WRAP protocol.
+ /// </summary>
+ internal class OAuthWrapChannel : Channel {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthWrapChannel"/> class.
+ /// </summary>
+ internal OAuthWrapChannel()
+ : base(new OAuthWrapMessageFactory()) {
+ }
+
+ /// <summary>
+ /// Gets the protocol message that may be in the given HTTP response.
+ /// </summary>
+ /// <param name="response">The response that is anticipated to contain an protocol message.</param>
+ /// <returns>
+ /// The deserialized message parts, if found. Null otherwise.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown when the response is not valid.</exception>
+ protected override IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response) {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Queues a message for sending in the response stream where the fields
+ /// are sent in the response stream in querystring style.
+ /// </summary>
+ /// <param name="response">The message to send as a response.</param>
+ /// <returns>
+ /// The pending user agent redirect based message to be sent as an HttpResponse.
+ /// </returns>
+ /// <remarks>
+ /// This method implements spec OAuth V1.0 section 5.3.
+ /// </remarks>
+ protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapMessageFactory.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapMessageFactory.cs
new file mode 100644
index 0000000..b3b9abb
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapMessageFactory.cs
@@ -0,0 +1,99 @@
+//-----------------------------------------------------------------------
+// <copyright file="OAuthWrapMessageFactory.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuthWrap.Messages;
+
+ /// <summary>
+ /// The message factory for OAuth WRAP messages.
+ /// </summary>
+ internal class OAuthWrapMessageFactory : IMessageFactory {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="OAuthWrapMessageFactory"/> class.
+ /// </summary>
+ internal OAuthWrapMessageFactory() {
+ }
+
+ #region IMessageFactory Members
+
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="recipient">The intended or actual recipient of the request message.</param>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>
+ /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ public IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) {
+ Version version = Protocol.DefaultVersion;
+
+ if (fields.ContainsKey(Protocol.wrap_client_id) && fields.ContainsKey(Protocol.wrap_callback)) {
+ return new WebAppRequest(recipient.Location, version);
+ }
+
+ if (fields.ContainsKey(Protocol.wrap_client_id) && fields.ContainsKey(Protocol.wrap_verification_code)) {
+ return new WebAppAccessTokenRequest(recipient.Location, version);
+ }
+
+ if (fields.ContainsKey(Protocol.wrap_name)) {
+ return new ClientAccountUsernamePasswordRequest(recipient.Location, version);
+ }
+
+ if (fields.ContainsKey(Protocol.wrap_username)) {
+ return new UserNamePasswordRequest(recipient.Location, version);
+ }
+
+ if (fields.ContainsKey(Protocol.wrap_verification_code)) {
+ return new WebAppSuccessResponse(recipient.Location, version);
+ }
+
+ return null;
+ }
+
+ /// <summary>
+ /// Analyzes an incoming request message payload to discover what kind of
+ /// message is embedded in it and returns the type, or null if no match is found.
+ /// </summary>
+ /// <param name="request">The message that was sent as a request that resulted in the response.</param>
+ /// <param name="fields">The name/value pairs that make up the message payload.</param>
+ /// <returns>
+ /// A newly instantiated <see cref="IProtocolMessage"/>-derived object that this message can
+ /// deserialize to. Null if the request isn't recognized as a valid protocol message.
+ /// </returns>
+ public IDirectResponseProtocolMessage GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields) {
+ Version version = Protocol.DefaultVersion;
+
+ var accessTokenRequest = request as WebAppAccessTokenRequest;
+ if (accessTokenRequest != null) {
+ if (fields.ContainsKey(Protocol.wrap_access_token)) {
+ return new WebAppAccessTokenSuccessResponse(accessTokenRequest);
+ } else {
+ //return new AccessTokenWithVerificationCodeFailedResponse(accessTokenRequest);
+ }
+ }
+
+ var userAuthorization = request as UserNamePasswordRequest;
+ if (userAuthorization != null) {
+ if (fields.ContainsKey(Protocol.wrap_verification_code)) {
+ return new UserNamePasswordSuccessResponse(userAuthorization);
+ } else {
+ //return new UserAuthorizationViaUsernamePasswordFailedResponse(userAuthorization);
+ }
+ }
+
+ return null;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/ClientBase.cs b/src/DotNetOpenAuth/OAuthWrap/ClientBase.cs
new file mode 100644
index 0000000..fefa005
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/ClientBase.cs
@@ -0,0 +1,68 @@
+//-----------------------------------------------------------------------
+// <copyright file="ClientBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A base class for common OAuth WRAP Consumer behaviors.
+ /// </summary>
+ public class ClientBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientBase"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The token issuer.</param>
+ protected ClientBase(AuthorizationServerDescription authorizationServer) {
+ Contract.Requires<ArgumentNullException>(authorizationServer != null);
+ this.AuthorizationServer = authorizationServer;
+ }
+
+ /// <summary>
+ /// Gets the token issuer.
+ /// </summary>
+ /// <value>The token issuer.</value>
+ public AuthorizationServerDescription AuthorizationServer { get; private set; }
+
+ /// <summary>
+ /// Gets the OAuth WRAP channel.
+ /// </summary>
+ /// <value>The channel.</value>
+ public Channel Channel { get; private set; }
+
+ /// <summary>
+ /// Adds the necessary HTTP Authorization header to an HTTP request for protected resources
+ /// so that the Service Provider will allow the request through.
+ /// </summary>
+ /// <param name="request">The request for protected resources from the service provider.</param>
+ /// <param name="accessToken">The access token previously obtained from the Authorization Server.</param>
+ public static void AuthorizeRequest(HttpWebRequest request, string accessToken) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(accessToken));
+ WrapUtilities.AuthorizeWithOAuthWrap(request, accessToken);
+ }
+
+ /// <summary>
+ /// Adds the necessary HTTP Authorization header to an HTTP request for protected resources
+ /// so that the Service Provider will allow the request through.
+ /// </summary>
+ /// <param name="request">The request for protected resources from the service provider.</param>
+ /// <param name="authorization">The authorization for this request previously obtained via OAuth WRAP.</param>
+ public static void AuthorizeRequest(HttpWebRequest request, IWrapAuthorization authorization) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentNullException>(authorization != null);
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(authorization.AccessToken));
+ Contract.Requires<ProtocolException>(authorization.AccessTokenExpirationUtc < DateTime.UtcNow);
+ AuthorizeRequest(request, authorization.AccessToken);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/IClientTokenManager.cs b/src/DotNetOpenAuth/OAuthWrap/IClientTokenManager.cs
new file mode 100644
index 0000000..a51d2d6
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/IClientTokenManager.cs
@@ -0,0 +1,48 @@
+//-----------------------------------------------------------------------
+// <copyright file="IClientTokenManager.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+
+ [ContractClass(typeof(IClientTokenManagerContract))]
+ public interface IClientTokenManager {
+ IWrapAuthorization GetAuthorizationState(Uri callbackUrl, string clientState);
+ }
+
+ [ContractClassFor(typeof(IClientTokenManager))]
+ internal abstract class IClientTokenManagerContract : IClientTokenManager {
+ private IClientTokenManagerContract() {
+ }
+
+ #region IClientTokenManager Members
+
+ IWrapAuthorization IClientTokenManager.GetAuthorizationState(Uri callbackUrl, string clientState) {
+ Contract.Requires<ArgumentNullException>(callbackUrl != null);
+ throw new NotImplementedException();
+ }
+
+ #endregion
+ }
+
+ public interface IWrapAuthorization {
+ Uri Callback { get; set; }
+
+ string RefreshToken { get; set; }
+
+ string AccessToken { get; set; }
+
+ DateTime? AccessTokenExpirationUtc { get; set; }
+
+ string Scope { get; set; }
+
+ void Delete();
+ void SaveChanges();
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionFailedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionFailedResponse.cs
new file mode 100644
index 0000000..020192e
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionFailedResponse.cs
@@ -0,0 +1,21 @@
+//-----------------------------------------------------------------------
+// <copyright file="AssertionFailedResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ /// <summary>
+ /// A response from the Authorization Server to the Client to indicate that a
+ /// request for an access code failed, probably due to an invalid assertion.
+ /// </summary>
+ internal class AssertionFailedResponse : UnauthorizedResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AssertionFailedResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal AssertionFailedResponse(AssertionRequest request)
+ : base(request) {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionRequest.cs
new file mode 100644
index 0000000..ac693e4
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionRequest.cs
@@ -0,0 +1,65 @@
+//-----------------------------------------------------------------------
+// <copyright file="AssertionRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A request from a Client to an Authorization Server with some assertion for an access token.
+ /// </summary>
+ internal class AssertionRequest : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AssertionRequest"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ /// <param name="version">The version.</param>
+ internal AssertionRequest(Uri authorizationServer, Version version)
+ : base(version, MessageTransport.Direct, authorizationServer) {
+ this.HttpMethods = HttpDeliveryMethods.PostRequest;
+ }
+
+ /// <summary>
+ /// Gets or sets the format of the assertion as defined by the Authorization Server.
+ /// </summary>
+ /// <value>The assertion format.</value>
+ [MessagePart(Protocol.wrap_assertion_format, IsRequired = true, AllowEmpty = false)]
+ internal string AssertionFormat { get; set; }
+
+ /// <summary>
+ /// Gets or sets the assertion.
+ /// </summary>
+ /// <value>The assertion.</value>
+ [MessagePart(Protocol.wrap_assertion, IsRequired = true, AllowEmpty = false)]
+ internal string Assertion { get; set; }
+
+ /// <summary>
+ /// Gets or sets an optional authorization scope as defined by the Authorization Server.
+ /// </summary>
+ [MessagePart(Protocol.wrap_scope, IsRequired = false, AllowEmpty = true)]
+ internal string Scope { get; set; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ protected override void EnsureValidMessage() {
+ base.EnsureValidMessage();
+ ErrorUtilities.VerifyProtocol(this.Recipient.IsTransportSecure(), OAuthWrapStrings.HttpsRequired);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionSuccessResponse.cs
new file mode 100644
index 0000000..f42e1f6
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionSuccessResponse.cs
@@ -0,0 +1,40 @@
+//-----------------------------------------------------------------------
+// <copyright file="AssertionSuccessResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A response from the Authorization Server to the Client containing an access code.
+ /// </summary>
+ internal class AssertionSuccessResponse : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AssertionSuccessResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal AssertionSuccessResponse(ClientAccountUsernamePasswordRequest request)
+ : base(request) {
+ }
+
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ /// <value>The access token.</value>
+ [MessagePart(Protocol.wrap_access_token, IsRequired = true, AllowEmpty = false)]
+ internal string AccessToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the lifetime of the access token.
+ /// </summary>
+ /// <value>The lifetime.</value>
+ [MessagePart(Protocol.wrap_access_token_expires_in, IsRequired = false, Encoder = typeof(TimespanSecondsEncoder))]
+ internal TimeSpan? Lifetime { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordFailedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordFailedResponse.cs
new file mode 100644
index 0000000..57ce588
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordFailedResponse.cs
@@ -0,0 +1,22 @@
+//-----------------------------------------------------------------------
+// <copyright file="ClientAccountUsernamePasswordFailedResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ /// <summary>
+ /// A response from the Authorization Server to the Client to indicate that a
+ /// request for an access code failed, probably due to an invalid account
+ /// name and password.
+ /// </summary>
+ internal class ClientAccountUsernamePasswordFailedResponse : UnauthorizedResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientAccountUsernamePasswordFailedResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal ClientAccountUsernamePasswordFailedResponse(ClientAccountUsernamePasswordRequest request)
+ : base(request) {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordRequest.cs
new file mode 100644
index 0000000..9f47c57
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordRequest.cs
@@ -0,0 +1,69 @@
+//-----------------------------------------------------------------------
+// <copyright file="ClientAccountUsernamePasswordRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A request for an access token for a client application that has its
+ /// own (non-user affiliated) client name and password.
+ /// </summary>
+ /// <remarks>
+ /// This is somewhat analogous to 2-legged OAuth.
+ /// </remarks>
+ internal class ClientAccountUsernamePasswordRequest : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientAccountUsernamePasswordRequest"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ /// <param name="version">The version.</param>
+ internal ClientAccountUsernamePasswordRequest(Uri authorizationServer, Version version)
+ : base(version, MessageTransport.Direct, authorizationServer) {
+ this.HttpMethods = HttpDeliveryMethods.PostRequest;
+ }
+
+ /// <summary>
+ /// Gets or sets the account name.
+ /// </summary>
+ /// <value>The name on the account.</value>
+ [MessagePart(Protocol.wrap_name, IsRequired = true, AllowEmpty = false)]
+ internal string Name { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user's password.
+ /// </summary>
+ /// <value>The password.</value>
+ [MessagePart(Protocol.wrap_password, IsRequired = true, AllowEmpty = false)]
+ internal string Password { get; set; }
+
+ /// <summary>
+ /// Gets or sets an optional authorization scope as defined by the Authorization Server.
+ /// </summary>
+ [MessagePart(Protocol.wrap_scope, IsRequired = false, AllowEmpty = true)]
+ internal string Scope { get; set; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ protected override void EnsureValidMessage() {
+ base.EnsureValidMessage();
+ ErrorUtilities.VerifyProtocol(this.Recipient.IsTransportSecure(), OAuthWrapStrings.HttpsRequired);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordSuccessResponse.cs
new file mode 100644
index 0000000..c53bdea
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/ClientAccountAndPassword/ClientAccountUsernamePasswordSuccessResponse.cs
@@ -0,0 +1,47 @@
+//-----------------------------------------------------------------------
+// <copyright file="ClientAccountUsernamePasswordSuccessResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A response from the Authorization Server to the Client containing an access code.
+ /// </summary>
+ internal class ClientAccountUsernamePasswordSuccessResponse : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ClientAccountUsernamePasswordSuccessResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal ClientAccountUsernamePasswordSuccessResponse(ClientAccountUsernamePasswordRequest request)
+ : base(request) {
+ }
+
+ /// <summary>
+ /// Gets or sets the refresh token.
+ /// </summary>
+ /// <value>The token.</value>
+ [MessagePart(Protocol.wrap_refresh_token, IsRequired = true, AllowEmpty = false)]
+ internal string RefreshToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ /// <value>The access token.</value>
+ [MessagePart(Protocol.wrap_access_token, IsRequired = true, AllowEmpty = false)]
+ internal string AccessToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the lifetime of the access token.
+ /// </summary>
+ /// <value>The lifetime.</value>
+ [MessagePart(Protocol.wrap_access_token_expires_in, IsRequired = false, Encoder = typeof(TimespanSecondsEncoder))]
+ internal TimeSpan? Lifetime { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/IMessageWithClientState.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/IMessageWithClientState.cs
new file mode 100644
index 0000000..8054d27
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/IMessageWithClientState.cs
@@ -0,0 +1,13 @@
+//-----------------------------------------------------------------------
+// <copyright file="IMessageWithClientState.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using DotNetOpenAuth.Messaging;
+
+ internal interface IMessageWithClientState : IProtocolMessage {
+ string ClientState { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs
new file mode 100644
index 0000000..9986d19
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs
@@ -0,0 +1,205 @@
+//-----------------------------------------------------------------------
+// <copyright file="MessageBase.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A common message base class for OAuth WRAP messages.
+ /// </summary>
+ public class MessageBase : IDirectedProtocolMessage, IDirectResponseProtocolMessage {
+ /// <summary>
+ /// A dictionary to contain extra message data.
+ /// </summary>
+ private Dictionary<string, string> extraData = new Dictionary<string, string>();
+
+ /// <summary>
+ /// The originating request.
+ /// </summary>
+ private IDirectedProtocolMessage originatingRequest;
+
+ /// <summary>
+ /// The backing field for the <see cref="IMessage.Version"/> property.
+ /// </summary>
+ private Version version;
+
+ /// <summary>
+ /// A value indicating whether this message is a direct or indirect message.
+ /// </summary>
+ private MessageTransport messageTransport;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageBase"/> class
+ /// that is used for direct response messages.
+ /// </summary>
+ /// <param name="version">The version.</param>
+ protected MessageBase(Version version) {
+ Contract.Requires<ArgumentNullException>(version != null);
+ this.messageTransport = MessageTransport.Direct;
+ this.version = version;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageBase"/> class.
+ /// </summary>
+ /// <param name="request">The originating request.</param>
+ protected MessageBase(IDirectedProtocolMessage request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ this.originatingRequest = request;
+ this.messageTransport = MessageTransport.Direct;
+ this.version = request.Version;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageBase"/> class
+ /// that is used for directed messages.
+ /// </summary>
+ /// <param name="version">The version.</param>
+ /// <param name="messageTransport">The message transport.</param>
+ /// <param name="recipient">The recipient.</param>
+ protected MessageBase(Version version, MessageTransport messageTransport, Uri recipient) {
+ Contract.Requires<ArgumentNullException>(version != null);
+ Contract.Requires<ArgumentNullException>(recipient != null);
+
+ this.version = version;
+ this.messageTransport = messageTransport;
+ this.Recipient = recipient;
+ this.HttpMethods = HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.PostRequest;
+ }
+
+ #region IMessage Properties
+
+ /// <summary>
+ /// Gets the version of the protocol or extension this message is prepared to implement.
+ /// </summary>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ Version IMessage.Version {
+ get { return this.version; }
+ }
+
+ /// <summary>
+ /// Gets the extra, non-standard Protocol parameters included in the message.
+ /// </summary>
+ /// <value></value>
+ /// <remarks>
+ /// Implementations of this interface should ensure that this property never returns null.
+ /// </remarks>
+ public IDictionary<string, string> ExtraData {
+ get { return this.extraData; }
+ }
+
+ #endregion
+
+ #region IProtocolMessage Members
+
+ /// <summary>
+ /// Gets the level of protection this message requires.
+ /// </summary>
+ /// <value><see cref="MessageProtections.None"/></value>
+ MessageProtections IProtocolMessage.RequiredProtection {
+ get { return MessageProtections.None; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this is a direct or indirect message.
+ /// </summary>
+ /// <value></value>
+ MessageTransport IProtocolMessage.Transport {
+ get { return this.messageTransport; }
+ }
+
+ #endregion
+
+ #region IDirectedProtocolMessage Members
+
+ /// <summary>
+ /// Gets the preferred method of transport for the message.
+ /// </summary>
+ /// <remarks>
+ /// For indirect messages this will likely be GET+POST, which both can be simulated in the user agent:
+ /// the GET with a simple 301 Redirect, and the POST with an HTML form in the response with javascript
+ /// to automate submission.
+ /// </remarks>
+ HttpDeliveryMethods IDirectedProtocolMessage.HttpMethods {
+ get { return this.HttpMethods; }
+ }
+
+ /// <summary>
+ /// Gets the URL of the intended receiver of this message.
+ /// </summary>
+ Uri IDirectedProtocolMessage.Recipient {
+ get { return this.Recipient; }
+ }
+
+ #endregion
+
+ #region IDirectResponseProtocolMessage Members
+
+ /// <summary>
+ /// Gets the originating request message that caused this response to be formed.
+ /// </summary>
+ IDirectedProtocolMessage IDirectResponseProtocolMessage.OriginatingRequest {
+ get { return this.originatingRequest; }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets or sets the preferred method of transport for the message.
+ /// </summary>
+ /// <remarks>
+ /// For indirect messages this will likely be GET+POST, which both can be simulated in the user agent:
+ /// the GET with a simple 301 Redirect, and the POST with an HTML form in the response with javascript
+ /// to automate submission.
+ /// </remarks>
+ protected HttpDeliveryMethods HttpMethods { get; set; }
+
+ /// <summary>
+ /// Gets the URL of the intended receiver of this message.
+ /// </summary>
+ protected Uri Recipient { get; private set; }
+
+ #region IMessage Methods
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ void IMessage.EnsureValidMessage() {
+ this.EnsureValidMessage();
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ protected virtual void EnsureValidMessage() {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenFailedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenFailedResponse.cs
new file mode 100644
index 0000000..5463791
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenFailedResponse.cs
@@ -0,0 +1,25 @@
+//-----------------------------------------------------------------------
+// <copyright file="RefreshAccessTokenFailedResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ /// <summary>
+ /// A response from the Authorization Server to the Client to indicate that a
+ /// request for an access token renewal failed, probably due to an invalid
+ /// refresh token.
+ /// </summary>
+ /// <remarks>
+ /// This message type is shared by the Web App, Rich App, and Username/Password profiles.
+ /// </remarks>
+ internal class RefreshAccessTokenFailedResponse : UnauthorizedResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RefreshAccessTokenFailedResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal RefreshAccessTokenFailedResponse(RefreshAccessTokenRequest request)
+ : base(request) {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs
new file mode 100644
index 0000000..a5de51a
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs
@@ -0,0 +1,57 @@
+//-----------------------------------------------------------------------
+// <copyright file="RefreshAccessTokenRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A request from the client to the authorization server for a new access token
+ /// using a refresh token, after a previously supplied access token has expired.
+ /// </summary>
+ /// <remarks>
+ /// This message type is shared by the Web App, Rich App, and Username/Password profiles.
+ /// </remarks>
+ internal class RefreshAccessTokenRequest : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RefreshAccessTokenRequest"/> class.
+ /// </summary>
+ /// <param name="refreshTokenEndpoint">The authorization server's Refresh Token endpoint.</param>
+ /// <param name="version">The version.</param>
+ internal RefreshAccessTokenRequest(Uri refreshTokenEndpoint, Version version)
+ : base(version, MessageTransport.Direct, refreshTokenEndpoint) {
+ this.HttpMethods = HttpDeliveryMethods.PostRequest;
+ }
+
+ /// <summary>
+ /// Gets or sets the refresh token that was received in
+ /// <see cref="UserNamePasswordSuccessResponse.RefreshToken"/>.
+ /// </summary>
+ /// <value>The refresh token.</value>
+ [MessagePart(Protocol.wrap_refresh_token, IsRequired = true, AllowEmpty = false)]
+ internal string RefreshToken { get; set; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ protected override void EnsureValidMessage() {
+ base.EnsureValidMessage();
+ ErrorUtilities.VerifyProtocol(this.Recipient.IsTransportSecure(), OAuthWrapStrings.HttpsRequired);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenSuccessResponse.cs
new file mode 100644
index 0000000..36a98e5
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenSuccessResponse.cs
@@ -0,0 +1,44 @@
+//-----------------------------------------------------------------------
+// <copyright file="RefreshAccessTokenSuccessResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A response from the Authorization Server to the Consumer containing a delegation code
+ /// that the Consumer should use to obtain an access token.
+ /// </summary>
+ /// <remarks>
+ /// This message type is shared by the Web App, Rich App, and Username/Password profiles.
+ /// </remarks>
+ internal class RefreshAccessTokenSuccessResponse : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RefreshAccessTokenSuccessResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal RefreshAccessTokenSuccessResponse(RefreshAccessTokenRequest request)
+ : base(request) {
+ }
+
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ /// <value>The access token.</value>
+ [MessagePart(Protocol.wrap_access_token, IsRequired = true, AllowEmpty = false)]
+ internal string AccessToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the lifetime of the access token.
+ /// </summary>
+ /// <value>The lifetime.</value>
+ [MessagePart(Protocol.wrap_access_token_expires_in, IsRequired = false, Encoder = typeof(TimespanSecondsEncoder))]
+ internal TimeSpan? Lifetime { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenFailedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenFailedResponse.cs
new file mode 100644
index 0000000..be4f001
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenFailedResponse.cs
@@ -0,0 +1,21 @@
+//-----------------------------------------------------------------------
+// <copyright file="RichAppAccessTokenFailedResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ /// <summary>
+ /// A response from the Authorization Server to the Client in the event
+ /// that an access token could not be granted.
+ /// </summary>
+ internal class RichAppAccessTokenFailedResponse : UnauthorizedResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RichAppAccessTokenFailedResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal RichAppAccessTokenFailedResponse(RichAppAccessTokenRequest request)
+ : base(request) {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenRequest.cs
new file mode 100644
index 0000000..f0ad943
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenRequest.cs
@@ -0,0 +1,61 @@
+//-----------------------------------------------------------------------
+// <copyright file="RichAppAccessTokenRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A message from the Client to the Authorization Server exchanging a
+ /// verification code for refresh and access tokens.
+ /// </summary>
+ internal class RichAppAccessTokenRequest : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RichAppAccessTokenRequest"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ /// <param name="version">The version.</param>
+ internal RichAppAccessTokenRequest(Uri authorizationServer, Version version)
+ : base(version, MessageTransport.Direct, authorizationServer) {
+ this.HttpMethods = HttpDeliveryMethods.PostRequest;
+ }
+
+ /// <summary>
+ /// Gets or sets the identifier by which this client is known to the Authorization Server.
+ /// </summary>
+ /// <value>The client identifier.</value>
+ [MessagePart(Protocol.wrap_client_id, IsRequired = true, AllowEmpty = false)]
+ internal string ClientIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the verification code previously communicated to the Client
+ /// in <see cref="RichAppResponse.VerificationCode"/>.
+ /// </summary>
+ /// <value>The verification code.</value>
+ [MessagePart(Protocol.wrap_verification_code, IsRequired = true, AllowEmpty = false)]
+ internal string VerificationCode { get; set; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ protected override void EnsureValidMessage() {
+ base.EnsureValidMessage();
+ ErrorUtilities.VerifyProtocol(this.Recipient.IsTransportSecure(), OAuthWrapStrings.HttpsRequired);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenSuccessResponse.cs
new file mode 100644
index 0000000..8330176
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppAccessTokenSuccessResponse.cs
@@ -0,0 +1,45 @@
+//-----------------------------------------------------------------------
+// <copyright file="RichAppAccessTokenSuccessResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The direct response message that contains the access token from the Authorization Server
+ /// to the Client.
+ /// </summary>
+ internal class RichAppAccessTokenSuccessResponse : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RichAppAccessTokenSuccessResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal RichAppAccessTokenSuccessResponse(RichAppAccessTokenRequest request)
+ : base(request) {
+ }
+
+ /// <summary>
+ /// Gets or sets the refresh token.
+ /// </summary>
+ /// <value>The token.</value>
+ [MessagePart(Protocol.wrap_refresh_token, IsRequired = true, AllowEmpty = false)]
+ internal string RefreshToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ /// <value>The token.</value>
+ [MessagePart(Protocol.wrap_access_token, IsRequired = true, AllowEmpty = false)]
+ internal string AccessToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the lifetime of the access token.
+ /// </summary>
+ /// <value>The lifetime.</value>
+ [MessagePart(Protocol.wrap_access_token_expires_in, IsRequired = false, AllowEmpty = false, Encoder = typeof(TimespanSecondsEncoder))]
+ internal TimeSpan? Lifetime { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppRequest.cs
new file mode 100644
index 0000000..9856687
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppRequest.cs
@@ -0,0 +1,68 @@
+//-----------------------------------------------------------------------
+// <copyright file="RichAppRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A request from a rich app Client to an Authorization Server requested
+ /// authorization to access user Protected Data.
+ /// </summary>
+ internal class RichAppRequest : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RichAppRequest"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ /// <param name="version">The version.</param>
+ internal RichAppRequest(Uri authorizationServer, Version version)
+ : base(version, MessageTransport.Indirect, authorizationServer) {
+ }
+
+ /// <summary>
+ /// Gets or sets the client identifier previously obtained from the Authorization Server.
+ /// </summary>
+ /// <value>The client identifier.</value>
+ [MessagePart(Protocol.wrap_client_id, IsRequired = true, AllowEmpty = false)]
+ internal string ClientIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the callback URL.
+ /// </summary>
+ /// <value>
+ /// An absolute URL to which the Authorization Server will redirect the User back after
+ /// the user has approved the authorization request.
+ /// </value>
+ /// <remarks>
+ /// Authorization Servers MAY require that the wrap_callback URL match the previously
+ /// registered value for the Client Identifier.
+ /// </remarks>
+ [MessagePart(Protocol.wrap_callback, IsRequired = false, AllowEmpty = false)]
+ internal Uri Callback { get; set; }
+
+ /// <summary>
+ /// Gets or sets state of the client that should be sent back with the authorization response.
+ /// </summary>
+ /// <value>
+ /// An opaque value that Clients can use to maintain state associated with this request.
+ /// </value>
+ /// <remarks>
+ /// If this value is present, the Authorization Server MUST return it to the Client's Callback URL.
+ /// </remarks>
+ [MessagePart(Protocol.wrap_client_state, IsRequired = false, AllowEmpty = true)]
+ internal string ClientState { get; set; }
+
+ /// <summary>
+ /// Gets or sets the scope.
+ /// </summary>
+ /// <value>The Authorization Server MAY define authorization scope values for the Client to include.</value>
+ [MessagePart(Protocol.wrap_scope, IsRequired = false, AllowEmpty = true)]
+ internal string Scope { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppResponse.cs
new file mode 100644
index 0000000..678d956
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/RichApp/RichAppResponse.cs
@@ -0,0 +1,60 @@
+//-----------------------------------------------------------------------
+// <copyright file="RichAppResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// An indirect response from the Authorization Server to the rich app Client
+ /// with the verification code.
+ /// </summary>
+ internal class RichAppResponse : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="RichAppResponse"/> class.
+ /// </summary>
+ /// <param name="clientCallback">The client callback.</param>
+ /// <param name="version">The version.</param>
+ internal RichAppResponse(Uri clientCallback, Version version)
+ : base(version, MessageTransport.Indirect, clientCallback) {
+ }
+
+ /// <summary>
+ /// Gets or sets the verification code.
+ /// </summary>
+ /// <value>
+ /// The long-lived credential assigned by the Authorization Server to this Client for
+ /// use in accessing the authorizing user's protected resources.
+ /// </value>
+ [MessagePart(Protocol.wrap_verification_code, IsRequired = true, AllowEmpty = false)]
+ internal string VerificationCode { get; set; }
+
+ /// <summary>
+ /// Gets or sets state of the client that should be sent back with the authorization response.
+ /// </summary>
+ /// <value>
+ /// An opaque value that Clients can use to maintain state associated with this request.
+ /// </value>
+ /// <remarks>
+ /// This parameter is required if the Client included it in <see cref="RichAppRequest.ClientState"/>.
+ /// </remarks>
+ [MessagePart(Protocol.wrap_client_state, IsRequired = false, AllowEmpty = true)]
+ internal string ClientState { get; set; }
+
+ /// <summary>
+ /// Gets a value indicating whether the user granted the authorization request.
+ /// </summary>
+ /// <value>
+ /// <c>true</c> if authorization is granted; otherwise, <c>false</c>.
+ /// </value>
+ internal bool IsGranted {
+ get { return !string.IsNullOrEmpty(this.VerificationCode) && this.VerificationCode != Protocol.user_denied; }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UnauthorizedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UnauthorizedResponse.cs
new file mode 100644
index 0000000..901b6ff
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UnauthorizedResponse.cs
@@ -0,0 +1,47 @@
+//-----------------------------------------------------------------------
+// <copyright file="UnauthorizedResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System.Net;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A direct response that is simply a 401 Unauthorized with an
+ /// WWW-Authenticate: OAuth header.
+ /// </summary>
+ internal class UnauthorizedResponse : MessageBase, IHttpDirectResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnauthorizedResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal UnauthorizedResponse(IDirectedProtocolMessage request)
+ : base(request) {
+ }
+
+ #region IHttpDirectResponse Members
+
+ /// <summary>
+ /// Gets the HTTP status code that the direct response should be sent with.
+ /// </summary>
+ HttpStatusCode IHttpDirectResponse.HttpStatusCode {
+ get { return HttpStatusCode.Unauthorized; }
+ }
+
+ /// <summary>
+ /// 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() {
+ { HttpResponseHeader.WwwAuthenticate, Protocol.HttpAuthorizationScheme },
+ };
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordCaptchaResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordCaptchaResponse.cs
new file mode 100644
index 0000000..192af58
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordCaptchaResponse.cs
@@ -0,0 +1,54 @@
+//-----------------------------------------------------------------------
+// <copyright file="UsernamePasswordCaptchaResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A response from the Authorization Server indicating the Client must have the user
+ /// complete a CAPTCHA puzzle prior to authorization.
+ /// </summary>
+ internal class UsernamePasswordCaptchaResponse : MessageBase, IHttpDirectResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UsernamePasswordCaptchaResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal UsernamePasswordCaptchaResponse(UserNamePasswordRequest request)
+ : base(request) {
+ }
+
+ #region IHttpDirectResponse Members
+
+ /// <summary>
+ /// Gets the HTTP status code that the direct response should be sent with.
+ /// </summary>
+ HttpStatusCode IHttpDirectResponse.HttpStatusCode {
+ get { return HttpStatusCode.BadRequest; }
+ }
+
+ /// <summary>
+ /// 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(); }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets or sets the URL to the CAPTCHA puzzle.
+ /// </summary>
+ /// <value>The captcha URL.</value>
+ [MessagePart(Protocol.wrap_captcha_url, IsRequired = true, AllowEmpty = false)]
+ internal Uri CaptchaPuzzle { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordFailedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordFailedResponse.cs
new file mode 100644
index 0000000..fa98b83
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordFailedResponse.cs
@@ -0,0 +1,22 @@
+//-----------------------------------------------------------------------
+// <copyright file="UserNamePasswordFailedResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ /// <summary>
+ /// A response from the Authorization Server to the Consumer to indicate that a
+ /// request for a delegation code failed, probably due to an invalid
+ /// username and password.
+ /// </summary>
+ internal class UserNamePasswordFailedResponse : UnauthorizedResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserNamePasswordFailedResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal UserNamePasswordFailedResponse(UserNamePasswordRequest request)
+ : base(request) {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordRequest.cs
new file mode 100644
index 0000000..b4a72ff
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordRequest.cs
@@ -0,0 +1,93 @@
+//-----------------------------------------------------------------------
+// <copyright file="UserNamePasswordRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A request for a delegation code in exchnage for a user's confidential
+ /// username and password.
+ /// </summary>
+ /// <remarks>
+ /// After this request has been sent, the consumer application MUST discard
+ /// the confidential user credentials and use the delegation code going forward.
+ /// </remarks>
+ internal class UserNamePasswordRequest : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserNamePasswordRequest"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ /// <param name="version">The version.</param>
+ internal UserNamePasswordRequest(Uri authorizationServer, Version version)
+ : base(version, MessageTransport.Direct, authorizationServer) {
+ this.HttpMethods = HttpDeliveryMethods.PostRequest;
+ }
+
+ /// <summary>
+ /// Gets or sets the client identifier previously obtained from the Authorization Server.
+ /// </summary>
+ /// <value>The client identifier.</value>
+ [MessagePart(Protocol.wrap_client_id, IsRequired = true, AllowEmpty = false)]
+ internal string ClientIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user's account username.
+ /// </summary>
+ /// <value>The username on the user's account.</value>
+ [MessagePart(Protocol.wrap_username, IsRequired = true, AllowEmpty = false)]
+ internal string UserName { get; set; }
+
+ /// <summary>
+ /// Gets or sets the user's password.
+ /// </summary>
+ /// <value>The password.</value>
+ [MessagePart(Protocol.wrap_password, IsRequired = true, AllowEmpty = false)]
+ internal string Password { get; set; }
+
+ /// <summary>
+ /// Gets or sets the CAPTCHA puzzle that the user just solved, if applicable.
+ /// </summary>
+ /// <value>The captcha puzzle location.</value>
+ [MessagePart(Protocol.wrap_captcha_url, IsRequired = false, AllowEmpty = false)]
+ internal Uri CaptchaPuzzle { get; set; }
+
+ /// <summary>
+ /// Gets or sets the solution to the CAPTCHA puzzle the user just solved, if applicable.
+ /// </summary>
+ /// <value>The CAPTCHA solution.</value>
+ [MessagePart(Protocol.wrap_captcha_solution, IsRequired = false, AllowEmpty = false)]
+ internal string CaptchaSolution { get; set; }
+
+ /// <summary>
+ /// Gets or sets the scope.
+ /// </summary>
+ /// <value>The scope.</value>
+ [MessagePart(Protocol.wrap_scope, IsRequired = false, AllowEmpty = true)]
+ internal string Scope { get; set; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ protected override void EnsureValidMessage() {
+ base.EnsureValidMessage();
+ ErrorUtilities.VerifyProtocol(this.Recipient.IsTransportSecure(), OAuthWrapStrings.HttpsRequired);
+ ErrorUtilities.VerifyProtocol((this.CaptchaPuzzle == null) == (this.CaptchaSolution == null), "CAPTCHA puzzle and solution must either be both absent or both present.");
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordSuccessResponse.cs
new file mode 100644
index 0000000..7afe7da
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordSuccessResponse.cs
@@ -0,0 +1,51 @@
+//-----------------------------------------------------------------------
+// <copyright file="UserNamePasswordSuccessResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A response from the Authorization Server to the Client containing a refresh token
+ /// and an access token.
+ /// </summary>
+ internal class UserNamePasswordSuccessResponse : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserNamePasswordSuccessResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal UserNamePasswordSuccessResponse(UserNamePasswordRequest request)
+ : base(request) {
+ }
+
+ /// <summary>
+ /// Gets or sets the verification code.
+ /// </summary>
+ /// <value>
+ /// The long-lived credential assigned by the Authorization Server to this Client for
+ /// use in accessing the authorizing user's protected resources.
+ /// </value>
+ [MessagePart(Protocol.wrap_refresh_token, IsRequired = true, AllowEmpty = false)]
+ internal string RefreshToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ /// <value>The access token.</value>
+ [MessagePart(Protocol.wrap_access_token, IsRequired = true, AllowEmpty = false)]
+ internal string AccessToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the lifetime of the access token.
+ /// </summary>
+ /// <value>The lifetime.</value>
+ [MessagePart(Protocol.wrap_access_token_expires_in, IsRequired = false, Encoder = typeof(TimespanSecondsEncoder))]
+ internal TimeSpan? Lifetime { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordVerificationResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordVerificationResponse.cs
new file mode 100644
index 0000000..9b4602c
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordVerificationResponse.cs
@@ -0,0 +1,55 @@
+//-----------------------------------------------------------------------
+// <copyright file="UserNamePasswordVerificationResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A response from the Authorization Server to the client indicating that the user
+ /// must visit a URL to complete an additional verification step before proceeding.
+ /// </summary>
+ internal class UserNamePasswordVerificationResponse : MessageBase, IHttpDirectResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UserNamePasswordVerificationResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal UserNamePasswordVerificationResponse(UserNamePasswordRequest request)
+ : base(request) {
+ }
+
+ #region IHttpDirectResponse Members
+
+ /// <summary>
+ /// Gets the HTTP status code that the direct response should be sent with.
+ /// </summary>
+ /// <value><see cref="HttpStatusCode.BadRequest"/></value>
+ HttpStatusCode IHttpDirectResponse.HttpStatusCode {
+ get { return HttpStatusCode.BadRequest; }
+ }
+
+ /// <summary>
+ /// Gets the HTTP headers to add to the response.
+ /// </summary>
+ WebHeaderCollection IHttpDirectResponse.Headers {
+ get { return new WebHeaderCollection(); }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets or sets the verification URL the user must visit with a browser
+ /// to complete some step to defeat automated attacks.
+ /// </summary>
+ /// <value>The verification URL.</value>
+ [MessagePart(Protocol.wrap_verification_url, IsRequired = true, AllowEmpty = false)]
+ internal Uri VerificationUrl { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenBadClientResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenBadClientResponse.cs
new file mode 100644
index 0000000..6758e93
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenBadClientResponse.cs
@@ -0,0 +1,22 @@
+//-----------------------------------------------------------------------
+// <copyright file="WebAppAccessTokenBadClientResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ /// <summary>
+ /// A response from the Authorization Server to the Client in the event
+ /// that the <see cref="WebAppAccessTokenRequest"/> message had an
+ /// invalid Client Identifier and Client Secret combination.
+ /// </summary>
+ internal class WebAppAccessTokenBadClientResponse : UnauthorizedResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebAppAccessTokenBadClientResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal WebAppAccessTokenBadClientResponse(WebAppAccessTokenRequest request)
+ : base(request) {
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenFailedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenFailedResponse.cs
new file mode 100644
index 0000000..7cdc2a9
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenFailedResponse.cs
@@ -0,0 +1,56 @@
+//-----------------------------------------------------------------------
+// <copyright file="WebAppAccessTokenFailedResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A response from the Authorization Server to the Client in the event
+ /// that the <see cref="WebAppAccessTokenRequest"/> message had an
+ /// invalid calback URL or verification code.
+ /// </summary>
+ internal class WebAppAccessTokenFailedResponse : MessageBase, IHttpDirectResponse {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebAppAccessTokenFailedResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal WebAppAccessTokenFailedResponse(WebAppAccessTokenRequest request)
+ : base(request) {
+ }
+
+ #region IHttpDirectResponse Members
+
+ /// <summary>
+ /// Gets the HTTP status code that the direct response should be sent with.
+ /// </summary>
+ /// <value></value>
+ HttpStatusCode IHttpDirectResponse.HttpStatusCode {
+ get { return System.Net.HttpStatusCode.BadRequest; }
+ }
+
+ /// <summary>
+ /// 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(); }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Gets or sets the error reason.
+ /// </summary>
+ /// <value>"expired_verification_code" or "invalid_callback".</value>
+ [MessagePart(Protocol.wrap_error_reason, IsRequired = false, AllowEmpty = true)]
+ internal string ErrorReason { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenRequest.cs
new file mode 100644
index 0000000..f64a0d2
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenRequest.cs
@@ -0,0 +1,90 @@
+//-----------------------------------------------------------------------
+// <copyright file="WebAppAccessTokenRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuthWrap.ChannelElements;
+
+ /// <summary>
+ /// A message sent by the Client directly to the Authorization Server to exchange
+ /// the verification code for an Access Token.
+ /// </summary>
+ /// <remarks>
+ /// Used by the Web App (and Rich App?) profiles.
+ /// </remarks>
+ internal class WebAppAccessTokenRequest : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebAppAccessTokenRequest"/> class.
+ /// </summary>
+ /// <param name="accessTokenEndpoint">The Authorization Server's access token endpoint URL.</param>
+ /// <param name="version">The version.</param>
+ internal WebAppAccessTokenRequest(Uri accessTokenEndpoint, Version version)
+ : base(version, MessageTransport.Direct, accessTokenEndpoint) {
+ this.HttpMethods = HttpDeliveryMethods.PostRequest;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebAppAccessTokenRequest"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ internal WebAppAccessTokenRequest(AuthorizationServerDescription authorizationServer)
+ : this(authorizationServer.AccessTokenEndpoint, authorizationServer.Version) {
+ Contract.Requires<ArgumentNullException>(authorizationServer != null);
+ Contract.Requires<ArgumentException>(authorizationServer.Version != null);
+ Contract.Requires<ArgumentException>(authorizationServer.AccessTokenEndpoint != null);
+ }
+
+ /// <summary>
+ /// Gets or sets the identifier by which this client is known to the Authorization Server.
+ /// </summary>
+ /// <value>The client identifier.</value>
+ [MessagePart(Protocol.wrap_client_id, IsRequired = true, AllowEmpty = false)]
+ internal string ClientIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the client secret.
+ /// </summary>
+ /// <value>The client secret.</value>
+ [MessagePart(Protocol.wrap_client_secret, IsRequired = true, AllowEmpty = false)]
+ internal string ClientSecret { get; set; }
+
+ /// <summary>
+ /// Gets or sets the verification code previously communicated to the Client
+ /// in <see cref="WebAppSuccessResponse.VerificationCode"/>.
+ /// </summary>
+ /// <value>The verification code.</value>
+ [MessagePart(Protocol.wrap_verification_code, IsRequired = true, AllowEmpty = false)]
+ internal string VerificationCode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the callback URL used in <see cref="WebAppRequest.Callback"/>
+ /// </summary>
+ /// <value>
+ /// The Callback URL used to obtain the Verification Code.
+ /// </value>
+ [MessagePart(Protocol.wrap_callback, IsRequired = true, AllowEmpty = false)]
+ internal Uri Callback { get; set; }
+
+ /// <summary>
+ /// Checks the message state for conformity to the protocol specification
+ /// and throws an exception if the message is invalid.
+ /// </summary>
+ /// <remarks>
+ /// <para>Some messages have required fields, or combinations of fields that must relate to each other
+ /// in specialized ways. After deserializing a message, this method checks the state of the
+ /// message to see if it conforms to the protocol.</para>
+ /// <para>Note that this property should <i>not</i> check signatures or perform any state checks
+ /// outside this scope of this particular message.</para>
+ /// </remarks>
+ /// <exception cref="ProtocolException">Thrown if the message is invalid.</exception>
+ protected override void EnsureValidMessage() {
+ base.EnsureValidMessage();
+ ErrorUtilities.VerifyProtocol(this.Recipient.IsTransportSecure(), OAuthWrapStrings.HttpsRequired);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenSuccessResponse.cs
new file mode 100644
index 0000000..ec44712
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppAccessTokenSuccessResponse.cs
@@ -0,0 +1,45 @@
+//-----------------------------------------------------------------------
+// <copyright file="WebAppAccessTokenSuccessResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The direct response message that contains the access token from the Authorization Server
+ /// to the Client.
+ /// </summary>
+ internal class WebAppAccessTokenSuccessResponse : MessageBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebAppAccessTokenSuccessResponse"/> class.
+ /// </summary>
+ /// <param name="request">The request.</param>
+ internal WebAppAccessTokenSuccessResponse(WebAppAccessTokenRequest request)
+ : base(request) {
+ }
+
+ /// <summary>
+ /// Gets or sets the refresh token.
+ /// </summary>
+ /// <value>The token.</value>
+ [MessagePart(Protocol.wrap_refresh_token, IsRequired = true, AllowEmpty = false)]
+ internal string RefreshToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ /// <value>The token.</value>
+ [MessagePart(Protocol.wrap_access_token, IsRequired = true, AllowEmpty = false)]
+ internal string AccessToken { get; set; }
+
+ /// <summary>
+ /// Gets or sets the lifetime of the access token.
+ /// </summary>
+ /// <value>The lifetime.</value>
+ [MessagePart(Protocol.wrap_access_token_expires_in, IsRequired = false, AllowEmpty = false, Encoder = typeof(TimespanSecondsEncoder))]
+ internal TimeSpan? Lifetime { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppFailedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppFailedResponse.cs
new file mode 100644
index 0000000..ea7b0b7
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppFailedResponse.cs
@@ -0,0 +1,47 @@
+//-----------------------------------------------------------------------
+// <copyright file="WebAppFailedResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The message the Authorization Server MAY use to send the user back to the Client
+ /// following the user's denial to grant Consumer with authorization of
+ /// access to requested resources.
+ /// </summary>
+ internal class WebAppFailedResponse : MessageBase, IMessageWithClientState {
+ /// <summary>
+ /// A constant parameter that indicates the user refused to grant the requested authorization.
+ /// </summary>
+ [MessagePart(Protocol.wrap_error_reason, IsRequired = true)]
+ private const string ErrorReason = Protocol.user_denied;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebAppFailedResponse"/> class.
+ /// </summary>
+ /// <param name="clientCallback">The recipient of the message.</param>
+ /// <param name="version">The version.</param>
+ internal WebAppFailedResponse(Uri clientCallback, Version version) :
+ base(version, MessageTransport.Indirect, clientCallback) {
+ Contract.Requires<ArgumentNullException>(version != null);
+ Contract.Requires<ArgumentNullException>(clientCallback != null);
+ }
+
+ /// <summary>
+ /// Gets or sets the state of the client that was supplied to the Authorization Server.
+ /// </summary>
+ /// <value>
+ /// An opaque value that Clients can use to maintain state associated with the authorization request.
+ /// </value>
+ /// <remarks>
+ /// If this value is present, the Authorization Server MUST return it to the Client's callback URL.
+ /// </remarks>
+ [MessagePart(Protocol.wrap_client_state, IsRequired = false, AllowEmpty = true)]
+ public string ClientState { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppRequest.cs
new file mode 100644
index 0000000..b0da9aa
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppRequest.cs
@@ -0,0 +1,80 @@
+//-----------------------------------------------------------------------
+// <copyright file="WebAppRequest.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuthWrap.ChannelElements;
+
+ /// <summary>
+ /// A message sent by a web application Client to the AuthorizationServer
+ /// via the user agent to obtain authorization from the user and prepare
+ /// to issue an access token to the Consumer if permission is granted.
+ /// </summary>
+ internal class WebAppRequest : MessageBase, IMessageWithClientState {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebAppRequest"/> class.
+ /// </summary>
+ /// <param name="userAuthorizationEndpoint">The Authorization Server's user authorization URL to direct the user to.</param>
+ /// <param name="version">The protocol version.</param>
+ internal WebAppRequest(Uri userAuthorizationEndpoint, Version version)
+ : base(version, MessageTransport.Indirect, userAuthorizationEndpoint) {
+ Contract.Requires<ArgumentNullException>(userAuthorizationEndpoint != null);
+ Contract.Requires<ArgumentNullException>(version != null);
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebAppRequest"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ internal WebAppRequest(AuthorizationServerDescription authorizationServer)
+ : this(authorizationServer.UserAuthorizationEndpoint, authorizationServer.Version) {
+ Contract.Requires<ArgumentNullException>(authorizationServer != null);
+ Contract.Requires<ArgumentException>(authorizationServer.Version != null);
+ Contract.Requires<ArgumentException>(authorizationServer.UserAuthorizationEndpoint != null);
+ }
+
+ /// <summary>
+ /// Gets or sets state of the client that should be sent back with the authorization response.
+ /// </summary>
+ /// <value>
+ /// An opaque value that Clients can use to maintain state associated with this request.
+ /// </value>
+ /// <remarks>
+ /// If this value is present, the Authorization Server MUST return it to the Client's Callback URL.
+ /// </remarks>
+ [MessagePart(Protocol.wrap_client_state, IsRequired = false, AllowEmpty = true)]
+ public string ClientState { get; set; }
+
+ /// <summary>
+ /// Gets or sets the identifier by which this client is known to the Authorization Server.
+ /// </summary>
+ [MessagePart(Protocol.wrap_client_id, IsRequired = true, AllowEmpty = false)]
+ internal string ClientIdentifier { get; set; }
+
+ /// <summary>
+ /// Gets or sets the callback URL.
+ /// </summary>
+ /// <value>
+ /// An absolute URL to which the Authorization Server will redirect the User back after
+ /// the user has approved the authorization request.
+ /// </value>
+ /// <remarks>
+ /// Authorization Servers MAY require that the wrap_callback URL match the previously
+ /// registered value for the Client Identifier.
+ /// </remarks>
+ [MessagePart(Protocol.wrap_callback, IsRequired = true, AllowEmpty = false)]
+ internal Uri Callback { get; set; }
+
+ /// <summary>
+ /// Gets or sets the scope.
+ /// </summary>
+ /// <value>The Authorization Server MAY define authorization scope values for the Client to include.</value>
+ [MessagePart(Protocol.wrap_scope, IsRequired = false, AllowEmpty = true)]
+ internal string Scope { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppSuccessResponse.cs
new file mode 100644
index 0000000..3bbc59e
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebApp/WebAppSuccessResponse.cs
@@ -0,0 +1,49 @@
+//-----------------------------------------------------------------------
+// <copyright file="WebAppSuccessResponse.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap.Messages {
+ using System;
+ using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The message sent by the Authorization Server to the Client via the user agent
+ /// to indicate that user authorization was granted, and to return the user
+ /// to the Client where they started their experience.
+ /// </summary>
+ internal class WebAppSuccessResponse : MessageBase, IMessageWithClientState {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebAppSuccessResponse"/> class.
+ /// </summary>
+ /// <param name="clientCallback">The client callback.</param>
+ /// <param name="version">The protocol version.</param>
+ internal WebAppSuccessResponse(Uri clientCallback, Version version)
+ : base(version, MessageTransport.Indirect, clientCallback) {
+ Contract.Requires<ArgumentNullException>(version != null);
+ Contract.Requires<ArgumentNullException>(clientCallback != null);
+ }
+
+ /// <summary>
+ /// Gets or sets some state as provided by the client in the authorization request.
+ /// </summary>
+ /// <value>An opaque value defined by the client.</value>
+ /// <remarks>
+ /// REQUIRED if the Client sent the value in the <see cref="WebAppRequest"/>.
+ /// </remarks>
+ [MessagePart(Protocol.wrap_client_state, IsRequired = false, AllowEmpty = true)]
+ public string ClientState { get; set; }
+
+ /// <summary>
+ /// Gets or sets the verification code.
+ /// </summary>
+ /// <value>
+ /// The long-lived credential assigned by the Authorization Server to this Consumer for
+ /// use in accessing the authorizing user's protected resources.
+ /// </value>
+ [MessagePart(Protocol.wrap_verification_code, IsRequired = true, AllowEmpty = true)]
+ internal string VerificationCode { get; set; }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs
new file mode 100644
index 0000000..326beb6
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs
@@ -0,0 +1,90 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+// This code was generated by a tool.
+// Runtime Version:4.0.30128.0
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap {
+ using System;
+
+
+ /// <summary>
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ /// </summary>
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class OAuthWrapStrings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal OAuthWrapStrings() {
+ }
+
+ /// <summary>
+ /// Returns the cached ResourceManager instance used by this class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DotNetOpenAuth.OAuthWrap.OAuthWrapStrings", typeof(OAuthWrapStrings).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ /// <summary>
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ /// </summary>
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Failed to obtain access token. Authorization Server reports reason: {0}.
+ /// </summary>
+ internal static string CannotObtainAccessTokenWithReason {
+ get {
+ return ResourceManager.GetString("CannotObtainAccessTokenWithReason", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to This message can only be sent over HTTPS..
+ /// </summary>
+ internal static string HttpsRequired {
+ get {
+ return ResourceManager.GetString("HttpsRequired", resourceCulture);
+ }
+ }
+
+ /// <summary>
+ /// Looks up a localized string similar to Failed to obtain access token due to invalid Client Identifier or Client Secret..
+ /// </summary>
+ internal static string InvalidClientCredentials {
+ get {
+ return ResourceManager.GetString("InvalidClientCredentials", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx
new file mode 100644
index 0000000..d488810
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="CannotObtainAccessTokenWithReason" xml:space="preserve">
+ <value>Failed to obtain access token. Authorization Server reports reason: {0}</value>
+ </data>
+ <data name="HttpsRequired" xml:space="preserve">
+ <value>This message can only be sent over HTTPS.</value>
+ </data>
+ <data name="InvalidClientCredentials" xml:space="preserve">
+ <value>Failed to obtain access token due to invalid Client Identifier or Client Secret.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/DotNetOpenAuth/OAuthWrap/Protocol.cs b/src/DotNetOpenAuth/OAuthWrap/Protocol.cs
new file mode 100644
index 0000000..b8e3962
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/Protocol.cs
@@ -0,0 +1,188 @@
+// <auto-generated/> // disable StyleCop on this file
+//-----------------------------------------------------------------------
+// <copyright file="Protocol.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap {
+ using System;
+ using System.Collections.Generic;
+
+ /// <summary>
+ /// An enumeration of the OAuth WRAP protocol versions supported by this library.
+ /// </summary>
+ public enum ProtocolVersion {
+ /// <summary>
+ /// The OAuth WRAP 1.0 specification.
+ /// </summary>
+ V10,
+ }
+
+ /// <summary>
+ /// Protocol constants for OAuth WRAP.
+ /// </summary>
+ internal class Protocol {
+ /// <summary>
+ /// The HTTP authorization scheme "WRAP";
+ /// </summary>
+ internal const string HttpAuthorizationScheme = "WRAP";
+
+ /// <summary>
+ /// The format of the HTTP Authorization header value that authorizes OAuth WRAP requests.
+ /// </summary>
+ internal const string HttpAuthorizationHeaderFormat = "WRAP access_token=\"{0}\"";
+
+ /// <summary>
+ /// The "wrap_client_state" string.
+ /// </summary>
+ internal const string wrap_client_state = "wrap_client_state";
+
+ /// <summary>
+ /// The "wrap_callback" string.
+ /// </summary>
+ internal const string wrap_callback = "wrap_callback";
+
+ /// <summary>
+ /// The "wrap_client_id" string.
+ /// </summary>
+ internal const string wrap_client_id = "wrap_client_id";
+
+ /// <summary>
+ /// The "wrap_scope" string.
+ /// </summary>
+ internal const string wrap_scope = "wrap_scope";
+
+ /// <summary>
+ /// The "wrap_client_secret" string.
+ /// </summary>
+ internal const string wrap_client_secret = "wrap_client_secret";
+
+ /// <summary>
+ /// The "wrap_verification_code" string.
+ /// </summary>
+ internal const string wrap_verification_code = "wrap_verification_code";
+
+ /// <summary>
+ /// The "wrap_verification_url" string.
+ /// </summary>
+ internal const string wrap_verification_url = "wrap_verification_url";
+
+ /// <summary>
+ /// The "wrap_error_reason" string.
+ /// </summary>
+ internal const string wrap_error_reason = "wrap_error_reason";
+
+ /// <summary>
+ /// The "wrap_access_token" string.
+ /// </summary>
+ internal const string wrap_access_token = "wrap_access_token";
+
+ /// <summary>
+ /// The "wrap_refresh_token" string.
+ /// </summary>
+ internal const string wrap_refresh_token = "wrap_refresh_token";
+
+ /// <summary>
+ /// The "wrap_access_token_expires_in" string.
+ /// </summary>
+ internal const string wrap_access_token_expires_in = "wrap_access_token_expires_in";
+
+ /// <summary>
+ /// The "expired_delegation_code" string.
+ /// </summary>
+ internal const string expired_delegation_code = "expired_delegation_code";
+
+ /// <summary>
+ /// The "wrap_username" string.
+ /// </summary>
+ internal const string wrap_username = "wrap_username";
+
+ /// <summary>
+ /// The "wrap_password" string.
+ /// </summary>
+ internal const string wrap_password = "wrap_password";
+
+ /// <summary>
+ /// The "wrap_name" string.
+ /// </summary>
+ internal const string wrap_name = "wrap_name";
+
+ /// <summary>
+ /// The "wrap_assertion_format" string.
+ /// </summary>
+ internal const string wrap_assertion_format = "wrap_assertion_format";
+
+ /// <summary>
+ /// The "wrap_assertion" string.
+ /// </summary>
+ internal const string wrap_assertion = "wrap_assertion";
+
+ /// <summary>
+ /// The "wrap_SAML" string.
+ /// </summary>
+ internal const string wrap_saml = "wrap_SAML";
+
+ /// <summary>
+ /// The "wrap_SWT" string.
+ /// </summary>
+ internal const string wrap_swt = "wrap_SWT";
+
+ /// <summary>
+ /// The "wrap_captcha_url" string.
+ /// </summary>
+ internal const string wrap_captcha_url = "wrap_captcha_url";
+
+ /// <summary>
+ /// The "wrap_captcha_solution" string.
+ /// </summary>
+ internal const string wrap_captcha_solution = "wrap_captcha_solution";
+
+ /// <summary>
+ /// The "user_denied" string.
+ /// </summary>
+ internal const string user_denied = "user_denied";
+
+ /// <summary>
+ /// Gets the <see cref="Protocol"/> instance with values initialized for V1.0 of the protocol.
+ /// </summary>
+ internal static readonly Protocol V10 = new Protocol {
+ Version = new Version(1, 0),
+ ProtocolVersion = ProtocolVersion.V10,
+ };
+
+ /// <summary>
+ /// A list of all supported OAuth versions, in order starting from newest version.
+ /// </summary>
+ internal static readonly List<Protocol> AllVersions = new List<Protocol>() { V10 };
+
+ /// <summary>
+ /// The default (or most recent) supported version of the OpenID protocol.
+ /// </summary>
+ internal static readonly Protocol Default = AllVersions[0];
+
+ /// <summary>
+ /// Gets or sets the OAuth WRAP version represented by this instance.
+ /// </summary>
+ /// <value>The version.</value>
+ internal Version Version { get; private set; }
+
+ /// <summary>
+ /// Gets or sets the OAuth WRAP version represented by this instance.
+ /// </summary>
+ /// <value>The protocol version.</value>
+ internal ProtocolVersion ProtocolVersion { get; private set; }
+
+ /// <summary>
+ /// Gets the OAuth Protocol instance to use for the given version.
+ /// </summary>
+ /// <param name="version">The OAuth version to get.</param>
+ /// <returns>A matching <see cref="Protocol"/> instance.</returns>
+ public static Protocol Lookup(ProtocolVersion version) {
+ switch (version) {
+ case ProtocolVersion.V10: return Protocol.V10;
+ default: throw new ArgumentOutOfRangeException("version");
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs b/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs
new file mode 100644
index 0000000..d0ebb09
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs
@@ -0,0 +1,113 @@
+//-----------------------------------------------------------------------
+// <copyright file="WebAppClient.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Text;
+ using System.Web;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OAuthWrap.Messages;
+
+ /// <summary>
+ /// An OAuth WRAP consumer designed for web applications.
+ /// </summary>
+ public class WebAppClient : ClientBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="WebAppClient"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
+ public WebAppClient(AuthorizationServerDescription authorizationServer)
+ : base(authorizationServer) {
+ }
+
+ /// <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 client secret shared with the Authorization Server.
+ /// </summary>
+ public string ClientSecret { get; set; }
+
+ public IClientTokenManager TokenManager { get; set; }
+
+ public WebAppRequest PrepareRequestUserAuthorization(IWrapAuthorization authorizationState) {
+ Contract.Requires<ArgumentNullException>(authorizationState != null);
+ Contract.Requires<InvalidOperationException>(authorizationState.Callback != null || (HttpContext.Current != null && HttpContext.Current.Request != null), MessagingStrings.HttpContextRequired);
+ Contract.Requires<InvalidOperationException>(!string.IsNullOrEmpty(this.ClientIdentifier));
+ Contract.Ensures(Contract.Result<WebAppRequest>() != null);
+ Contract.Ensures(Contract.Result<WebAppRequest>().Callback == authorizationState.Callback);
+ Contract.Ensures(Contract.Result<WebAppRequest>().ClientIdentifier == this.ClientIdentifier);
+
+ if (authorizationState.Callback == null) {
+ authorizationState.Callback = this.Channel.GetRequestFromContext().UrlBeforeRewriting;
+ authorizationState.SaveChanges();
+ }
+
+ var request = new WebAppRequest(this.AuthorizationServer) {
+ ClientIdentifier = this.ClientIdentifier,
+ Callback = authorizationState.Callback,
+ Scope = authorizationState.Scope,
+ };
+
+ return request;
+ }
+
+ public IWrapAuthorization ProcessUserAuthorization() {
+ Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired);
+ return this.ProcessUserAuthorization(this.Channel.GetRequestFromContext());
+ }
+
+ public IWrapAuthorization ProcessUserAuthorization(HttpRequestInfo request) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<InvalidOperationException>(!string.IsNullOrEmpty(this.ClientIdentifier));
+ Contract.Requires<InvalidOperationException>(!string.IsNullOrEmpty(this.ClientSecret));
+ var response = this.Channel.ReadFromRequest<IMessageWithClientState>(request);
+ if (response != null) {
+ IWrapAuthorization authorizationState = this.TokenManager.GetAuthorizationState(request.UrlBeforeRewriting, response.ClientState);
+ ErrorUtilities.VerifyProtocol(authorizationState != null, "Unexpected OAuth WRAP authorization response received with callback and client state that does not match an expected value.");
+ var success = response as WebAppSuccessResponse;
+ var failure = response as WebAppFailedResponse;
+ ErrorUtilities.VerifyProtocol(success != null || failure != null, MessagingStrings.UnexpectedMessageReceivedOfMany);
+ if (success != null) {
+ var accessTokenRequest = new WebAppAccessTokenRequest(this.AuthorizationServer) {
+ ClientSecret = this.ClientSecret,
+ ClientIdentifier = this.ClientIdentifier,
+ Callback = authorizationState.Callback,
+ VerificationCode = success.VerificationCode,
+ };
+ IProtocolMessage accessTokenResponse = this.Channel.Request(accessTokenRequest);
+ var accessTokenSuccess = accessTokenResponse as WebAppAccessTokenSuccessResponse;
+ var badClientAccessTokenResponse = accessTokenResponse as WebAppAccessTokenBadClientResponse;
+ var failedAccessTokenResponse = accessTokenResponse as WebAppAccessTokenFailedResponse;
+ if (accessTokenSuccess != null) {
+ authorizationState.AccessToken = accessTokenSuccess.AccessToken;
+ authorizationState.RefreshToken = accessTokenSuccess.RefreshToken;
+ authorizationState.AccessTokenExpirationUtc = DateTime.UtcNow + accessTokenSuccess.Lifetime;
+ authorizationState.SaveChanges();
+ } else if (badClientAccessTokenResponse != null) {
+ authorizationState.Delete();
+ ErrorUtilities.ThrowProtocol(OAuthWrapStrings.InvalidClientCredentials);
+ } else { // failedAccessTokenResponse != null
+ authorizationState.Delete();
+ ErrorUtilities.ThrowProtocol(OAuthWrapStrings.CannotObtainAccessTokenWithReason, failedAccessTokenResponse.ErrorReason);
+ }
+ } else { // failure
+ Logger.Wrap.Info("User refused to grant the requested authorization at the Authorization Server.");
+ authorizationState.Delete();
+ }
+
+ return authorizationState;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs b/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs
new file mode 100644
index 0000000..a892364
--- /dev/null
+++ b/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs
@@ -0,0 +1,34 @@
+//-----------------------------------------------------------------------
+// <copyright file="WrapUtilities.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuthWrap {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Globalization;
+ using System.Linq;
+ using System.Net;
+ using System.Text;
+
+ /// <summary>
+ /// Some common utility methods for OAuth WRAP.
+ /// </summary>
+ public static class WrapUtilities {
+ /// <summary>
+ /// Authorizes an HTTP request using an OAuth WRAP access token in an HTTP Authorization header.
+ /// </summary>
+ /// <param name="request">The request to authorize.</param>
+ /// <param name="accessToken">The access token previously obtained from the Authorization Server.</param>
+ public static void AuthorizeWithOAuthWrap(this HttpWebRequest request, string accessToken) {
+ Contract.Requires<ArgumentNullException>(request != null);
+ Contract.Requires<ArgumentException>(!string.IsNullOrEmpty(accessToken));
+ request.Headers[HttpRequestHeader.Authorization] = string.Format(
+ CultureInfo.InvariantCulture,
+ Protocol.HttpAuthorizationHeaderFormat,
+ accessToken);
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OpenId/Messages/DirectErrorResponse.cs b/src/DotNetOpenAuth/OpenId/Messages/DirectErrorResponse.cs
index ad20ff7..ec2f7d5 100644
--- a/src/DotNetOpenAuth/OpenId/Messages/DirectErrorResponse.cs
+++ b/src/DotNetOpenAuth/OpenId/Messages/DirectErrorResponse.cs
@@ -36,6 +36,14 @@ namespace DotNetOpenAuth.OpenId.Messages {
get { return HttpStatusCode.BadRequest; }
}
+ /// <summary>
+ /// 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(); }
+ }
+
#endregion
/// <summary>