diff options
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> |