diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2010-05-31 08:00:50 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2010-05-31 08:00:50 -0700 |
commit | ad60330d66985c4892b7e0b7ddb424be9ca867c8 (patch) | |
tree | 5efdb30c9b14e1928828b76c8a66822779a17198 /src | |
parent | 52a77983f11cfbb948a574585ba8069dcbcbd89b (diff) | |
download | DotNetOpenAuth-ad60330d66985c4892b7e0b7ddb424be9ca867c8.zip DotNetOpenAuth-ad60330d66985c4892b7e0b7ddb424be9ca867c8.tar.gz DotNetOpenAuth-ad60330d66985c4892b7e0b7ddb424be9ca867c8.tar.bz2 |
More work toward a working authorization server.
Diffstat (limited to 'src')
20 files changed, 300 insertions, 57 deletions
diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index d282777..bdfb911 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -308,12 +308,14 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="Messaging\StandardMessageFactory.cs" /> <Compile Include="OAuthWrap\AuthorizationServerBase.cs" /> <Compile Include="OAuthWrap\AuthorizationState.cs" /> + <Compile Include="OAuthWrap\ChannelElements\AuthServerBindingElementBase.cs" /> <Compile Include="OAuthWrap\ChannelElements\IAccessTokenRequest.cs" /> <Compile Include="OAuthWrap\ChannelElements\OAuthWrapResourceServerChannel.cs" /> <Compile Include="Messaging\StandardMessageFactoryChannel.cs" /> <Compile Include="OAuthWrap\ChannelElements\TimestampEncoder.cs" /> <Compile Include="OAuthWrap\ChannelElements\VerificationCode.cs" /> <Compile Include="OAuthWrap\ChannelElements\WebAppVerificationCodeBindingElement.cs" /> + <Compile Include="OAuthWrap\ChannelElements\AuthServerWebServerFlowBindingElement.cs" /> <Compile Include="OAuthWrap\IAccessTokenAnalyzer.cs" /> <Compile Include="OAuthWrap\IAuthorizationServer.cs" /> <Compile Include="OAuthWrap\IAuthorizationState.cs" /> diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs index 1be62f5..21fd53a 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. -// Runtime Version:4.0.30319.1 +// Runtime Version:4.0.30426.0 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -349,6 +349,15 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Looks up a localized string similar to The following message parts had constant value requirements that were unsatisfied: {0}. + /// </summary> + internal static string RequiredMessagePartConstantIncorrect { + get { + return ResourceManager.GetString("RequiredMessagePartConstantIncorrect", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to The following required non-empty parameters were empty in the {0} message: {1}. /// </summary> internal static string RequiredNonEmptyParameterWasEmpty { diff --git a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx index cb80442..3a265f1 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingStrings.resx +++ b/src/DotNetOpenAuth/Messaging/MessagingStrings.resx @@ -306,4 +306,7 @@ <data name="StandardMessageFactoryUnsupportedMessageType" xml:space="preserve"> <value>This message factory does not support message type(s): {0}</value> </data> -</root> + <data name="RequiredMessagePartConstantIncorrect" xml:space="preserve"> + <value>The following message parts had constant value requirements that were unsatisfied: {0}</value> + </data> +</root>
\ No newline at end of file diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs index 17b5304..808d5b8 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs @@ -93,6 +93,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { try { this.CheckRequiredMessagePartsArePresent(parts.Keys, true); this.CheckRequiredProtocolMessagePartsAreNotEmpty(parts, true); + this.CheckMessagePartsConstantValues(parts, true); } catch (ProtocolException) { Logger.Messaging.ErrorFormat( "Error while performing basic validation of {0} with these message parts:{1}{2}", @@ -112,7 +113,8 @@ namespace DotNetOpenAuth.Messaging.Reflection { Contract.Requires<ArgumentNullException>(parts != null); return this.CheckRequiredMessagePartsArePresent(parts.Keys, false) && - this.CheckRequiredProtocolMessagePartsAreNotEmpty(parts, false); + this.CheckRequiredProtocolMessagePartsAreNotEmpty(parts, false) && + this.CheckMessagePartsConstantValues(parts, false); } /// <summary> @@ -185,6 +187,33 @@ namespace DotNetOpenAuth.Messaging.Reflection { return true; } + private bool CheckMessagePartsConstantValues(IDictionary<string, string> partValues, bool throwOnFailure) + { + Contract.Requires<ArgumentNullException>(partValues != null); + + var badConstantValues = (from part in this.Mapping.Values + where part.IsConstantValueAvailableStatically + where partValues.ContainsKey(part.Name) + where !string.Equals(partValues[part.Name], part.StaticConstantValue, StringComparison.Ordinal) + select part.Name).ToArray(); + if (badConstantValues.Length > 0) { + if (throwOnFailure) { + ErrorUtilities.ThrowProtocol( + MessagingStrings.RequiredMessagePartConstantIncorrect, + this.MessageType.FullName, + string.Join(", ", badConstantValues)); + } else { + Logger.Messaging.DebugFormat( + MessagingStrings.RequiredMessagePartConstantIncorrect, + this.MessageType.FullName, + badConstantValues.ToStringDeferred()); + return false; + } + } + + return true; + } + /// <summary> /// Reflects over some <see cref="IMessage"/>-implementing type /// and prepares to serialize/deserialize instances of that type. diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index 4590c44..a9ec171 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -159,6 +159,7 @@ namespace DotNetOpenAuth.Messaging.Reflection { (this.field.Attributes & FieldAttributes.InitOnly) == FieldAttributes.InitOnly || (this.field.Attributes & constAttributes) == constAttributes)) { this.IsConstantValue = true; + this.IsConstantValueAvailableStatically = this.field.IsStatic; } else if (this.property != null && !this.property.CanWrite) { this.IsConstantValue = true; } @@ -194,6 +195,21 @@ namespace DotNetOpenAuth.Messaging.Reflection { internal bool IsConstantValue { get; set; } /// <summary> + /// Gets or sets a value indicating whether this part is defined as a constant field and can be read without a message instance. + /// </summary> + internal bool IsConstantValueAvailableStatically { get; set; } + + /// <summary> + /// Gets the static constant value for this message part without a message instance. + /// </summary> + internal string StaticConstantValue { + get { + Contract.Requires<InvalidOperationException>(this.IsConstantValueAvailableStatically); + return this.ToString(this.field.GetValue(null)); + } + } + + /// <summary> /// Sets the member of a given message to some given value. /// Used in deserialization. /// </summary> diff --git a/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs b/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs index abf3359..2eacf93 100644 --- a/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs +++ b/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs @@ -144,7 +144,8 @@ namespace DotNetOpenAuth.Messaging { var matches = this.requestMessageTypes.Keys .Where(message => message.CheckMessagePartsPassBasicValidation(fields)) - .OrderByDescending(message => message.Mapping.Count) + .OrderByDescending(message => CountInCommon(message.Mapping.Keys, fields.Keys)) + .ThenByDescending(message => message.Mapping.Count) .CacheGeneratedResults(); var match = matches.FirstOrDefault(); if (match != null) { diff --git a/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs index 1875bd1..f6f2041 100644 --- a/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs +++ b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs @@ -18,6 +18,7 @@ namespace DotNetOpenAuth.OAuthWrap { protected AuthorizationServerBase(IAuthorizationServer authorizationServer) { Contract.Requires<ArgumentNullException>(authorizationServer != null, "authorizationServer"); this.AuthorizationServer = authorizationServer; + this.Channel = new OAuthWrapAuthorizationServerChannel(authorizationServer); } public Channel Channel { get; set; } @@ -27,16 +28,5 @@ namespace DotNetOpenAuth.OAuthWrap { } public IAuthorizationServer AuthorizationServer { get; set; } - - protected IConsumerDescription GetClient(string clientIdentifier) { - Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(clientIdentifier)); - Contract.Ensures(Contract.Result<IConsumerDescription>() != null); - - try { - return this.AuthorizationServer.GetClient(clientIdentifier); - } catch (KeyNotFoundException ex) { - throw ErrorUtilities.Wrap(ex, OAuth.OAuthStrings.ConsumerOrTokenSecretNotFound); - } - } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerBindingElementBase.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerBindingElementBase.cs new file mode 100644 index 0000000..c84d37d --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerBindingElementBase.cs @@ -0,0 +1,84 @@ +//----------------------------------------------------------------------- +// <copyright file="AuthServerBindingElementBase.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.ChannelElements { + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using Messaging; + + internal abstract class AuthServerBindingElementBase : IChannelBindingElement { + /// <summary> + /// Initializes a new instance of the <see cref="AuthServerBindingElementBase"/> class. + /// </summary> + protected AuthServerBindingElementBase() + { + } + + /// <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; } + + protected OAuthWrapAuthorizationServerChannel OAuthChannel { + get { return (OAuthWrapAuthorizationServerChannel)this.Channel; } + } + + /// <summary> + /// Gets the authorization server hosting this channel. + /// </summary> + /// <value>The authorization server.</value> + protected IAuthorizationServer AuthorizationServer { + get { return this.OAuthChannel.AuthorizationServer; } + } + + /// <summary> + /// Gets the protection commonly offered (if any) by this binding element. + /// </summary> + /// <value></value> + /// <remarks> + /// This value is used to assist in sorting binding elements in the channel stack. + /// </remarks> + public abstract MessageProtections Protection { get; } + + /// <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 abstract MessageProtections? ProcessOutgoingMessage(IProtocolMessage message); + + /// <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 abstract MessageProtections? ProcessIncomingMessage(IProtocolMessage message); + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerWebServerFlowBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerWebServerFlowBindingElement.cs new file mode 100644 index 0000000..2f7b49e --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerWebServerFlowBindingElement.cs @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------- +// <copyright file="WebServerFlowBindingElement.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.ChannelElements { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + using DotNetOpenAuth.OAuthWrap.Messages; + using Messaging; + + internal class AuthServerWebServerFlowBindingElement : AuthServerBindingElementBase { + /// <summary> + /// Initializes a new instance of the <see cref="AuthServerWebServerFlowBindingElement"/> class. + /// </summary> + internal AuthServerWebServerFlowBindingElement() { + } + + /// <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 override 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 override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + 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 override MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + var authorizationRequest = message as WebAppRequest; + if (authorizationRequest != null) { + var client = this.AuthorizationServer.GetClientOrThrow(authorizationRequest.ClientIdentifier); + ErrorUtilities.VerifyProtocol(client.Callback == null || client.Callback == authorizationRequest.Callback, OAuthWrapStrings.CallbackMismatch, client.Callback, authorizationRequest.Callback); + } + return null; + } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs index aae511d..f286e4d 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs @@ -161,6 +161,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { var bindingElements = new List<IChannelBindingElement>(); if (authorizationServer != null) { + bindingElements.Add(new AuthServerWebServerFlowBindingElement()); bindingElements.Add(new WebAppVerificationCodeBindingElement()); } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs index 3a46517..e99a685 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs @@ -20,13 +20,14 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// </summary> /// <param name="channel">The channel.</param> /// <param name="callback">The callback.</param> - internal VerificationCode(OAuthWrapAuthorizationServerChannel channel, Uri callback, string scope) + internal VerificationCode(OAuthWrapAuthorizationServerChannel channel, Uri callback, string scope, string username) : this(channel) { Contract.Requires<ArgumentNullException>(channel != null, "channel"); Contract.Requires<ArgumentNullException>(callback != null, "callback"); this.CallbackHash = this.CalculateCallbackHash(callback); this.Scope = scope; + this.User = username; this.CreationDateUtc = DateTime.UtcNow; this.Nonce = Convert.ToBase64String(MessagingUtilities.GetNonCryptoRandomData(NonceLength)); } @@ -50,10 +51,13 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { [MessagePart("cb")] private string CallbackHash { get; set; } - [MessagePart("scope")] + [MessagePart] internal string Scope { get; set; } - [MessagePart("nonce")] + [MessagePart] + internal string User { get; set; } + + [MessagePart] internal string Nonce { get; set; } [MessagePart("timestamp", Encoder = typeof(TimestampEncoder))] @@ -67,6 +71,12 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// before it passes through the channel binding elements. /// </summary> void IMessageWithEvents.OnSending() { + // Encrypt the authorizing username so as to not expose unintended private user data + // to the client or any eavesdropping third party. + if (this.User != null) { + // TODO: code here + } + this.Signature = CalculateSignature(); } @@ -77,6 +87,11 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { void IMessageWithEvents.OnReceiving() { // Verify that the verification code was issued by this authorization server. ErrorUtilities.VerifyProtocol(string.Equals(this.Signature, this.CalculateSignature(), StringComparison.Ordinal), Protocol.bad_verification_code); + + // Decrypt the authorizing username. + if (this.User != null) { + // TODO: code here + } } internal static VerificationCode Decode(OAuthWrapAuthorizationServerChannel channel, string value) { diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs index f85cf98..31639f3 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebAppVerificationCodeBindingElement.cs @@ -17,7 +17,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// A binding element for OAuth 2.0 authorization servers that create/verify /// issued verification codes as part of obtaining access/refresh tokens. /// </summary> - internal class WebAppVerificationCodeBindingElement : IChannelBindingElement { + internal class WebAppVerificationCodeBindingElement : AuthServerBindingElementBase { private const string VerificationCodeContext = "{VerificationCode}"; /// <summary> @@ -27,28 +27,16 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } /// <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> /// <value>Always <c>MessageProtections.None</c></value> /// <remarks> /// This value is used to assist in sorting binding elements in the channel stack. /// </remarks> - public MessageProtections Protection { + public override MessageProtections Protection { get { return MessageProtections.None; } } - protected OAuthWrapAuthorizationServerChannel OAuthChannel { - get { return (OAuthWrapAuthorizationServerChannel)this.Channel; } - } - /// <summary> /// Gets the maximum message age from the standard expiration binding element. /// </summary> @@ -57,14 +45,6 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } /// <summary> - /// Gets the authorization server hosting this channel. - /// </summary> - /// <value>The authorization server.</value> - private IAuthorizationServer AuthorizationServer { - get { return this.OAuthChannel.AuthorizationServer; } - } - - /// <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> @@ -76,13 +56,13 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { + public override MessageProtections? ProcessOutgoingMessage(IProtocolMessage message) { var response = message as WebAppSuccessResponse; if (response != null) { - var directResponse = response as IDirectResponseProtocolMessage; - var request = directResponse.OriginatingRequest as WebAppRequest; + var directResponse = (IDirectResponseProtocolMessage)response; + var request = (WebAppRequest)directResponse.OriginatingRequest; - var code = new VerificationCode(this.OAuthChannel, request.Callback, request.Scope); + var code = new VerificationCode(this.OAuthChannel, request.Callback, request.Scope, response.AuthorizingUsername); response.VerificationCode = code.Encode(); return MessageProtections.None; @@ -108,7 +88,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// Implementations that provide message protection must honor the /// <see cref="MessagePartAttribute.RequiredProtection"/> properties where applicable. /// </remarks> - public MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { + public override MessageProtections? ProcessIncomingMessage(IProtocolMessage message) { var request = message as WebAppAccessTokenRequest; if (request != null) { var client = this.AuthorizationServer.GetClient(request.ClientIdentifier); diff --git a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs index c92ecc7..fccf067 100644 --- a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs @@ -16,6 +16,12 @@ namespace DotNetOpenAuth.OAuthWrap { [ContractClass(typeof(IAuthorizationServerContract))] public interface IAuthorizationServer { + /// <summary> + /// Gets the client with a given identifier. + /// </summary> + /// <param name="clientIdentifier">The client identifier.</param> + /// <returns>The client registration. Never null.</returns> + /// <exception cref="ArgumentException">Thrown when no client with the given identifier is registered with this authorization server.</exception> IConsumerDescription GetClient(string clientIdentifier); byte[] Secret { get; } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs index 9986d19..738846b 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/MessageBase.cs @@ -49,11 +49,13 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { /// Initializes a new instance of the <see cref="MessageBase"/> class. /// </summary> /// <param name="request">The originating request.</param> - protected MessageBase(IDirectedProtocolMessage request) { + /// <param name="recipient">The recipient of the directed message. Null if not applicable.</param> + protected MessageBase(IDirectedProtocolMessage request, Uri recipient = null) { Contract.Requires<ArgumentNullException>(request != null); this.originatingRequest = request; this.messageTransport = MessageTransport.Direct; this.version = request.Version; + this.Recipient = recipient; } /// <summary> diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppSuccessResponse.cs index e93acca..2cfb017 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppSuccessResponse.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebAppSuccessResponse.cs @@ -32,7 +32,7 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { /// <param name="clientCallback">The client callback.</param> /// <param name="request">The request.</param> internal WebAppSuccessResponse(Uri clientCallback, WebAppRequest request) - : this(clientCallback, ((IMessage)request).Version) { + : base(request, clientCallback) { Contract.Requires<ArgumentNullException>(clientCallback != null, "clientCallback"); Contract.Requires<ArgumentNullException>(request != null, "request"); ((IMessageWithClientState)this).ClientState = ((IMessageWithClientState)request).ClientState; @@ -57,5 +57,10 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { /// </value> [MessagePart(Protocol.code, IsRequired = true, AllowEmpty = true)] internal string VerificationCode { get; set; } + + /// <summary> + /// Gets or sets the authorizing user's account name. + /// </summary> + internal string AuthorizingUsername { get; set; } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs index d5836ff..35417e1 100644 --- a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.Designer.cs @@ -61,6 +61,15 @@ namespace DotNetOpenAuth.OAuthWrap { } /// <summary> + /// Looks up a localized string similar to Client's pre-registered callback URL ({0}) does not match the one found in the authorization request ({1}).. + /// </summary> + internal static string CallbackMismatch { + get { + return ResourceManager.GetString("CallbackMismatch", resourceCulture); + } + } + + /// <summary> /// Looks up a localized string similar to Failed to obtain access token. Authorization Server reports reason: {0}. /// </summary> internal static string CannotObtainAccessTokenWithReason { diff --git a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx index 2e70624..ae2cc6c 100644 --- a/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx +++ b/src/DotNetOpenAuth/OAuthWrap/OAuthWrapStrings.resx @@ -117,6 +117,9 @@ <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> + <data name="CallbackMismatch" xml:space="preserve"> + <value>Client's pre-registered callback URL ({0}) does not match the one found in the authorization request ({1}).</value> + </data> <data name="CannotObtainAccessTokenWithReason" xml:space="preserve"> <value>Failed to obtain access token. Authorization Server reports reason: {0}</value> </data> diff --git a/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs index b403f91..837d571 100644 --- a/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/WebAppAuthorizationServer.cs @@ -40,10 +40,11 @@ namespace DotNetOpenAuth.OAuthWrap { return message; } - public void ApproveAuthorizationRequest(WebAppRequest authorizationRequest, Uri callback = null) { + public void ApproveAuthorizationRequest(WebAppRequest authorizationRequest, string username, Uri callback = null) { Contract.Requires<ArgumentNullException>(authorizationRequest != null, "authorizationRequest"); var response = this.PrepareApproveAuthorizationRequest(authorizationRequest, callback); + response.AuthorizingUsername = username; this.Channel.Send(response); } @@ -59,7 +60,6 @@ namespace DotNetOpenAuth.OAuthWrap { return this.TryPrepareAccessTokenResponse(this.Channel.GetRequestFromContext(), out response); } - public bool TryPrepareAccessTokenResponse(HttpRequestInfo httpRequestInfo, out IDirectResponseProtocolMessage response) { Contract.Requires<ArgumentNullException>(httpRequestInfo != null, "httpRequestInfo"); @@ -95,7 +95,7 @@ namespace DotNetOpenAuth.OAuthWrap { callback = this.GetCallback(authorizationRequest); } - var client = GetClient(authorizationRequest.ClientIdentifier); + var client = this.AuthorizationServer.GetClientOrThrow(authorizationRequest.ClientIdentifier); var response = new WebAppSuccessResponse(callback, authorizationRequest); return response; } diff --git a/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs b/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs index 4ac1430..0e26994 100644 --- a/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs +++ b/src/DotNetOpenAuth/OAuthWrap/WebAppClient.cs @@ -64,16 +64,14 @@ namespace DotNetOpenAuth.OAuthWrap { return request; } - public IAuthorizationState ProcessUserAuthorization() { - Contract.Requires<InvalidOperationException>(HttpContext.Current != null && HttpContext.Current.Request != null, MessagingStrings.HttpContextRequired); - return this.ProcessUserAuthorization(this.Channel.GetRequestFromContext()); - } - - public IAuthorizationState ProcessUserAuthorization(HttpRequestInfo request) { - Contract.Requires<ArgumentNullException>(request != null); + public IAuthorizationState ProcessUserAuthorization(HttpRequestInfo request = null) { Contract.Requires<InvalidOperationException>(!string.IsNullOrEmpty(this.ClientIdentifier)); Contract.Requires<InvalidOperationException>(!string.IsNullOrEmpty(this.ClientSecret)); + if (request == null) { + request = this.Channel.GetRequestFromContext(); + } + IMessageWithClientState response; if (this.Channel.TryReadFromRequest<IMessageWithClientState>(request, out response)) { Uri callback = MessagingUtilities.StripMessagePartsFromQueryString(request.UrlBeforeRewriting, this.Channel.MessageDescriptions.Get(response)); diff --git a/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs b/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs index 7e04f3f..86a9c5d 100644 --- a/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs +++ b/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs @@ -4,6 +4,8 @@ // </copyright> //----------------------------------------------------------------------- +using DotNetOpenAuth.Messaging; + namespace DotNetOpenAuth.OAuthWrap { using System; using System.Collections.Generic; @@ -30,5 +32,18 @@ namespace DotNetOpenAuth.OAuthWrap { Protocol.HttpAuthorizationHeaderFormat, accessToken); } + + internal static DotNetOpenAuth.OAuth.ChannelElements.IConsumerDescription GetClientOrThrow(this IAuthorizationServer authorizationServer, string clientIdentifier) { + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(clientIdentifier)); + Contract.Ensures(Contract.Result<DotNetOpenAuth.OAuth.ChannelElements.IConsumerDescription>() != null); + + try { + return authorizationServer.GetClient(clientIdentifier); + } catch (KeyNotFoundException ex) { + throw ErrorUtilities.Wrap(ex, OAuth.OAuthStrings.ConsumerOrTokenSecretNotFound); + } catch (ArgumentException ex) { + throw ErrorUtilities.Wrap(ex, OAuth.OAuthStrings.ConsumerOrTokenSecretNotFound); + } + } } } |