diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2009-06-05 22:23:28 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2009-06-05 22:23:28 -0700 |
commit | 63daff25f5ea47e6bd7826c21fe2ba9905185e8c (patch) | |
tree | 7be29fb11774c8895050790004ab9cfb16310aa8 /src | |
parent | da17dd303e4dbf663882cf926678cd7794077e9f (diff) | |
download | DotNetOpenAuth-63daff25f5ea47e6bd7826c21fe2ba9905185e8c.zip DotNetOpenAuth-63daff25f5ea47e6bd7826c21fe2ba9905185e8c.tar.gz DotNetOpenAuth-63daff25f5ea47e6bd7826c21fe2ba9905185e8c.tar.bz2 |
Initial change to comply with OAuth 1.0a.
Still need to come up with a plan to handle Consumers without callback, including mobile and set-top devices with limited keyboards.
No tests yet.
Limited thought given so far to interoperability with 1.0 vs. 1.0a.
Diffstat (limited to 'src')
29 files changed, 439 insertions, 90 deletions
diff --git a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs index be3c563..bf9dc2b 100644 --- a/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs +++ b/src/DotNetOpenAuth.Test/Mocks/InMemoryTokenManager.cs @@ -15,6 +15,7 @@ namespace DotNetOpenAuth.Test.Mocks { internal class InMemoryTokenManager : IConsumerTokenManager, IServiceProviderTokenManager { private Dictionary<string, string> consumersAndSecrets = new Dictionary<string, string>(); private Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>(); + private Dictionary<string, string> tokensAndVerifiers = new Dictionary<string, string>(); /// <summary> /// Request tokens that have been issued, and whether they have been authorized yet. @@ -97,6 +98,14 @@ namespace DotNetOpenAuth.Test.Mocks { return this.consumersAndSecrets[consumerKey]; } + public void SetRequestTokenVerifier(string requestToken, string verifier) { + this.tokensAndVerifiers[requestToken] = verifier; + } + + public string GetRequestTokenVerifier(string requestToken) { + return this.tokensAndVerifiers[requestToken]; + } + #endregion /// <summary> diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs index ca63b50..627db8f 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/PlaintextSigningBindingElementTest.cs @@ -4,9 +4,9 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.Test.ChannelElements -{ +namespace DotNetOpenAuth.Test.ChannelElements { using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -17,7 +17,7 @@ namespace DotNetOpenAuth.Test.ChannelElements public void HttpsSignatureGeneration() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest); - ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint); + ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version); message.ConsumerSecret = "cs"; message.TokenSecret = "ts"; Assert.IsNotNull(target.ProcessOutgoingMessage(message)); @@ -29,7 +29,7 @@ namespace DotNetOpenAuth.Test.ChannelElements public void HttpsSignatureVerification() { MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest); ITamperProtectionChannelBindingElement target = new PlaintextSigningBindingElement(); - ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint); + ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version); message.ConsumerSecret = "cs"; message.TokenSecret = "ts"; message.SignatureMethod = "PLAINTEXT"; @@ -41,7 +41,7 @@ namespace DotNetOpenAuth.Test.ChannelElements public void HttpsSignatureVerificationNotApplicable() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("https://localtest", HttpDeliveryMethods.GetRequest); - ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint); + ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version); message.ConsumerSecret = "cs"; message.TokenSecret = "ts"; message.SignatureMethod = "ANOTHERALGORITHM"; @@ -53,7 +53,7 @@ namespace DotNetOpenAuth.Test.ChannelElements public void HttpSignatureGeneration() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("http://localtest", HttpDeliveryMethods.GetRequest); - ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint); + ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version); message.ConsumerSecret = "cs"; message.TokenSecret = "ts"; @@ -67,7 +67,7 @@ namespace DotNetOpenAuth.Test.ChannelElements public void HttpSignatureVerification() { SigningBindingElementBase target = new PlaintextSigningBindingElement(); MessageReceivingEndpoint endpoint = new MessageReceivingEndpoint("http://localtest", HttpDeliveryMethods.GetRequest); - ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint); + ITamperResistantOAuthMessage message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version); message.ConsumerSecret = "cs"; message.TokenSecret = "ts"; message.SignatureMethod = "PLAINTEXT"; diff --git a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs index 93c0b3f..32ccf9e 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ChannelElements/SigningBindingElementBaseTests.cs @@ -7,6 +7,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; + using DotNetOpenAuth.OAuth; using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuth.Messages; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -63,7 +64,7 @@ namespace DotNetOpenAuth.Test.ChannelElements { internal static UnauthorizedTokenRequest CreateTestRequestTokenMessage(MessageDescriptionCollection messageDescriptions, MessageReceivingEndpoint endpoint) { endpoint = endpoint ?? new MessageReceivingEndpoint("https://www.google.com/accounts/OAuthGetRequestToken", HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest); - UnauthorizedTokenRequest message = new UnauthorizedTokenRequest(endpoint); + UnauthorizedTokenRequest message = new UnauthorizedTokenRequest(endpoint, Protocol.Default.Version); message.ConsumerKey = "nerdbank.org"; ((ITamperResistantOAuthMessage)message).ConsumerSecret = "nerdbanksecret"; var signedMessage = (ITamperResistantOAuthMessage)message; diff --git a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs index 6a2551a..a6c7306 100644 --- a/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs +++ b/src/DotNetOpenAuth.Test/OAuth/ProtocolTests.cs @@ -23,12 +23,12 @@ namespace DotNetOpenAuth.Test { [TestMethod] public void AuthorizationHeaderScheme() { - Assert.AreEqual("OAuth", Protocol.V10.AuthorizationHeaderScheme); + Assert.AreEqual("OAuth", Protocol.AuthorizationHeaderScheme); } [TestMethod] public void ParameterPrefix() { - Assert.AreEqual("oauth_", Protocol.V10.ParameterPrefix); + Assert.AreEqual("oauth_", Protocol.ParameterPrefix); } } } diff --git a/src/DotNetOpenAuth.sln b/src/DotNetOpenAuth.sln index 171ab07..336f138 100644 --- a/src/DotNetOpenAuth.sln +++ b/src/DotNetOpenAuth.sln @@ -17,6 +17,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specs", "Specs", "{CD57219F-24F4-4136-8741-6063D0D7A031}" ProjectSection(SolutionItems) = preProject ..\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\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 ..\doc\specs\openid-authentication-2_0.html = ..\doc\specs\openid-authentication-2_0.html diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 441c714..6f66c7c 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -236,6 +236,8 @@ <Compile Include="OAuth\ChannelElements\SigningBindingElementChain.cs" /> <Compile Include="OAuth\ChannelElements\StandardTokenGenerator.cs" /> <Compile Include="OAuth\ChannelElements\TokenType.cs" /> + <Compile Include="OAuth\ChannelElements\UriOrOobEncoder.cs" /> + <Compile Include="OAuth\ChannelElements\VerificationCodeBindingElement.cs" /> <Compile Include="OAuth\ConsumerBase.cs" /> <Compile Include="OAuth\DesktopConsumer.cs" /> <Compile Include="GlobalSuppressions.cs" /> diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs index e1c1e3f..bc4a03d 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/IServiceProviderTokenManager.cs @@ -23,5 +23,19 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <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); + + /// <summary> + /// Sets the verifier code associated with an authorized 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); } } diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs index 5ac1aa8..f0aa64a 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthChannel.cs @@ -63,7 +63,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <see cref="OAuthConsumerMessageFactory"/> or <see cref="OAuthServiceProviderMessageFactory"/>. /// </param> internal OAuthChannel(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager, IMessageFactory messageTypeProvider) - : base(messageTypeProvider, new OAuthHttpMethodBindingElement(), signingBindingElement, new StandardExpirationBindingElement(), new StandardReplayProtectionBindingElement(store)) { + : base(messageTypeProvider, InitializeBindingElements(signingBindingElement, store, tokenManager)) { ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager"); this.TokenManager = tokenManager; @@ -122,7 +122,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { string authorization = request.Headers[HttpRequestHeader.Authorization]; if (authorization != null) { string[] authorizationSections = authorization.Split(';'); // TODO: is this the right delimiter? - string oauthPrefix = Protocol.Default.AuthorizationHeaderScheme + " "; + string oauthPrefix = Protocol.AuthorizationHeaderScheme + " "; // The Authorization header may have multiple uses, and OAuth may be just one of them. // Go through each one looking for an OAuth one. @@ -241,6 +241,29 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { } /// <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> + /// <returns>An array of binding elements used to initialize the channel.</returns> + private static IChannelBindingElement[] InitializeBindingElements(ITamperProtectionChannelBindingElement signingBindingElement, INonceStore store, ITokenManager tokenManager) { + var bindingElements = new List<IChannelBindingElement> { + new OAuthHttpMethodBindingElement(), + signingBindingElement, + new StandardExpirationBindingElement(), + new StandardReplayProtectionBindingElement(store), + }; + + var spTokenManager = tokenManager as IServiceProviderTokenManager; + if (spTokenManager != null) { + bindingElements.Insert(0, new VerificationCodeBindingElement(spTokenManager)); + } + + return bindingElements.ToArray(); + } + + /// <summary> /// Uri-escapes the names and values in a dictionary per OAuth 1.0 section 5.1. /// </summary> /// <param name="source">The dictionary with names and values to encode.</param> @@ -307,7 +330,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { httpRequest.Method = GetHttpMethod(requestMessage); StringBuilder authorization = new StringBuilder(); - authorization.Append(protocol.AuthorizationHeaderScheme); + authorization.Append(Protocol.AuthorizationHeaderScheme); authorization.Append(" "); foreach (var pair in fields) { string key = MessagingUtilities.EscapeUriDataStringRfc3986(pair.Key); diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs index fce351b..3f18d82 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthConsumerMessageFactory.cs @@ -44,7 +44,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { MessageBase message = null; if (fields.ContainsKey("oauth_token")) { - message = new UserAuthorizationResponse(recipient.Location); + message = new UserAuthorizationResponse(recipient.Location, Protocol.Default.Version); } if (message != null) { @@ -92,9 +92,10 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { var unauthorizedTokenRequest = request as UnauthorizedTokenRequest; var authorizedTokenRequest = request as AuthorizedTokenRequest; if (unauthorizedTokenRequest != null) { - message = new UnauthorizedTokenResponse(unauthorizedTokenRequest); + 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); + message = new AuthorizedTokenResponse(authorizedTokenRequest, Protocol.Default.Version); } else { Logger.OAuth.ErrorFormat("Unexpected response message given the request type {0}", request.GetType().Name); throw new ProtocolException(OAuthStrings.InvalidIncomingMessage); diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs index 1aaea7f..b3f750e 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthServiceProviderMessageFactory.cs @@ -55,9 +55,9 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { MessageBase message = null; - if (fields.ContainsKey("oauth_consumer_key") && - !fields.ContainsKey("oauth_token")) { - message = new UnauthorizedTokenRequest(recipient); + if (fields.ContainsKey("oauth_consumer_key") && !fields.ContainsKey("oauth_token")) { + Protocol protocol = fields.ContainsKey("oauth_callback") ? Protocol.V10a : Protocol.V10; + message = new UnauthorizedTokenRequest(recipient, protocol.Version); } else if (fields.ContainsKey("oauth_consumer_key") && fields.ContainsKey("oauth_token")) { // Discern between RequestAccessToken and AccessProtectedResources, @@ -65,11 +65,11 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { // is in the token parameter. bool tokenTypeIsAccessToken = this.tokenManager.GetTokenType(fields["oauth_token"]) == TokenType.AccessToken; - message = tokenTypeIsAccessToken ? (MessageBase)new AccessProtectedResourceRequest(recipient) : - new AuthorizedTokenRequest(recipient); + message = tokenTypeIsAccessToken ? (MessageBase)new AccessProtectedResourceRequest(recipient, Protocol.Default.Version) : + new AuthorizedTokenRequest(recipient, Protocol.Default.Version); } else { // fail over to the message with no required fields at all. - message = new UserAuthorizationRequest(recipient); + message = new UserAuthorizationRequest(recipient, Protocol.Default.Version); } if (message != null) { diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoder.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoder.cs new file mode 100644 index 0000000..9652e24 --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/UriOrOobEncoder.cs @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------- +// <copyright file="UriOrOobEncoder.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuth.ChannelElements { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging.Reflection; + + /// <summary> + /// An URI encoder that translates null <see cref="Uri"/> references as "oob" + /// instead of an empty/missing argument. + /// </summary> + internal class UriOrOobEncoder : IMessagePartEncoder { + /// <summary> + /// The string constant "oob", used to indicate an out-of-band configuration. + /// </summary> + private const string OutOfBandConfiguration = "oob"; + + /// <summary> + /// Initializes a new instance of the <see cref="UriOrOobEncoder"/> class. + /// </summary> + internal UriOrOobEncoder() { + } + + #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) { + Uri uriValue = (Uri)value; + return uriValue != null ? uriValue.AbsoluteUri : OutOfBandConfiguration; + } + + /// <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/OAuth/ChannelElements/VerificationCodeBindingElement.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/VerificationCodeBindingElement.cs new file mode 100644 index 0000000..f1e14f4 --- /dev/null +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/VerificationCodeBindingElement.cs @@ -0,0 +1,119 @@ +//----------------------------------------------------------------------- +// <copyright file="VerificationCodeBindingElement.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 System.Linq; + using System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth.Messages; + + /// <summary> + /// A binding element for Service Providers to manage the verification code on applicable messages. + /// </summary> + internal class VerificationCodeBindingElement : IChannelBindingElement { + /// <summary> + /// The length of the verifier code (in raw bytes before base64 encoding) to generate. + /// </summary> + private const int VerifierCodeLength = 5; + + /// <summary> + /// The token manager offered by the service provider. + /// </summary> + private IServiceProviderTokenManager tokenManager; + + /// <summary> + /// Initializes a new instance of the <see cref="VerificationCodeBindingElement"/> class. + /// </summary> + /// <param name="tokenManager">The token manager.</param> + internal VerificationCodeBindingElement(IServiceProviderTokenManager tokenManager) { + Contract.Requires(tokenManager != null); + ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager"); + + this.tokenManager = tokenManager; + } + + #region IChannelBindingElement Members + + /// <summary> + /// Gets or sets the channel that this binding element belongs to. + /// </summary> + /// <remarks> + /// This property is set by the channel when it is first constructed. + /// </remarks> + public Channel Channel { get; set; } + + /// <summary> + /// Gets the protection commonly offered (if any) by this binding element. + /// </summary> + /// <remarks> + /// This value is used to assist in sorting binding elements in the channel stack. + /// </remarks> + public MessageProtections Protection { + get { return MessageProtections.None; } + } + + /// <summary> + /// Prepares a message for sending based on the rules of this channel binding element. + /// </summary> + /// <param name="message">The message to prepare for sending.</param> + /// <returns> + /// The protections (if any) that this binding element applied to the message. + /// Null if this binding element did not even apply to this binding element. + /// </returns> + /// <remarks> + /// Implementations that provide message protection must honor the + /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. + /// </remarks> + public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + ErrorUtilities.VerifyArgumentNotNull(message, "message"); + + var response = message as UserAuthorizationResponse; + if (response != null && response.Version >= Protocol.V10a.Version) { + ErrorUtilities.VerifyInternal(response.VerificationCode == null, "VerificationCode was unexpectedly already set."); + response.VerificationCode = MessagingUtilities.GetCryptoRandomDataAsBase64(VerifierCodeLength); + this.tokenManager.SetRequestTokenVerifier(response.RequestToken, response.VerificationCode); + return MessageProtections.None; + } + + return null; + } + + /// <summary> + /// Performs any transformation on an incoming message that may be necessary and/or + /// validates an incoming message based on the rules of this channel binding element. + /// </summary> + /// <param name="message">The incoming message to process.</param> + /// <returns> + /// The protections (if any) that this binding element applied to the message. + /// Null if this binding element did not even apply to this binding element. + /// </returns> + /// <exception cref="ProtocolException"> + /// Thrown when the binding element rules indicate that this message is invalid and should + /// NOT be processed. + /// </exception> + /// <remarks> + /// Implementations that provide message protection must honor the + /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. + /// </remarks> + public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + ErrorUtilities.VerifyArgumentNotNull(message, "message"); + + var request = message as AuthorizedTokenRequest; + if (request != null && request.Version >= Protocol.V10a.Version) { + string expectedVerifier = this.tokenManager.GetRequestTokenVerifier(request.RequestToken); + ErrorUtilities.VerifyProtocol(string.Equals(request.VerificationCode, expectedVerifier, StringComparison.Ordinal), OAuthStrings.IncorrectVerifier); + return MessageProtections.None; + } + + return null; + } + + #endregion + } +} diff --git a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs index 2392172..81da0ac 100644 --- a/src/DotNetOpenAuth/OAuth/ConsumerBase.cs +++ b/src/DotNetOpenAuth/OAuth/ConsumerBase.cs @@ -167,7 +167,7 @@ namespace DotNetOpenAuth.OAuth { ErrorUtilities.VerifyArgumentNotNull(endpoint, "endpoint"); ErrorUtilities.VerifyNonZeroLength(accessToken, "accessToken"); - AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint) { + AccessProtectedResourceRequest message = new AccessProtectedResourceRequest(endpoint, Protocol.Default.Version) { AccessToken = accessToken, ConsumerKey = this.ConsumerKey, }; @@ -190,7 +190,7 @@ namespace DotNetOpenAuth.OAuth { [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Justification = "Two results")] protected internal UserAuthorizationRequest PrepareRequestUserAuthorization(Uri callback, IDictionary<string, string> requestParameters, IDictionary<string, string> redirectParameters, out string requestToken) { // Obtain an unauthorized request token. - var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint) { + var token = new UnauthorizedTokenRequest(this.ServiceProvider.RequestTokenEndpoint, Protocol.Default.Version) { ConsumerKey = this.ConsumerKey, }; var tokenAccessor = this.Channel.MessageDescriptions.GetAccessor(token); @@ -200,7 +200,7 @@ namespace DotNetOpenAuth.OAuth { // Request user authorization. ITokenContainingMessage assignedRequestToken = requestTokenResponse; - var requestAuthorization = new UserAuthorizationRequest(this.ServiceProvider.UserAuthorizationEndpoint, assignedRequestToken.Token) { + var requestAuthorization = new UserAuthorizationRequest(this.ServiceProvider.UserAuthorizationEndpoint, assignedRequestToken.Token, Protocol.Default.Version) { Callback = callback, }; var requestAuthorizationAccessor = this.Channel.MessageDescriptions.GetAccessor(requestAuthorization); @@ -215,7 +215,7 @@ namespace DotNetOpenAuth.OAuth { /// <param name="requestToken">The request token that the user has authorized.</param> /// <returns>The access token assigned by the Service Provider.</returns> protected AuthorizedTokenResponse ProcessUserAuthorization(string requestToken) { - var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint) { + var requestAccess = new AuthorizedTokenRequest(this.ServiceProvider.AccessTokenEndpoint, Protocol.Default.Version) { RequestToken = requestToken, ConsumerKey = this.ConsumerKey, }; diff --git a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs index 62e02de..b60fda4 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/AccessProtectedResourceRequest.cs @@ -5,6 +5,7 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.Messages { + using System; using System.Diagnostics.CodeAnalysis; using DotNetOpenAuth.Messaging; @@ -17,8 +18,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="AccessProtectedResourceRequest"/> class. /// </summary> /// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param> - protected internal AccessProtectedResourceRequest(MessageReceivingEndpoint serviceProvider) - : base(MessageTransport.Direct, serviceProvider) { + /// <param name="version">The OAuth version.</param> + protected internal AccessProtectedResourceRequest(MessageReceivingEndpoint serviceProvider, Version version) + : base(MessageTransport.Direct, serviceProvider, version) { } /// <summary> diff --git a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs index 2d4793c..c96e82b 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenRequest.cs @@ -5,6 +5,7 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.Messages { + using System; using System.Globalization; using DotNetOpenAuth.Messaging; @@ -20,8 +21,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="AuthorizedTokenRequest"/> class. /// </summary> /// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param> - internal AuthorizedTokenRequest(MessageReceivingEndpoint serviceProvider) - : base(MessageTransport.Direct, serviceProvider) { + /// <param name="version">The OAuth version.</param> + internal AuthorizedTokenRequest(MessageReceivingEndpoint serviceProvider, Version version) + : base(MessageTransport.Direct, serviceProvider, version) { } /// <summary> @@ -39,6 +41,13 @@ namespace DotNetOpenAuth.OAuth.Messages { internal string RequestToken { get; set; } /// <summary> + /// Gets or sets the verification code received by the Consumer from the Service Provider + /// in the <see cref="UserAuthorizationResponse.VerificationCode"/> property. + /// </summary> + [MessagePart("oauth_verifier", IsRequired = true, AllowEmpty = false, MinVersion = Protocol.V10aVersion)] + internal string VerificationCode { get; set; } + + /// <summary> /// Checks the message state for conformity to the protocol specification /// and throws an exception if the message is invalid. /// </summary> diff --git a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs index 14413a5..d87c381 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/AuthorizedTokenResponse.cs @@ -5,6 +5,7 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.Messages { + using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using DotNetOpenAuth.Messaging; @@ -18,8 +19,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="AuthorizedTokenResponse"/> class. /// </summary> /// <param name="originatingRequest">The originating request.</param> - protected internal AuthorizedTokenResponse(AuthorizedTokenRequest originatingRequest) - : base(MessageProtections.None, originatingRequest) { + /// <param name="version">The OAuth version.</param> + protected internal AuthorizedTokenResponse(AuthorizedTokenRequest originatingRequest, Version version) + : base(MessageProtections.None, originatingRequest, version) { } /// <summary> diff --git a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs index e0269db..89e0276 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/MessageBase.cs @@ -62,12 +62,15 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </summary> /// <param name="protectionRequired">The level of protection the message requires.</param> /// <param name="originatingRequest">The request that asked for this direct response.</param> - protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest) { + /// <param name="version">The OAuth version.</param> + protected MessageBase(MessageProtections protectionRequired, IDirectedProtocolMessage originatingRequest, Version version) { ErrorUtilities.VerifyArgumentNotNull(originatingRequest, "originatingRequest"); + ErrorUtilities.VerifyArgumentNotNull(version, "version"); this.protectionRequired = protectionRequired; this.transport = MessageTransport.Direct; this.originatingRequest = originatingRequest; + this.Version = version; } /// <summary> @@ -76,14 +79,15 @@ namespace DotNetOpenAuth.OAuth.Messages { /// <param name="protectionRequired">The level of protection the message requires.</param> /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param> /// <param name="recipient">The URI that a directed message will be delivered to.</param> - protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient) { - if (recipient == null) { - throw new ArgumentNullException("recipient"); - } + /// <param name="version">The OAuth version.</param> + protected MessageBase(MessageProtections protectionRequired, MessageTransport transport, MessageReceivingEndpoint recipient, Version version) { + ErrorUtilities.VerifyArgumentNotNull(recipient, "recipient"); + ErrorUtilities.VerifyArgumentNotNull(version, "version"); this.protectionRequired = protectionRequired; this.transport = transport; this.recipient = recipient; + this.Version = version; } #region IProtocolMessage Properties @@ -163,9 +167,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// <summary> /// Gets the version of the protocol this message is prepared to implement. /// </summary> - protected virtual Version Version { - get { return new Version(1, 0); } - } + protected internal Version Version { get; private set; } /// <summary> /// Gets the level of protection this message requires. diff --git a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs index d1abb58..eb85cb5 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/SignedMessageBase.cs @@ -31,8 +31,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </summary> /// <param name="transport">A value indicating whether this message requires a direct or indirect transport.</param> /// <param name="recipient">The URI that a directed message will be delivered to.</param> - internal SignedMessageBase(MessageTransport transport, MessageReceivingEndpoint recipient) - : base(MessageProtections.All, transport, recipient) { + /// <param name="version">The OAuth version.</param> + internal SignedMessageBase(MessageTransport transport, MessageReceivingEndpoint recipient, Version version) + : base(MessageProtections.All, transport, recipient, version) { ITamperResistantOAuthMessage self = (ITamperResistantOAuthMessage)this; HttpDeliveryMethods methods = ((IDirectedProtocolMessage)this).HttpMethods; self.HttpMethod = (methods & HttpDeliveryMethods.PostRequest) != 0 ? "POST" : "GET"; diff --git a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs index e491bad..fceac01 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenRequest.cs @@ -5,8 +5,10 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth.Messages { + using System; using System.Collections.Generic; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth.ChannelElements; /// <summary> /// A direct message sent from Consumer to Service Provider to request a Request Token. @@ -16,11 +18,23 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="UnauthorizedTokenRequest"/> class. /// </summary> /// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param> - protected internal UnauthorizedTokenRequest(MessageReceivingEndpoint serviceProvider) - : base(MessageTransport.Direct, serviceProvider) { + /// <param name="version">The OAuth version.</param> + protected internal UnauthorizedTokenRequest(MessageReceivingEndpoint serviceProvider, Version version) + : base(MessageTransport.Direct, serviceProvider, version) { } /// <summary> + /// Gets or sets the absolute URL to which the Service Provider will redirect the + /// User back when the Obtaining User Authorization step is completed. + /// </summary> + /// <value> + /// The callback URL; or <c>null</c> if the Consumer is unable to receive + /// callbacks or a callback URL has been established via other means. + /// </value> + [MessagePart("oauth_callback", IsRequired = true, AllowEmpty = false, MinVersion = Protocol.V10aVersion, Encoder = typeof(UriOrOobEncoder))] + public Uri Callback { get; set; } + + /// <summary> /// Gets the extra, non-OAuth parameters that will be included in the message. /// </summary> public new IDictionary<string, string> ExtraData { diff --git a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs index 285dec7..23a138c 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UnauthorizedTokenResponse.cs @@ -21,11 +21,12 @@ namespace DotNetOpenAuth.OAuth.Messages { /// <param name="requestMessage">The unauthorized request token message that this message is being generated in response to.</param> /// <param name="requestToken">The request token.</param> /// <param name="tokenSecret">The token secret.</param> + /// <param name="version">The OAuth version.</param> /// <remarks> /// This constructor is used by the Service Provider to send the message. /// </remarks> - protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest requestMessage, string requestToken, string tokenSecret) - : this(requestMessage) { + protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest requestMessage, string requestToken, string tokenSecret, Version version) + : this(requestMessage, version) { ErrorUtilities.VerifyArgumentNotNull(requestToken, "requestToken"); ErrorUtilities.VerifyArgumentNotNull(tokenSecret, "tokenSecret"); @@ -37,9 +38,10 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="UnauthorizedTokenResponse"/> class. /// </summary> /// <param name="originatingRequest">The originating request.</param> + /// <param name="version">The OAuth version.</param> /// <remarks>This constructor is used by the consumer to deserialize the message.</remarks> - protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest originatingRequest) - : base(MessageProtections.None, originatingRequest) { + protected internal UnauthorizedTokenResponse(UnauthorizedTokenRequest originatingRequest, Version version) + : base(MessageProtections.None, originatingRequest, version) { } /// <summary> @@ -84,5 +86,13 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </summary> [MessagePart("oauth_token_secret", IsRequired = true)] protected internal string TokenSecret { get; set; } + + /// <summary> + /// Gets a value indicating whether the Service Provider recognized the callback parameter in the request. + /// </summary> + [MessagePart("oauth_callback_confirmed", IsRequired = true, MinVersion = Protocol.V10aVersion)] + private bool CallbackConfirmed { + get { return true; } + } } } diff --git a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs index f1af0bc..0924ac6 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationRequest.cs @@ -21,8 +21,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </summary> /// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param> /// <param name="requestToken">The request token.</param> - internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider, string requestToken) - : this(serviceProvider) { + /// <param name="version">The OAuth version.</param> + internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider, string requestToken, Version version) + : this(serviceProvider, version) { this.RequestToken = requestToken; } @@ -30,8 +31,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="UserAuthorizationRequest"/> class. /// </summary> /// <param name="serviceProvider">The URI of the Service Provider endpoint to send this message to.</param> - internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider) - : base(MessageProtections.None, MessageTransport.Indirect, serviceProvider) { + /// <param name="version">The OAuth version.</param> + internal UserAuthorizationRequest(MessageReceivingEndpoint serviceProvider, Version version) + : base(MessageProtections.None, MessageTransport.Indirect, serviceProvider, version) { } /// <summary> @@ -65,7 +67,7 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Gets or sets a URL the Service Provider will use to redirect the User back /// to the Consumer when Obtaining User Authorization is complete. Optional. /// </summary> - [MessagePart("oauth_callback", IsRequired = false)] + [MessagePart("oauth_callback", IsRequired = false, MaxVersion = "1.0")] internal Uri Callback { get; set; } } } diff --git a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs index da6a909..a9a43fb 100644 --- a/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs +++ b/src/DotNetOpenAuth/OAuth/Messages/UserAuthorizationResponse.cs @@ -19,8 +19,9 @@ namespace DotNetOpenAuth.OAuth.Messages { /// Initializes a new instance of the <see cref="UserAuthorizationResponse"/> class. /// </summary> /// <param name="consumer">The URI of the Consumer endpoint to send this message to.</param> - internal UserAuthorizationResponse(Uri consumer) - : base(MessageProtections.None, MessageTransport.Indirect, new MessageReceivingEndpoint(consumer, HttpDeliveryMethods.GetRequest)) { + /// <param name="version">The OAuth version.</param> + internal UserAuthorizationResponse(Uri consumer, Version version) + : base(MessageProtections.None, MessageTransport.Indirect, new MessageReceivingEndpoint(consumer, HttpDeliveryMethods.GetRequest), version) { } /// <summary> @@ -36,5 +37,19 @@ namespace DotNetOpenAuth.OAuth.Messages { /// </summary> [MessagePart("oauth_token", IsRequired = true)] internal string RequestToken { get; set; } + + /// <summary> + /// Gets or sets the verification code that must accompany the request to exchange the + /// authorized request token for an access token. + /// </summary> + /// <value>An unguessable value passed to the Consumer via the User and REQUIRED to complete the process.</value> + /// <remarks> + /// If the Consumer did not provide a callback URL, the Service Provider SHOULD display the value of the + /// verification code, and instruct the User to manually inform the Consumer that authorization is + /// completed. If the Service Provider knows a Consumer to be running on a mobile device or set-top box, + /// the Service Provider SHOULD ensure that the verifier value is suitable for manual entry. + /// </remarks> + [MessagePart("oauth_verifier", IsRequired = true, AllowEmpty = false, MinVersion = Protocol.V10aVersion)] + internal string VerificationCode { get; set; } } } diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs index 63e348a..d1811c4 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:2.0.50727.3521 +// Runtime Version:2.0.50727.4918 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -97,6 +97,15 @@ namespace DotNetOpenAuth.OAuth { } /// <summary> + /// Looks up a localized string similar to oauth_verifier argument was incorrect.. + /// </summary> + internal static string IncorrectVerifier { + get { + return ResourceManager.GetString("IncorrectVerifier", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to An invalid OAuth message received and discarded.. /// </summary> internal static string InvalidIncomingMessage { diff --git a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx index 3ba4da1..2e82bdb 100644 --- a/src/DotNetOpenAuth/OAuth/OAuthStrings.resx +++ b/src/DotNetOpenAuth/OAuth/OAuthStrings.resx @@ -129,6 +129,9 @@ <data name="ConsumerOrTokenSecretNotFound" xml:space="preserve"> <value>Failure looking up secret for consumer or token.</value> </data> + <data name="IncorrectVerifier" xml:space="preserve"> + <value>oauth_verifier argument was incorrect.</value> + </data> <data name="InvalidIncomingMessage" xml:space="preserve"> <value>An invalid OAuth message received and discarded.</value> </data> diff --git a/src/DotNetOpenAuth/OAuth/Protocol.cs b/src/DotNetOpenAuth/OAuth/Protocol.cs index 88615ff..9417efa 100644 --- a/src/DotNetOpenAuth/OAuth/Protocol.cs +++ b/src/DotNetOpenAuth/OAuth/Protocol.cs @@ -12,6 +12,21 @@ namespace DotNetOpenAuth.OAuth { using DotNetOpenAuth.Messaging; /// <summary> + /// An enumeration of the OAuth protocol versions supported by this library. + /// </summary> + public enum ProtocolVersion { + /// <summary> + /// OAuth 1.0 specification + /// </summary> + V10, + + /// <summary> + /// OAuth 1.0a specification + /// </summary> + V10a, + } + + /// <summary> /// Constants used in the OAuth protocol. /// </summary> /// <remarks> @@ -25,63 +40,92 @@ namespace DotNetOpenAuth.OAuth { internal const string DataContractNamespaceV10 = "http://oauth.net/core/1.0/"; /// <summary> + /// The prefix used for all key names in the protocol. + /// </summary> + internal const string ParameterPrefix = "oauth_"; + + /// <summary> + /// The string representation of a <see cref="Version"/> instance to be used to represent OAuth 1.0a. + /// </summary> + internal const string V10aVersion = "1.0.1"; + + /// <summary> + /// The scheme to use in Authorization header message requests. + /// </summary> + internal const string AuthorizationHeaderScheme = "OAuth"; + + /// <summary> /// Gets the <see cref="Protocol"/> instance with values initialized for V1.0 of the protocol. /// </summary> internal static readonly Protocol V10 = new Protocol { dataContractNamespace = DataContractNamespaceV10, + Version = new Version(1, 0), }; /// <summary> - /// The namespace to use for this version of the protocol. + /// Gets the <see cref="Protocol"/> instance with values initialized for V1.0a of the protocol. /// </summary> - private string dataContractNamespace; + internal static readonly Protocol V10a = new Protocol { + dataContractNamespace = DataContractNamespaceV10, + Version = new Version(V10aVersion), + }; /// <summary> - /// The prefix used for all key names in the protocol. + /// A list of all supported OAuth versions, in order starting from newest version. /// </summary> - private string parameterPrefix = "oauth_"; + internal static readonly List<Protocol> AllVersions = new List<Protocol>() { V10a, V10 }; /// <summary> - /// The scheme to use in Authorization header message requests. + /// The default (or most recent) supported version of the OpenID protocol. /// </summary> - private string authorizationHeaderScheme = "OAuth"; + internal static readonly Protocol Default = AllVersions[0]; /// <summary> - /// Gets the default <see cref="Protocol"/> instance. + /// The namespace to use for this version of the protocol. /// </summary> - internal static Protocol Default { get { return V10; } } + private string dataContractNamespace; /// <summary> - /// Gets the namespace to use for this version of the protocol. + /// Initializes a new instance of the <see cref="Protocol"/> class. /// </summary> - internal string DataContractNamespace { - get { return this.dataContractNamespace; } + internal Protocol() { } /// <summary> - /// Gets the prefix used for all key names in the protocol. + /// Gets the version used to represent OAuth 1.0a. /// </summary> - internal string ParameterPrefix { - get { return this.parameterPrefix; } - } + internal Version Version { get; private set; } /// <summary> - /// Gets the scheme to use in Authorization header message requests. + /// Gets the namespace to use for this version of the protocol. /// </summary> - internal string AuthorizationHeaderScheme { - get { return this.authorizationHeaderScheme; } + internal string DataContractNamespace { + get { return this.dataContractNamespace; } } /// <summary> - /// Gets an instance of <see cref="Protocol"/> given a <see cref="Version"/>. + /// Gets the OAuth Protocol instance to use for the given version. /// </summary> - /// <param name="version">The version of the protocol that is desired.</param> - /// <returns>The <see cref="Protocol"/> instance representing the requested version.</returns> - internal static Protocol Lookup(Version version) { - switch (version.Major) { - case 1: return Protocol.V10; + /// <param name="version">The OAuth version to get.</param> + /// <returns>A matching <see cref="Protocol"/> instance.</returns> + public static Protocol Lookup(ProtocolVersion version) { + switch (version) { + case ProtocolVersion.V10: return Protocol.V10; + case ProtocolVersion.V10a: return Protocol.V10a; default: throw new ArgumentOutOfRangeException("version"); } } + + /// <summary> + /// Gets the OAuth Protocol instance to use for the given version. + /// </summary> + /// <param name="version">The OAuth version to get.</param> + /// <returns>A matching <see cref="Protocol"/> instance.</returns> + internal static Protocol Lookup(Version version) { + ErrorUtilities.VerifyArgumentNotNull(version, "version"); + Protocol protocol = AllVersions.FirstOrDefault(p => p.Version == version); + ErrorUtilities.VerifyArgumentInRange(protocol != null, "version"); + return protocol; + } } } diff --git a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs index 345c6a2..38b7a60 100644 --- a/src/DotNetOpenAuth/OAuth/ServiceProvider.cs +++ b/src/DotNetOpenAuth/OAuth/ServiceProvider.cs @@ -48,7 +48,7 @@ namespace DotNetOpenAuth.OAuth { /// <param name="serviceDescription">The endpoints and behavior on the Service Provider.</param> /// <param name="tokenManager">The host's method of storing and recalling tokens and secrets.</param> /// <param name="messageTypeProvider">An object that can figure out what type of message is being received for deserialization.</param> - public ServiceProvider(ServiceProviderDescription serviceDescription, ITokenManager tokenManager, OAuthServiceProviderMessageFactory messageTypeProvider) { + public ServiceProvider(ServiceProviderDescription serviceDescription, IServiceProviderTokenManager tokenManager, OAuthServiceProviderMessageFactory messageTypeProvider) { ErrorUtilities.VerifyArgumentNotNull(serviceDescription, "serviceDescription"); ErrorUtilities.VerifyArgumentNotNull(tokenManager, "tokenManager"); ErrorUtilities.VerifyArgumentNotNull(messageTypeProvider, "messageTypeProvider"); @@ -156,13 +156,11 @@ namespace DotNetOpenAuth.OAuth { /// <param name="request">The token request message the Consumer sent that the Service Provider is now responding to.</param> /// <returns>The response message to send using the <see cref="Channel"/>, after optionally adding extra data to it.</returns> public UnauthorizedTokenResponse PrepareUnauthorizedTokenMessage(UnauthorizedTokenRequest request) { - if (request == null) { - throw new ArgumentNullException("request"); - } + ErrorUtilities.VerifyArgumentNotNull(request, "request"); string token = this.TokenGenerator.GenerateRequestToken(request.ConsumerKey); string secret = this.TokenGenerator.GenerateSecret(); - UnauthorizedTokenResponse response = new UnauthorizedTokenResponse(request, token, secret); + UnauthorizedTokenResponse response = new UnauthorizedTokenResponse(request, token, secret, Protocol.Default.Version); return response; } @@ -231,7 +229,7 @@ namespace DotNetOpenAuth.OAuth { ErrorUtilities.VerifyArgumentNotNull(request, "request"); ErrorUtilities.VerifyArgumentNotNull(callback, "callback"); - var authorization = new UserAuthorizationResponse(request.Callback) { + var authorization = new UserAuthorizationResponse(request.Callback, Protocol.Default.Version) { RequestToken = request.RequestToken, }; return authorization; @@ -282,7 +280,7 @@ namespace DotNetOpenAuth.OAuth { string accessToken = this.TokenGenerator.GenerateAccessToken(request.ConsumerKey); string tokenSecret = this.TokenGenerator.GenerateSecret(); this.TokenManager.ExpireRequestTokenAndStoreNewAccessToken(request.ConsumerKey, request.RequestToken, accessToken, tokenSecret); - var grantAccess = new AuthorizedTokenResponse(request) { + var grantAccess = new AuthorizedTokenResponse(request, Protocol.Default.Version) { AccessToken = accessToken, TokenSecret = tokenSecret, }; diff --git a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs index 4636829..bdfd10d 100644 --- a/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs +++ b/src/DotNetOpenAuth/OAuth/ServiceProviderDescription.cs @@ -26,9 +26,15 @@ namespace DotNetOpenAuth.OAuth { /// Initializes a new instance of the <see cref="ServiceProviderDescription"/> class. /// </summary> public ServiceProviderDescription() { + this.Version = Protocol.Default.Version; } /// <summary> + /// Gets or sets the OAuth version supported by the Service Provider. + /// </summary> + public Version Version { get; set; } + + /// <summary> /// Gets or sets the URL used to obtain an unauthorized Request Token, /// described in Section 6.1 (Obtaining an Unauthorized Request Token). /// </summary> @@ -43,7 +49,7 @@ namespace DotNetOpenAuth.OAuth { } set { - if (value != null && UriUtil.QueryStringContainPrefixedParameters(value.Location, OAuth.Protocol.V10.ParameterPrefix)) { + if (value != null && UriUtil.QueryStringContainPrefixedParameters(value.Location, OAuth.Protocol.ParameterPrefix)) { throw new ArgumentException(OAuthStrings.RequestUrlMustNotHaveOAuthParameters); } diff --git a/src/DotNetOpenAuth/OAuth/WebConsumer.cs b/src/DotNetOpenAuth/OAuth/WebConsumer.cs index bbf6115..7de73c7 100644 --- a/src/DotNetOpenAuth/OAuth/WebConsumer.cs +++ b/src/DotNetOpenAuth/OAuth/WebConsumer.cs @@ -39,7 +39,7 @@ namespace DotNetOpenAuth.OAuth { /// Requires HttpContext.Current. /// </remarks> public UserAuthorizationRequest PrepareRequestUserAuthorization() { - Uri callback = this.Channel.GetRequestFromContext().UrlBeforeRewriting.StripQueryArgumentsWithPrefix(Protocol.Default.ParameterPrefix); + Uri callback = this.Channel.GetRequestFromContext().UrlBeforeRewriting.StripQueryArgumentsWithPrefix(Protocol.ParameterPrefix); return this.PrepareRequestUserAuthorization(callback, null, null); } diff --git a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs index 37da8a3..635a3c0 100644 --- a/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs +++ b/src/DotNetOpenAuth/OpenId/Provider/ProviderEndpoint.cs @@ -102,7 +102,7 @@ namespace DotNetOpenAuth.OpenId.Provider { public static void SendResponse() { var pendingRequest = PendingAuthenticationRequest; PendingAuthenticationRequest = null; - Provider.SendResponse(pendingRequest); + Provider.SendResponse(pendingRequest); } /// <summary> |