diff options
Diffstat (limited to 'src')
24 files changed, 1537 insertions, 3 deletions
diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln index 55d43e8..cfa9cd4 100644 --- a/src/DotNetOpenAuth.sln +++ b/src/DotNetOpenAuth.sln @@ -16,6 +16,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{CD57219F ..\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 + ..\doc\specs\OAuthWRAP-v0.9.7.2.pdf = ..\doc\specs\OAuthWRAP-v0.9.7.2.pdf ..\doc\specs\OpenID OAuth Extension.htm = ..\doc\specs\OpenID OAuth Extension.htm ..\doc\specs\openid-attribute-exchange-1_0.html = ..\doc\specs\openid-attribute-exchange-1_0.html ..\doc\specs\openid-authentication-1_1.html = ..\doc\specs\openid-authentication-1_1.html diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 3bfecae..fec065d 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -445,7 +445,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" /> @@ -586,6 +586,30 @@ 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\ChannelElements\UriOrOutOfBandEncoding.cs" /> + <Compile Include="OAuthWrap\ConsumerBase.cs" /> + <Compile Include="OAuthWrap\Messages\AccessTokenFailedResponse.cs" /> + <Compile Include="OAuthWrap\Messages\AccessTokenSuccessResponse.cs" /> + <Compile Include="OAuthWrap\Messages\AccessTokenWithConsumerNamePasswordRequest.cs" /> + <Compile Include="OAuthWrap\Messages\AccessTokenWithSamlRequest.cs" /> + <Compile Include="OAuthWrap\Messages\MessageBase.cs" /> + <Compile Include="OAuthWrap\Messages\AccessTokenWithDelegationCodeRequest.cs" /> + <Compile Include="OAuthWrap\Messages\UserAuthorizationInUserAgentDeniedResponse.cs" /> + <Compile Include="OAuthWrap\Messages\UserAuthorizationInUserAgentRequest.cs" /> + <Compile Include="OAuthWrap\Messages\UserAuthorizationInUserAgentGrantedResponse.cs" /> + <Compile Include="OAuthWrap\Messages\UserAuthorizationViaUsernamePasswordFailedResponse.cs" /> + <Compile Include="OAuthWrap\Messages\UserAuthorizationViaUsernamePasswordRequest.cs" /> + <Compile Include="OAuthWrap\Messages\UserAuthorizationViaUsernamePasswordSuccessResponse.cs" /> + <Compile Include="OAuthWrap\Protocol.cs" /> + <Compile Include="OAuthWrap\SimpleAuthStrings.Designer.cs"> + <AutoGen>True</AutoGen> + <DesignTime>True</DesignTime> + <DependentUpon>SimpleAuthStrings.resx</DependentUpon> + </Compile> + <Compile Include="OAuthWrap\AuthorizationServerDescription.cs" /> + <Compile Include="OAuthWrap\WebConsumer.cs" /> <Compile Include="Util.cs" /> <Compile Include="OAuth\Protocol.cs" /> <Compile Include="OAuth\ServiceProvider.cs" /> @@ -698,6 +722,10 @@ http://opensource.org/licenses/ms-pl.html <EmbeddedResource Include="OpenId\RelyingParty\OpenIdRelyingPartyAjaxControlBase.js"> <Copyright>$(StandardCopyright)</Copyright> </EmbeddedResource> + <EmbeddedResource Include="OAuthWrap\SimpleAuthStrings.resx"> + <Generator>ResXFileCodeGenerator</Generator> + <LastGenOutput>SimpleAuthStrings.Designer.cs</LastGenOutput> + </EmbeddedResource> <EmbeddedResource Include="Strings.sr.resx" /> <EmbeddedResource Include="Xrds\XrdsStrings.sr.resx" /> </ItemGroup> @@ -730,4 +758,4 @@ http://opensource.org/licenses/ms-pl.html </ItemGroup> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(ProjectRoot)tools\DotNetOpenAuth.targets" /> -</Project> +</Project>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/OpenId/Extensions/ProviderAuthenticationPolicy/TimespanSecondsEncoder.cs b/src/DotNetOpenAuth/Messaging/TimespanSecondsEncoder.cs index cc3f7cc..3ad1bd8 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,12 @@ 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> + internal TimespanSecondsEncoder() { + } + #region IMessagePartEncoder Members /// <summary> diff --git a/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerDescription.cs b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerDescription.cs new file mode 100644 index 0000000..8f8f218 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerDescription.cs @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------- +// <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.Version = Protocol.DefaultVersion; + } + + /// <summary> + /// Initializes a new instance of the <see cref="AuthorizationServerDescription"/> class. + /// </summary> + /// <param name="endpointUrl">The endpoint URL of the Token Issuer.</param> + public AuthorizationServerDescription(Uri endpointUrl) + : this() { + this.EndpointUrl = endpointUrl; + } + + /// <summary> + /// Gets or sets the endpoint URL of the Token Issuer. + /// </summary> + /// <value>The endpoint URL.</value> + public Uri EndpointUrl { get; set; } + + /// <summary> + /// Gets or sets the version of the OAuth WRAP protocol to use with this Token Issuer. + /// </summary> + /// <value>The version.</value> + public Version Version { get; set; } + } +} 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..62cceb8 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapMessageFactory.cs @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------- +// <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.sa_consumer_key) && fields.ContainsKey(Protocol.sa_callback)) { + return new UserAuthorizationInUserAgentRequest(recipient.Location, version); + } + + if (fields.ContainsKey(Protocol.sa_consumer_key) && fields.ContainsKey(Protocol.sa_delegation_code)) { + return new AccessTokenWithDelegationCodeRequest(recipient.Location, version); + } + + if (fields.ContainsKey(Protocol.sa_name)) { + return new AccessTokenWithConsumerNamePasswordRequest(version); + } + + if (fields.ContainsKey(Protocol.sa_username)) { + return new UserAuthorizationViaUsernamePasswordRequest(version); + } + + if (fields.ContainsKey(Protocol.sa_saml)) { + return new AccessTokenWithSamlRequest(version); + } + + if (fields.ContainsKey(Protocol.sa_delegation_code)) { + return new UserAuthorizationInUserAgentGrantedResponse(recipient.Location, version); + } + + if (fields.ContainsKey(Protocol.sa_error_reason)) { + return new UserAuthorizationInUserAgentDeniedResponse(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 AccessTokenWithDelegationCodeRequest; + if (accessTokenRequest != null) { + if (fields.ContainsKey(Protocol.sa_token)) { + return new AccessTokenSuccessResponse(accessTokenRequest); + } else { + return new AccessTokenFailedResponse(accessTokenRequest); + } + } + + var userAuthorization = request as UserAuthorizationViaUsernamePasswordRequest; + if (userAuthorization != null) { + if (fields.ContainsKey(Protocol.sa_delegation_code)) { + return new UserAuthorizationViaUsernamePasswordSuccessResponse(userAuthorization); + } else { + return new UserAuthorizationViaUsernamePasswordFailedResponse(userAuthorization); + } + } + + return null; + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/UriOrOutOfBandEncoding.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/UriOrOutOfBandEncoding.cs new file mode 100644 index 0000000..2917cba --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/UriOrOutOfBandEncoding.cs @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------- +// <copyright file="UriOrOutOfBandEncoding.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.Messaging.Reflection; + + /// <summary> + /// An URI encoder that translates null <see cref="Uri"/> references as "out_of_band" + /// instead of an empty/missing argument. + /// </summary> + /// <remarks> + /// This class is functionality equivalent to the <see cref="DotNetOpenAuth.OAuth.ChannelElements.UriOrOobEncoding"/> + /// encoding element, except that instead of using "oob" for null Uri values, + /// "out_of_band" is used. + /// </remarks> + internal class UriOrOutOfBandEncoding : IMessagePartNullEncoder { + /// <summary> + /// The string constant "oob", used to indicate an out-of-band configuration. + /// </summary> + private const string OutOfBandConfiguration = "out_of_band"; + + /// <summary> + /// Initializes a new instance of the <see cref="UriOrOutOfBandEncoding"/> class. + /// </summary> + public UriOrOutOfBandEncoding() { + } + + #region IMessagePartNullEncoder Members + + /// <summary> + /// Gets the string representation to include in a serialized message + /// when the message part has a <c>null</c> value. + /// </summary> + /// <value></value> + public string EncodedNullValue { + get { return OutOfBandConfiguration; } + } + + #endregion + + #region IMessagePartEncoder Members + + /// <summary> + /// Encodes the specified value. + /// </summary> + /// <param name="value">The value. Guaranteed to never be null.</param> + /// <returns> + /// The <paramref name="value"/> in string form, ready for message transport. + /// </returns> + public string Encode(object value) { + ErrorUtilities.VerifyArgumentNotNull(value, "value"); + + Uri uriValue = (Uri)value; + return uriValue.AbsoluteUri; + } + + /// <summary> + /// Decodes the specified value. + /// </summary> + /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param> + /// <returns> + /// The deserialized form of the given string. + /// </returns> + /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception> + public object Decode(string value) { + if (string.Equals(value, OutOfBandConfiguration, StringComparison.Ordinal)) { + return null; + } else { + return new Uri(value, UriKind.Absolute); + } + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ConsumerBase.cs b/src/DotNetOpenAuth/OAuthWrap/ConsumerBase.cs new file mode 100644 index 0000000..d45562d --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ConsumerBase.cs @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------- +// <copyright file="ConsumerBase.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.Net; + using System.Text; + using DotNetOpenAuth.Messaging; + + /// <summary> + /// A base class for common OAuth WRAP Consumer behaviors. + /// </summary> + public class ConsumerBase { + /// <summary> + /// Initializes a new instance of the <see cref="ConsumerBase"/> class. + /// </summary> + /// <param name="tokenIssuer">The token issuer.</param> + protected ConsumerBase(AuthorizationServerDescription tokenIssuer) { + ErrorUtilities.VerifyArgumentNotNull(tokenIssuer, "tokenIssuer"); + this.TokenIssuer = tokenIssuer; + } + + /// <summary> + /// Initializes a new instance of the <see cref="ConsumerBase"/> class. + /// </summary> + /// <param name="tokenIssuerEndpoint">The token issuer endpoint.</param> + protected ConsumerBase(Uri tokenIssuerEndpoint) + : this(new AuthorizationServerDescription(tokenIssuerEndpoint)) { + } + + /// <summary> + /// Initializes a new instance of the <see cref="ConsumerBase"/> class. + /// </summary> + /// <param name="tokenIssuerEndpoint">The token issuer endpoint.</param> + protected ConsumerBase(string tokenIssuerEndpoint) + : this(new Uri(tokenIssuerEndpoint)) { + } + + /// <summary> + /// Gets the token issuer. + /// </summary> + /// <value>The token issuer.</value> + public AuthorizationServerDescription TokenIssuer { get; private set; } + + /// <summary> + /// Gets the channel. + /// </summary> + /// <value>The channel.</value> + public Channel Channel { get; private set; } + + /// <summary> + /// Adds the necessary HTTP 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 Token Issuer.</param> + public static void AuthorizeRequest(HttpWebRequest request, string accessToken) { + ErrorUtilities.VerifyArgumentNotNull(request, "request"); + request.Headers[HttpRequestHeader.Authorization] = Protocol.HttpAuthorizationScheme + " " + accessToken; + } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenFailedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenFailedResponse.cs new file mode 100644 index 0000000..dc0dd97 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenFailedResponse.cs @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessTokenFailedResponse.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> + /// The direct response message that may contain the reason the access token + /// was NOT returned from the Token Issuer to the Consumer. + /// </summary> + internal class AccessTokenFailedResponse : MessageBase, IHttpDirectResponse { + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenFailedResponse"/> class. + /// </summary> + /// <param name="request">The request.</param> + internal AccessTokenFailedResponse(AccessTokenWithDelegationCodeRequest request) + : base(request) { + } + + #region IHttpDirectResponse Members + + /// <summary> + /// Gets the HTTP status code that the direct respones should be sent with. + /// </summary> + /// <value><see cref="HttpStatusCode.Unauthorized"/></value> + HttpStatusCode IHttpDirectResponse.HttpStatusCode { + get { return HttpStatusCode.Unauthorized; } + } + + #endregion + + /// <summary> + /// Gets or sets the error reason. + /// </summary> + /// <value> + /// The reason for the failure. Among other values, it may be <c>null</c> + /// or expired_delegation_code. + /// </value> + [MessagePart(Protocol.sa_error_reason, IsRequired = false, AllowEmpty = true)] + internal string ErrorReason { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenSuccessResponse.cs new file mode 100644 index 0000000..d38a2e1 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenSuccessResponse.cs @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessTokenSuccessResponse.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 Token Issuer + /// to the Consumer. + /// </summary> + internal class AccessTokenSuccessResponse : MessageBase { + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenSuccessResponse"/> class. + /// </summary> + /// <param name="request">The request.</param> + internal AccessTokenSuccessResponse(AccessTokenWithDelegationCodeRequest request) + : base(request) { + } + + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenSuccessResponse"/> class. + /// </summary> + /// <param name="request">The request.</param> + internal AccessTokenSuccessResponse(AccessTokenWithConsumerNamePasswordRequest request) + : base(request) { + } + + /// <summary> + /// Gets or sets the access token. + /// </summary> + /// <value>The token.</value> + [MessagePart(Protocol.sa_token, IsRequired = true, AllowEmpty = false)] + internal string Token { get; set; } + + /// <summary> + /// Gets or sets the lifetime of the access token. + /// </summary> + /// <value>The lifetime.</value> + [MessagePart(Protocol.sa_token_expires_in, IsRequired = false, AllowEmpty = false, Encoder = typeof(TimespanSecondsEncoder))] + internal TimeSpan? Lifetime { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenWithConsumerNamePasswordRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenWithConsumerNamePasswordRequest.cs new file mode 100644 index 0000000..605e27e --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenWithConsumerNamePasswordRequest.cs @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessTokenWithConsumerNamePasswordRequest.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 consumer application that has its + /// own (non-user affiliated) consumer name and password. + /// </summary> + internal class AccessTokenWithConsumerNamePasswordRequest : MessageBase { + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenWithConsumerNamePasswordRequest"/> class. + /// </summary> + /// <param name="version">The version.</param> + internal AccessTokenWithConsumerNamePasswordRequest(Version version) + : base(version) { + } + + /// <summary> + /// Gets or sets the account name. + /// </summary> + /// <value>The consumer name.</value> + [MessagePart(Protocol.sa_name, IsRequired = true, AllowEmpty = false)] + public string Name { get; set; } + + /// <summary> + /// Gets or sets the account password. + /// </summary> + /// <value>The password.</value> + [MessagePart(Protocol.sa_password, IsRequired = true, AllowEmpty = true)] + public string Password { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenWithDelegationCodeRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenWithDelegationCodeRequest.cs new file mode 100644 index 0000000..4374657 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenWithDelegationCodeRequest.cs @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessTokenWithDelegationCodeRequest.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.Messages { + using System; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuthWrap.ChannelElements; + + /// <summary> + /// A message sent by the Consumer directly to the Token Issuer to exchange + /// the delegation code for an Access Token. + /// </summary> + internal class AccessTokenWithDelegationCodeRequest : MessageBase, IDirectedProtocolMessage { + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenWithDelegationCodeRequest"/> class. + /// </summary> + /// <param name="tokenIssuer">The token issuer.</param> + /// <param name="version">The version.</param> + internal AccessTokenWithDelegationCodeRequest(Uri tokenIssuer, Version version) + : base(version, MessageTransport.Direct, tokenIssuer) { + this.HttpMethods = HttpDeliveryMethods.PostRequest; + } + + /// <summary> + /// Gets or sets the consumer key. + /// </summary> + /// <value>The consumer key.</value> + [MessagePart(Protocol.sa_consumer_key, IsRequired = true, AllowEmpty = false)] + internal string ConsumerKey { get; set; } + + /// <summary> + /// Gets or sets the consumer secret. + /// </summary> + /// <value>The consumer secret.</value> + [MessagePart(Protocol.sa_consumer_secret, IsRequired = true, AllowEmpty = false)] + internal string ConsumerSecret { get; set; } + + /// <summary> + /// Gets or sets the delegation code. + /// </summary> + /// <value>The delegation code.</value> + [MessagePart(Protocol.sa_delegation_code, IsRequired = true, AllowEmpty = false)] + internal string DelegationCode { get; set; } + + /// <summary> + /// Gets or sets the callback URL. + /// </summary> + /// <value> + /// An absolute URL to which the Token Issuer will redirect the User back after + /// the user has approved the authorization request. + /// </value> + /// <remarks> + /// Consumers which are unable to receive callbacks MUST use <c>null</c> to indicate it + /// will receive the Verification Code out of band. + /// </remarks> + [MessagePart(Protocol.sa_callback, IsRequired = true, AllowEmpty = false, Encoder = typeof(UriOrOutOfBandEncoding))] + 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(), SimpleAuthStrings.HttpsRequired); + } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenWithSamlRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenWithSamlRequest.cs new file mode 100644 index 0000000..1a610ed --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenWithSamlRequest.cs @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------- +// <copyright file="AccessTokenWithSamlRequest.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 consumer application that can + /// issue a SAML assertion to prove its identity. + /// </summary> + internal class AccessTokenWithSamlRequest : MessageBase { + /// <summary> + /// Initializes a new instance of the <see cref="AccessTokenWithSamlRequest"/> class. + /// </summary> + /// <param name="version">The version.</param> + internal AccessTokenWithSamlRequest(Version version) + : base(version) { + } + + /// <summary> + /// Gets or sets the SAML token. + /// </summary> + /// <value>A SAML token serialized as an XML document.</value> + [MessagePart(Protocol.sa_saml, IsRequired = true, AllowEmpty = false)] + public string Saml { get; set; } + + /// <summary> + /// Gets or sets the SWT. + /// </summary> + /// <value>The SWT (TODO: what is that?).</value> + /// <remarks> + /// The spec says that the SWT parameter is required for certain scenarios, + /// so we mark it as optional here since the scenario may or may not apply. + /// </remarks> + [MessagePart(Protocol.sa_swt, IsRequired = false, AllowEmpty = false)] + public string Swt { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs new file mode 100644 index 0000000..ad4612a --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs @@ -0,0 +1,204 @@ +//----------------------------------------------------------------------- +// <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 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) { + ErrorUtilities.VerifyArgumentNotNull(version, "version"); + 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) { + ErrorUtilities.VerifyArgumentNotNull(request, "request"); + 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) { + ErrorUtilities.VerifyArgumentNotNull(version, "version"); + ErrorUtilities.VerifyArgumentNotNull(recipient, "recipient"); + + 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/UserAuthorizationInUserAgentDeniedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationInUserAgentDeniedResponse.cs new file mode 100644 index 0000000..44268c5 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationInUserAgentDeniedResponse.cs @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------- +// <copyright file="UserAuthorizationInUserAgentDeniedResponse.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.Messages { + using System; + using DotNetOpenAuth.Messaging; + + /// <summary> + /// The message the Token Issuer MAY use to send the user back to the Consumer + /// following the user's denial to grant Consumer with authorization of + /// access to requested resources. + /// </summary> + public class UserAuthorizationInUserAgentDeniedResponse : MessageBase, IDirectedProtocolMessage { + /// <summary> + /// A constant parameter that indicates the user refused to grant the requested authorization. + /// </summary> + [MessagePart(Protocol.sa_error_reason, IsRequired = true)] + private const string ErrorReason = Protocol.sa_error_reason_denied; + + /// <summary> + /// Initializes a new instance of the <see cref="UserAuthorizationInUserAgentDeniedResponse"/> class. + /// </summary> + /// <param name="consumerCallback">The recipient of the message.</param> + /// <param name="version">The version.</param> + internal UserAuthorizationInUserAgentDeniedResponse(Uri consumerCallback, Version version) : + base(version, MessageTransport.Indirect, consumerCallback) { + } + + /// <summary> + /// Gets or sets the state of the consumer. + /// </summary> + /// <value> + /// An opaque value that Consumers can use to maintain state associated with this request. + /// </value> + /// <remarks> + /// If this value is present, the Token Issuer MUST return it to the Consumer's callback URL. + /// </remarks> + [MessagePart(Protocol.sa_consumer_state, IsRequired = false, AllowEmpty = true)] + public string ConsumerState { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationInUserAgentGrantedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationInUserAgentGrantedResponse.cs new file mode 100644 index 0000000..ae43d5c --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationInUserAgentGrantedResponse.cs @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------- +// <copyright file="UserAuthorizationInUserAgentGrantedResponse.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.Messages { + using System; + using DotNetOpenAuth.Messaging; + + /// <summary> + /// The message sent by the Token Issuer to the Consumer via the user agent + /// to indicate that user authorization was granted, and to return the user + /// to the Consumer where they started their experience. + /// </summary> + internal class UserAuthorizationInUserAgentGrantedResponse : MessageBase, IDirectedProtocolMessage { + /// <summary> + /// Initializes a new instance of the <see cref="UserAuthorizationInUserAgentGrantedResponse"/> class. + /// </summary> + /// <param name="consumerCallback">The consumer callback.</param> + /// <param name="version">The protocol version.</param> + internal UserAuthorizationInUserAgentGrantedResponse(Uri consumerCallback, Version version) + : base(version, MessageTransport.Indirect, consumerCallback) { + } + + /// <summary> + /// Gets or sets the delegation code. + /// </summary> + /// <value> + /// The long-lived credential assigned by the Token Issuer to this Consumer for + /// use in accessing the authorizing user's protected resources. + /// </value> + [MessagePart(Protocol.sa_delegation_code, IsRequired = true, AllowEmpty = true)] + internal string DelegationCode { get; set; } + + /// <summary> + /// Gets or sets the state of the consumer as provided by the consumer in the + /// authorization request. + /// </summary> + /// <value>The state of the consumer.</value> + /// <remarks> + /// REQUIRED if the Consumer sent the value in the <see cref="UserAuthorizationRequestInUserAgentRequest"/>. + /// </remarks> + [MessagePart(Protocol.sa_consumer_state, IsRequired = false, AllowEmpty = true)] + internal string ConsumerState { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationInUserAgentRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationInUserAgentRequest.cs new file mode 100644 index 0000000..7e5191f --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationInUserAgentRequest.cs @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------- +// <copyright file="UserAuthorizationInUserAgentRequest.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.Messages { + using System; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuthWrap.ChannelElements; + + /// <summary> + /// A message sent by the Consumer to the Token Issuer via the user agent + /// to get the Token Issuer to obtain authorization from the user and prepare + /// to issue an access token to the Consumer if permission is granted. + /// </summary> + public class UserAuthorizationInUserAgentRequest : MessageBase, IDirectedProtocolMessage { + /// <summary> + /// Initializes a new instance of the <see cref="UserAuthorizationInUserAgentRequest"/> class. + /// </summary> + /// <param name="tokenIssuer">The token issuer URL to direct the user to.</param> + /// <param name="version">The protocol version.</param> + public UserAuthorizationInUserAgentRequest(Uri tokenIssuer, Version version) + : base(version, MessageTransport.Indirect, tokenIssuer) { + } + + /// <summary> + /// Gets or sets the consumer key. + /// </summary> + /// <value>The consumer key.</value> + [MessagePart(Protocol.sa_consumer_key, IsRequired = true, AllowEmpty = false)] + public string ConsumerKey { get; set; } + + /// <summary> + /// Gets or sets the callback URL. + /// </summary> + /// <value> + /// An absolute URL to which the Token Issuer will redirect the User back after + /// the user has approved the authorization request. + /// </value> + /// <remarks> + /// Consumers which are unable to receive callbacks MUST use <c>null</c> to indicate it + /// will receive the Delegation Code out of band. + /// </remarks> + [MessagePart(Protocol.sa_callback, IsRequired = true, AllowEmpty = false, Encoder = typeof(UriOrOutOfBandEncoding))] + public Uri Callback { get; set; } + + /// <summary> + /// Gets or sets the state of the consumer. + /// </summary> + /// <value> + /// An opaque value that Consumers can use to maintain state associated with this request. + /// </value> + /// <remarks> + /// If this value is present, the Token Issuer MUST return it to the Consumer's callback URL. + /// </remarks> + [MessagePart(Protocol.sa_consumer_state, IsRequired = false, AllowEmpty = true)] + public string ConsumerState { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationViaUsernamePasswordFailedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationViaUsernamePasswordFailedResponse.cs new file mode 100644 index 0000000..4a6fdf4 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationViaUsernamePasswordFailedResponse.cs @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------- +// <copyright file="UserAuthorizationViaUsernamePasswordFailedResponse.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 Token Issuer to the Consumer to indicate that a + /// request for a delegation code failed, probably due to an invalid + /// username and password. + /// </summary> + internal class UserAuthorizationViaUsernamePasswordFailedResponse : MessageBase, IHttpDirectResponse { + /// <summary> + /// Initializes a new instance of the <see cref="UserAuthorizationViaUsernamePasswordFailedResponse"/> class. + /// </summary> + /// <param name="request">The request.</param> + internal UserAuthorizationViaUsernamePasswordFailedResponse(UserAuthorizationViaUsernamePasswordRequest request) + : base(request) { + } + + #region IHttpDirectResponse Members + + /// <summary> + /// Gets the HTTP status code that the direct respones should be sent with. + /// </summary> + /// <value><see cref="HttpStatusCode.Unauthorized"/></value> + HttpStatusCode IHttpDirectResponse.HttpStatusCode { + get { return HttpStatusCode.Unauthorized; } + } + + #endregion + + /// <summary> + /// Gets or sets the error reason. + /// </summary> + /// <value> + /// The reason for the failure. Among other values, it may be <c>null</c> + /// or invalid_user_credentials. + /// </value> + [MessagePart(Protocol.sa_error_reason, IsRequired = false, AllowEmpty = true)] + internal string ErrorReason { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationViaUsernamePasswordRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationViaUsernamePasswordRequest.cs new file mode 100644 index 0000000..cf01940 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationViaUsernamePasswordRequest.cs @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------- +// <copyright file="UserAuthorizationViaUsernamePasswordRequest.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 UserAuthorizationViaUsernamePasswordRequest : MessageBase { + /// <summary> + /// Initializes a new instance of the <see cref="UserAuthorizationViaUsernamePasswordRequest"/> class. + /// </summary> + /// <param name="version">The version.</param> + internal UserAuthorizationViaUsernamePasswordRequest(Version version) + : base(version) { + } + + /// <summary> + /// Gets or sets the consumer key. + /// </summary> + /// <value>The consumer key.</value> + [MessagePart(Protocol.sa_consumer_key, IsRequired = true, AllowEmpty = false)] + internal string ConsumerKey { get; set; } + + /// <summary> + /// Gets or sets the consumer secret. + /// </summary> + /// <value>The consumer secret.</value> + [MessagePart(Protocol.sa_consumer_secret, IsRequired = true, AllowEmpty = false)] + internal string ConsumerSecret { get; set; } + + /// <summary> + /// Gets or sets the username. + /// </summary> + /// <value>The name of the user.</value> + [MessagePart(Protocol.sa_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.sa_password, IsRequired = true, AllowEmpty = false)] + internal string Password { 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(), SimpleAuthStrings.HttpsRequired); + } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationViaUsernamePasswordSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationViaUsernamePasswordSuccessResponse.cs new file mode 100644 index 0000000..11c9a20 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAuthorizationViaUsernamePasswordSuccessResponse.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------- +// <copyright file="UserAuthorizationViaUsernamePasswordSuccessResponse.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 Token Issuer to the Consumer containing a delegation code + /// that the Consumer should use to obtain an access token. + /// </summary> + internal class UserAuthorizationViaUsernamePasswordSuccessResponse : MessageBase { + /// <summary> + /// Initializes a new instance of the <see cref="UserAuthorizationViaUsernamePasswordSuccessResponse"/> class. + /// </summary> + /// <param name="request">The request.</param> + internal UserAuthorizationViaUsernamePasswordSuccessResponse(UserAuthorizationViaUsernamePasswordRequest request) + : base(request) { + } + + /// <summary> + /// Gets or sets the delegation code. + /// </summary> + /// <value> + /// The long-lived credential assigned by the Token Issuer to this Consumer for + /// use in accessing the authorizing user's protected resources. + /// </value> + [MessagePart(Protocol.sa_delegation_code, IsRequired = true, AllowEmpty = true)] + internal string DelegationCode { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/Protocol.cs b/src/DotNetOpenAuth/OAuthWrap/Protocol.cs new file mode 100644 index 0000000..59d77c1 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/Protocol.cs @@ -0,0 +1,105 @@ +// <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; + + /// <summary> + /// Protocol constants for OAuth WRAP. + /// </summary> + internal class Protocol { + /// <summary> + /// The default (latest) version of the OAuth WRAP protocol. + /// </summary> + internal static readonly Version DefaultVersion = V10; + + /// <summary> + /// The initial (1.0) version of OAuth WRAP. + /// </summary> + internal static readonly Version V10 = new Version(1, 0); + + /// <summary> + /// The HTTP authorization scheme "SimpleAPIAuth"; + /// </summary> + internal const string HttpAuthorizationScheme = "SimpleAPIAuth"; + + /// <summary> + /// The "sa_consumer_state" string. + /// </summary> + internal const string sa_consumer_state = "sa_consumer_state"; + + /// <summary> + /// The "sa_callback" string. + /// </summary> + internal const string sa_callback = "sa_callback"; + + /// <summary> + /// The "sa_consumer_key" string. + /// </summary> + internal const string sa_consumer_key = "sa_consumer_key"; + + /// <summary> + /// The "sa_consumer_secret" string. + /// </summary> + internal const string sa_consumer_secret = "sa_consumer_secret"; + + /// <summary> + /// The "sa_delegation_code" string. + /// </summary> + internal const string sa_delegation_code = "sa_delegation_code"; + + /// <summary> + /// The "sa_error_reason" string. + /// </summary> + internal const string sa_error_reason = "sa_error_reason"; + + /// <summary> + /// The "user_denied" string. + /// </summary> + internal const string sa_error_reason_denied = "user_denied"; + + /// <summary> + /// The "sa_token" string. + /// </summary> + internal const string sa_token = "sa_token"; + + /// <summary> + /// The "sa_token_expires_in" string. + /// </summary> + internal const string sa_token_expires_in = "sa_token_expires_in"; + + /// <summary> + /// The "expired_delegation_code" string. + /// </summary> + internal const string expired_delegation_code = "expired_delegation_code"; + + /// <summary> + /// The "sa_username" string. + /// </summary> + internal const string sa_username = "sa_username"; + + /// <summary> + /// The "sa_password" string. + /// </summary> + internal const string sa_password = "sa_password"; + + /// <summary> + /// The "sa_name" string. + /// </summary> + internal const string sa_name = "sa_name"; + + /// <summary> + /// The "sa_SAML" string. + /// </summary> + internal const string sa_saml = "sa_SAML"; + + /// <summary> + /// The "sa_SWT" string. + /// </summary> + internal const string sa_swt = "sa_SWT"; + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/SimpleAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuthWrap/SimpleAuthStrings.Designer.cs new file mode 100644 index 0000000..8cf74ed --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/SimpleAuthStrings.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// <auto-generated> +// This code was generated by a tool. +// Runtime Version:4.0.30118.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 SimpleAuthStrings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal SimpleAuthStrings() { + } + + /// <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.SimpleAuthStrings", typeof(SimpleAuthStrings).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 This message can only be sent over HTTPS.. + /// </summary> + internal static string HttpsRequired { + get { + return ResourceManager.GetString("HttpsRequired", resourceCulture); + } + } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/SimpleAuthStrings.resx b/src/DotNetOpenAuth/OAuthWrap/SimpleAuthStrings.resx new file mode 100644 index 0000000..b8a8d62 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/SimpleAuthStrings.resx @@ -0,0 +1,123 @@ +<?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="HttpsRequired" xml:space="preserve"> + <value>This message can only be sent over HTTPS.</value> + </data> +</root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/OAuthWrap/WebConsumer.cs b/src/DotNetOpenAuth/OAuthWrap/WebConsumer.cs new file mode 100644 index 0000000..3470b62 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/WebConsumer.cs @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------- +// <copyright file="WebConsumer.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; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuthWrap.Messages; + + /// <summary> + /// An OAuth WRAP consumer designed for web applications. + /// </summary> + public class WebConsumer : ConsumerBase { + /// <summary> + /// Initializes a new instance of the <see cref="WebConsumer"/> class. + /// </summary> + /// <param name="tokenIssuer">The token issuer.</param> + public WebConsumer(AuthorizationServerDescription tokenIssuer) + : base(tokenIssuer) { + } + + /// <summary> + /// Initializes a new instance of the <see cref="WebConsumer"/> class. + /// </summary> + /// <param name="tokenIssuerEndpoint">The token issuer endpoint.</param> + public WebConsumer(Uri tokenIssuerEndpoint) + : base(tokenIssuerEndpoint) { + } + + /// <summary> + /// Initializes a new instance of the <see cref="WebConsumer"/> class. + /// </summary> + /// <param name="tokenIssuerEndpoint">The token issuer endpoint.</param> + public WebConsumer(string tokenIssuerEndpoint) + : base(tokenIssuerEndpoint) { + } + + public UserAuthorizationInUserAgentRequest PrepareRequestUserAuthorization(string consumerKey) { + var request = new UserAuthorizationInUserAgentRequest(this.TokenIssuer.EndpointUrl, this.TokenIssuer.Version); + request.ConsumerKey = consumerKey; + request.Callback = this.Channel.GetRequestFromContext().UrlBeforeRewriting; + return request; + } + + public IDirectedProtocolMessage ProcessUserAuthorization() { + return this.ProcessUserAuthorization(this.Channel.GetRequestFromContext()); + } + + public IDirectedProtocolMessage ProcessUserAuthorization(HttpRequestInfo request) { + ErrorUtilities.VerifyArgumentNotNull(request, "request"); + IDirectedProtocolMessage message = this.Channel.ReadFromRequest(); + if (message != null) { + ErrorUtilities.VerifyProtocol( + message is UserAuthorizationInUserAgentGrantedResponse || message is UserAuthorizationInUserAgentDeniedResponse, + MessagingStrings.UnexpectedMessageReceivedOfMany); + } + return message; + } + } +} |