diff options
21 files changed, 378 insertions, 205 deletions
diff --git a/samples/OAuthServiceProvider/App_Code/DataClasses.dbml b/samples/OAuthServiceProvider/App_Code/DataClasses.dbml index c05df5f..78518c2 100644 --- a/samples/OAuthServiceProvider/App_Code/DataClasses.dbml +++ b/samples/OAuthServiceProvider/App_Code/DataClasses.dbml @@ -25,6 +25,9 @@ <Column Name="ConsumerId" Type="System.Int32" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" /> <Column Name="ConsumerKey" Type="System.String" DbType="NVarChar(50) NOT NULL" CanBeNull="false" /> <Column Name="ConsumerSecret" Type="System.String" DbType="NVarChar(50) NOT NULL" CanBeNull="false" /> + <Column Member="Callback" Type="System.String" CanBeNull="true" /> + <Column Member="VerificationCodeFormat" Type="DotNetOpenAuth.OAuth.VerificationCodeFormat" CanBeNull="false" /> + <Column Member="VerificationCodeLength" Type="System.Int32" CanBeNull="false" /> <Association Name="OAuthConsumer_OAuthToken" Member="OAuthTokens" ThisKey="ConsumerId" OtherKey="ConsumerId" Type="OAuthToken" /> </Type> </Table> diff --git a/samples/OAuthServiceProvider/App_Code/DataClasses.dbml.layout b/samples/OAuthServiceProvider/App_Code/DataClasses.dbml.layout index e8195a7..71bd4aa 100644 --- a/samples/OAuthServiceProvider/App_Code/DataClasses.dbml.layout +++ b/samples/OAuthServiceProvider/App_Code/DataClasses.dbml.layout @@ -14,10 +14,10 @@ <elementListCompartment Id="eba736b9-f9ec-484b-8083-c77155a49e4e" absoluteBounds="3.515, 1.085, 1.9700000000000002, 0.8262939453125" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" /> </nestedChildShapes> </classShape> - <classShape Id="f909becb-85b1-4fe6-bb16-3feb3e4fe3ee" absoluteBounds="0.625, 3.25, 2, 1.3862939453124998"> + <classShape Id="f909becb-85b1-4fe6-bb16-3feb3e4fe3ee" absoluteBounds="0.625, 3.25, 2, 1.9631982421874996"> <DataClassMoniker Name="/DataClassesDataContext/OAuthConsumer" /> <nestedChildShapes> - <elementListCompartment Id="464308c4-d112-4448-b0c9-d9b82fb0ca4e" absoluteBounds="0.64, 3.71, 1.9700000000000002, 0.8262939453125" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" /> + <elementListCompartment Id="464308c4-d112-4448-b0c9-d9b82fb0ca4e" absoluteBounds="0.64, 3.71, 1.9700000000000002, 1.4031982421875" name="DataPropertiesCompartment" titleTextColor="Black" itemTextColor="Black" /> </nestedChildShapes> </classShape> <classShape Id="895ebbc8-8352-4c04-9e53-b8e6c8302d36" absoluteBounds="3.5, 3.125, 2, 2.9247054036458326"> @@ -33,14 +33,14 @@ <classShapeMoniker Id="8a79b099-7f87-4766-907a-db2c3e1b5716" /> </nodes> </associationConnector> - <associationConnector edgePoints="[(2.625 : 3.94314697265625); (3.5 : 3.94314697265625)]" fixedFrom="NotFixed" fixedTo="NotFixed"> + <associationConnector edgePoints="[(2.625 : 4.23159912109375); (3.5 : 4.23159912109375)]" fixedFrom="Algorithm" fixedTo="Algorithm"> <AssociationMoniker Name="/DataClassesDataContext/OAuthConsumer/OAuthConsumer_OAuthToken" /> <nodes> <classShapeMoniker Id="f909becb-85b1-4fe6-bb16-3feb3e4fe3ee" /> <classShapeMoniker Id="895ebbc8-8352-4c04-9e53-b8e6c8302d36" /> </nodes> </associationConnector> - <associationConnector edgePoints="[(0.53125 : 2.27089680989583); (0.53125 : 5.37424967447917); (3.5 : 5.37424967447917)]" fixedFrom="NotFixed" fixedTo="NotFixed"> + <associationConnector edgePoints="[(0.53125 : 2.27089680989583); (0.53125 : 5.66270182291667); (3.5 : 5.66270182291667)]" fixedFrom="Algorithm" fixedTo="Algorithm"> <AssociationMoniker Name="/DataClassesDataContext/User/User_OAuthToken" /> <nodes> <classShapeMoniker Id="696d2c69-040e-411d-9257-bb664b743834" /> diff --git a/samples/OAuthServiceProvider/App_Code/DataClasses.designer.cs b/samples/OAuthServiceProvider/App_Code/DataClasses.designer.cs index 09b7b53..b66e75f 100644 --- a/samples/OAuthServiceProvider/App_Code/DataClasses.designer.cs +++ b/samples/OAuthServiceProvider/App_Code/DataClasses.designer.cs @@ -483,6 +483,12 @@ public partial class OAuthConsumer : INotifyPropertyChanging, INotifyPropertyCha private string _ConsumerSecret; + private string _Callback; + + private DotNetOpenAuth.OAuth.VerificationCodeFormat _VerificationCodeFormat; + + private int _VerificationCodeLength; + private EntitySet<OAuthToken> _OAuthTokens; #region Extensibility Method Definitions @@ -495,6 +501,12 @@ public partial class OAuthConsumer : INotifyPropertyChanging, INotifyPropertyCha partial void OnConsumerKeyChanged(); partial void OnConsumerSecretChanging(string value); partial void OnConsumerSecretChanged(); + partial void OnCallbackChanging(string value); + partial void OnCallbackChanged(); + partial void OnVerificationCodeFormatChanging(DotNetOpenAuth.OAuth.VerificationCodeFormat value); + partial void OnVerificationCodeFormatChanged(); + partial void OnVerificationCodeLengthChanging(int value); + partial void OnVerificationCodeLengthChanged(); #endregion public OAuthConsumer() @@ -563,6 +575,66 @@ public partial class OAuthConsumer : INotifyPropertyChanging, INotifyPropertyCha } } + [Column(Storage="_Callback")] + public string Callback + { + get + { + return this._Callback; + } + set + { + if ((this._Callback != value)) + { + this.OnCallbackChanging(value); + this.SendPropertyChanging(); + this._Callback = value; + this.SendPropertyChanged("Callback"); + this.OnCallbackChanged(); + } + } + } + + [Column(Storage="_VerificationCodeFormat")] + public DotNetOpenAuth.OAuth.VerificationCodeFormat VerificationCodeFormat + { + get + { + return this._VerificationCodeFormat; + } + set + { + if ((this._VerificationCodeFormat != value)) + { + this.OnVerificationCodeFormatChanging(value); + this.SendPropertyChanging(); + this._VerificationCodeFormat = value; + this.SendPropertyChanged("VerificationCodeFormat"); + this.OnVerificationCodeFormatChanged(); + } + } + } + + [Column(Storage="_VerificationCodeLength")] + public int VerificationCodeLength + { + get + { + return this._VerificationCodeLength; + } + set + { + if ((this._VerificationCodeLength != value)) + { + this.OnVerificationCodeLengthChanging(value); + this.SendPropertyChanging(); + this._VerificationCodeLength = value; + this.SendPropertyChanged("VerificationCodeLength"); + this.OnVerificationCodeLengthChanged(); + } + } + } + [Association(Name="OAuthConsumer_OAuthToken", Storage="_OAuthTokens", ThisKey="ConsumerId", OtherKey="ConsumerId")] public EntitySet<OAuthToken> OAuthTokens { diff --git a/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs b/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs index 143bbfb..f4f34de 100644 --- a/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs +++ b/samples/OAuthServiceProvider/App_Code/DatabaseTokenManager.cs @@ -14,61 +14,18 @@ using DotNetOpenAuth.OAuth.Messages; public class DatabaseTokenManager : IServiceProviderTokenManager { #region IServiceProviderTokenManager - public string GetConsumerSecret(string consumerKey) { + public IConsumerDescription GetConsumer(string consumerKey) { var consumerRow = Global.DataContext.OAuthConsumers.SingleOrDefault( consumerCandidate => consumerCandidate.ConsumerKey == consumerKey); if (consumerRow == null) { throw new ArgumentException(); } - return consumerRow.ConsumerSecret; + return consumerRow; } - public void SetRequestTokenVerifier(string requestToken, string verifier) { - if (String.IsNullOrEmpty(requestToken)) { - throw new ArgumentNullException("requestToken"); - } - if (String.IsNullOrEmpty(verifier)) { - throw new ArgumentNullException("verifier"); - } - - Global.DataContext.OAuthTokens.First(token => token.Token == requestToken).RequestTokenVerifier = verifier; - } - - public string GetRequestTokenVerifier(string requestToken) { - if (String.IsNullOrEmpty(requestToken)) { - throw new ArgumentNullException("requestToken"); - } - - return Global.DataContext.OAuthTokens.First(token => token.Token == requestToken).RequestTokenVerifier; - } - - public void SetRequestTokenCallback(string requestToken, Uri callback) { - if (String.IsNullOrEmpty(requestToken)) { - throw new ArgumentNullException("requestToken"); - } - - Global.DataContext.OAuthTokens.First(token => token.Token == requestToken).RequestTokenCallback = callback.AbsoluteUri; - } - - public Uri GetRequestTokenCallback(string requestToken) { - string callback = Global.DataContext.OAuthTokens.First(token => token.Token == requestToken).RequestTokenCallback; - return callback != null ? new Uri(callback) : null; - } - - public void SetTokenConsumerVersion(string token, Version version) { - if (String.IsNullOrEmpty(token)) { - throw new ArgumentNullException("token"); - } - if (version == null) { - throw new ArgumentNullException("version"); - } - - Global.DataContext.OAuthTokens.First(t => t.Token == token).ConsumerVersion = version.ToString(); - } - - public Version GetTokenConsumerVersion(string token) { - return new Version(Global.DataContext.OAuthTokens.First(t => t.Token == token).ConsumerVersion); + public IServiceProviderRequestToken GetRequestToken(string token) { + return Global.DataContext.OAuthTokens.First(t => t.Token == token); } #endregion diff --git a/samples/OAuthServiceProvider/App_Code/OAuthConsumer.cs b/samples/OAuthServiceProvider/App_Code/OAuthConsumer.cs new file mode 100644 index 0000000..2de1e85 --- /dev/null +++ b/samples/OAuthServiceProvider/App_Code/OAuthConsumer.cs @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthConsumer.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using DotNetOpenAuth.OAuth.ChannelElements; + +public partial class OAuthConsumer : IConsumerDescription { + #region IConsumerDescription Members + + string IConsumerDescription.Key { + get { return this.ConsumerKey; } + } + + string IConsumerDescription.Secret { + get { return this.ConsumerSecret; } + } + + System.Security.Cryptography.X509Certificates.X509Certificate2 IConsumerDescription.Certificate { + get { return null; } + } + + Uri IConsumerDescription.Callback { + get { return new Uri(this.Callback); } + } + + DotNetOpenAuth.OAuth.VerificationCodeFormat IConsumerDescription.VerificationCodeFormat { + get { return this.VerificationCodeFormat; } + } + + int IConsumerDescription.VerificationCodeLength { + get { return this.VerificationCodeLength; } + } + + #endregion +} diff --git a/samples/OAuthServiceProvider/App_Code/OAuthToken.cs b/samples/OAuthServiceProvider/App_Code/OAuthToken.cs new file mode 100644 index 0000000..445e88c --- /dev/null +++ b/samples/OAuthServiceProvider/App_Code/OAuthToken.cs @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------- +// <copyright file="OAuthToken.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using DotNetOpenAuth.OAuth.ChannelElements; + +public partial class OAuthToken : IServiceProviderRequestToken { + #region IServiceProviderRequestToken Members + + string IServiceProviderRequestToken.Token { + get { return this.Token; } + } + + string IServiceProviderRequestToken.ConsumerKey { + get { return this.OAuthConsumer.ConsumerKey; } + } + + Uri IServiceProviderRequestToken.Callback { + get { return new Uri(this.RequestTokenCallback); } + set { this.RequestTokenCallback = value.AbsoluteUri; } + } + + string IServiceProviderRequestToken.VerificationCode { + get { return this.RequestTokenVerifier; } + set { this.RequestTokenVerifier = value; } + } + + Version IServiceProviderRequestToken.ConsumerVersion { + get { return new Version(this.ConsumerVersion); } + set { this.ConsumerVersion = value.ToString(); } + } + + #endregion +} diff --git a/samples/OAuthServiceProvider/Members/Authorize.aspx.cs b/samples/OAuthServiceProvider/Members/Authorize.aspx.cs index e396017..f936c60 100644 --- a/samples/OAuthServiceProvider/Members/Authorize.aspx.cs +++ b/samples/OAuthServiceProvider/Members/Authorize.aspx.cs @@ -63,7 +63,7 @@ public partial class Authorize : System.Web.UI.Page { string verifier = ServiceProvider.CreateVerificationCode(VerificationCodeFormat.AlphaNumericNoLookAlikes, 10); verificationCodeLabel.Text = verifier; ITokenContainingMessage requestTokenMessage = pending; - Global.TokenManager.SetRequestTokenVerifier(requestTokenMessage.Token, verifier); + Global.TokenManager.GetRequestToken(requestTokenMessage.Token).VerificationCode = verifier; } } } diff --git a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs index f9cf612..5585107 100644 --- a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs +++ b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs @@ -14,7 +14,7 @@ namespace DotNetOpenAuth.Test.Mocks { using DotNetOpenAuth.OAuth.Messages; internal class InMemoryTokenManager : IConsumerTokenManager, IServiceProviderTokenManager { - private Dictionary<string, string> consumersAndSecrets = new Dictionary<string, string>(); + private KeyedCollectionDelegate<string, ConsumerInfo> consumers = new KeyedCollectionDelegate<string, ConsumerInfo>(c => c.Key); private KeyedCollectionDelegate<string, TokenInfo> tokens = new KeyedCollectionDelegate<string, TokenInfo>(t => t.Token); /// <summary> @@ -30,11 +30,11 @@ namespace DotNetOpenAuth.Test.Mocks { #region IConsumerTokenManager Members public string ConsumerKey { - get { return this.consumersAndSecrets.Keys.Single(); } + get { return this.consumers.Single().Key; } } public string ConsumerSecret { - get { return this.consumersAndSecrets.Values.Single(); } + get { return this.consumers.Single().Secret; } } #endregion @@ -46,7 +46,7 @@ namespace DotNetOpenAuth.Test.Mocks { } public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response) { - this.tokens.Add(new TokenInfo { Token = response.Token, Secret = response.TokenSecret }); + this.tokens.Add(new TokenInfo { ConsumerKey = request.ConsumerKey, Token = response.Token, Secret = response.TokenSecret }); this.requestTokens.Add(response.Token, false); } @@ -94,32 +94,12 @@ namespace DotNetOpenAuth.Test.Mocks { #region IServiceProviderTokenManager Members - public string GetConsumerSecret(string consumerKey) { - return this.consumersAndSecrets[consumerKey]; + public IConsumerDescription GetConsumer(string consumerKey) { + return this.consumers[consumerKey]; } - public void SetRequestTokenVerifier(string requestToken, string verifier) { - this.tokens[requestToken].Verifier = verifier; - } - - public string GetRequestTokenVerifier(string requestToken) { - return this.tokens[requestToken].Verifier; - } - - public void SetRequestTokenCallback(string requestToken, Uri callback) { - this.tokens[requestToken].Callback = callback; - } - - public Uri GetRequestTokenCallback(string requestToken) { - return this.tokens[requestToken].Callback; - } - - public void SetTokenConsumerVersion(string token, Version version) { - this.tokens[token].ConsumerVersion = version; - } - - public Version GetTokenConsumerVersion(string token) { - return this.tokens[token].ConsumerVersion; + public IServiceProviderRequestToken GetRequestToken(string token) { + return this.tokens[token]; } #endregion @@ -130,7 +110,7 @@ namespace DotNetOpenAuth.Test.Mocks { /// </summary> /// <param name="consumerDescription">The consumer description.</param> internal void AddConsumer(ConsumerDescription consumerDescription) { - this.consumersAndSecrets.Add(consumerDescription.ConsumerKey, consumerDescription.ConsumerSecret); + this.consumers.Add(new ConsumerInfo { Key = consumerDescription.ConsumerKey, Secret = consumerDescription.ConsumerSecret }); } /// <summary> @@ -145,12 +125,36 @@ namespace DotNetOpenAuth.Test.Mocks { this.requestTokens[requestToken] = true; } - private class TokenInfo { - internal string Token; - internal string Verifier; - internal string Secret; - internal Uri Callback; - internal Version ConsumerVersion; + private class TokenInfo : IServiceProviderRequestToken { + public string ConsumerKey { get; set; } + + public string Token { get; set; } + + public string VerificationCode { get; set; } + + public Uri Callback { get; set; } + + public Version ConsumerVersion { get; set; } + + internal string Secret { get; set; } + } + + private class ConsumerInfo : IConsumerDescription { + #region IConsumerDescription Members + + public string Key { get; set; } + + public string Secret { get; set; } + + public System.Security.Cryptography.X509Certificates.X509Certificate2 Certificate { get; set; } + + public Uri Callback { get; set; } + + public DotNetOpenAuth.OAuth.VerificationCodeFormat VerificationCodeFormat { get; set; } + + public int VerificationCodeLength { get; set; } + + #endregion } } } diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs index 449a033..1251e2c 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/OAuthChannelTests.cs @@ -33,7 +33,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { base.SetUp(); this.webRequestHandler = new TestWebRequestHandler(); - this.signingElement = new RsaSha1SigningBindingElement(); + this.signingElement = new RsaSha1SigningBindingElement(new InMemoryTokenManager()); this.nonceStore = new NonceMemoryStore(StandardExpirationBindingElement.DefaultMaximumMessageAge); this.channel = new OAuthChannel(this.signingElement, this.nonceStore, new InMemoryTokenManager(), new TestMessageFactory()); this.accessor = OAuthChannel_Accessor.AttachShadow(this.channel); @@ -47,22 +47,22 @@ namespace DotNetOpenAuth.Test.ChannelElements { [TestMethod, ExpectedException(typeof(ArgumentNullException))] public void CtorNullStore() { - new OAuthChannel(new RsaSha1SigningBindingElement(), null, new InMemoryTokenManager(), new TestMessageFactory()); + new OAuthChannel(this.signingElement, null, new InMemoryTokenManager(), new TestMessageFactory()); } [TestMethod, ExpectedException(typeof(ArgumentNullException))] public void CtorNullTokenManager() { - new OAuthChannel(new RsaSha1SigningBindingElement(), this.nonceStore, null, new TestMessageFactory()); + new OAuthChannel(this.signingElement, this.nonceStore, null, new TestMessageFactory()); } [TestMethod] public void CtorSimpleConsumer() { - new OAuthChannel(new RsaSha1SigningBindingElement(), this.nonceStore, (IConsumerTokenManager)new InMemoryTokenManager()); + new OAuthChannel(this.signingElement, this.nonceStore, (IConsumerTokenManager)new InMemoryTokenManager()); } [TestMethod] public void CtorSimpleServiceProvider() { - new OAuthChannel(new RsaSha1SigningBindingElement(), this.nonceStore, (IServiceProviderTokenManager)new InMemoryTokenManager()); + new OAuthChannel(this.signingElement, this.nonceStore, (IServiceProviderTokenManager)new InMemoryTokenManager()); } [TestMethod] diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 01be98f..ae23e2e 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -224,7 +224,7 @@ <Compile Include="Messaging\Reflection\IMessagePartEncoder.cs" /> <Compile Include="Messaging\Reflection\IMessagePartNullEncoder.cs" /> <Compile Include="Messaging\Reflection\MessageDescriptionCollection.cs" /> - <Compile Include="OAuth\ChannelElements\IConsumerCertificateProvider.cs" /> + <Compile Include="OAuth\ChannelElements\IConsumerDescription.cs" /> <Compile Include="OAuth\ChannelElements\IConsumerTokenManager.cs" /> <Compile Include="OAuth\ChannelElements\IServiceProviderTokenManager.cs" /> <Compile Include="OAuth\ChannelElements\OAuthConsumerMessageFactory.cs" /> @@ -233,6 +233,7 @@ <Compile Include="OAuth\ChannelElements\OAuthHttpMethodBindingElement.cs" /> <Compile Include="OAuth\ChannelElements\PlaintextSigningBindingElement.cs" /> <Compile Include="OAuth\ChannelElements\HmacSha1SigningBindingElement.cs" /> + <Compile Include="OAuth\ChannelElements\IServiceProviderRequestToken.cs" /> <Compile Include="OAuth\ChannelElements\SigningBindingElementBaseContract.cs" /> <Compile Include="OAuth\ChannelElements\SigningBindingElementChain.cs" /> <Compile Include="OAuth\ChannelElements\StandardTokenGenerator.cs" /> diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerCertificateProvider.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerCertificateProvider.cs deleted file mode 100644 index 7e6ae54..0000000 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerCertificateProvider.cs +++ /dev/null @@ -1,23 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="IConsumerCertificateProvider.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.OAuth.ChannelElements { - using System.Security.Cryptography.X509Certificates; - - /// <summary> - /// A provider that hosts can implement to hook up their RSA-SHA1 binding elements - /// to their list of known Consumers' certificates. - /// </summary> - public interface IConsumerCertificateProvider { - /// <summary> - /// Gets the certificate that can be used to verify the signature of an incoming - /// message from a Consumer. - /// </summary> - /// <param name="consumerMessage">The incoming message from some Consumer.</param> - /// <returns>The public key from the Consumer's X.509 Certificate, if one can be found; otherwise <c>null</c>.</returns> - X509Certificate2 GetCertificate(ITamperResistantOAuthMessage consumerMessage); - } -} diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerDescription.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerDescription.cs new file mode 100644 index 0000000..db505d5 --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IConsumerDescription.cs @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------- +// <copyright file="IConsumerDescription.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth.ChannelElements { + using System; + using System.Security.Cryptography.X509Certificates; + + /// <summary> + /// A description of a consumer from a Service Provider's point of view. + /// </summary> + public interface IConsumerDescription { + /// <summary> + /// Gets the Consumer key. + /// </summary> + string Key { get; } + + /// <summary> + /// Gets the consumer secret. + /// </summary> + string Secret { get; } + + /// <summary> + /// Gets the certificate that can be used to verify the signature of an incoming + /// message from a Consumer. + /// </summary> + /// <returns>The public key from the Consumer's X.509 Certificate, if one can be found; otherwise <c>null</c>.</returns> + /// <remarks> + /// This property must be implemented only if the RSA-SHA1 algorithm is supported by the Service Provider. + /// </remarks> + X509Certificate2 Certificate { get; } + + /// <summary> + /// Gets the callback URI that this consumer has pre-registered with the service provider, if any. + /// </summary> + /// <value>A URI that user authorization responses should be directed to; or <c>null</c> if no preregistered callback was arranged.</value> + Uri Callback { get; } + + /// <summary> + /// Gets the verification code format that is most appropriate for this consumer + /// when a callback URI is not available. + /// </summary> + /// <value>A set of characters that can be easily keyed in by the user given the Consumer's + /// application type and form factor.</value> + /// <remarks> + /// The value <see cref="OAuth.VerificationCodeFormat.IncludedInCallback"/> should NEVER be returned + /// since this property is only used in no callback scenarios anyway. + /// </remarks> + VerificationCodeFormat VerificationCodeFormat { get; } + + /// <summary> + /// Gets the length of the verification code to issue for this Consumer. + /// </summary> + /// <value>A positive number, generally at least 4.</value> + int VerificationCodeLength { get; } + } +} diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs new file mode 100644 index 0000000..48f39a6 --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderRequestToken.cs @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------- +// <copyright file="IServiceProviderRequestToken.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth.ChannelElements { + using System; + using DotNetOpenAuth.OAuth.Messages; + + /// <summary> + /// A description of a request token and its metadata as required by a Service Provider + /// </summary> + public interface IServiceProviderRequestToken { + /// <summary> + /// Gets the token itself. + /// </summary> + string Token { get; } + + /// <summary> + /// Gets the consumer key that requested this token. + /// </summary> + string ConsumerKey { get; } + + /// <summary> + /// Gets or sets the callback associated specifically with this token, if any. + /// </summary> + /// <value>The callback URI; or <c>null</c> if no callback was specifically assigned to this token.</value> + Uri Callback { get; set; } + + /// <summary> + /// Gets or sets the verifier that the consumer must include in the <see cref="AuthorizedTokenRequest"/> + /// message to exchange this request token for an access token. + /// </summary> + /// <value>The verifier code, or <c>null</c> if none has been assigned (yet).</value> + string VerificationCode { get; set; } + + /// <summary> + /// Gets or sets the version of the Consumer that requested this token. + /// </summary> + /// <remarks> + /// This property is used to determine whether a <see cref="VerificationCode"/> must be + /// generated when the user authorizes the Consumer or not. + /// </remarks> + Version ConsumerVersion { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs index 58b61a4..f841aa9 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs @@ -16,54 +16,18 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> public interface IServiceProviderTokenManager : ITokenManager { /// <summary> - /// Gets the Consumer Secret for a given a Consumer Key. + /// Gets the Consumer description for a given a Consumer Key. /// </summary> /// <param name="consumerKey">The Consumer Key.</param> - /// <returns>The Consumer Secret.</returns> + /// <returns>A description of the consumer.</returns> /// <exception cref="ArgumentException">Thrown if the consumer key cannot be found.</exception> - /// <exception cref="InvalidOperationException">May be thrown if called when the signature algorithm does not require a consumer secret, such as when RSA-SHA1 is used.</exception> - string GetConsumerSecret(string consumerKey); + IConsumerDescription GetConsumer(string consumerKey); /// <summary> - /// Sets the verifier code associated with an authorized request token. + /// Gets details on the named request token. /// </summary> - /// <param name="requestToken">The request token.</param> - /// <param name="verifier">The verification code.</param> - void SetRequestTokenVerifier(string requestToken, string verifier); - - /// <summary> - /// Gets the verifier code associated with an authorized request token. - /// </summary> - /// <param name="requestToken">The request token that the Consumer is exchanging for an access token.</param> - /// <returns>The verifier code that was generated when previously authorizing the request token.</returns> - string GetRequestTokenVerifier(string requestToken); - - /// <summary> - /// Sets the request token consumer callback. - /// </summary> - /// <param name="requestToken">The request token.</param> - /// <param name="callback">The callback.</param> - void SetRequestTokenCallback(string requestToken, Uri callback); - - /// <summary> - /// Gets the request token consumer callback. - /// </summary> - /// <param name="requestToken">The request token.</param> - /// <returns>The callback Uri. May be <c>null</c>.</returns> - Uri GetRequestTokenCallback(string requestToken); - - /// <summary> - /// Sets the OAuth version used by the Consumer to request a token. - /// </summary> - /// <param name="token">The token.</param> - /// <param name="version">The OAuth version.</param> - void SetTokenConsumerVersion(string token, Version version); - - /// <summary> - /// Gets the OAuth version used by the Consumer to request a token. - /// </summary> - /// <param name="token">The token.</param> - /// <returns>The OAuth version</returns> - Version GetTokenConsumerVersion(string token); + /// <param name="token">The request token.</param> + /// <returns>A description of the token</returns> + IServiceProviderRequestToken GetRequestToken(string token); } } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs index e2a58d0..2487845 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -389,7 +389,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { ErrorUtilities.VerifyInternal(consumerKey == consumerTokenManager.ConsumerKey, "The token manager consumer key and the consumer key set earlier do not match!"); return consumerTokenManager.ConsumerSecret; } else { - return ((IServiceProviderTokenManager)this.TokenManager).GetConsumerSecret(consumerKey); + return ((IServiceProviderTokenManager)this.TokenManager).GetConsumer(consumerKey).Secret; } } } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs index e8e8382..d8e3af2 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs @@ -59,7 +59,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { if (fields.TryGetValue("oauth_token", out token)) { // Discern between 1.0 and 1.0a requests by checking on the consumer version we stored // when the consumer first requested an unauthorized token. - protocol = Protocol.Lookup(this.tokenManager.GetTokenConsumerVersion(token)); + protocol = Protocol.Lookup(this.tokenManager.GetRequestToken(token).ConsumerVersion); } if (fields.ContainsKey("oauth_consumer_key") && !fields.ContainsKey("oauth_token")) { diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs index 779f2c5..4f8b5e5 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/RsaSha1SigningBindingElement.cs @@ -6,6 +6,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { using System; + using System.Diagnostics.Contracts; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; @@ -16,15 +17,24 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> public class RsaSha1SigningBindingElement : SigningBindingElementBase { /// <summary> + /// The name of the hash algorithm to use. + /// </summary> + private const string HashAlgorithmName = "RSA-SHA1"; + + /// <summary> + /// The token manager for the service provider. + /// </summary> + private IServiceProviderTokenManager tokenManager; + + /// <summary> /// Initializes a new instance of the <see cref="RsaSha1SigningBindingElement"/> class /// for use by Consumers. /// </summary> /// <param name="signingCertificate">The certificate used to sign outgoing messages.</param> public RsaSha1SigningBindingElement(X509Certificate2 signingCertificate) - : this() { - if (signingCertificate == null) { - throw new ArgumentNullException("signingCertificate"); - } + : base(HashAlgorithmName) { + Contract.Requires(signingCertificate != null); + ErrorUtilities.VerifyArgumentNotNull(signingCertificate, "signingCertificate"); this.SigningCertificate = signingCertificate; } @@ -33,21 +43,21 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// Initializes a new instance of the <see cref="RsaSha1SigningBindingElement"/> class /// for use by Service Providers. /// </summary> - public RsaSha1SigningBindingElement() - : base("RSA-SHA1") { + /// <param name="tokenManager">The token manager.</param> + public RsaSha1SigningBindingElement(IServiceProviderTokenManager tokenManager) + : base(HashAlgorithmName) { + Contract.Requires(tokenManager != null); + ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager"); + + this.tokenManager = tokenManager; } /// <summary> - /// Gets or sets the certificate used to sign outgoing messages. + /// Gets or sets the certificate used to sign outgoing messages. Used only by Consumers. /// </summary> public X509Certificate2 SigningCertificate { get; set; } /// <summary> - /// Gets or sets the consumer certificate provider. - /// </summary> - public IConsumerCertificateProvider ConsumerCertificateProvider { get; set; } - - /// <summary> /// Calculates a signature for a given message. /// </summary> /// <param name="message">The message to sign.</param> @@ -56,13 +66,8 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// This method signs the message per OAuth 1.0 section 9.3. /// </remarks> protected override string GetSignature(ITamperResistantOAuthMessage message) { - if (message == null) { - throw new ArgumentNullException("message"); - } - - if (this.SigningCertificate == null) { - throw new InvalidOperationException(OAuthStrings.X509CertificateNotProvidedForSigning); - } + ErrorUtilities.VerifyArgumentNotNull(message, "message"); + ErrorUtilities.VerifyOperation(this.SigningCertificate != null, OAuthStrings.X509CertificateNotProvidedForSigning); string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message)); byte[] data = Encoding.ASCII.GetBytes(signatureBaseString); @@ -80,16 +85,14 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <c>true</c> if the signature on the message is valid; otherwise, <c>false</c>. /// </returns> protected override bool IsSignatureValid(ITamperResistantOAuthMessage message) { - if (this.ConsumerCertificateProvider == null) { - throw new InvalidOperationException(OAuthStrings.ConsumerCertificateProviderNotAvailable); - } + ErrorUtilities.VerifyInternal(this.tokenManager != null, "No token manager available for fetching Consumer public certificates."); string signatureBaseString = ConstructSignatureBaseString(message, this.Channel.MessageDescriptions.GetAccessor(message)); byte[] data = Encoding.ASCII.GetBytes(signatureBaseString); byte[] carriedSignature = Convert.FromBase64String(message.Signature); - X509Certificate2 cert = this.ConsumerCertificateProvider.GetCertificate(message); + X509Certificate2 cert = this.tokenManager.GetConsumer(message.ConsumerKey).Certificate; if (cert == null) { Logger.Signatures.WarnFormat("Incoming message from consumer '{0}' could not be matched with an appropriate X.509 certificate for signature verification.", message.ConsumerKey); return false; @@ -105,10 +108,11 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// </summary> /// <returns>A new instance of the binding element.</returns> protected override ITamperProtectionChannelBindingElement Clone() { - return new RsaSha1SigningBindingElement() { - ConsumerCertificateProvider = this.ConsumerCertificateProvider, - SigningCertificate = this.SigningCertificate, - }; + if (this.tokenManager != null) { + return new RsaSha1SigningBindingElement(this.tokenManager); + } else { + return new RsaSha1SigningBindingElement(this.SigningCertificate); + } } } } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs index 2ec5e0e..b8273c3 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/TokenHandlingBindingElement.cs @@ -71,7 +71,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { var userAuthResponse = message as UserAuthorizationResponse; if (userAuthResponse != null && userAuthResponse.Version >= Protocol.V10a.Version) { - this.tokenManager.SetRequestTokenVerifier(userAuthResponse.RequestToken, userAuthResponse.VerificationCode); + this.tokenManager.GetRequestToken(userAuthResponse.RequestToken).VerificationCode = userAuthResponse.VerificationCode; return MessageProtections.None; } @@ -79,9 +79,9 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { var grantRequestTokenResponse = message as UnauthorizedTokenResponse; if (grantRequestTokenResponse != null) { this.tokenManager.StoreNewRequestToken(grantRequestTokenResponse.RequestMessage, grantRequestTokenResponse); - this.tokenManager.SetTokenConsumerVersion(grantRequestTokenResponse.RequestToken, grantRequestTokenResponse.Version); + this.tokenManager.GetRequestToken(grantRequestTokenResponse.RequestToken).ConsumerVersion = grantRequestTokenResponse.Version; if (grantRequestTokenResponse.RequestMessage.Callback != null) { - this.tokenManager.SetRequestTokenCallback(grantRequestTokenResponse.RequestToken, grantRequestTokenResponse.RequestMessage.Callback); + this.tokenManager.GetRequestToken(grantRequestTokenResponse.RequestToken).Callback = grantRequestTokenResponse.RequestMessage.Callback; } return MessageProtections.None; @@ -112,7 +112,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { var authorizedTokenRequest = message as AuthorizedTokenRequest; if (authorizedTokenRequest != null && authorizedTokenRequest.Version >= Protocol.V10a.Version) { - string expectedVerifier = this.tokenManager.GetRequestTokenVerifier(authorizedTokenRequest.RequestToken); + string expectedVerifier = this.tokenManager.GetRequestToken(authorizedTokenRequest.RequestToken).VerificationCode; ErrorUtilities.VerifyProtocol(string.Equals(authorizedTokenRequest.VerificationCode, expectedVerifier, StringComparison.Ordinal), OAuthStrings.IncorrectVerifier); return MessageProtections.None; } diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs index d1811c4..b9391ec 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs @@ -79,15 +79,6 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> - /// Looks up a localized string similar to The RSA-SHA1 signing binding element's consumer certificate provider has not been set, so no incoming messages from consumers using this signature method can be verified.. - /// </summary> - internal static string ConsumerCertificateProviderNotAvailable { - get { - return ResourceManager.GetString("ConsumerCertificateProviderNotAvailable", resourceCulture); - } - } - - /// <summary> /// Looks up a localized string similar to Failure looking up secret for consumer or token.. /// </summary> internal static string ConsumerOrTokenSecretNotFound { diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx index 2e82bdb..0de00a3 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx @@ -123,9 +123,6 @@ <data name="BadAccessTokenInProtectedResourceRequest" xml:space="preserve"> <value>The access token '{0}' is invalid or expired.</value> </data> - <data name="ConsumerCertificateProviderNotAvailable" xml:space="preserve"> - <value>The RSA-SHA1 signing binding element's consumer certificate provider has not been set, so no incoming messages from consumers using this signature method can be verified.</value> - </data> <data name="ConsumerOrTokenSecretNotFound" xml:space="preserve"> <value>Failure looking up secret for consumer or token.</value> </data> diff --git a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs index 311e531..721d40b 100644 --- a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs +++ b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs @@ -236,7 +236,23 @@ namespace DotNetOpenAuth.OAuth { // It is very important for us to ignore the oauth_callback argument in the // UserAuthorizationRequest if the Consumer is a 1.0a consumer or else we // open up a security exploit. - Uri callback = request.Version >= Protocol.V10a.Version ? this.TokenManager.GetRequestTokenCallback(request.RequestToken) : request.Callback; + IServiceProviderRequestToken token = this.TokenManager.GetRequestToken(request.RequestToken); + Uri callback; + if (request.Version >= Protocol.V10a.Version) { + // In OAuth 1.0a, we'll prefer the token-specific callback to the pre-registered one. + if (token.Callback != null) { + callback = token.Callback; + } else { + IConsumerDescription consumer = this.TokenManager.GetConsumer(token.ConsumerKey); + callback = consumer.Callback; + } + } else { + // In OAuth 1.0, we'll prefer the pre-registered callback over the token-specific one + // since 1.0 has a security weakness for user-modified callback URIs. + IConsumerDescription consumer = this.TokenManager.GetConsumer(token.ConsumerKey); + callback = consumer.Callback ?? request.Callback; + } + return callback != null ? this.PrepareAuthorizationResponse(request, callback) : null; } |