diff options
69 files changed, 1288 insertions, 318 deletions
diff --git a/Settings.StyleCop b/Settings.StyleCop index 7ba99fa..a408101 100644 --- a/Settings.StyleCop +++ b/Settings.StyleCop @@ -56,6 +56,7 @@ <AnalyzerSettings> <CollectionProperty Name="Hungarian"> <Value>ax</Value> + <Value>iv</Value> <Value>op</Value> <Value>rp</Value> <Value>sp</Value> diff --git a/samples/OAuthConsumer/SampleWcf2.aspx.cs b/samples/OAuthConsumer/SampleWcf2.aspx.cs index b8eda9e..7fae00b 100644 --- a/samples/OAuthConsumer/SampleWcf2.aspx.cs +++ b/samples/OAuthConsumer/SampleWcf2.aspx.cs @@ -15,7 +15,7 @@ using OAuthConsumer.SampleServiceProvider; public partial class SampleWcf2 : System.Web.UI.Page { - private static InMemoryClientTokenManager TokenManager = new InMemoryClientTokenManager(); + private static InMemoryClientTokenManager tokenManager = new InMemoryClientTokenManager(); private static IAuthorizationState Authorization { get { return (AuthorizationState)HttpContext.Current.Session["Authorization"]; } @@ -44,7 +44,7 @@ var client = CreateClient(); string clientState; - var response = client.PrepareRequestUserAuthorization(TokenManager.NewAuthorization(scope, out clientState)); + var response = client.PrepareRequestUserAuthorization(tokenManager.NewAuthorization(scope, out clientState)); response.ClientState = clientState; client.Channel.Send(response); } @@ -75,9 +75,24 @@ } } + private static WebServerClient CreateClient() { + var authServerDescription = new AuthorizationServerDescription { + TokenEndpoint = new Uri("http://localhost:65169/OAuth2.ashx/token"), + AuthorizationEndpoint = new Uri("http://localhost:65169/OAuth2.ashx/auth"), + }; + + var client = new WebServerClient(authServerDescription) { + ClientIdentifier = "sampleconsumer", + ClientSecret = "samplesecret", + TokenManager = tokenManager, + }; + + return client; + } + private T CallService<T>(Func<DataApiClient, T> predicate) { DataApiClient client = new DataApiClient(); - //var serviceEndpoint = new MessageReceivingEndpoint(client.Endpoint.Address.Uri, HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest); + ////var serviceEndpoint = new MessageReceivingEndpoint(client.Endpoint.Address.Uri, HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.PostRequest); if (Authorization == null) { throw new InvalidOperationException("No access token!"); } @@ -93,18 +108,5 @@ return predicate(client); } } - - private static WebServerClient CreateClient() { - var authServerDescription = new AuthorizationServerDescription { - TokenEndpoint = new Uri("http://localhost:65169/OAuth2.ashx/token"), - AuthorizationEndpoint = new Uri("http://localhost:65169/OAuth2.ashx/auth"), - }; - var client = new WebServerClient(authServerDescription) { - ClientIdentifier = "sampleconsumer", - ClientSecret = "samplesecret", - TokenManager = TokenManager, - }; - return client; - } } }
\ No newline at end of file diff --git a/samples/OAuthConsumer/SignInWithTwitter.aspx.cs b/samples/OAuthConsumer/SignInWithTwitter.aspx.cs index 5bc46ef..e104f3a 100644 --- a/samples/OAuthConsumer/SignInWithTwitter.aspx.cs +++ b/samples/OAuthConsumer/SignInWithTwitter.aspx.cs @@ -33,7 +33,7 @@ } protected void signInButton_Click(object sender, ImageClickEventArgs e) { - TwitterConsumer.StartSignInWithTwitter(forceLoginCheckbox.Checked).Send(); + TwitterConsumer.StartSignInWithTwitter(this.forceLoginCheckbox.Checked).Send(); } } }
\ No newline at end of file diff --git a/samples/OAuthConsumerWpf/Authorize2.xaml.cs b/samples/OAuthConsumerWpf/Authorize2.xaml.cs index 98709e1..91435da 100644 --- a/samples/OAuthConsumerWpf/Authorize2.xaml.cs +++ b/samples/OAuthConsumerWpf/Authorize2.xaml.cs @@ -1,20 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Shapes; -using DotNetOpenAuth.OAuthWrap; -using System.Diagnostics.Contracts; -using System.Windows.Navigation; - -namespace DotNetOpenAuth.Samples.OAuthConsumerWpf { +namespace DotNetOpenAuth.Samples.OAuthConsumerWpf { + using System; + using System.Collections.Generic; + using System.Diagnostics.Contracts; + using System.Linq; + using System.Text; + using System.Windows; + using System.Windows.Controls; + using System.Windows.Data; + using System.Windows.Documents; + using System.Windows.Input; + using System.Windows.Media; + using System.Windows.Media.Imaging; + using System.Windows.Navigation; + using System.Windows.Shapes; + using DotNetOpenAuth.OAuthWrap; + /// <summary> /// Interaction logic for Authorize2.xaml /// </summary> @@ -34,7 +34,7 @@ namespace DotNetOpenAuth.Samples.OAuthConsumerWpf { public IAuthorizationState Authorization { get; set; } private void webBrowser_Navigating(object sender, System.Windows.Forms.WebBrowserNavigatingEventArgs e) { - locationChanged(e.Url); + this.locationChanged(e.Url); } private void locationChanged(Uri location) { @@ -46,12 +46,11 @@ namespace DotNetOpenAuth.Samples.OAuthConsumerWpf { } private void webBrowser_Navigated(object sender, System.Windows.Forms.WebBrowserNavigatedEventArgs e) { - locationChanged(e.Url); + this.locationChanged(e.Url); } private void webBrowser_LocationChanged(object sender, EventArgs e) { - locationChanged(webBrowser.Url); + this.locationChanged(webBrowser.Url); } - } } diff --git a/samples/OAuthConsumerWpf/MainWindow.xaml.cs b/samples/OAuthConsumerWpf/MainWindow.xaml.cs index 1586e0d..749a626 100644 --- a/samples/OAuthConsumerWpf/MainWindow.xaml.cs +++ b/samples/OAuthConsumerWpf/MainWindow.xaml.cs @@ -224,7 +224,7 @@ var address = new Uri(wrapResourceUrlBox.Text); address = new Uri(wrapResourceUrlBox.Text + (string.IsNullOrEmpty(address.Query) ? "?" : string.Empty) + "access_token=" + Uri.EscapeDataString(authorizePopup.Authorization.AccessToken)); var request = (HttpWebRequest)WebRequest.Create(address); - + // This method tacks on the Authorization header client.AuthorizeRequest(request, authorizePopup.Authorization); @@ -240,7 +240,6 @@ } catch (DotNetOpenAuth.Messaging.ProtocolException ex) { MessageBox.Show(this, ex.Message); } - } } } diff --git a/samples/OAuthServiceProvider/Code/Global.cs b/samples/OAuthServiceProvider/Code/Global.cs index a43cf57..fd7d475 100644 --- a/samples/OAuthServiceProvider/Code/Global.cs +++ b/samples/OAuthServiceProvider/Code/Global.cs @@ -22,6 +22,8 @@ /// </summary> public static log4net.ILog Logger = log4net.LogManager.GetLogger("DotNetOpenAuth.OAuthServiceProvider"); + public static WebServerAuthorizationServer AuthorizationServer = new WebServerAuthorizationServer(new OAuth2AuthorizationServer()); + /// <summary> /// Gets the transaction-protected database connection for the current request. /// </summary> @@ -58,8 +60,6 @@ set { HttpContext.Current.Session["authrequest"] = value; } } - public static WebServerAuthorizationServer AuthorizationServer = new WebServerAuthorizationServer(new OAuth2AuthorizationServer()); - private static DataClassesDataContext dataContextSimple { get { if (HttpContext.Current != null) { diff --git a/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs b/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs index 75778f5..e281b07 100644 --- a/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs +++ b/samples/OAuthServiceProvider/Code/OAuth2AuthorizationServer.cs @@ -11,9 +11,9 @@ using DotNetOpenAuth.OAuthWrap.ChannelElements; internal class OAuth2AuthorizationServer : IAuthorizationServer { - private static readonly byte[] secret; + internal static readonly RSAParameters AsymmetricKey; - internal static readonly RSAParameters asymmetricKey; + private static readonly byte[] secret; private readonly INonceStore nonceStore = new DatabaseNonceStore(); @@ -23,7 +23,7 @@ secret = new byte[16]; crypto.GetBytes(secret); - asymmetricKey = new RSACryptoServiceProvider().ExportParameters(true); + AsymmetricKey = new RSACryptoServiceProvider().ExportParameters(true); } #region Implementation of IAuthorizationServer @@ -37,7 +37,7 @@ } public RSAParameters AccessTokenSigningPrivateKey { - get { return asymmetricKey; } + get { return AsymmetricKey; } } public IConsumerDescription GetClient(string clientIdentifier) { diff --git a/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs b/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs index a48288a..92038bd 100644 --- a/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs +++ b/samples/OAuthServiceProvider/Code/OAuthAuthorizationManager.cs @@ -7,10 +7,9 @@ using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Security; - using DotNetOpenAuth; using DotNetOpenAuth.OAuth; - using DotNetOpenAuth.OAuthWrap; using DotNetOpenAuth.OAuth.ChannelElements; + using DotNetOpenAuth.OAuthWrap; /// <summary> /// A WCF extension to authenticate incoming messages using OAuth. @@ -28,7 +27,7 @@ Uri requestUri = operationContext.RequestContext.RequestMessage.Properties["OriginalHttpRequestUri"] as Uri; try { - var principal = VerifyOAuth2(httpDetails, requestUri); + var principal = this.VerifyOAuth2(httpDetails, requestUri); if (principal != null) { var policy = new OAuthPrincipalAuthorizationPolicy(principal); var policies = new List<IAuthorizationPolicy> { @@ -77,8 +76,8 @@ // we use the same public/private key. var resourceServer = new ResourceServer( new StandardAccessTokenAnalyzer( - OAuth2AuthorizationServer.asymmetricKey, - OAuth2AuthorizationServer.asymmetricKey)); + OAuth2AuthorizationServer.AsymmetricKey, + OAuth2AuthorizationServer.AsymmetricKey)); string username, scope; var error = resourceServer.VerifyAccess(new DotNetOpenAuth.Messaging.HttpRequestInfo(httpDetails, requestUri), out username, out scope); diff --git a/samples/OAuthServiceProvider/Members/Authorize.aspx.cs b/samples/OAuthServiceProvider/Members/Authorize.aspx.cs index 74b44c5..faa2147 100644 --- a/samples/OAuthServiceProvider/Members/Authorize.aspx.cs +++ b/samples/OAuthServiceProvider/Members/Authorize.aspx.cs @@ -29,8 +29,8 @@ } else { ITokenContainingMessage pendingToken = Global.PendingOAuthAuthorization; var token = Global.DataContext.OAuthTokens.Single(t => t.Token == pendingToken.Token); - desiredAccessLabel.Text = token.Scope; - consumerLabel.Text = Global.TokenManager.GetConsumerForToken(token.Token).ConsumerKey; + this.desiredAccessLabel.Text = token.Scope; + this.consumerLabel.Text = Global.TokenManager.GetConsumerForToken(token.Token).ConsumerKey; // Generate an unpredictable secret that goes to the user agent and must come back // with authorization to guarantee the user interacted with this page rather than @@ -52,7 +52,7 @@ this.AuthorizationSecret = null; // clear one time use secret var pending = Global.PendingOAuthAuthorization; Global.AuthorizePendingRequestToken(); - multiView.ActiveViewIndex = 1; + this.multiView.ActiveViewIndex = 1; ServiceProvider sp = new ServiceProvider(Constants.SelfDescription, Global.TokenManager); var response = sp.PrepareAuthorizationResponse(pending); diff --git a/samples/OAuthServiceProvider/Members/Authorize2.aspx.cs b/samples/OAuthServiceProvider/Members/Authorize2.aspx.cs index 9e33573..0c14bfd 100644 --- a/samples/OAuthServiceProvider/Members/Authorize2.aspx.cs +++ b/samples/OAuthServiceProvider/Members/Authorize2.aspx.cs @@ -2,10 +2,10 @@ using System; using System.Collections.Generic; using System.Linq; + using System.Security.Cryptography; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; - using System.Security.Cryptography; using Code; public partial class Authorize2 : System.Web.UI.Page { diff --git a/samples/OAuthServiceProvider/OAuth2.ashx.cs b/samples/OAuthServiceProvider/OAuth2.ashx.cs index cd76254..272502c 100644 --- a/samples/OAuthServiceProvider/OAuth2.ashx.cs +++ b/samples/OAuthServiceProvider/OAuth2.ashx.cs @@ -9,11 +9,18 @@ using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuthWrap; - /// <summary> - /// Summary description for OAuth2 - /// </summary> public class OAuth2 : IHttpHandler, IRequiresSessionState { /// <summary> + /// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance. + /// </summary> + /// <value>Always <c>true</c></value> + /// <returns>true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false. + /// </returns> + public bool IsReusable { + get { return true; } + } + + /// <summary> /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface. /// </summary> /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> @@ -42,15 +49,5 @@ break; } } - - /// <summary> - /// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance. - /// </summary> - /// <value>Always <c>true</c></value> - /// <returns>true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false. - /// </returns> - public bool IsReusable { - get { return true; } - } } }
\ No newline at end of file diff --git a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs index 1f95a46..8c2b5bd 100644 --- a/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs +++ b/src/DotNetOpenAuth.Test/OpenId/AssociationHandshakeTests.cs @@ -80,7 +80,7 @@ namespace DotNetOpenAuth.Test.OpenId { op.SecuritySettings.MaximumHashBitLength = 160; // Force OP to reject HMAC-SHA256 // Receive initial request for an HMAC-SHA256 association. - AutoResponsiveRequest req = (AutoResponsiveRequest) op.GetRequest(); + AutoResponsiveRequest req = (AutoResponsiveRequest)op.GetRequest(); AutoResponsiveRequest_Accessor reqAccessor = AutoResponsiveRequest_Accessor.AttachShadow(req); AssociateRequest associateRequest = (AssociateRequest)reqAccessor.RequestMessage; Assert.AreEqual(protocol.Args.SignatureAlgorithm.HMAC_SHA256, associateRequest.AssociationType); diff --git a/src/DotNetOpenAuth/DotNetOpenAuth.csproj b/src/DotNetOpenAuth/DotNetOpenAuth.csproj index 543a725..650e01a 100644 --- a/src/DotNetOpenAuth/DotNetOpenAuth.csproj +++ b/src/DotNetOpenAuth/DotNetOpenAuth.csproj @@ -316,6 +316,7 @@ http://opensource.org/licenses/ms-pl.html <Compile Include="OAuthWrap\ChannelElements\AuthServerBindingElementBase.cs" /> <Compile Include="OAuthWrap\ChannelElements\IAccessTokenRequest.cs" /> <Compile Include="OAuthWrap\ChannelElements\IAuthorizationDescription.cs" /> + <Compile Include="OAuthWrap\ChannelElements\ITokenCarryingRequest.cs" /> <Compile Include="OAuthWrap\ChannelElements\OAuthWrapResourceServerChannel.cs" /> <Compile Include="Messaging\StandardMessageFactoryChannel.cs" /> <Compile Include="OAuthWrap\ChannelElements\RefreshToken.cs" /> diff --git a/src/DotNetOpenAuth/Messaging/Channel.cs b/src/DotNetOpenAuth/Messaging/Channel.cs index 1e632ea..28babee 100644 --- a/src/DotNetOpenAuth/Messaging/Channel.cs +++ b/src/DotNetOpenAuth/Messaging/Channel.cs @@ -244,6 +244,12 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Gets or sets the XML dictionary reader quotas. + /// </summary> + /// <value>The XML dictionary reader quotas.</value> + protected virtual XmlDictionaryReaderQuotas XmlDictionaryReaderQuotas { get; set; } + + /// <summary> /// Sends an indirect message (either a request or response) /// or direct message response for transmission to a remote party /// and ends execution on the current page or handler. @@ -699,6 +705,7 @@ namespace DotNetOpenAuth.Messaging { /// </summary> /// <param name="message">The message to forward.</param> /// <param name="fields">The pre-serialized fields from the message.</param> + /// <param name="payloadInFragment">if set to <c>true</c> the redirect will contain the message payload in the #fragment portion of the URL rather than the ?querystring.</param> /// <returns>The encoded HTTP response.</returns> [Pure] protected virtual OutgoingWebResponse Create301RedirectResponse(IDirectedProtocolMessage message, IDictionary<string, string> fields, bool payloadInFragment = false) { @@ -801,6 +808,11 @@ namespace DotNetOpenAuth.Messaging { /// </remarks> protected abstract OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response); + /// <summary> + /// Serializes the given message as a JSON string. + /// </summary> + /// <param name="message">The message to serialize.</param> + /// <returns>A JSON string.</returns> protected virtual string SerializeAsJson(IMessage message) { Contract.Requires<ArgumentNullException>(message != null, "message"); @@ -815,8 +827,11 @@ namespace DotNetOpenAuth.Messaging { return json; } - protected virtual XmlDictionaryReaderQuotas XmlDictionaryReaderQuotas { get; set; } - + /// <summary> + /// Deserializes from flat data from a JSON object. + /// </summary> + /// <param name="json">A JSON string.</param> + /// <returns>The simple "key":"value" pairs from a JSON-encoded object.</returns> protected virtual IDictionary<string, string> DeserializeFromJson(string json) { Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(json)); diff --git a/src/DotNetOpenAuth/Messaging/IHttpIndirectResponse.cs b/src/DotNetOpenAuth/Messaging/IHttpIndirectResponse.cs index f3dde51..7d0fe0c 100644 --- a/src/DotNetOpenAuth/Messaging/IHttpIndirectResponse.cs +++ b/src/DotNetOpenAuth/Messaging/IHttpIndirectResponse.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------- -// <copyright file="IHttpDirectResponse.cs" company="Andrew Arnott"> +// <copyright file="IHttpIndirectResponse.cs" company="Andrew Arnott"> // Copyright (c) Andrew Arnott. All rights reserved. // </copyright> //----------------------------------------------------------------------- @@ -13,6 +13,10 @@ namespace DotNetOpenAuth.Messaging { /// HTTP transport specific properties. /// </summary> public interface IHttpIndirectResponse { + /// <summary> + /// Gets a value indicating whether the payload for the message should be included + /// in the redirect fragment instead of the query string or POST entity. + /// </summary> bool Include301RedirectPayloadInFragment { get; } } } diff --git a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs index 0d560c1..950791f 100644 --- a/src/DotNetOpenAuth/Messaging/MessageSerializer.cs +++ b/src/DotNetOpenAuth/Messaging/MessageSerializer.cs @@ -53,6 +53,11 @@ namespace DotNetOpenAuth.Messaging { return new MessageSerializer(messageType); } + /// <summary> + /// Reads JSON as a flat dictionary into a message. + /// </summary> + /// <param name="messageDictionary">The message dictionary to fill with the JSON-deserialized data.</param> + /// <param name="reader">The JSON reader.</param> internal static void DeserializeJsonAsFlatDictionary(IDictionary<string, string> messageDictionary, XmlDictionaryReader reader) { Contract.Requires<ArgumentNullException>(messageDictionary != null, "messageDictionary"); Contract.Requires<ArgumentNullException>(reader != null, "reader"); @@ -107,6 +112,7 @@ namespace DotNetOpenAuth.Messaging { /// Reads the data from a message instance and writes a XML/JSON encoding of it. /// </summary> /// <param name="messageDictionary">The message to be serialized.</param> + /// <param name="writer">The writer to use for the serialized form.</param> /// <remarks> /// Use <see cref="System.Runtime.Serialization.Json.JsonReaderWriterFactory.CreateJsonWriter"/> /// to create the <see cref="XmlDictionaryWriter"/> instance capable of emitting JSON. @@ -197,19 +203,19 @@ namespace DotNetOpenAuth.Messaging { /// <summary> /// Determines whether the specified type is numeric. /// </summary> - /// <param name="type">The type.</param> + /// <param name="type">The type to test.</param> /// <returns> /// <c>true</c> if the specified type is numeric; otherwise, <c>false</c>. /// </returns> private static bool IsNumeric(Type type) { - return type.IsAssignableFrom(typeof(Double)) - || type.IsAssignableFrom(typeof(Single)) - || type.IsAssignableFrom(typeof(Int16)) - || type.IsAssignableFrom(typeof(Int32)) - || type.IsAssignableFrom(typeof(Int64)) - || type.IsAssignableFrom(typeof(UInt16)) - || type.IsAssignableFrom(typeof(UInt32)) - || type.IsAssignableFrom(typeof(UInt64)); + return type.IsAssignableFrom(typeof(double)) + || type.IsAssignableFrom(typeof(float)) + || type.IsAssignableFrom(typeof(short)) + || type.IsAssignableFrom(typeof(int)) + || type.IsAssignableFrom(typeof(long)) + || type.IsAssignableFrom(typeof(ushort)) + || type.IsAssignableFrom(typeof(uint)) + || type.IsAssignableFrom(typeof(ulong)); } #if CONTRACTS_FULL diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index b6a083f..cbd7f51 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -522,6 +522,12 @@ namespace DotNetOpenAuth.Messaging { return Encoding.UTF8.GetString(plainText); } + /// <summary> + /// Performs asymmetric encryption of a given buffer. + /// </summary> + /// <param name="crypto">The asymmetric encryption provider to use for encryption.</param> + /// <param name="buffer">The buffer to encrypt.</param> + /// <returns>The encrypted data.</returns> internal static byte[] EncryptWithRandomSymmetricKey(this RSACryptoServiceProvider crypto, byte[] buffer) { Contract.Requires<ArgumentNullException>(crypto != null, "crypto"); Contract.Requires<ArgumentNullException>(buffer != null, "buffer"); @@ -549,6 +555,12 @@ namespace DotNetOpenAuth.Messaging { return encryptedStream.ToArray(); } + /// <summary> + /// Performs asymmetric decryption of a given buffer. + /// </summary> + /// <param name="crypto">The asymmetric encryption provider to use for decryption.</param> + /// <param name="buffer">The buffer to decrypt.</param> + /// <returns>The decrypted data.</returns> internal static byte[] DecryptWithRandomSymmetricKey(this RSACryptoServiceProvider crypto, byte[] buffer) { Contract.Requires<ArgumentNullException>(crypto != null, "crypto"); Contract.Requires<ArgumentNullException>(buffer != null, "buffer"); @@ -967,6 +979,20 @@ namespace DotNetOpenAuth.Messaging { } } + /// <summary> + /// Adds a set of name-value pairs to the end of a given URL + /// as part of the fragment piece. Prefixes a # or & before + /// first element as necessary. + /// </summary> + /// <param name="builder">The UriBuilder to add arguments to.</param> + /// <param name="args"> + /// The arguments to add to the query. + /// If null, <paramref name="builder"/> is not changed. + /// </param> + /// <remarks> + /// If the parameters to add match names of parameters that already are defined + /// in the fragment, the existing ones are <i>not</i> replaced. + /// </remarks> internal static void AppendFragmentArgs(this UriBuilder builder, IEnumerable<KeyValuePair<string, string>> args) { Contract.Requires<ArgumentNullException>(builder != null); diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs index 808d5b8..53a5cdd 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessageDescription.cs @@ -187,6 +187,12 @@ namespace DotNetOpenAuth.Messaging.Reflection { return true; } + /// <summary> + /// Checks that a bunch of message part values meet the constant value requirements of this message description. + /// </summary> + /// <param name="partValues">The part values.</param> + /// <param name="throwOnFailure">if set to <c>true</c>, this method will throw on failure.</param> + /// <returns>A value indicating whether all the requirements are met.</returns> private bool CheckMessagePartsConstantValues(IDictionary<string, string> partValues, bool throwOnFailure) { Contract.Requires<ArgumentNullException>(partValues != null); diff --git a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs index a530df5..6aa20e5 100644 --- a/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth/Messaging/Reflection/MessagePart.cs @@ -210,6 +210,9 @@ namespace DotNetOpenAuth.Messaging.Reflection { } } + /// <summary> + /// Gets the type of the declared member. + /// </summary> internal Type MemberDeclaredType { get { return this.memberDeclaredType; } } diff --git a/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs b/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs index 2eacf93..d5aeba4 100644 --- a/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs +++ b/src/DotNetOpenAuth/Messaging/StandardMessageFactory.cs @@ -144,7 +144,7 @@ namespace DotNetOpenAuth.Messaging { var matches = this.requestMessageTypes.Keys .Where(message => message.CheckMessagePartsPassBasicValidation(fields)) - .OrderByDescending(message => CountInCommon(message.Mapping.Keys, fields.Keys)) + .OrderByDescending(message => this.CountInCommon(message.Mapping.Keys, fields.Keys)) .ThenByDescending(message => message.Mapping.Count) .CacheGeneratedResults(); var match = matches.FirstOrDefault(); @@ -180,8 +180,8 @@ namespace DotNetOpenAuth.Messaging { where message.CheckMessagePartsPassBasicValidation(fields) let ctors = this.FindMatchingResponseConstructors(message, request.GetType()) where ctors.Any() - orderby GetDerivationDistance(ctors.First().GetParameters()[0].ParameterType, request.GetType()) , - CountInCommon(message.Mapping.Keys, fields.Keys) descending , + orderby GetDerivationDistance(ctors.First().GetParameters()[0].ParameterType, request.GetType()), + this.CountInCommon(message.Mapping.Keys, fields.Keys) descending, message.Mapping.Count descending select message).CacheGeneratedResults(); var match = matches.FirstOrDefault(); @@ -240,6 +240,12 @@ namespace DotNetOpenAuth.Messaging { return (IDirectResponseProtocolMessage)ctor.Invoke(new object[] { request }); } + /// <summary> + /// Gets the hierarchical distance between a type and a type it derives from or implements. + /// </summary> + /// <param name="assignableType">The base type or interface.</param> + /// <param name="derivedType">The concrete class that implements the <paramref name="assignableType"/>.</param> + /// <returns>The distance between the two types. 0 if the types are equivalent, 1 if the type immediately derives from or implements the base type, or progressively higher integers.</returns> private static int GetDerivationDistance(Type assignableType, Type derivedType) { Contract.Requires<ArgumentNullException>(assignableType != null, "assignableType"); Contract.Requires<ArgumentNullException>(derivedType != null, "derivedType"); @@ -261,6 +267,12 @@ namespace DotNetOpenAuth.Messaging { return steps; } + /// <summary> + /// Finds constructors for response messages that take a given request message type. + /// </summary> + /// <param name="messageDescription">The message description.</param> + /// <param name="requestType">Type of the request message.</param> + /// <returns>A sequence of matching constructors.</returns> private IEnumerable<ConstructorInfo> FindMatchingResponseConstructors(MessageDescription messageDescription, Type requestType) { Contract.Requires<ArgumentNullException>(messageDescription != null); Contract.Requires<ArgumentNullException>(requestType != null); diff --git a/src/DotNetOpenAuth/Messaging/StandardMessageFactoryChannel.cs b/src/DotNetOpenAuth/Messaging/StandardMessageFactoryChannel.cs index ab31d97..147b420 100644 --- a/src/DotNetOpenAuth/Messaging/StandardMessageFactoryChannel.cs +++ b/src/DotNetOpenAuth/Messaging/StandardMessageFactoryChannel.cs @@ -12,14 +12,26 @@ namespace DotNetOpenAuth.Messaging { using System.Text; using Reflection; + /// <summary> + /// A channel that uses the standard message factory. + /// </summary> public abstract class StandardMessageFactoryChannel : Channel { + /// <summary> + /// The message types receivable by this channel. + /// </summary> private readonly ICollection<Type> messageTypes; + + /// <summary> + /// The protocol versions supported by this channel. + /// </summary> private readonly ICollection<Version> versions; /// <summary> /// Initializes a new instance of the <see cref="StandardMessageFactoryChannel"/> class. /// </summary> - /// <param name="bindingElements">The binding elements.</param> + /// <param name="messageTypes">The message types that might be encountered.</param> + /// <param name="versions">All the possible message versions that might be encountered.</param> + /// <param name="bindingElements">The binding elements to apply to the channel.</param> protected StandardMessageFactoryChannel(ICollection<Type> messageTypes, ICollection<Version> versions, params IChannelBindingElement[] bindingElements) : base(new StandardMessageFactory(), bindingElements) { Contract.Requires<ArgumentNullException>(messageTypes != null, "messageTypes"); @@ -34,21 +46,6 @@ namespace DotNetOpenAuth.Messaging { /// Gets or sets a tool that can figure out what kind of message is being received /// so it can be deserialized. /// </summary> - protected override IMessageFactory MessageFactory { - get { - return (StandardMessageFactory)base.MessageFactory; - } - - set { - StandardMessageFactory newValue = (StandardMessageFactory)value; - base.MessageFactory = newValue; - } - } - - /// <summary> - /// Gets or sets a tool that can figure out what kind of message is being received - /// so it can be deserialized. - /// </summary> internal StandardMessageFactory StandardMessageFactory { get { return (Messaging.StandardMessageFactory)this.MessageFactory; } set { this.MessageFactory = value; } @@ -72,6 +69,28 @@ namespace DotNetOpenAuth.Messaging { } } + /// <summary> + /// Gets or sets a tool that can figure out what kind of message is being received + /// so it can be deserialized. + /// </summary> + protected override IMessageFactory MessageFactory { + get { + return (StandardMessageFactory)base.MessageFactory; + } + + set { + StandardMessageFactory newValue = (StandardMessageFactory)value; + base.MessageFactory = newValue; + } + } + + /// <summary> + /// Generates all the message descriptions for a given set of message types and versions. + /// </summary> + /// <param name="messageTypes">The message types.</param> + /// <param name="versions">The message versions.</param> + /// <param name="descriptionsCache">The cache to use when obtaining the message descriptions.</param> + /// <returns>The generated/retrieved message descriptions.</returns> private static IEnumerable<MessageDescription> GetMessageDescriptions(ICollection<Type> messageTypes, ICollection<Version> versions, MessageDescriptionCollection descriptionsCache) { Contract.Requires<ArgumentNullException>(messageTypes != null, "messageTypes"); diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs index 48fe813..83954bf 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/OAuthPrincipal.cs @@ -28,6 +28,15 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { /// <summary> /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class. /// </summary> + /// <param name="username">The username.</param> + /// <param name="roles">The roles this user belongs to.</param> + public OAuthPrincipal(string username, string[] roles) + : this(new OAuthIdentity(username), roles) { + } + + /// <summary> + /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class. + /// </summary> /// <param name="token">The access token.</param> internal OAuthPrincipal(IServiceProviderAccessToken token) : this(token.Username, token.Roles) { @@ -47,15 +56,6 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { } /// <summary> - /// Initializes a new instance of the <see cref="OAuthPrincipal"/> class. - /// </summary> - /// <param name="username">The username.</param> - /// <param name="roles">The roles this user belongs to.</param> - public OAuthPrincipal(string username, string[] roles) - : this(new OAuthIdentity(username), roles) { - } - - /// <summary> /// Gets the access token used to create this principal. /// </summary> /// <value>A non-empty string.</value> diff --git a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs index 7b369c3..4ff52fd 100644 --- a/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs +++ b/src/DotNetOpenAuth/OAuth/ChannelElements/SigningBindingElementBaseContract.cs @@ -15,8 +15,7 @@ namespace DotNetOpenAuth.OAuth.ChannelElements { [ContractClassFor(typeof(SigningBindingElementBase))] internal abstract class SigningBindingElementBaseContract : SigningBindingElementBase { /// <summary> - /// Prevents a default instance of the SigningBindingElementBaseContract - /// class from being created. + /// Prevents a default instance of the SigningBindingElementBaseContract class from being created. /// </summary> private SigningBindingElementBaseContract() : base(string.Empty) { diff --git a/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs index 84a0b9a..db6aa2d 100644 --- a/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs +++ b/src/DotNetOpenAuth/OAuthWrap/AuthorizationServerBase.cs @@ -16,22 +16,48 @@ namespace DotNetOpenAuth.OAuthWrap { using DotNetOpenAuth.OAuthWrap.Messages; using OAuth.ChannelElements; + /// <summary> + /// A base class for authorization server facade classes. + /// </summary> public abstract class AuthorizationServerBase { + /// <summary> + /// Initializes a new instance of the <see cref="AuthorizationServerBase"/> class. + /// </summary> + /// <param name="authorizationServer">The authorization server.</param> protected AuthorizationServerBase(IAuthorizationServer authorizationServer) { Contract.Requires<ArgumentNullException>(authorizationServer != null, "authorizationServer"); this.OAuthChannel = new OAuthWrapAuthorizationServerChannel(authorizationServer); } + /// <summary> + /// Gets the channel. + /// </summary> + /// <value>The channel.</value> public Channel Channel { get { return this.OAuthChannel; } } + /// <summary> + /// Gets the authorization server. + /// </summary> + /// <value>The authorization server.</value> public IAuthorizationServer AuthorizationServer { get { return this.OAuthChannel.AuthorizationServer; } } + /// <summary> + /// Gets the channel. + /// </summary> internal OAuthWrapAuthorizationServerChannel OAuthChannel { get; private set; } + /// <summary> + /// Prepares the response to an access token request. + /// </summary> + /// <param name="request">The request for an access token.</param> + /// <param name="accessTokenEncryptingPublicKey">The public key to encrypt the access token to, such that the resource server will be able to decrypt it.</param> + /// <param name="accessTokenLifetime">The access token's lifetime.</param> + /// <param name="includeRefreshToken">If set to <c>true</c>, the response will include a long-lived refresh token.</param> + /// <returns>The response message to send to the client.</returns> public virtual IDirectResponseProtocolMessage PrepareAccessTokenResponse(IAccessTokenRequest request, RSAParameters accessTokenEncryptingPublicKey, TimeSpan? accessTokenLifetime = null, bool includeRefreshToken = true) { Contract.Requires<ArgumentNullException>(request != null, "request"); diff --git a/src/DotNetOpenAuth/OAuthWrap/AuthorizationState.cs b/src/DotNetOpenAuth/OAuthWrap/AuthorizationState.cs index ac4112f..6ca7b4f 100644 --- a/src/DotNetOpenAuth/OAuthWrap/AuthorizationState.cs +++ b/src/DotNetOpenAuth/OAuthWrap/AuthorizationState.cs @@ -17,20 +17,52 @@ namespace DotNetOpenAuth.OAuthWrap { public AuthorizationState() { } + /// <summary> + /// Gets or sets the callback URL used to obtain authorization. + /// </summary> + /// <value>The callback URL.</value> public Uri Callback { get; set; } + /// <summary> + /// Gets or sets the long-lived token used to renew the short-lived <see cref="AccessToken"/>. + /// </summary> + /// <value>The refresh token.</value> public string RefreshToken { get; set; } + /// <summary> + /// Gets or sets the access token. + /// </summary> + /// <value>The access token.</value> public string AccessToken { get; set; } + /// <summary> + /// Gets or sets the access token secret. + /// </summary> + /// <value>The access token secret.</value> public string AccessTokenSecret { get; set; } + /// <summary> + /// Gets or sets the type of the access token secret. + /// </summary> + /// <value>The type of the access token secret.</value> public string AccessTokenSecretType { get; set; } + /// <summary> + /// Gets or sets the access token UTC expiration date. + /// </summary> + /// <value></value> public DateTime? AccessTokenExpirationUtc { get; set; } + /// <summary> + /// Gets or sets the access token issue date UTC. + /// </summary> + /// <value>The access token issue date UTC.</value> public DateTime? AccessTokenIssueDateUtc { get; set; } + /// <summary> + /// Gets or sets the scope the token is (to be) authorized for. + /// </summary> + /// <value>The scope.</value> public string Scope { get; set; } /// <summary> @@ -41,10 +73,23 @@ namespace DotNetOpenAuth.OAuthWrap { /// </value> public bool IsDeleted { get; set; } + /// <summary> + /// Deletes this authorization, including access token and refresh token where applicable. + /// </summary> + /// <remarks> + /// This method is invoked when an authorization attempt fails, is rejected, is revoked, or + /// expires and cannot be renewed. + /// </remarks> public virtual void Delete() { this.IsDeleted = true; } + /// <summary> + /// Saves any changes made to this authorization object's properties. + /// </summary> + /// <remarks> + /// This method is invoked after DotNetOpenAuth changes any property. + /// </remarks> public virtual void SaveChanges() { } } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs index a461a86..1943021 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessRequestBindingElement.cs @@ -1,4 +1,10 @@ -namespace DotNetOpenAuth.OAuthWrap.ChannelElements { +//----------------------------------------------------------------------- +// <copyright file="AccessRequestBindingElement.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; @@ -21,10 +27,29 @@ internal AccessRequestBindingElement() { } + /// <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 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) { var tokenRequest = message as ITokenCarryingRequest; if (tokenRequest != null) { @@ -37,6 +62,23 @@ 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 tokenRequest = message as ITokenCarryingRequest; if (tokenRequest != null) { diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs index 59ebd6e..0fba167 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AccessToken.cs @@ -14,15 +14,17 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; + /// <summary> + /// A short-lived token that accompanies HTTP requests to protected data to authorize the request. + /// </summary> internal class AccessToken : AuthorizationDataBag { /// <summary> /// Initializes a new instance of the <see cref="AccessToken"/> class. /// </summary> - /// <param name="channel">The channel.</param> - private AccessToken(RSAParameters signingKey, RSAParameters encryptingKey) - : base(signingKey, encryptingKey) { - } - + /// <param name="signingKey">The signing key.</param> + /// <param name="encryptingKey">The encrypting key.</param> + /// <param name="authorization">The authorization to be described by the access token.</param> + /// <param name="lifetime">The lifetime of the access token.</param> internal AccessToken(RSAParameters signingKey, RSAParameters encryptingKey, IAuthorizationDescription authorization, TimeSpan? lifetime) : this(signingKey, encryptingKey) { Contract.Requires<ArgumentNullException>(authorization != null, "authorization"); @@ -34,9 +36,30 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { this.Lifetime = lifetime; } + /// <summary> + /// Initializes a new instance of the <see cref="AccessToken"/> class. + /// </summary> + /// <param name="signingKey">The signing key.</param> + /// <param name="encryptingKey">The encrypting key.</param> + private AccessToken(RSAParameters signingKey, RSAParameters encryptingKey) + : base(signingKey, encryptingKey) { + } + + /// <summary> + /// Gets or sets the lifetime of the access token. + /// </summary> + /// <value>The lifetime.</value> [MessagePart] internal TimeSpan? Lifetime { get; set; } + /// <summary> + /// Deserializes an access token. + /// </summary> + /// <param name="signingKey">The signing public key.</param> + /// <param name="encryptingKey">The encrypting private key.</param> + /// <param name="value">The access token.</param> + /// <param name="containingMessage">The message containing this token.</param> + /// <returns>The access token.</returns> internal static AccessToken Decode(RSAParameters signingKey, RSAParameters encryptingKey, string value, IProtocolMessage containingMessage) { Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value)); Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage"); @@ -47,6 +70,11 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { return self; } + /// <summary> + /// Populates this instance with data from a given string. + /// </summary> + /// <param name="value">The value to deserialize from.</param> + /// <param name="containingMessage">The message that contained this token.</param> protected override void Decode(string value, IProtocolMessage containingMessage) { base.Decode(value, containingMessage); diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerAllFlowsBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerAllFlowsBindingElement.cs index 16f1b87..3c4d3e2 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerAllFlowsBindingElement.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerAllFlowsBindingElement.cs @@ -13,6 +13,10 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { using DotNetOpenAuth.OAuthWrap.Messages; using Messaging; + /// <summary> + /// A binding element that should be applied for authorization server channels regardless of which flows + /// are supported. + /// </summary> internal class AuthServerAllFlowsBindingElement : AuthServerBindingElementBase { /// <summary> /// Initializes a new instance of the <see cref="AuthServerAllFlowsBindingElement"/> class. diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerBindingElementBase.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerBindingElementBase.cs index c84d37d..dfc03f8 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerBindingElementBase.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthServerBindingElementBase.cs @@ -11,6 +11,9 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { using System.Text; using Messaging; + /// <summary> + /// The base class for any authorization server channel binding element. + /// </summary> internal abstract class AuthServerBindingElementBase : IChannelBindingElement { /// <summary> /// Initializes a new instance of the <see cref="AuthServerBindingElementBase"/> class. @@ -27,6 +30,20 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// </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 abstract MessageProtections Protection { get; } + + /// <summary> + /// Gets the channel that this binding element belongs to. + /// </summary> + /// <remarks> + /// This property is set by the channel when it is first constructed. + /// </remarks> protected OAuthWrapAuthorizationServerChannel OAuthChannel { get { return (OAuthWrapAuthorizationServerChannel)this.Channel; } } @@ -40,15 +57,6 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } /// <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> diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs index 7be544a..ef7e390 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/AuthorizationDataBag.cs @@ -14,25 +14,59 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; + /// <summary> + /// A data bag that stores authorization data. + /// </summary> internal abstract class AuthorizationDataBag : DataBag, IAuthorizationDescription { + /// <summary> + /// Initializes a new instance of the <see cref="AuthorizationDataBag"/> class. + /// </summary> + /// <param name="secret">The symmetric secret to use for signing and encrypting.</param> + /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param> + /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param> + /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected AuthorizationDataBag(byte[] secret, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : base(secret, signed, encrypted, compressed, maximumAge, decodeOnceOnly) { } + /// <summary> + /// Initializes a new instance of the <see cref="AuthorizationDataBag"/> class. + /// </summary> + /// <param name="signingKey">The asymmetric private key to use for signing the token.</param> + /// <param name="encryptingKey">The asymmetric public key to use for encrypting the token.</param> + /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected AuthorizationDataBag(RSAParameters? signingKey = null, RSAParameters? encryptingKey = null, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : base(signingKey, encryptingKey, compressed, maximumAge, decodeOnceOnly) { } + /// <summary> + /// Gets or sets the identifier of the client authorized to access protected data. + /// </summary> + /// <value></value> [MessagePart] public string ClientIdentifier { get; set; } + /// <summary> + /// Gets the date this authorization was established or the token was issued. + /// </summary> + /// <value>A date/time expressed in UTC.</value> public DateTime UtcIssued { get { return this.UtcCreationDate; } } + /// <summary> + /// Gets or sets the name on the account whose data on the resource server is accessible using this authorization. + /// </summary> [MessagePart] public string User { get; set; } + /// <summary> + /// Gets or sets the scope of operations the client is allowed to invoke. + /// </summary> [MessagePart] public string Scope { get; set; } } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs index ccc9baf..4994468 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/DataBag.cs @@ -18,36 +18,79 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { using DotNetOpenAuth.Messaging.Reflection; using DotNetOpenAuth.OAuthWrap.Messages; + /// <summary> + /// A collection of message parts that will be serialized into a single string, + /// to be set into a larger message. + /// </summary> internal abstract class DataBag : MessageBase { + /// <summary> + /// The message description cache to use for data bag types. + /// </summary> private static readonly MessageDescriptionCollection MessageDescriptions = new MessageDescriptionCollection(); + /// <summary> + /// The length of the nonce to include in tokens that can be decoded once only. + /// </summary> private const int NonceLength = 6; + /// <summary> + /// The symmetric secret used for signing/encryption of verification codes and refresh tokens. + /// </summary> private readonly byte[] symmetricSecret; + /// <summary> + /// The hashing algorithm to use while signing when using a symmetric secret. + /// </summary> + private readonly HashAlgorithm symmetricHasher; + + /// <summary> + /// The crypto to use for signing access tokens. + /// </summary> private readonly RSACryptoServiceProvider asymmetricSigning; + /// <summary> + /// The crypto to use for encrypting access tokens. + /// </summary> private readonly RSACryptoServiceProvider asymmetricEncrypting; + /// <summary> + /// The hashing algorithm to use for asymmetric signatures. + /// </summary> private readonly HashAlgorithm hasherForAsymmetricSigning; + /// <summary> + /// A value indicating whether the data in this instance will be protected against tampering. + /// </summary> private readonly bool signed; - protected HashAlgorithm hasher; - + /// <summary> + /// The nonce store to use to ensure that this instance is only decoded once. + /// </summary> private readonly INonceStore decodeOnceOnly; + /// <summary> + /// The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>. + /// </summary> private readonly TimeSpan? maximumAge; + /// <summary> + /// A value indicating whether the data in this instance will be protected against eavesdropping. + /// </summary> private readonly bool encrypted; + /// <summary> + /// A value indicating whether the data in this instance will be GZip'd. + /// </summary> private readonly bool compressed; - [MessagePart("t", IsRequired = true, AllowEmpty = false)] - private string BagType { - get { return this.GetType().Name; } - } - + /// <summary> + /// Initializes a new instance of the <see cref="DataBag"/> class. + /// </summary> + /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param> + /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param> + /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected DataBag(bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : base(Protocol.Default.Version) { Contract.Requires<ArgumentException>(signed || decodeOnceOnly == null, "A signature must be applied if this data is meant to be decoded only once."); @@ -60,6 +103,14 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { this.compressed = compressed; } + /// <summary> + /// Initializes a new instance of the <see cref="DataBag"/> class. + /// </summary> + /// <param name="signingKey">The asymmetric private key to use for signing the token.</param> + /// <param name="encryptingKey">The asymmetric public key to use for encrypting the token.</param> + /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected DataBag(RSAParameters? signingKey = null, RSAParameters? encryptingKey = null, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : this(signingKey.HasValue, encryptingKey.HasValue, compressed, maximumAge, decodeOnceOnly) { if (signingKey.HasValue) { @@ -75,36 +126,73 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { this.hasherForAsymmetricSigning = new SHA1CryptoServiceProvider(); } + /// <summary> + /// Initializes a new instance of the <see cref="DataBag"/> class. + /// </summary> + /// <param name="symmetricSecret">The symmetric secret to use for signing and encrypting.</param> + /// <param name="signed">A value indicating whether the data in this instance will be protected against tampering.</param> + /// <param name="encrypted">A value indicating whether the data in this instance will be protected against eavesdropping.</param> + /// <param name="compressed">A value indicating whether the data in this instance will be GZip'd.</param> + /// <param name="maximumAge">The maximum age of a token that can be decoded; useful only when <see cref="decodeOnceOnly"/> is <c>true</c>.</param> + /// <param name="decodeOnceOnly">The nonce store to use to ensure that this instance is only decoded once.</param> protected DataBag(byte[] symmetricSecret = null, bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) : this(signed, encrypted, compressed, maximumAge, decodeOnceOnly) { - Contract.Requires<ArgumentException>(symmetricSecret != null || (signed == null && encrypted == null), "A secret is required when signing or encrypting is required."); + Contract.Requires<ArgumentException>(symmetricSecret != null || (!signed && !encrypted), "A secret is required when signing or encrypting is required."); if (symmetricSecret != null) { - this.hasher = new HMACSHA256(symmetricSecret); + this.symmetricHasher = new HMACSHA256(symmetricSecret); } this.symmetricSecret = symmetricSecret; } - [MessagePart("sig")] - private string Signature { get; set; } - + /// <summary> + /// Gets or sets the nonce. + /// </summary> + /// <value>The nonce.</value> [MessagePart] internal string Nonce { get; set; } + /// <summary> + /// Gets or sets the UTC creation date of this token. + /// </summary> + /// <value>The UTC creation date.</value> [MessagePart("timestamp", IsRequired = true, Encoder = typeof(TimestampEncoder))] internal DateTime UtcCreationDate { get; set; } + /// <summary> + /// Gets the type of this instance. + /// </summary> + /// <value>The type of the bag.</value> + /// <remarks> + /// This ensures that one token cannot be misused as another kind of token. + /// </remarks> + [MessagePart("t", IsRequired = true, AllowEmpty = false)] + private string BagType { + get { return this.GetType().Name; } + } + + /// <summary> + /// Gets or sets the signature. + /// </summary> + /// <value>The signature.</value> + [MessagePart("sig")] + private string Signature { get; set; } + + /// <summary> + /// Serializes this instance as a string that can be sent as part of a larger message. + /// </summary> + /// <returns>The serialized version of the data in this instance.</returns> internal virtual string Encode() { Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); this.UtcCreationDate = DateTime.UtcNow; - if (decodeOnceOnly != null) { + if (this.decodeOnceOnly != null) { this.Nonce = Convert.ToBase64String(MessagingUtilities.GetNonCryptoRandomData(NonceLength)); } - if (signed) { + if (this.signed) { this.Signature = this.CalculateSignature(); } @@ -113,28 +201,33 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { byte[] encoded = Encoding.UTF8.GetBytes(value); - if (compressed) { + if (this.compressed) { encoded = MessagingUtilities.Compress(encoded); } - if (encrypted) { + if (this.encrypted) { encoded = this.Encrypt(encoded); } return Convert.ToBase64String(encoded); } + /// <summary> + /// Populates this instance with data from a given string. + /// </summary> + /// <param name="value">The value to deserialize from.</param> + /// <param name="containingMessage">The message that contained this token.</param> protected virtual void Decode(string value, IProtocolMessage containingMessage) { Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value)); Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage"); byte[] encoded = Convert.FromBase64String(value); - if (encrypted) { + if (this.encrypted) { encoded = this.Decrypt(encoded); } - if (compressed) { + if (this.compressed) { encoded = MessagingUtilities.Decompress(encoded); } @@ -145,12 +238,12 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { var fields = MessageDescriptions.GetAccessor(this); serializer.Deserialize(HttpUtility.ParseQueryString(value).ToDictionary(), fields); - if (signed) { + if (this.signed) { // Verify that the verification code was issued by this authorization server. ErrorUtilities.VerifyProtocol(this.IsSignatureValid(), Protocol.bad_verification_code); } - if (maximumAge.HasValue) { + if (this.maximumAge.HasValue) { // Has this verification code expired? DateTime expirationDate = this.UtcCreationDate + this.maximumAge.Value; if (expirationDate < DateTime.UtcNow) { @@ -159,7 +252,7 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } // Has this verification code already been used to obtain an access/refresh token? - if (decodeOnceOnly != null) { + if (this.decodeOnceOnly != null) { ErrorUtilities.VerifyInternal(this.maximumAge.HasValue, "Oops! How can we validate a nonce without a maximum message age?"); string context = "{" + GetType().FullName + "}"; if (!this.decodeOnceOnly.StoreNonce(context, this.Nonce, this.UtcCreationDate)) { @@ -169,6 +262,12 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } } + /// <summary> + /// Determines whether the signature on this instance is valid. + /// </summary> + /// <returns> + /// <c>true</c> if the signature is valid; otherwise, <c>false</c>. + /// </returns> private bool IsSignatureValid() { if (this.asymmetricSigning != null) { byte[] bytesToSign = this.GetBytesToSign(); @@ -184,17 +283,21 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// </summary> /// <returns>The calculated signature.</returns> private string CalculateSignature() { - Contract.Requires<InvalidOperationException>(this.asymmetricSigning != null || this.hasher != null); + Contract.Requires<InvalidOperationException>(this.asymmetricSigning != null || this.symmetricHasher != null); byte[] bytesToSign = this.GetBytesToSign(); if (this.asymmetricSigning != null) { byte[] signature = this.asymmetricSigning.SignData(bytesToSign, this.hasherForAsymmetricSigning); return Convert.ToBase64String(signature); } else { - return Convert.ToBase64String(this.hasher.ComputeHash(bytesToSign)); + return Convert.ToBase64String(this.symmetricHasher.ComputeHash(bytesToSign)); } } + /// <summary> + /// Gets the bytes to sign. + /// </summary> + /// <returns>A buffer of the bytes to sign.</returns> private byte[] GetBytesToSign() { // Sign the data, being sure to avoid any impact of the signature field itself. var fields = MessageDescriptions.GetAccessor(this); @@ -207,6 +310,11 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { return bytesToSign; } + /// <summary> + /// Encrypts the specified value using either the symmetric or asymmetric encryption algorithm as appropriate. + /// </summary> + /// <param name="value">The value.</param> + /// <returns>The encrypted value.</returns> private byte[] Encrypt(byte[] value) { Contract.Requires<InvalidOperationException>(this.asymmetricEncrypting != null || this.symmetricSecret != null); @@ -217,6 +325,11 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } } + /// <summary> + /// Decrypts the specified value using either the symmetric or asymmetric encryption algorithm as appropriate. + /// </summary> + /// <param name="value">The value.</param> + /// <returns>The decrypted value.</returns> private byte[] Decrypt(byte[] value) { Contract.Requires<InvalidOperationException>(this.asymmetricEncrypting != null || this.symmetricSecret != null); diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAccessTokenRequest.cs index a32bddf..e270eac 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAccessTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAccessTokenRequest.cs @@ -12,27 +12,25 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { using Messages; using Messaging; - internal interface ITokenCarryingRequest : IDirectedProtocolMessage { - string CodeOrToken { get; set; } - - CodeOrTokenType CodeOrTokenType { get; } - - IAuthorizationDescription AuthorizationDescription { get; set; } - } - + /// <summary> + /// A message from the client to the authorization server requesting an access token. + /// </summary> public interface IAccessTokenRequest : IDirectedProtocolMessage { + /// <summary> + /// Gets the client identifier. + /// </summary> + /// <value>The client identifier.</value> string ClientIdentifier { get; } + /// <summary> + /// Gets the client secret. + /// </summary> + /// <value>The client secret.</value> string ClientSecret { get; } + /// <summary> + /// Gets the type of access token secret requested. + /// </summary> string SecretType { get; } } - - internal enum CodeOrTokenType { - VerificationCode, - - RefreshToken, - - AccessToken, - } } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAuthorizationDescription.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAuthorizationDescription.cs index 0940160..9e75594 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAuthorizationDescription.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/IAuthorizationDescription.cs @@ -38,11 +38,20 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { string Scope { get; } } + /// <summary> + /// Code contract for the <see cref="IAuthorizationDescription"/> interface. + /// </summary> [ContractClassFor(typeof(IAuthorizationDescription))] internal abstract class IAuthorizationDescriptionContract : IAuthorizationDescription { + /// <summary> + /// Prevents a default instance of the <see cref="IAuthorizationDescriptionContract"/> class from being created. + /// </summary> private IAuthorizationDescriptionContract() { } + /// <summary> + /// Gets the identifier of the client authorized to access protected data. + /// </summary> string IAuthorizationDescription.ClientIdentifier { get { Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); @@ -50,10 +59,17 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } } + /// <summary> + /// Gets the date this authorization was established or the token was issued. + /// </summary> + /// <value>A date/time expressed in UTC.</value> DateTime IAuthorizationDescription.UtcIssued { get { throw new NotImplementedException(); } } + /// <summary> + /// Gets the name on the account whose data on the resource server is accessible using this authorization. + /// </summary> string IAuthorizationDescription.User { get { Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>())); @@ -61,9 +77,11 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { } } + /// <summary> + /// Gets the scope of operations the client is allowed to invoke. + /// </summary> string IAuthorizationDescription.Scope { get { throw new NotImplementedException(); } } } - } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/ITokenCarryingRequest.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/ITokenCarryingRequest.cs new file mode 100644 index 0000000..335bf28 --- /dev/null +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/ITokenCarryingRequest.cs @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------- +// <copyright file="ITokenCarryingRequest.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.ChannelElements { + using Messaging; + + /// <summary> + /// The various types of tokens created by the authorization server. + /// </summary> + internal enum CodeOrTokenType { + /// <summary> + /// The code issued to the client after the user has approved authorization. + /// </summary> + VerificationCode, + + /// <summary> + /// The long-lived token issued to the client that enables it to obtain + /// short-lived access tokens later. + /// </summary> + RefreshToken, + + /// <summary> + /// A (typically) short-lived token. + /// </summary> + AccessToken, + } + + /// <summary> + /// A message that carries some kind of token from the client to the authorization or resource server. + /// </summary> + internal interface ITokenCarryingRequest : IDirectedProtocolMessage { + /// <summary> + /// Gets or sets the verification code or refresh/access token. + /// </summary> + /// <value>The code or token.</value> + string CodeOrToken { get; set; } + + /// <summary> + /// Gets the type of the code or token. + /// </summary> + /// <value>The type of the code or token.</value> + CodeOrTokenType CodeOrTokenType { get; } + + /// <summary> + /// Gets or sets the authorization that the token describes. + /// </summary> + IAuthorizationDescription AuthorizationDescription { get; set; } + } +} diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs index e6ddad1..029d583 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapAuthorizationServerChannel.cs @@ -25,42 +25,49 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// The channel for the OAuth WRAP protocol. /// </summary> internal class OAuthWrapAuthorizationServerChannel : StandardMessageFactoryChannel { + /// <summary> + /// The messages receivable by this channel. + /// </summary> private static readonly Type[] MessageTypes = new Type[] { - typeof(Messages.RefreshAccessTokenRequest), - typeof(Messages.AccessTokenSuccessResponse), - typeof(Messages.AccessTokenFailedResponse), - typeof(Messages.UnauthorizedResponse), - typeof(Messages.AssertionRequest), - typeof(Messages.ClientCredentialsRequest), - typeof(Messages.DeviceRequest), - typeof(Messages.DeviceResponse), - typeof(Messages.DeviceAccessTokenRequest), - typeof(Messages.UserNamePasswordRequest), - typeof(Messages.UserNamePasswordSuccessResponse), - typeof(Messages.UserNamePasswordVerificationResponse), - typeof(Messages.UserNamePasswordFailedResponse), - typeof(Messages.UsernamePasswordCaptchaResponse), - typeof(Messages.WebServerRequest), - typeof(Messages.WebServerSuccessResponse), - typeof(Messages.WebServerFailedResponse), - typeof(Messages.WebServerAccessTokenRequest), - typeof(Messages.UserAgentRequest), - typeof(Messages.UserAgentSuccessResponse), - typeof(Messages.UserAgentFailedResponse), - }; + typeof(Messages.RefreshAccessTokenRequest), + typeof(Messages.AccessTokenSuccessResponse), + typeof(Messages.AccessTokenFailedResponse), + typeof(Messages.UnauthorizedResponse), + typeof(Messages.AssertionRequest), + typeof(Messages.ClientCredentialsRequest), + typeof(Messages.DeviceRequest), + typeof(Messages.DeviceResponse), + typeof(Messages.DeviceAccessTokenRequest), + typeof(Messages.UserNamePasswordRequest), + typeof(Messages.UserNamePasswordSuccessResponse), + typeof(Messages.UserNamePasswordVerificationResponse), + typeof(Messages.UserNamePasswordFailedResponse), + typeof(Messages.UsernamePasswordCaptchaResponse), + typeof(Messages.WebServerRequest), + typeof(Messages.WebServerSuccessResponse), + typeof(Messages.WebServerFailedResponse), + typeof(Messages.WebServerAccessTokenRequest), + typeof(Messages.UserAgentRequest), + typeof(Messages.UserAgentSuccessResponse), + typeof(Messages.UserAgentFailedResponse), + }; + /// <summary> + /// The protocol versions supported by this channel. + /// </summary> private static readonly Version[] Versions = Protocol.AllVersions.Select(v => v.Version).ToArray(); /// <summary> /// Initializes a new instance of the <see cref="OAuthWrapAuthorizationServerChannel"/> class. /// </summary> + /// <param name="authorizationServer">The authorization server.</param> protected internal OAuthWrapAuthorizationServerChannel(IAuthorizationServer authorizationServer = null) : base(MessageTypes, Versions, InitializeBindingElements(authorizationServer)) { this.AuthorizationServer = authorizationServer; } /// <summary> - /// Gets or sets the authorization server. + /// Gets the authorization server. /// </summary> /// <value>The authorization server. Will be null for channels serving clients.</value> public IAuthorizationServer AuthorizationServer { get; private set; } @@ -147,6 +154,13 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { return webResponse; } + /// <summary> + /// Gets the protocol message that may be embedded in the given HTTP request. + /// </summary> + /// <param name="request">The request to search for an embedded message.</param> + /// <returns> + /// The deserialized message, if one is found. Null otherwise. + /// </returns> protected override IDirectedProtocolMessage ReadFromRequestCore(HttpRequestInfo request) { if (!string.IsNullOrEmpty(request.Url.Fragment)) { var fields = HttpUtility.ParseQueryString(request.Url.Fragment.Substring(1)).ToDictionary(); diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapResourceServerChannel.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapResourceServerChannel.cs index b3671fd..53821d2 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapResourceServerChannel.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/OAuthWrapResourceServerChannel.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------- -// <copyright file="OAuthWrapChannel.cs" company="Andrew Arnott"> +// <copyright file="OAuthWrapResourceServerChannel.cs" company="Andrew Arnott"> // Copyright (c) Andrew Arnott. All rights reserved. // </copyright> //----------------------------------------------------------------------- @@ -21,10 +21,16 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// The channel for the OAuth WRAP protocol. /// </summary> internal class OAuthWrapResourceServerChannel : StandardMessageFactoryChannel { + /// <summary> + /// The messages receivable by this channel. + /// </summary> private static readonly Type[] MessageTypes = new Type[] { typeof(Messages.AccessProtectedResourceRequest), }; + /// <summary> + /// The protocol versions supported by this channel. + /// </summary> private static readonly Version[] Versions = Protocol.AllVersions.Select(v => v.Version).ToArray(); /// <summary> diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs index 751b9bf..e95c5cc 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/RefreshToken.cs @@ -12,16 +12,16 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { using System.Text; using DotNetOpenAuth.Messaging; + /// <summary> + /// The refresh token issued to a client by an authorization server that allows the client + /// to periodically obtain new short-lived access tokens. + /// </summary> internal class RefreshToken : AuthorizationDataBag { /// <summary> /// Initializes a new instance of the <see cref="RefreshToken"/> class. /// </summary> - /// <param name="channel">The channel.</param> - private RefreshToken(byte[] secret) - : base(secret, true, true) { - Contract.Requires<ArgumentNullException>(secret != null, "secret"); - } - + /// <param name="secret">The symmetric secret used to sign/encrypt refresh tokens.</param> + /// <param name="authorization">The authorization this refresh token should describe.</param> internal RefreshToken(byte[] secret, IAuthorizationDescription authorization) : this(secret) { Contract.Requires<ArgumentNullException>(secret != null, "secret"); @@ -33,6 +33,22 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { this.Scope = authorization.Scope; } + /// <summary> + /// Initializes a new instance of the <see cref="RefreshToken"/> class. + /// </summary> + /// <param name="secret">The symmetric secret used to sign/encrypt refresh tokens.</param> + private RefreshToken(byte[] secret) + : base(secret, true, true) { + Contract.Requires<ArgumentNullException>(secret != null, "secret"); + } + + /// <summary> + /// Deserializes a refresh token. + /// </summary> + /// <param name="secret">The symmetric secret used to sign and encrypt the token.</param> + /// <param name="value">The token.</param> + /// <param name="containingMessage">The message containing this token.</param> + /// <returns>The refresh token.</returns> internal static RefreshToken Decode(byte[] secret, string value, IProtocolMessage containingMessage) { Contract.Requires<ArgumentNullException>(secret != null, "secret"); Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(value)); diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/TimestampEncoder.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/TimestampEncoder.cs index 8edc382..26b3508 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/TimestampEncoder.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/TimestampEncoder.cs @@ -9,6 +9,9 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { using System.Globalization; using DotNetOpenAuth.Messaging.Reflection; + /// <summary> + /// Translates between a <see cref="DateTime"/> and the number of seconds between it and 1/1/1970 12 AM + /// </summary> internal class TimestampEncoder : IMessagePartEncoder { /// <summary> /// The reference date and time for calculating time stamps. diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs index ecdd2b3..dcbec63 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/VerificationCode.cs @@ -11,16 +11,25 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Bindings; + /// <summary> + /// Represents the verification code created when a user approves authorization that + /// allows the client to request an access/refresh token. + /// </summary> internal class VerificationCode : AuthorizationDataBag { - private HashAlgorithm hasher = new SHA256Managed(); + /// <summary> + /// The hash algorithm used on the callback URI. + /// </summary> + private readonly HashAlgorithm hasher = new SHA256Managed(); /// <summary> /// Initializes a new instance of the <see cref="VerificationCode"/> class. /// </summary> - /// <param name="channel">The channel.</param> - /// <param name="callback">The callback.</param> + /// <param name="secret">The symmetric secret to use in signing/encryption.</param> + /// <param name="nonceStore">The nonce store to use to ensure verification codes are used only once.</param> + /// <param name="clientIdentifier">The client identifier.</param> + /// <param name="callback">The callback the client used to obtain authorization.</param> /// <param name="scope">The scope.</param> - /// <param name="username">The username.</param> + /// <param name="username">The name on the account that authorized access.</param> internal VerificationCode(byte[] secret, INonceStore nonceStore, string clientIdentifier, Uri callback, string scope, string username) : this(secret, nonceStore) { Contract.Requires<ArgumentNullException>(secret != null, "secret"); @@ -38,16 +47,14 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { /// <summary> /// Initializes a new instance of the <see cref="VerificationCode"/> class. /// </summary> - /// <param name="channel">The channel.</param> + /// <param name="secret">The symmetric secret to use in signing/encryption.</param> + /// <param name="nonceStore">The nonce store to use to ensure verification codes are used only once.</param> private VerificationCode(byte[] secret, INonceStore nonceStore) : base(secret, true, true, false, MaximumMessageAge, nonceStore) { Contract.Requires<ArgumentNullException>(secret != null, "secret"); Contract.Requires<ArgumentNullException>(nonceStore != null, "nonceStore"); } - [MessagePart("cb")] - private string CallbackHash { get; set; } - /// <summary> /// Gets the maximum message age from the standard expiration binding element. /// </summary> @@ -55,6 +62,20 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { get { return StandardExpirationBindingElement.MaximumMessageAge; } } + /// <summary> + /// Gets or sets the hash of the callback URL. + /// </summary> + [MessagePart("cb")] + private string CallbackHash { get; set; } + + /// <summary> + /// Deserializes a verification code. + /// </summary> + /// <param name="secret">The symmetric secret used to sign and encrypt the token.</param> + /// <param name="nonceStore">The nonce store to use to ensure verification codes can only be exchanged once.</param> + /// <param name="value">The verification token.</param> + /// <param name="containingMessage">The message containing this verification code.</param> + /// <returns>The verification code.</returns> internal static VerificationCode Decode(byte[] secret, INonceStore nonceStore, string value, IProtocolMessage containingMessage) { Contract.Requires<ArgumentNullException>(secret != null, "secret"); Contract.Requires<ArgumentNullException>(nonceStore != null, "nonceStore"); @@ -67,10 +88,26 @@ namespace DotNetOpenAuth.OAuthWrap.ChannelElements { return self; } + /// <summary> + /// Verifies the the given callback URL matches the callback originally given in the authorization request. + /// </summary> + /// <param name="callback">The callback.</param> + /// <remarks> + /// This method serves to verify that the callback URL given in the original authorization request + /// and the callback URL given in the access token request match. + /// </remarks> + /// <exception cref="ProtocolException">Thrown when the callback URLs do not match.</exception> internal void VerifyCallback(Uri callback) { ErrorUtilities.VerifyProtocol(string.Equals(this.CallbackHash, this.CalculateCallbackHash(callback), StringComparison.Ordinal), Protocol.redirect_uri_mismatch); } + /// <summary> + /// Calculates the hash of the callback URL. + /// </summary> + /// <param name="callback">The callback whose hash should be calculated.</param> + /// <returns> + /// A base64 encoding of the hash of the URL. + /// </returns> private string CalculateCallbackHash(Uri callback) { return this.hasher.ComputeHash(callback.AbsoluteUri); } diff --git a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebServerVerificationCodeBindingElement.cs b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebServerVerificationCodeBindingElement.cs index 2a5bbd0..1f0666f 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebServerVerificationCodeBindingElement.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ChannelElements/WebServerVerificationCodeBindingElement.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------- -// <copyright file="WebAppVerificationCodeBindingElement.cs" company="Andrew Arnott"> +// <copyright file="WebServerVerificationCodeBindingElement.cs" company="Andrew Arnott"> // Copyright (c) Andrew Arnott. All rights reserved. // </copyright> //----------------------------------------------------------------------- diff --git a/src/DotNetOpenAuth/OAuthWrap/ClientBase.cs b/src/DotNetOpenAuth/OAuthWrap/ClientBase.cs index 8ca276a..a733b9b 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ClientBase.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ClientBase.cs @@ -129,6 +129,11 @@ namespace DotNetOpenAuth.OAuthWrap { authorization.SaveChanges(); } + /// <summary> + /// Updates the authorization state maintained by the client with the content of an outgoing response. + /// </summary> + /// <param name="authorizationState">The authorization state maintained by the client.</param> + /// <param name="accessTokenSuccess">The access token containing response message.</param> internal void UpdateAuthorizationWithResponse(IAuthorizationState authorizationState, IAccessTokenSuccessResponse accessTokenSuccess) { Contract.Requires<ArgumentNullException>(authorizationState != null, "authorizationState"); Contract.Requires<ArgumentNullException>(accessTokenSuccess != null, "accessTokenSuccess"); @@ -140,7 +145,8 @@ namespace DotNetOpenAuth.OAuthWrap { authorizationState.AccessTokenIssueDateUtc = DateTime.UtcNow; if (accessTokenSuccess.Scope != null && accessTokenSuccess.Scope != authorizationState.Scope) { if (authorizationState.Scope != null) { - Logger.Wrap.InfoFormat("Requested scope of \"{0}\" changed to \"{1}\" by authorization server.", + Logger.Wrap.InfoFormat( + "Requested scope of \"{0}\" changed to \"{1}\" by authorization server.", authorizationState.Scope, accessTokenSuccess.Scope); } @@ -151,6 +157,11 @@ namespace DotNetOpenAuth.OAuthWrap { authorizationState.SaveChanges(); } + /// <summary> + /// Calculates the fraction of life remaining in an access token. + /// </summary> + /// <param name="authorization">The authorization to measure.</param> + /// <returns>A fractional number no greater than 1. Could be negative if the access token has already expired.</returns> private double ProportionalLifeRemaining(IAuthorizationState authorization) { Contract.Requires<ArgumentNullException>(authorization != null, "authorization"); Contract.Requires<ArgumentException>(authorization.AccessTokenIssueDateUtc.HasValue); diff --git a/src/DotNetOpenAuth/OAuthWrap/IAccessTokenAnalyzer.cs b/src/DotNetOpenAuth/OAuthWrap/IAccessTokenAnalyzer.cs index 98bd60a..4e692c6 100644 --- a/src/DotNetOpenAuth/OAuthWrap/IAccessTokenAnalyzer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/IAccessTokenAnalyzer.cs @@ -12,21 +12,48 @@ namespace DotNetOpenAuth.OAuthWrap { using System.Text; using DotNetOpenAuth.Messaging; + /// <summary> + /// An interface that resource server hosts should implement if they accept access tokens + /// issued by non-DotNetOpenAuth authorization servers. + /// </summary> public interface IAccessTokenAnalyzer { + /// <summary> + /// Reads an access token to find out what data it authorizes access to. + /// </summary> + /// <param name="message">The message carrying the access token.</param> + /// <param name="accessToken">The access token.</param> + /// <param name="user">The user whose data is accessible with this access token.</param> + /// <param name="scope">The scope of access authorized by this access token.</param> + /// <returns>A value indicating whether this access token is valid.</returns> bool TryValidateAccessToken(IDirectedProtocolMessage message, string accessToken, out string user, out string scope); } + /// <summary> + /// Code contract for the <see cref="IAccessTokenAnalyzer"/> interface. + /// </summary> internal abstract class IAccessTokenAnalyzerContract : IAccessTokenAnalyzer { + /// <summary> + /// Prevents a default instance of the <see cref="IAccessTokenAnalyzerContract"/> class from being created. + /// </summary> private IAccessTokenAnalyzerContract() { } + /// <summary> + /// Reads an access token to find out what data it authorizes access to. + /// </summary> + /// <param name="message">The message carrying the access token.</param> + /// <param name="accessToken">The access token.</param> + /// <param name="user">The user whose data is accessible with this access token.</param> + /// <param name="scope">The scope of access authorized by this access token.</param> + /// <returns> + /// A value indicating whether this access token is valid. + /// </returns> bool IAccessTokenAnalyzer.TryValidateAccessToken(IDirectedProtocolMessage message, string accessToken, out string user, out string scope) { Contract.Requires<ArgumentNullException>(message != null, "message"); Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(accessToken)); Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn<string>(out user) != null)); - + throw new NotImplementedException(); } } - } diff --git a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs index e43f6c8..c263277 100644 --- a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationServer.cs @@ -15,9 +15,37 @@ namespace DotNetOpenAuth.OAuthWrap { using DotNetOpenAuth.OAuth.ChannelElements; using DotNetOpenAuth.OAuthWrap.ChannelElements; + /// <summary> + /// Provides host-specific authorization server services needed by this library. + /// </summary> [ContractClass(typeof(IAuthorizationServerContract))] public interface IAuthorizationServer { /// <summary> + /// Gets the secret used to symmetrically encrypt and sign verification codes and refresh tokens. + /// </summary> + /// <remarks> + /// This secret should be kept strictly confidential in the authorization server(s) + /// and NOT shared with the resource server. Anyone with this secret can mint + /// tokens to essentially grant themselves access to anything they want. + /// </remarks> + byte[] Secret { get; } + + /// <summary> + /// Gets the asymmetric private key to use for signing access tokens. + /// </summary> + /// <remarks> + /// The public key in the private/public key pair will be used by the resource + /// servers to validate that the access token is minted by a trusted authorization server. + /// </remarks> + RSAParameters AccessTokenSigningPrivateKey { get; } + + /// <summary> + /// Gets the verification code nonce store to use to ensure that verification codes can only be used once. + /// </summary> + /// <value>The verification code nonce store.</value> + INonceStore VerificationCodeNonceStore { get; } + + /// <summary> /// Gets the client with a given identifier. /// </summary> /// <param name="clientIdentifier">The client identifier.</param> @@ -48,51 +76,51 @@ namespace DotNetOpenAuth.OAuthWrap { /// account or piece of hardware in which the tokens were stored. </para> /// </remarks> bool IsAuthorizationValid(IAuthorizationDescription authorization); + } + + /// <summary> + /// Code Contract for the <see cref="IAuthorizationServer"/> interface. + /// </summary> + [ContractClassFor(typeof(IAuthorizationServer))] + internal abstract class IAuthorizationServerContract : IAuthorizationServer { + /// <summary> + /// Prevents a default instance of the <see cref="IAuthorizationServerContract"/> class from being created. + /// </summary> + private IAuthorizationServerContract() { + } /// <summary> /// Gets the secret used to symmetrically encrypt and sign verification codes and refresh tokens. /// </summary> + /// <value></value> /// <remarks> /// This secret should be kept strictly confidential in the authorization server(s) /// and NOT shared with the resource server. Anyone with this secret can mint /// tokens to essentially grant themselves access to anything they want. /// </remarks> - byte[] Secret { get; } + byte[] IAuthorizationServer.Secret { + get { + Contract.Ensures(Contract.Result<byte[]>() != null); + throw new NotImplementedException(); + } + } /// <summary> /// Gets the asymmetric private key to use for signing access tokens. /// </summary> + /// <value></value> /// <remarks> /// The public key in the private/public key pair will be used by the resource /// servers to validate that the access token is minted by a trusted authorization server. /// </remarks> - RSAParameters AccessTokenSigningPrivateKey { get; } - - INonceStore VerificationCodeNonceStore { get; } - } - - [ContractClassFor(typeof(IAuthorizationServer))] - internal abstract class IAuthorizationServerContract : IAuthorizationServer { - private IAuthorizationServerContract() { - } - - IConsumerDescription IAuthorizationServer.GetClient(string clientIdentifier) { - Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(clientIdentifier)); - Contract.Ensures(Contract.Result<IConsumerDescription>() != null); - throw new NotImplementedException(); - } - - byte[] IAuthorizationServer.Secret { - get { - Contract.Ensures(Contract.Result<byte[]>() != null); - throw new NotImplementedException(); - } - } - RSAParameters IAuthorizationServer.AccessTokenSigningPrivateKey { get { throw new NotImplementedException(); } } + /// <summary> + /// Gets the verification code nonce store to use to ensure that verification codes can only be used once. + /// </summary> + /// <value>The verification code nonce store.</value> INonceStore IAuthorizationServer.VerificationCodeNonceStore { get { Contract.Ensures(Contract.Result<INonceStore>() != null); @@ -100,10 +128,43 @@ namespace DotNetOpenAuth.OAuthWrap { } } + /// <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 IAuthorizationServer.GetClient(string clientIdentifier) { + Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(clientIdentifier)); + Contract.Ensures(Contract.Result<IConsumerDescription>() != null); + throw new NotImplementedException(); + } + + /// <summary> + /// Determines whether a described authorization is (still) valid. + /// </summary> + /// <param name="authorization">The authorization.</param> + /// <returns> + /// <c>true</c> if the original authorization is still valid; otherwise, <c>false</c>. + /// </returns> + /// <remarks> + /// <para>When establishing that an authorization is still valid, + /// it's very important to only match on recorded authorizations that + /// meet these criteria:</para> + /// 1) The client identifier matches. + /// 2) The user account matches. + /// 3) The scope on the recorded authorization must include all scopes in the given authorization. + /// 4) The date the recorded authorization was issued must be <em>no later</em> that the date the given authorization was issued. + /// <para>One possible scenario is where the user authorized a client, later revoked authorization, + /// and even later reinstated authorization. This subsequent recorded authorization + /// would not satisfy requirement #4 in the above list. This is important because the revocation + /// the user went through should invalidate all previously issued tokens as a matter of + /// security in the event the user was revoking access in order to sever authorization on a stolen + /// account or piece of hardware in which the tokens were stored. </para> + /// </remarks> bool IAuthorizationServer.IsAuthorizationValid(IAuthorizationDescription authorization) { Contract.Requires<ArgumentNullException>(authorization != null, "authorization"); throw new NotImplementedException(); } } - } diff --git a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationState.cs b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationState.cs index caf6ddc..c0ac80d 100644 --- a/src/DotNetOpenAuth/OAuthWrap/IAuthorizationState.cs +++ b/src/DotNetOpenAuth/OAuthWrap/IAuthorizationState.cs @@ -4,8 +4,7 @@ // </copyright> //----------------------------------------------------------------------- -namespace DotNetOpenAuth.OAuthWrap -{ +namespace DotNetOpenAuth.OAuthWrap { using System; /// <summary> @@ -36,8 +35,16 @@ namespace DotNetOpenAuth.OAuthWrap /// <value>The access token secret.</value> string AccessTokenSecret { get; set; } + /// <summary> + /// Gets or sets the type of the access token secret. + /// </summary> + /// <value>The type of the access token secret.</value> string AccessTokenSecretType { get; set; } + /// <summary> + /// Gets or sets the access token issue date UTC. + /// </summary> + /// <value>The access token issue date UTC.</value> DateTime? AccessTokenIssueDateUtc { get; set; } /// <summary> diff --git a/src/DotNetOpenAuth/OAuthWrap/IClientTokenManager.cs b/src/DotNetOpenAuth/OAuthWrap/IClientTokenManager.cs index 3b593e3..c6beb90 100644 --- a/src/DotNetOpenAuth/OAuthWrap/IClientTokenManager.cs +++ b/src/DotNetOpenAuth/OAuthWrap/IClientTokenManager.cs @@ -8,6 +8,9 @@ namespace DotNetOpenAuth.OAuthWrap { using System; using System.Diagnostics.Contracts; + /// <summary> + /// A token manager implemented by some clients to assist in tracking authorization state. + /// </summary> [ContractClass(typeof(IClientTokenManagerContract))] public interface IClientTokenManager { /// <summary> @@ -32,6 +35,14 @@ namespace DotNetOpenAuth.OAuthWrap { #region IClientTokenManager Members + /// <summary> + /// Gets the state of the authorization for a given callback URL and client state. + /// </summary> + /// <param name="callbackUrl">The callback URL.</param> + /// <param name="clientState">State of the client stored at the beginning of an authorization request.</param> + /// <returns> + /// The authorization state; may be <c>null</c> if no authorization state matches. + /// </returns> IAuthorizationState IClientTokenManager.GetAuthorizationState(Uri callbackUrl, string clientState) { Contract.Requires<ArgumentNullException>(callbackUrl != null); throw new NotImplementedException(); diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/AccessProtectedResourceRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessProtectedResourceRequest.cs index dfdacb7..c6f2de7 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/AccessProtectedResourceRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessProtectedResourceRequest.cs @@ -12,39 +12,73 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { using ChannelElements; using Messaging; + /// <summary> + /// A message that accompanies an HTTP request to a resource server that provides authorization. + /// </summary> internal class AccessProtectedResourceRequest : MessageBase, ITokenCarryingRequest { /// <summary> /// Initializes a new instance of the <see cref="AccessProtectedResourceRequest"/> class. /// </summary> - /// <param name="version">The version.</param> /// <param name="recipient">The recipient.</param> + /// <param name="version">The version.</param> internal AccessProtectedResourceRequest(Uri recipient, Version version) : base(version, MessageTransport.Direct, recipient) { } + /// <summary> + /// Gets the type of the code or token. + /// </summary> + /// <value>The type of the code or token.</value> CodeOrTokenType ITokenCarryingRequest.CodeOrTokenType { get { return CodeOrTokenType.AccessToken; } } + /// <summary> + /// Gets or sets the verification code or refresh/access token. + /// </summary> + /// <value>The code or token.</value> string ITokenCarryingRequest.CodeOrToken { get { return this.AccessToken; } set { this.AccessToken = value; } } + /// <summary> + /// Gets or sets the authorization that the token describes. + /// </summary> IAuthorizationDescription ITokenCarryingRequest.AuthorizationDescription { get; set; } + /// <summary> + /// Gets or sets the access token. + /// </summary> + /// <value>The access token.</value> [MessagePart("token", IsRequired = true, AllowEmpty = false)] internal string AccessToken { get; set; } + /// <summary> + /// Gets or sets the nonce. + /// </summary> + /// <value>The nonce.</value> [MessagePart("nonce")] internal string Nonce { get; set; } + /// <summary> + /// Gets or sets the timestamp. + /// </summary> + /// <value>The timestamp.</value> [MessagePart("timestamp", Encoder = typeof(TimestampEncoder))] internal DateTime? Timestamp { get; set; } + /// <summary> + /// Gets or sets the signature. + /// </summary> + /// <value>The signature.</value> [MessagePart("signature")] internal string Signature { get; set; } + /// <summary> + /// Gets or sets the algorithm. + /// </summary> + /// <value>The algorithm.</value> [MessagePart("algorithm")] internal string Algorithm { get; set; } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenSuccessResponse.cs index 13cf250..cf1a90e 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenSuccessResponse.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/AccessTokenSuccessResponse.cs @@ -4,12 +4,11 @@ // </copyright> //----------------------------------------------------------------------- -using DotNetOpenAuth.OAuthWrap.ChannelElements; - namespace DotNetOpenAuth.OAuthWrap.Messages { using System; using System.Net; using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuthWrap.ChannelElements; /// <summary> /// A response from the Authorization Server to the Client containing a delegation code diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionRequest.cs index 2917a69..6137d19 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/Assertion/AssertionRequest.cs @@ -50,20 +50,6 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { public string ClientSecret { get; set; } /// <summary> - /// Gets or sets the format of the assertion as defined by the Authorization Server. - /// </summary> - /// <value>The assertion format.</value> - [MessagePart(Protocol.assertion_format, IsRequired = true, AllowEmpty = false)] - internal string AssertionFormat { get; set; } - - /// <summary> - /// Gets or sets the assertion. - /// </summary> - /// <value>The assertion.</value> - [MessagePart(Protocol.assertion, IsRequired = true, AllowEmpty = false)] - internal string Assertion { get; set; } - - /// <summary> /// Gets or sets an optional authorization scope as defined by the Authorization Server. /// </summary> [MessagePart(Protocol.scope, IsRequired = false, AllowEmpty = true)] @@ -79,10 +65,32 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { [MessagePart(Protocol.secret_type, IsRequired = false, AllowEmpty = false)] public string SecretType { get; internal set; } + /// <summary> + /// Gets the format the client is requesting the authorization server should deliver the request in. + /// </summary> + /// <value>The format.</value> ResponseFormat IOAuthDirectResponseFormat.Format { get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } } + /// <summary> + /// Gets or sets the format of the assertion as defined by the Authorization Server. + /// </summary> + /// <value>The assertion format.</value> + [MessagePart(Protocol.assertion_format, IsRequired = true, AllowEmpty = false)] + internal string AssertionFormat { get; set; } + + /// <summary> + /// Gets or sets the assertion. + /// </summary> + /// <value>The assertion.</value> + [MessagePart(Protocol.assertion, IsRequired = true, AllowEmpty = false)] + internal string Assertion { get; set; } + + /// <summary> + /// Gets or sets the format the client is requesting the authorization server should deliver the request in. + /// </summary> + /// <value>The format.</value> [MessagePart(Protocol.format, Encoder = typeof(ResponseFormatEncoder))] private ResponseFormat? Format { get; set; } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/ClientCredentials/ClientCredentialsRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/ClientCredentials/ClientCredentialsRequest.cs index 506a0db..56d5e3e 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/ClientCredentials/ClientCredentialsRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/ClientCredentials/ClientCredentialsRequest.cs @@ -55,15 +55,23 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { public string SecretType { get; set; } /// <summary> - /// Gets or sets an optional authorization scope as defined by the Authorization Server. + /// Gets the format the client is requesting the authorization server should deliver the request in. /// </summary> - [MessagePart(Protocol.scope, IsRequired = false, AllowEmpty = true)] - internal string Scope { get; set; } - + /// <value>The format.</value> ResponseFormat IOAuthDirectResponseFormat.Format { get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } } + /// <summary> + /// Gets or sets an optional authorization scope as defined by the Authorization Server. + /// </summary> + [MessagePart(Protocol.scope, IsRequired = false, AllowEmpty = true)] + internal string Scope { get; set; } + + /// <summary> + /// Gets or sets the format the client is requesting the authorization server should deliver the request in. + /// </summary> + /// <value>The format.</value> [MessagePart(Protocol.format, Encoder = typeof(ResponseFormatEncoder))] private ResponseFormat? Format { get; set; } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/Device/DeviceAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/Device/DeviceAccessTokenRequest.cs index 010e80a..4e1f850 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/Device/DeviceAccessTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/Device/DeviceAccessTokenRequest.cs @@ -18,6 +18,9 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { /// verification code for refresh and access tokens. /// </summary> internal class DeviceAccessTokenRequest : MessageBase, IAccessTokenRequest, IOAuthDirectResponseFormat { + /// <summary> + /// A constant that identifies the flow this message belongs to. + /// </summary> [MessagePart(Protocol.type, IsRequired = true)] private const string MessageType = "device_token"; @@ -52,19 +55,15 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { [MessagePart(Protocol.client_id, IsRequired = true, AllowEmpty = false)] public string ClientIdentifier { get; internal set; } + /// <summary> + /// Gets the client secret. + /// </summary> + /// <value>The client secret.</value> string IAccessTokenRequest.ClientSecret { get { return null; } } /// <summary> - /// Gets or sets the verification code previously communicated to the Client - /// in <see cref="DeviceResponse.VerificationCode"/>. - /// </summary> - /// <value>The verification code.</value> - [MessagePart(Protocol.code, IsRequired = true, AllowEmpty = false)] - internal string VerificationCode { get; set; } - - /// <summary> /// Gets or sets the type of the secret. /// </summary> /// <value>The type of the secret.</value> @@ -74,10 +73,26 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { [MessagePart(Protocol.secret_type, IsRequired = false, AllowEmpty = false)] public string SecretType { get; set; } + /// <summary> + /// Gets the format the client is requesting the authorization server should deliver the request in. + /// </summary> + /// <value>The format.</value> ResponseFormat IOAuthDirectResponseFormat.Format { get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } } + /// <summary> + /// Gets or sets the verification code previously communicated to the Client + /// in <see cref="DeviceResponse.VerificationCode"/>. + /// </summary> + /// <value>The verification code.</value> + [MessagePart(Protocol.code, IsRequired = true, AllowEmpty = false)] + internal string VerificationCode { get; set; } + + /// <summary> + /// Gets or sets the format the client is requesting the authorization server should deliver the request in. + /// </summary> + /// <value>The format.</value> [MessagePart(Protocol.format, Encoder = typeof(ResponseFormatEncoder))] private ResponseFormat? Format { get; set; } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/Device/DeviceRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/Device/DeviceRequest.cs index 836ea0d..dfffe20 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/Device/DeviceRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/Device/DeviceRequest.cs @@ -17,6 +17,9 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { /// authorization to access user Protected Data. /// </summary> internal class DeviceRequest : MessageBase, IOAuthDirectResponseFormat { + /// <summary> + /// A constant that identifies the type of message coming into the auth server. + /// </summary> [MessagePart(Protocol.type, IsRequired = true)] private const string MessageType = "device_code"; @@ -45,6 +48,14 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { } /// <summary> + /// Gets the format the client is requesting the authorization server should deliver the request in. + /// </summary> + /// <value>The format.</value> + ResponseFormat IOAuthDirectResponseFormat.Format { + get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } + } + + /// <summary> /// Gets or sets the client identifier previously obtained from the Authorization Server. /// </summary> /// <value>The client identifier.</value> @@ -58,10 +69,10 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { [MessagePart(Protocol.scope, IsRequired = false, AllowEmpty = true)] internal string Scope { get; set; } - ResponseFormat IOAuthDirectResponseFormat.Format { - get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } - } - + /// <summary> + /// Gets or sets the format the client is requesting the authorization server should deliver the request in. + /// </summary> + /// <value>The format.</value> [MessagePart(Protocol.format, Encoder = typeof(ResponseFormatEncoder))] private ResponseFormat? Format { get; set; } } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/IAccessTokenSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/IAccessTokenSuccessResponse.cs index 3aa3616..12e7b66 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/IAccessTokenSuccessResponse.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/IAccessTokenSuccessResponse.cs @@ -1,18 +1,48 @@ -namespace DotNetOpenAuth.OAuthWrap.Messages { +//----------------------------------------------------------------------- +// <copyright file="IAccessTokenSuccessResponse.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.Messages { using System; using System.Collections.Generic; using System.Linq; using System.Text; + /// <summary> + /// A response message from the authorization server to the client that contains an access token + /// and possibly a refresh token + /// </summary> internal interface IAccessTokenSuccessResponse { + /// <summary> + /// Gets the access token. + /// </summary> + /// <value>The access token.</value> string AccessToken { get; } + /// <summary> + /// Gets the access token secret. + /// </summary> + /// <value>The access token secret.</value> string AccessTokenSecret { get; } + /// <summary> + /// Gets the refresh token. + /// </summary> + /// <value>The refresh token.</value> string RefreshToken { get; } + /// <summary> + /// Gets the lifetime. + /// </summary> + /// <value>The lifetime.</value> TimeSpan? Lifetime { get; } + /// <summary> + /// Gets the scope. + /// </summary> + /// <value>The scope.</value> string Scope { get; } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/IMessageWithClientState.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/IMessageWithClientState.cs index 8054d27..1d42f54 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/IMessageWithClientState.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/IMessageWithClientState.cs @@ -7,7 +7,15 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { using DotNetOpenAuth.Messaging; + /// <summary> + /// A message carrying client state the authorization server should preserve on behalf of the client + /// during an authorization. + /// </summary> internal interface IMessageWithClientState : IProtocolMessage { + /// <summary> + /// Gets or sets the state of the client. + /// </summary> + /// <value>The state of the client.</value> string ClientState { get; set; } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/IOAuthDirectResponseFormat.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/IOAuthDirectResponseFormat.cs index fa7a919..5063995 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/IOAuthDirectResponseFormat.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/IOAuthDirectResponseFormat.cs @@ -10,7 +10,14 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { using System.Linq; using System.Text; + /// <summary> + /// A message the includes a request for the format the response message should come in. + /// </summary> internal interface IOAuthDirectResponseFormat { + /// <summary> + /// Gets the format the client is requesting the authorization server should deliver the request in. + /// </summary> + /// <value>The format.</value> ResponseFormat Format { get; } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/IRequestWithRedirectUri.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/IRequestWithRedirectUri.cs index fca1bbd..49fa0bb 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/IRequestWithRedirectUri.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/IRequestWithRedirectUri.cs @@ -1,12 +1,29 @@ -namespace DotNetOpenAuth.OAuthWrap.Messages { +//----------------------------------------------------------------------- +// <copyright file="IRequestWithRedirectUri.cs" company="Andrew Arnott"> +// Copyright (c) Andrew Arnott. All rights reserved. +// </copyright> +//----------------------------------------------------------------------- + +namespace DotNetOpenAuth.OAuthWrap.Messages { using System; using System.Collections.Generic; using System.Linq; using System.Text; + /// <summary> + /// A message that contains a callback parameter. + /// </summary> internal interface IRequestWithRedirectUri { + /// <summary> + /// Gets the client identifier. + /// </summary> + /// <value>The client identifier.</value> string ClientIdentifier { get; } + /// <summary> + /// Gets the callback. + /// </summary> + /// <value>The callback.</value> Uri Callback { get; } } } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs index ad4d45c..ba40f30 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/RefreshAccessTokenRequest.cs @@ -27,7 +27,6 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { /// <param name="version">The version.</param> internal RefreshAccessTokenRequest(Uri tokenEndpoint, Version version) : base(version, MessageTransport.Direct, tokenEndpoint) { - // We prefer URL encoding of the data. this.Format = ResponseFormat.Form; } @@ -40,15 +39,26 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { : this(authorizationServer.TokenEndpoint, authorizationServer.Version) { } + /// <summary> + /// Gets the type of the code or token. + /// </summary> + /// <value>The type of the code or token.</value> CodeOrTokenType ITokenCarryingRequest.CodeOrTokenType { get { return CodeOrTokenType.RefreshToken; } } + /// <summary> + /// Gets or sets the verification code or refresh/access token. + /// </summary> + /// <value>The code or token.</value> string ITokenCarryingRequest.CodeOrToken { get { return this.RefreshToken; } set { this.RefreshToken = value; } } + /// <summary> + /// Gets or sets the authorization that the token describes. + /// </summary> IAuthorizationDescription ITokenCarryingRequest.AuthorizationDescription { get; set; } /// <summary> @@ -79,6 +89,14 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { public string ClientSecret { get; set; } /// <summary> + /// Gets the format the client is requesting the authorization server should deliver the request in. + /// </summary> + /// <value>The format.</value> + ResponseFormat IOAuthDirectResponseFormat.Format { + get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } + } + + /// <summary> /// Gets or sets the refresh token. /// </summary> /// <value>The refresh token.</value> @@ -88,10 +106,10 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { [MessagePart(Protocol.refresh_token, IsRequired = true, AllowEmpty = false)] internal string RefreshToken { get; set; } - ResponseFormat IOAuthDirectResponseFormat.Format { - get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } - } - + /// <summary> + /// Gets or sets the format the client is requesting the authorization server should deliver the request in. + /// </summary> + /// <value>The format.</value> [MessagePart(Protocol.format, Encoder = typeof(ResponseFormatEncoder))] private ResponseFormat? Format { get; set; } } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/ResponseFormat.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/ResponseFormat.cs index 720c62b..985ec3e 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/ResponseFormat.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/ResponseFormat.cs @@ -5,14 +5,23 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuthWrap.Messages { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - + /// <summary> + /// The various formats a client can request a response to come in from an authorization server. + /// </summary> public enum ResponseFormat { + /// <summary> + /// The response should be JSON encoded. + /// </summary> Json, + + /// <summary> + /// The response should be XML encoded. + /// </summary> Xml, + + /// <summary> + /// The response should be URL encoded. + /// </summary> Form, } } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UnauthorizedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UnauthorizedResponse.cs index 77befee..f77977d 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/UnauthorizedResponse.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UnauthorizedResponse.cs @@ -59,21 +59,45 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { #endregion + /// <summary> + /// Gets or sets the error message. + /// </summary> + /// <value>The error message.</value> [MessagePart("error")] internal string ErrorMessage { get; set; } + /// <summary> + /// Gets or sets the realm. + /// </summary> + /// <value>The realm.</value> [MessagePart("realm")] internal string Realm { get; set; } + /// <summary> + /// Gets or sets the scope. + /// </summary> + /// <value>The scope.</value> [MessagePart("scope")] internal string Scope { get; set; } + /// <summary> + /// Gets or sets the algorithms. + /// </summary> + /// <value>The algorithms.</value> [MessagePart("algorithms")] internal string Algorithms { get; set; } + /// <summary> + /// Gets or sets the user endpoint. + /// </summary> + /// <value>The user endpoint.</value> [MessagePart("user-uri")] internal Uri UserEndpoint { get; set; } + /// <summary> + /// Gets or sets the token endpoint. + /// </summary> + /// <value>The token endpoint.</value> [MessagePart("token-uri")] internal Uri TokenEndpoint { get; set; } } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentFailedResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentFailedResponse.cs index e08d332..e027267 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentFailedResponse.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentFailedResponse.cs @@ -11,6 +11,9 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { using System.Text; using DotNetOpenAuth.Messaging; + /// <summary> + /// An authorization denied response message in the user-agent flow. + /// </summary> internal class UserAgentFailedResponse : MessageBase, IHttpIndirectResponse { /// <summary> /// A constant parameter that indicates the user refused to grant the requested authorization. @@ -39,6 +42,10 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { [MessagePart(Protocol.state, IsRequired = false, AllowEmpty = true)] public string ClientState { get; set; } + /// <summary> + /// Gets a value indicating whether the payload for the message should be included + /// in the redirect fragment instead of the query string or POST entity. + /// </summary> bool IHttpIndirectResponse.Include301RedirectPayloadInFragment { get { return true; } } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentRequest.cs index b675898..f4c39a0 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentRequest.cs @@ -11,6 +11,10 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { using System.Text; using DotNetOpenAuth.Messaging; + /// <summary> + /// A message requesting user authorization to access protected data on behalf + /// of an installed application or browser-hosted Javascript. + /// </summary> internal class UserAgentRequest : MessageBase, IRequestWithRedirectUri { /// <summary> /// The type of message. diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentSuccessResponse.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentSuccessResponse.cs index 0b8f6cd..c7e2ad5 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentSuccessResponse.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UserAgent/UserAgentSuccessResponse.cs @@ -11,6 +11,9 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { using System.Text; using DotNetOpenAuth.Messaging; + /// <summary> + /// A message from the authorization server to a user-agent client indicating that authorization has been granted. + /// </summary> internal class UserAgentSuccessResponse : MessageBase, IHttpIndirectResponse, IAccessTokenSuccessResponse { /// <summary> /// Initializes a new instance of the <see cref="UserAgentSuccessResponse"/> class. @@ -22,10 +25,18 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { { } + /// <summary> + /// Gets a value indicating whether the payload for the message should be included + /// in the redirect fragment instead of the query string or POST entity. + /// </summary> bool IHttpIndirectResponse.Include301RedirectPayloadInFragment { get { return true; } } + /// <summary> + /// Gets the access token. + /// </summary> + /// <value>The access token.</value> [MessagePart(Protocol.access_token, IsRequired = true, AllowEmpty = false)] public string AccessToken { get; internal set; } @@ -56,6 +67,10 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { [MessagePart(Protocol.access_token_secret, IsRequired = false, AllowEmpty = false)] public string AccessTokenSecret { get; internal set; } + /// <summary> + /// Gets the scope. + /// </summary> + /// <value>The scope.</value> string IAccessTokenSuccessResponse.Scope { get { return null; } } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordRequest.cs index dedcc3e..3bc886a 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/UsernameAndPassword/UserNamePasswordRequest.cs @@ -22,6 +22,9 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { /// the confidential user credentials and use the delegation code going forward. /// </remarks> internal class UserNamePasswordRequest : MessageBase, IAccessTokenRequest, IOAuthDirectResponseFormat { + /// <summary> + /// A constant that identifies the flow this request belongs to. + /// </summary> [MessagePart(Protocol.type, IsRequired = true)] private const string Type = "username"; @@ -67,6 +70,24 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { public string ClientSecret { get; internal set; } /// <summary> + /// Gets or sets the type of the secret. + /// </summary> + /// <value>The type of the secret.</value> + /// <remarks> + /// OPTIONAL. The access token secret type as described by Section 5.3 (Cryptographic Tokens Requests). If omitted, the authorization server will issue a bearer token (an access token without a matching secret) as described by Section 5.2 (Bearer Token Requests). + /// </remarks> + [MessagePart(Protocol.secret_type, IsRequired = false, AllowEmpty = false)] + public string SecretType { get; set; } + + /// <summary> + /// Gets the format the client is requesting the authorization server should deliver the request in. + /// </summary> + /// <value>The format.</value> + ResponseFormat IOAuthDirectResponseFormat.Format { + get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } + } + + /// <summary> /// Gets or sets the user's account username. /// </summary> /// <value>The username on the user's account.</value> @@ -102,19 +123,9 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { internal string Scope { get; set; } /// <summary> - /// Gets or sets the type of the secret. + /// Gets or sets the format the client is requesting the authorization server should deliver the request in. /// </summary> - /// <value>The type of the secret.</value> - /// <remarks> - /// OPTIONAL. The access token secret type as described by Section 5.3 (Cryptographic Tokens Requests). If omitted, the authorization server will issue a bearer token (an access token without a matching secret) as described by Section 5.2 (Bearer Token Requests). - /// </remarks> - [MessagePart(Protocol.secret_type, IsRequired = false, AllowEmpty = false)] - public string SecretType { get; set; } - - ResponseFormat IOAuthDirectResponseFormat.Format { - get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } - } - + /// <value>The format.</value> [MessagePart(Protocol.format, Encoder = typeof(ResponseFormatEncoder))] private ResponseFormat? Format { get; set; } diff --git a/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebServerAccessTokenRequest.cs b/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebServerAccessTokenRequest.cs index ff5eb9a..56f9d91 100644 --- a/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebServerAccessTokenRequest.cs +++ b/src/DotNetOpenAuth/OAuthWrap/Messages/WebServer/WebServerAccessTokenRequest.cs @@ -49,15 +49,26 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { this.Format = ResponseFormat.Form; } + /// <summary> + /// Gets the type of the code or token. + /// </summary> + /// <value>The type of the code or token.</value> CodeOrTokenType ITokenCarryingRequest.CodeOrTokenType { get { return CodeOrTokenType.VerificationCode; } } + /// <summary> + /// Gets or sets the verification code or refresh/access token. + /// </summary> + /// <value>The code or token.</value> string ITokenCarryingRequest.CodeOrToken { get { return this.VerificationCode; } set { this.VerificationCode = value; } } + /// <summary> + /// Gets or sets the authorization that the token describes. + /// </summary> IAuthorizationDescription ITokenCarryingRequest.AuthorizationDescription { get; set; } /// <summary> @@ -78,6 +89,20 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { public string ClientSecret { get; set; } /// <summary> + /// Gets or sets the type of the secret. + /// </summary> + /// <value>The type of the secret.</value> + /// <remarks> + /// OPTIONAL. The access token secret type as described by Section 5.3 (Cryptographic Tokens Requests). If omitted, the authorization server will issue a bearer token (an access token without a matching secret) as described by Section 5.2 (Bearer Token Requests). + /// </remarks> + [MessagePart(Protocol.secret_type, IsRequired = false, AllowEmpty = false)] + public string SecretType { get; set; } + + ResponseFormat IOAuthDirectResponseFormat.Format { + get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } + } + + /// <summary> /// Gets or sets the verification code previously communicated to the Client /// in <see cref="WebServerSuccessResponse.VerificationCode"/>. /// </summary> @@ -95,19 +120,9 @@ namespace DotNetOpenAuth.OAuthWrap.Messages { internal Uri Callback { get; set; } /// <summary> - /// Gets or sets the type of the secret. + /// Gets or sets the format the client is requesting the authorization server should deliver the request in. /// </summary> - /// <value>The type of the secret.</value> - /// <remarks> - /// OPTIONAL. The access token secret type as described by Section 5.3 (Cryptographic Tokens Requests). If omitted, the authorization server will issue a bearer token (an access token without a matching secret) as described by Section 5.2 (Bearer Token Requests). - /// </remarks> - [MessagePart(Protocol.secret_type, IsRequired = false, AllowEmpty = false)] - public string SecretType { get; set; } - - ResponseFormat IOAuthDirectResponseFormat.Format { - get { return this.Format.HasValue ? this.Format.Value : ResponseFormat.Json; } - } - + /// <value>The format.</value> [MessagePart(Protocol.format, Encoder = typeof(ResponseFormatEncoder))] private ResponseFormat? Format { get; set; } diff --git a/src/DotNetOpenAuth/OAuthWrap/ResourceServer.cs b/src/DotNetOpenAuth/OAuthWrap/ResourceServer.cs index 29f5172..1696ecd 100644 --- a/src/DotNetOpenAuth/OAuthWrap/ResourceServer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/ResourceServer.cs @@ -50,15 +50,30 @@ namespace DotNetOpenAuth.OAuthWrap { public AuthorizationServerDescription AuthorizationServerDescription { get; set; } /// <summary> - /// Gets or sets the channel. + /// Gets the channel. /// </summary> /// <value>The channel.</value> internal OAuthWrapResourceServerChannel Channel { get; private set; } + /// <summary> + /// Discovers what access the client should have considering the access token in the current request. + /// </summary> + /// <param name="username">The name on the account the client has access to.</param> + /// <param name="scope">The set of operations the client is authorized for.</param> + /// <returns>An error to return to the client if access is not authorized; <c>null</c> if access is granted.</returns> public OutgoingWebResponse VerifyAccess(out string username, out string scope) { return this.VerifyAccess(this.Channel.GetRequestFromContext(), out username, out scope); } + /// <summary> + /// Discovers what access the client should have considering the access token in the current request. + /// </summary> + /// <param name="httpRequestInfo">The HTTP request info.</param> + /// <param name="username">The name on the account the client has access to.</param> + /// <param name="scope">The set of operations the client is authorized for.</param> + /// <returns> + /// An error to return to the client if access is not authorized; <c>null</c> if access is granted. + /// </returns> public virtual OutgoingWebResponse VerifyAccess(HttpRequestInfo httpRequestInfo, out string username, out string scope) { Contract.Requires<ArgumentNullException>(httpRequestInfo != null, "httpRequestInfo"); diff --git a/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs b/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs index 01b2ef6..769fba1 100644 --- a/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/StandardAccessTokenAnalyzer.cs @@ -5,25 +5,46 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuthWrap { - using System; - using System.Collections.Generic; - using System.Diagnostics.Contracts; - using System.Linq; using System.Security.Cryptography; - using System.Text; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuthWrap.ChannelElements; + /// <summary> + /// An access token reader that understands DotNetOpenAuth authorization server issued tokens. + /// </summary> public class StandardAccessTokenAnalyzer : IAccessTokenAnalyzer { + /// <summary> + /// Initializes a new instance of the <see cref="StandardAccessTokenAnalyzer"/> class. + /// </summary> + /// <param name="authorizationServerPublicSigningKey">The authorization server public signing key.</param> + /// <param name="resourceServerPrivateEncryptionKey">The resource server private encryption key.</param> public StandardAccessTokenAnalyzer(RSAParameters authorizationServerPublicSigningKey, RSAParameters resourceServerPrivateEncryptionKey) { this.AuthorizationServerPublicSigningKey = authorizationServerPublicSigningKey; this.ResourceServerPrivateEncryptionKey = resourceServerPrivateEncryptionKey; } + /// <summary> + /// Gets the authorization server public signing key. + /// </summary> + /// <value>The authorization server public signing key.</value> public RSAParameters AuthorizationServerPublicSigningKey { get; private set; } + /// <summary> + /// Gets the resource server private encryption key. + /// </summary> + /// <value>The resource server private encryption key.</value> public RSAParameters ResourceServerPrivateEncryptionKey { get; private set; } + /// <summary> + /// Reads an access token to find out what data it authorizes access to. + /// </summary> + /// <param name="message">The message carrying the access token.</param> + /// <param name="accessToken">The access token.</param> + /// <param name="user">The user whose data is accessible with this access token.</param> + /// <param name="scope">The scope of access authorized by this access token.</param> + /// <returns> + /// A value indicating whether this access token is valid. + /// </returns> public bool TryValidateAccessToken(IDirectedProtocolMessage message, string accessToken, out string user, out string scope) { var token = AccessToken.Decode(this.AuthorizationServerPublicSigningKey, this.ResourceServerPrivateEncryptionKey, accessToken, message); user = token.User; diff --git a/src/DotNetOpenAuth/OAuthWrap/UserAgentClient.cs b/src/DotNetOpenAuth/OAuthWrap/UserAgentClient.cs index 9296cad..9b23bc1 100644 --- a/src/DotNetOpenAuth/OAuthWrap/UserAgentClient.cs +++ b/src/DotNetOpenAuth/OAuthWrap/UserAgentClient.cs @@ -7,16 +7,21 @@ namespace DotNetOpenAuth.OAuthWrap { using System; using System.Collections.Generic; + using System.Diagnostics.Contracts; using System.Linq; using System.Text; - using DotNetOpenAuth.OAuthWrap.Messages; using DotNetOpenAuth.Messaging; - using System.Diagnostics.Contracts; + using DotNetOpenAuth.OAuthWrap.Messages; + /// <summary> + /// The OAuth client for the user-agent flow, providing services for installed apps + /// and in-browser Javascript widgets. + /// </summary> public class UserAgentClient : ClientBase { /// <summary> /// Initializes a new instance of the <see cref="UserAgentClient"/> class. /// </summary> + /// <param name="authorizationServer">The token issuer.</param> public UserAgentClient(AuthorizationServerDescription authorizationServer) : base(authorizationServer) { } @@ -30,6 +35,13 @@ namespace DotNetOpenAuth.OAuthWrap { Contract.Requires<ArgumentNullException>(authorizationEndpoint != null, "authorizationEndpoint"); } + /// <summary> + /// Generates a URL that the user's browser can be directed to in order to authorize + /// this client to access protected data at some resource server. + /// </summary> + /// <param name="authorization">The authorization state that is tracking this particular request. Optional.</param> + /// <param name="immediate">If set to <c>true</c>, the authorization server will return immediately instead of interacting with the user. Authorization will only be granted if the authorization server determines it is safe to do so without asking the user first.</param> + /// <returns>A fully-qualified URL suitable to initiate the authorization flow.</returns> public Uri RequestUserAuthorization(IAuthorizationState authorization = null, bool immediate = false) { Contract.Requires<InvalidOperationException>(!string.IsNullOrEmpty(this.ClientIdentifier)); @@ -52,6 +64,12 @@ namespace DotNetOpenAuth.OAuthWrap { return this.Channel.PrepareResponse(request).GetDirectUriRequest(this.Channel); } + /// <summary> + /// Scans the incoming request for an authorization response message. + /// </summary> + /// <param name="actualRedirectUrl">The actual URL of the incoming HTTP request.</param> + /// <param name="authorization">The authorization.</param> + /// <returns>The granted authorization, or <c>null</c> if the incoming HTTP request did not contain an authorization server response or authorization was rejected.</returns> public IAuthorizationState ProcessUserAuthorization(Uri actualRedirectUrl, IAuthorizationState authorization = null) { Contract.Requires<ArgumentNullException>(actualRedirectUrl != null, "actualRedirectUrl"); diff --git a/src/DotNetOpenAuth/OAuthWrap/WebServerAuthorizationServer.cs b/src/DotNetOpenAuth/OAuthWrap/WebServerAuthorizationServer.cs index 5461e4c..de9d5b3 100644 --- a/src/DotNetOpenAuth/OAuthWrap/WebServerAuthorizationServer.cs +++ b/src/DotNetOpenAuth/OAuthWrap/WebServerAuthorizationServer.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------- -// <copyright file="WebAppAuthorizationServer.cs" company="Andrew Arnott"> +// <copyright file="WebServerAuthorizationServer.cs" company="Andrew Arnott"> // Copyright (c) Andrew Arnott. All rights reserved. // </copyright> //----------------------------------------------------------------------- @@ -15,6 +15,9 @@ namespace DotNetOpenAuth.OAuthWrap { using DotNetOpenAuth.OAuthWrap.ChannelElements; using DotNetOpenAuth.OAuthWrap.Messages; + /// <summary> + /// Authorization Server supporting the web server flow. + /// </summary> public class WebServerAuthorizationServer : AuthorizationServerBase { /// <summary> /// Initializes a new instance of the <see cref="WebServerAuthorizationServer"/> class. @@ -66,7 +69,7 @@ namespace DotNetOpenAuth.OAuthWrap { { Contract.Requires<ArgumentNullException>(httpRequestInfo != null, "httpRequestInfo"); - var request = ReadAccessTokenRequest(httpRequestInfo); + var request = this.ReadAccessTokenRequest(httpRequestInfo); if (request != null) { // This convenience method only encrypts access tokens assuming that this auth server diff --git a/src/DotNetOpenAuth/OAuthWrap/WebServerClient.cs b/src/DotNetOpenAuth/OAuthWrap/WebServerClient.cs index 977fddb..7e883b1 100644 --- a/src/DotNetOpenAuth/OAuthWrap/WebServerClient.cs +++ b/src/DotNetOpenAuth/OAuthWrap/WebServerClient.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------- -// <copyright file="WebAppClient.cs" company="Andrew Arnott"> +// <copyright file="WebServerClient.cs" company="Andrew Arnott"> // Copyright (c) Andrew Arnott. All rights reserved. // </copyright> //----------------------------------------------------------------------- @@ -27,12 +27,25 @@ namespace DotNetOpenAuth.OAuthWrap { : base(authorizationServer) { } + /// <summary> + /// Gets or sets the token manager. + /// </summary> + /// <value>The token manager.</value> public IClientTokenManager TokenManager { get; set; } + /// <summary> + /// Prepares a request for user authorization from an authorization server. + /// </summary> + /// <returns>The authorization request.</returns> public WebServerRequest PrepareRequestUserAuthorization() { return this.PrepareRequestUserAuthorization(new AuthorizationState()); } + /// <summary> + /// Prepares a request for user authorization from an authorization server. + /// </summary> + /// <param name="authorization">The authorization state to associate with this particular request.</param> + /// <returns>The authorization request.</returns> public WebServerRequest PrepareRequestUserAuthorization(IAuthorizationState authorization) { Contract.Requires<ArgumentNullException>(authorization != null); Contract.Requires<InvalidOperationException>(authorization.Callback != null || (HttpContext.Current != null && HttpContext.Current.Request != null), MessagingStrings.HttpContextRequired); @@ -56,6 +69,11 @@ namespace DotNetOpenAuth.OAuthWrap { return request; } + /// <summary> + /// Processes the authorization response from an authorization server, if available. + /// </summary> + /// <param name="request">The incoming HTTP request that may carry an authorization response.</param> + /// <returns>The authorization state that contains the details of the authorization.</returns> public IAuthorizationState ProcessUserAuthorization(HttpRequestInfo request = null) { Contract.Requires<InvalidOperationException>(!string.IsNullOrEmpty(this.ClientIdentifier)); Contract.Requires<InvalidOperationException>(!string.IsNullOrEmpty(this.ClientSecret)); diff --git a/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs b/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs index 4b38e8a..8000d02 100644 --- a/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs +++ b/src/DotNetOpenAuth/OAuthWrap/WrapUtilities.cs @@ -4,8 +4,6 @@ // </copyright> //----------------------------------------------------------------------- -using DotNetOpenAuth.Messaging; - namespace DotNetOpenAuth.OAuthWrap { using System; using System.Collections.Generic; @@ -14,6 +12,8 @@ namespace DotNetOpenAuth.OAuthWrap { using System.Linq; using System.Net; using System.Text; + using DotNetOpenAuth.Messaging; + using DotNetOpenAuth.OAuth.ChannelElements; /// <summary> /// Some common utility methods for OAuth WRAP. @@ -33,7 +33,13 @@ namespace DotNetOpenAuth.OAuthWrap { Uri.EscapeDataString(accessToken)); } - internal static DotNetOpenAuth.OAuth.ChannelElements.IConsumerDescription GetClientOrThrow(this IAuthorizationServer authorizationServer, string clientIdentifier) { + /// <summary> + /// Gets information about the client with a given identifier. + /// </summary> + /// <param name="authorizationServer">The authorization server.</param> + /// <param name="clientIdentifier">The client identifier.</param> + /// <returns>The client information. Never null.</returns> + internal static IConsumerDescription GetClientOrThrow(this IAuthorizationServer authorizationServer, string clientIdentifier) { Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(clientIdentifier)); Contract.Ensures(Contract.Result<DotNetOpenAuth.OAuth.ChannelElements.IConsumerDescription>() != null); |