diff options
Diffstat (limited to 'src/DotNetOpenAuth.OAuth.Consumer')
5 files changed, 235 insertions, 1 deletions
diff --git a/src/DotNetOpenAuth.OAuth.Consumer/DotNetOpenAuth.OAuth.Consumer.csproj b/src/DotNetOpenAuth.OAuth.Consumer/DotNetOpenAuth.OAuth.Consumer.csproj index cdbd2dc..301a5a3 100644 --- a/src/DotNetOpenAuth.OAuth.Consumer/DotNetOpenAuth.OAuth.Consumer.csproj +++ b/src/DotNetOpenAuth.OAuth.Consumer/DotNetOpenAuth.OAuth.Consumer.csproj @@ -18,6 +18,9 @@ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> </PropertyGroup> <ItemGroup> + <Compile Include="OAuth\ChannelElements\OAuthConsumerChannel.cs" /> + <Compile Include="OAuth\ChannelElements\OAuthConsumerMessageFactory.cs" /> + <Compile Include="OAuth\ChannelElements\RsaSha1ConsumerSigningBindingElement.cs" /> <Compile Include="OAuth\ConsumerBase.cs" /> <Compile Include="OAuth\DesktopConsumer.cs" /> <Compile Include="OAuth\WebConsumer.cs" /> diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerChannel.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerChannel.cs new file mode 100644 index 0000000..9d5e74a --- /dev/null +++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerChannel.cs @@ -0,0 +1,61 @@ +namespace DotNetOpenAuth.OAuth.ChannelElements { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Diagnostics.CodeAnalysis; + using System.Diagnostics.Contracts; + using DotNetOpenAuth.Messaging.Bindings; + using DotNetOpenAuth.Messaging; + + internal class OAuthConsumerChannel : OAuthChannel { + /// <summary> + /// Initializes a new instance of the <see cref="OAuthChannel"/> class. + /// </summary> + /// <param name="signingBindingElement">The binding element to use for signing.</param> + /// <param name="store">The web application store to use for nonces.</param> + /// <param name="tokenManager">The token manager instance to use.</param> + /// <param name="securitySettings">The security settings.</param> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentNullException>(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "securitySettings", Justification = "Code contracts")] + internal OAuthConsumerChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, IConsumerTokenManager tokenManager, ConsumerSecuritySettings securitySettings) + : base( + signingBindingElement, + store, + tokenManager, + securitySettings, + new OAuthConsumerMessageFactory(), + InitializeBindingElements(signingBindingElement, store, tokenManager, securitySettings)) { + Contract.Requires<ArgumentNullException>(tokenManager != null); + Contract.Requires<ArgumentNullException>(securitySettings != null); + Contract.Requires<ArgumentNullException>(signingBindingElement != null); + Contract.Requires<ArgumentException>(signingBindingElement.SignatureCallback == null, OAuthStrings.SigningElementAlreadyAssociatedWithChannel); + } + + /// <summary> + /// Gets the consumer secret for a given consumer key. + /// </summary> + /// <param name="consumerKey">The consumer key.</param> + /// <returns>The consumer secret.</returns> + protected override string GetConsumerSecret(string consumerKey) { + var consumerTokenManager = (IConsumerTokenManager)this.TokenManager; + ErrorUtilities.VerifyInternal(consumerKey == consumerTokenManager.ConsumerKey, "The token manager consumer key and the consumer key set earlier do not match!"); + return consumerTokenManager.ConsumerSecret; + } + + /// <summary> + /// Initializes the binding elements for the OAuth channel. + /// </summary> + /// <param name="signingBindingElement">The signing binding element.</param> + /// <param name="store">The nonce store.</param> + /// <param name="tokenManager">The token manager.</param> + /// <param name="securitySettings">The security settings.</param> + /// <returns> + /// An array of binding elements used to initialize the channel. + /// </returns> + private static IChannelBindingElement[] InitializeBindingElements(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager, SecuritySettings securitySettings) { + Contract.Requires(securitySettings != null); + + return OAuthChannel.InitializeBindingElements(signingBindingElement, store, tokenManager, securitySettings).ToArray(); + } + } +} diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs new file mode 100644 index 0000000..327b923 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthConsumerMessageFactory.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth.ChannelElements { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth.Messages; + + /// <summary> + /// An OAuth-protocol specific implementation of the <see cref="IMessageFactory"/> + /// interface. + /// </summary> + public class OAuthConsumerMessageFactory : IMessageFactory { + /// <summary> + /// Initializes a new instance of the <see cref="OAuthConsumerMessageFactory"/> class. + /// </summary> + protected internal OAuthConsumerMessageFactory() { + } + + #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> + /// <remarks> + /// The request messages are: + /// UserAuthorizationResponse + /// </remarks> + public virtual IDirectedProtocolMessage GetNewRequestMessage(MessageReceivingEndpoint recipient, IDictionary<string, string> fields) { + MessageBase message = null; + + if (fields.ContainsKey("oauth_token")) { + Protocol protocol = fields.ContainsKey("oauth_verifier") ? Protocol.V10a : Protocol.V10; + message = new UserAuthorizationResponse(recipient.Location, protocol.Version); + } + + if (message != null) { + message.SetAsIncoming(); + } + + return message; + } + + /// <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. + /// Null on a Consumer site that is receiving an indirect message from the Service Provider. + /// </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> + /// <remarks> + /// The response messages are: + /// UnauthorizedTokenResponse + /// AuthorizedTokenResponse + /// </remarks> + public virtual IDirectResponseProtocolMessage GetNewResponseMessage(IDirectedProtocolMessage request, IDictionary<string, string> fields) { + MessageBase message = null; + + // All response messages have the oauth_token field. + if (!fields.ContainsKey("oauth_token")) { + return null; + } + + // All direct message responses should have the oauth_token_secret field. + if (!fields.ContainsKey("oauth_token_secret")) { + Logger.OAuth.Error("An OAuth message was expected to contain an oauth_token_secret but didn't."); + return null; + } + + var unauthorizedTokenRequest = request as UnauthorizedTokenRequest; + var authorizedTokenRequest = request as AuthorizedTokenRequest; + if (unauthorizedTokenRequest != null) { + Protocol protocol = fields.ContainsKey("oauth_callback_confirmed") ? Protocol.V10a : Protocol.V10; + message = new UnauthorizedTokenResponse(unauthorizedTokenRequest, protocol.Version); + } else if (authorizedTokenRequest != null) { + message = new AuthorizedTokenResponse(authorizedTokenRequest); + } else { + Logger.OAuth.ErrorFormat("Unexpected response message given the request type {0}", request.GetType().Name); + throw new ProtocolException(OAuthStrings.InvalidIncomingMessage); + } + + if (message != null) { + message.SetAsIncoming(); + } + + return message; + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/RsaSha1ConsumerSigningBindingElement.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/RsaSha1ConsumerSigningBindingElement.cs new file mode 100644 index 0000000..ba451e5 --- /dev/null +++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ChannelElements/RsaSha1ConsumerSigningBindingElement.cs @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------- +// <copyright file="RsaSha1SigningBindingElement.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth.ChannelElements { + using System; + using System.Diagnostics.Contracts; + using System.Security.Cryptography; + using System.Security.Cryptography.X509Certificates; + using System.Text; + using DotNetOpenAuth.Messaging; + + /// <summary> + /// A binding element that signs outgoing messages and verifies the signature on incoming messages. + /// </summary> + public class RsaSha1ConsumerSigningBindingElement : RsaSha1SigningBindingElement { + /// <summary> + /// Initializes a new instance of the <see cref="RsaSha1SigningBindingElement"/> class. + /// </summary> + /// <param name="signingCertificate">The certificate used to sign outgoing messages.</param> + public RsaSha1ConsumerSigningBindingElement(X509Certificate2 signingCertificate) { + Contract.Requires<ArgumentNullException>(signingCertificate != null); + + this.SigningCertificate = signingCertificate; + } + + /// <summary> + /// Gets or sets the certificate used to sign outgoing messages. Used only by Consumers. + /// </summary> + public X509Certificate2 SigningCertificate { get; set; } + + protected override bool IsSignatureValid(ITamperResistantOAuthMessage message) { + throw new NotImplementedException(); + } + + /// <summary> + /// Calculates a signature for a given message. + /// </summary> + /// <param name="message">The message to sign.</param> + /// <returns>The signature for the message.</returns> + /// <remarks> + /// This method signs the message per OAuth 1.0 section 9.3. + /// </remarks> + protected override string GetSignature(ITamperResistantOAuthMessage message) { + ErrorUtilities.VerifyOperation(this.SigningCertificate != null, OAuthStrings.X509CertificateNotProvidedForSigning); + + string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message)); + byte[] data = Encoding.ASCII.GetBytes(signatureBaseString); + var provider = (RSACryptoServiceProvider)this.SigningCertificate.PrivateKey; + byte[] binarySignature = provider.SignData(data, "SHA1"); + string base64Signature = Convert.ToBase64String(binarySignature); + return base64Signature; + } + + protected override ITamperProtectionChannelBindingElement Clone() { + return new RsaSha1ConsumerSigningBindingElement(this.SigningCertificate); + } + } +} diff --git a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs index c02ca79..82ee92f 100644 --- a/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs +++ b/src/DotNetOpenAuth.OAuth.Consumer/OAuth/ConsumerBase.cs @@ -33,7 +33,7 @@ namespace DotNetOpenAuth.OAuth { ITamperProtectionChannelBindingElement signingElement = serviceDescription.CreateTamperProtectionElement(); INonceStore store = new NonceMemoryStore(StandardExpirationBindingElement.MaximumMessageAge); this.SecuritySettings = OAuthElement.Configuration.Consumer.SecuritySettings.CreateSecuritySettings(); - this.OAuthChannel = new OAuthChannel(signingElement, store, tokenManager, this.SecuritySettings); + this.OAuthChannel = new OAuthConsumerChannel(signingElement, store, tokenManager, this.SecuritySettings); this.ServiceProvider = serviceDescription; OAuthReporting.RecordFeatureAndDependencyUse(this, serviceDescription, tokenManager, null); |