diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2010-08-01 19:58:03 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2010-08-01 19:58:03 -0700 |
commit | 3208068bbd28cd50be5559662e33284c97e43b53 (patch) | |
tree | 703d8129d6e2292eed00bc29bba1be5a975b685f | |
parent | 7a700c1d1933a3a38f311bb0b2fcb7a96b122b0a (diff) | |
download | DotNetOpenAuth-3208068bbd28cd50be5559662e33284c97e43b53.zip DotNetOpenAuth-3208068bbd28cd50be5559662e33284c97e43b53.tar.gz DotNetOpenAuth-3208068bbd28cd50be5559662e33284c97e43b53.tar.bz2 |
Now StyleCop clean.
28 files changed, 815 insertions, 611 deletions
diff --git a/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs b/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs index 69181f5..d5509aa 100644 --- a/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs +++ b/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs @@ -15,8 +15,13 @@ namespace RelyingPartyLogic { using DotNetOpenAuth.OAuth2.Messages; public class OAuthServiceProvider { - private const string PendingAuthorizationRequestSessionKey = "PendingAuthorizationRequest"; - + private const string PendingAuthorizationRequestSessionKey = "PendingAuthorizationRequest";
+
+ /// <summary>
+ /// The lock to synchronize initialization of the <see cref="authorizationServer"/> field.
+ /// </summary>
+ private static readonly object InitializerLock = new object();
+
/// <summary> /// The shared service description for this web site. /// </summary> @@ -28,11 +33,6 @@ namespace RelyingPartyLogic { private static AuthorizationServer authorizationServer; /// <summary> - /// The lock to synchronize initialization of the <see cref="authorizationServer"/> field. - /// </summary> - private static readonly object InitializerLock = new object(); - - /// <summary> /// Gets the service provider. /// </summary> /// <value>The service provider.</value> diff --git a/samples/OAuthAuthorizationServer/Controllers/AccountController.cs b/samples/OAuthAuthorizationServer/Controllers/AccountController.cs index 1361376..73d5999 100644 --- a/samples/OAuthAuthorizationServer/Controllers/AccountController.cs +++ b/samples/OAuthAuthorizationServer/Controllers/AccountController.cs @@ -1,28 +1,19 @@ -namespace OAuthAuthorizationServer.Controllers { - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Linq; - using System.Security.Principal; - using System.Web; - using System.Web.Mvc; - using System.Web.Routing; - using System.Web.Security; - - using DotNetOpenAuth.OpenId; - using DotNetOpenAuth.OpenId.RelyingParty; - - using OAuthAuthorizationServer.Models; - - using DotNetOpenAuth.Messaging; - +namespace OAuthAuthorizationServer.Controllers {
+ using System;
+ using System.Web.Mvc;
+ using System.Web.Security;
+
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.OpenId.RelyingParty;
+
+ using OAuthAuthorizationServer.Models;
+
[HandleError] public class AccountController : Controller { - // ************************************** // URL: /Account/LogOn // ************************************** - public ActionResult LogOn() { return View(); } @@ -36,7 +27,7 @@ request.AddCallbackArguments("returnUrl", returnUrl); return request.RedirectingResponse.AsActionResult(); } else { - ModelState.AddModelError("", "The identifier you supplied is not recognized as a valid OpenID Identifier."); + ModelState.AddModelError(string.Empty, "The identifier you supplied is not recognized as a valid OpenID Identifier."); } } @@ -52,8 +43,8 @@ case AuthenticationStatus.Authenticated: FormsAuthentication.SetAuthCookie(response.ClaimedIdentifier, false); return this.Redirect(returnUrl); - default: - ModelState.AddModelError("", "An error occurred during login."); + default:
+ ModelState.AddModelError(string.Empty, "An error occurred during login."); break; } } @@ -64,7 +55,6 @@ // ************************************** // URL: /Account/LogOff // ************************************** - public ActionResult LogOff() { FormsAuthentication.SignOut(); diff --git a/samples/OAuthAuthorizationServer/Controllers/HomeController.cs b/samples/OAuthAuthorizationServer/Controllers/HomeController.cs index 5a6a8d3..78075cf 100644 --- a/samples/OAuthAuthorizationServer/Controllers/HomeController.cs +++ b/samples/OAuthAuthorizationServer/Controllers/HomeController.cs @@ -1,16 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; - -namespace OAuthAuthorizationServer.Controllers { - using System.Configuration; - using System.Data.SqlClient; - using System.IO; - - using OAuthAuthorizationServer.Code; - +namespace OAuthAuthorizationServer.Controllers {
+ using System.Configuration;
+ using System.Data.SqlClient;
+ using System.IO;
+ using System.Linq;
+ using System.Web.Mvc;
+
+ using OAuthAuthorizationServer.Code;
+
[HandleError] public class HomeController : Controller { public ActionResult Index() { diff --git a/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs b/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs index 0eb7c83..3c7ba53 100644 --- a/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs +++ b/samples/OAuthAuthorizationServer/Controllers/OAuthController.cs @@ -32,11 +32,12 @@ #else [Obsolete("You must use a real key for a real app.", true)] private static readonly RSAParameters ResourceServerEncryptionPublicKey = new RSAParameters(); -#endif - - /// <summary> - /// The OAuth 2.0 token endpoint. - /// </summary> +#endif
+
+ /// <summary>
+ /// The OAuth 2.0 token endpoint.
+ /// </summary>
+ /// <returns>The response to the Client.</returns>
public ActionResult Token() { var request = this.authorizationServer.ReadAccessTokenRequest(); if (request != null) { @@ -56,8 +57,12 @@ } throw new HttpException((int)HttpStatusCode.BadRequest, "Missing OAuth 2.0 request message."); - } - + }
+
+ /// <summary>
+ /// Prompts the user to authorize a client to access the user's private data.
+ /// </summary>
+ /// <returns>The browser HTML response that prompts the user to authorize the client.</returns>
[Authorize, AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)] public ActionResult Authorize() { var pendingRequest = this.authorizationServer.ReadAuthorizationRequest(); @@ -80,11 +85,16 @@ }; return View(model); - } - + }
+
+ /// <summary>
+ /// Processes the user's response as to whether to authorize a Client to access his/her private data.
+ /// </summary>
+ /// <param name="isApproved">if set to <c>true</c>, the user has authorized the Client; <c>false</c> otherwise.</param>
+ /// <returns>HTML response that redirects the browser to the Client.</returns>
[Authorize, HttpPost, ValidateAntiForgeryToken] - public ActionResult AuthorizeResponse(bool isApproved) { - var pendingRequest = authorizationServer.ReadAuthorizationRequest(); + public ActionResult AuthorizeResponse(bool isApproved) {
+ var pendingRequest = this.authorizationServer.ReadAuthorizationRequest(); if (pendingRequest == null) { throw new HttpException((int)HttpStatusCode.BadRequest, "Missing authorization request."); } @@ -103,13 +113,13 @@ }); // In this simple sample, the user either agrees to the entire scope requested by the client or none of it. - // But in a real app, you could grant a reduced scope of access to the client by passing a scope parameter to this method. - response = authorizationServer.PrepareApproveAuthorizationRequest(pendingRequest, User.Identity.Name); - } else { - response = authorizationServer.PrepareRejectAuthorizationRequest(pendingRequest); + // But in a real app, you could grant a reduced scope of access to the client by passing a scope parameter to this method.
+ response = this.authorizationServer.PrepareApproveAuthorizationRequest(pendingRequest, User.Identity.Name); + } else {
+ response = this.authorizationServer.PrepareRejectAuthorizationRequest(pendingRequest); } - return authorizationServer.Channel.PrepareResponse(response).AsActionResult(); + return this.authorizationServer.Channel.PrepareResponse(response).AsActionResult(); } } } diff --git a/samples/OAuthAuthorizationServer/Global.asax.cs b/samples/OAuthAuthorizationServer/Global.asax.cs index 62fd93b..97e0016 100644 --- a/samples/OAuthAuthorizationServer/Global.asax.cs +++ b/samples/OAuthAuthorizationServer/Global.asax.cs @@ -1,19 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; - -namespace OAuthAuthorizationServer { - using System.Text; - - using DotNetOpenAuth.OAuth2; - using Code; - - // Note: For instructions on enabling IIS6 or IIS7 classic mode, - // visit http://go.microsoft.com/?LinkId=9394801 - +namespace OAuthAuthorizationServer {
+ using System;
+ using System.Linq;
+ using System.Text;
+ using System.Web;
+ using System.Web.Mvc;
+ using System.Web.Routing;
+
+ using Code;
+
+ /// <summary>
+ /// The global MVC Application.
+ /// </summary>
+ /// <remarks>
+ /// Note: For instructions on enabling IIS6 or IIS7 classic mode,
+ /// visit http://go.microsoft.com/?LinkId=9394801
+ /// </remarks>
public class MvcApplication : System.Web.HttpApplication { /// <summary> /// An application memory cache of recent log messages. @@ -27,53 +28,17 @@ namespace OAuthAuthorizationServer { public static DatabaseNonceStore NonceStore { get; set; } - public static void RegisterRoutes(RouteCollection routes) { - routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); - - routes.MapRoute( - "Default", // Route name - "{controller}/{action}/{id}", // URL with parameters - new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults - ); - } - - protected void Application_Start() { - AreaRegistration.RegisterAllAreas(); - - RegisterRoutes(RouteTable.Routes); - - NonceStore = new DatabaseNonceStore(); - - log4net.Config.XmlConfigurator.Configure(); - Logger.Info("Sample starting..."); - } - - private void Application_End(object sender, EventArgs e) { - Logger.Info("Sample shutting down..."); - - // this would be automatic, but in partial trust scenarios it is not. - log4net.LogManager.Shutdown(); - } - - private void Application_Error(object sender, EventArgs e) { - Logger.Error("An unhandled exception occurred in ASP.NET processing: " + Server.GetLastError(), Server.GetLastError()); - } - - private void Application_EndRequest(object sender, EventArgs e) { - CommitAndCloseDatabaseIfNecessary(); - } - /// <summary> /// Gets the transaction-protected database connection for the current request. /// </summary> public static DataClassesDataContext DataContext { get { - DataClassesDataContext dataContext = dataContextSimple; + DataClassesDataContext dataContext = DataContextSimple; if (dataContext == null) { dataContext = new DataClassesDataContext(); dataContext.Connection.Open(); dataContext.Transaction = dataContext.Connection.BeginTransaction(); - dataContextSimple = dataContext; + DataContextSimple = dataContext; } return dataContext; @@ -84,7 +49,7 @@ namespace OAuthAuthorizationServer { get { return DataContext.Users.SingleOrDefault(user => user.OpenIDClaimedIdentifier == HttpContext.Current.User.Identity.Name); } } - private static DataClassesDataContext dataContextSimple { + private static DataClassesDataContext DataContextSimple { get { if (HttpContext.Current != null) { return HttpContext.Current.Items["DataContext"] as DataClassesDataContext; @@ -100,10 +65,45 @@ namespace OAuthAuthorizationServer { throw new InvalidOperationException(); } } - } - + }
+
+ public static void RegisterRoutes(RouteCollection routes) {
+ routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
+
+ routes.MapRoute(
+ "Default", // Route name
+ "{controller}/{action}/{id}", // URL with parameters
+ new { controller = "Home", action = "Index", id = UrlParameter.Optional }); // Parameter defaults
+ }
+
+ protected void Application_Start() {
+ AreaRegistration.RegisterAllAreas();
+
+ RegisterRoutes(RouteTable.Routes);
+
+ NonceStore = new DatabaseNonceStore();
+
+ log4net.Config.XmlConfigurator.Configure();
+ Logger.Info("Sample starting...");
+ }
+
+ protected void Application_End(object sender, EventArgs e) {
+ Logger.Info("Sample shutting down...");
+
+ // this would be automatic, but in partial trust scenarios it is not.
+ log4net.LogManager.Shutdown();
+ }
+
+ protected void Application_Error(object sender, EventArgs e) {
+ Logger.Error("An unhandled exception occurred in ASP.NET processing: " + Server.GetLastError(), Server.GetLastError());
+ }
+
+ protected void Application_EndRequest(object sender, EventArgs e) {
+ CommitAndCloseDatabaseIfNecessary();
+ }
+
private static void CommitAndCloseDatabaseIfNecessary() { - var dataContext = dataContextSimple; + var dataContext = DataContextSimple; if (dataContext != null) { dataContext.SubmitChanges(); dataContext.Transaction.Commit(); diff --git a/samples/OAuthClient/SampleWcf2.aspx.cs b/samples/OAuthClient/SampleWcf2.aspx.cs index 7e0ea10..058ba47 100644 --- a/samples/OAuthClient/SampleWcf2.aspx.cs +++ b/samples/OAuthClient/SampleWcf2.aspx.cs @@ -64,10 +64,10 @@ foreach (var li in this.scopeList.Items.OfType<ListItem>().Where(li => Authorization.Scope.Contains(li.Value))) { li.Selected = true; } - authorizationLabel.Text = "Authorization received!"; + this.authorizationLabel.Text = "Authorization received!"; if (Authorization.AccessTokenExpirationUtc.HasValue) { - TimeSpan timeLeft = Authorization.AccessTokenExpirationUtc.Value - DateTime.UtcNow; - authorizationLabel.Text += string.Format(CultureInfo.CurrentCulture, " (access token expires in {0} minutes)", Math.Round(timeLeft.TotalMinutes, 1)); + TimeSpan timeLeft = Authorization.AccessTokenExpirationUtc.Value - DateTime.UtcNow;
+ this.authorizationLabel.Text += string.Format(CultureInfo.CurrentCulture, " (access token expires in {0} minutes)", Math.Round(timeLeft.TotalMinutes, 1)); } } @@ -118,8 +118,8 @@ // Refresh the access token if it expires and if its lifetime is too short to be of use. if (Authorization.AccessTokenExpirationUtc.HasValue) { if (Client.RefreshToken(Authorization, TimeSpan.FromSeconds(30))) { - TimeSpan timeLeft = Authorization.AccessTokenExpirationUtc.Value - DateTime.UtcNow; - authorizationLabel.Text += string.Format(CultureInfo.CurrentCulture, " - just renewed for {0} more minutes)", Math.Round(timeLeft.TotalMinutes, 1)); + TimeSpan timeLeft = Authorization.AccessTokenExpirationUtc.Value - DateTime.UtcNow;
+ this.authorizationLabel.Text += string.Format(CultureInfo.CurrentCulture, " - just renewed for {0} more minutes)", Math.Round(timeLeft.TotalMinutes, 1)); } } diff --git a/samples/OAuthResourceServer/Code/Global.cs b/samples/OAuthResourceServer/Code/Global.cs index 6e03fc2..8470bbd 100644 --- a/samples/OAuthResourceServer/Code/Global.cs +++ b/samples/OAuthResourceServer/Code/Global.cs @@ -12,16 +12,6 @@ /// The web application global events and properties. /// </summary> public class Global : HttpApplication { - /// <summary> - /// An application memory cache of recent log messages. - /// </summary> - public static StringBuilder LogMessages = new StringBuilder(); - - /// <summary> - /// The logger for this sample to use. - /// </summary> - public static log4net.ILog Logger = log4net.LogManager.GetLogger("DotNetOpenAuth.OAuthResourceServer"); - #if SAMPLESONLY /// <summary> /// The FOR SAMPLE ONLY hard-coded public key of the authorization server that is used to verify the signature on access tokens. @@ -36,32 +26,44 @@ Exponent = new byte[] { 1, 0, 1 }, Modulus = new byte[] { 210, 95, 53, 12, 203, 114, 150, 23, 23, 88, 4, 200, 47, 219, 73, 54, 146, 253, 126, 121, 105, 91, 118, 217, 182, 167, 140, 6, 67, 112, 97, 183, 66, 112, 245, 103, 136, 222, 205, 28, 196, 45, 6, 223, 192, 76, 56, 180, 90, 120, 144, 19, 31, 193, 37, 129, 186, 214, 36, 53, 204, 53, 108, 133, 112, 17, 133, 244, 3, 12, 230, 29, 243, 51, 79, 253, 10, 111, 185, 23, 74, 230, 99, 94, 78, 49, 209, 39, 95, 213, 248, 212, 22, 4, 222, 145, 77, 190, 136, 230, 134, 70, 228, 241, 194, 216, 163, 234, 52, 1, 64, 181, 139, 128, 90, 255, 214, 60, 168, 233, 254, 110, 31, 102, 58, 67, 201, 33 }, }; - - /// <summary> - /// The FOR SAMPLE ONLY hard-coded private key used to decrypt access tokens intended for this resource server. - /// </summary> - /// <remarks> - /// In a real app, the resource server would likely use its own HTTPS certificate or some other certificate stored securely - /// on the server rather than hard-coded in compiled code for security and ease of changing the certificate in case it was compromised. - /// </remarks> - internal static readonly RSAParameters ResourceServerEncryptionPrivateKey = new RSAParameters { - Exponent = new byte[] { 1, 0, 1 }, - Modulus = new byte[] { 166, 175, 117, 169, 211, 251, 45, 215, 55, 53, 202, 65, 153, 155, 92, 219, 235, 243, 61, 170, 101, 250, 221, 214, 239, 175, 238, 175, 239, 20, 144, 72, 227, 221, 4, 219, 32, 225, 101, 96, 18, 33, 117, 176, 110, 123, 109, 23, 29, 85, 93, 50, 129, 163, 113, 57, 122, 212, 141, 145, 17, 31, 67, 165, 181, 91, 117, 23, 138, 251, 198, 132, 188, 213, 10, 157, 116, 229, 48, 168, 8, 127, 28, 156, 239, 124, 117, 36, 232, 100, 222, 23, 52, 186, 239, 5, 63, 207, 185, 16, 137, 73, 137, 147, 252, 71, 9, 239, 113, 27, 88, 255, 91, 56, 192, 142, 210, 21, 34, 81, 204, 239, 57, 60, 140, 249, 15, 101 }, - P = new byte[] { 227, 25, 96, 71, 220, 99, 11, 55, 15, 241, 153, 20, 32, 213, 68, 127, 246, 162, 153, 204, 98, 26, 10, 99, 46, 189, 35, 18, 162, 180, 184, 134, 230, 198, 156, 87, 52, 174, 74, 155, 163, 204, 252, 51, 232, 189, 135, 172, 88, 24, 52, 174, 72, 157, 81, 90, 118, 59, 142, 154, 152, 201, 62, 177 }, - Q = new byte[] { 187, 229, 223, 233, 118, 20, 5, 251, 85, 8, 196, 3, 220, 232, 38, 159, 15, 95, 174, 162, 36, 13, 138, 239, 16, 85, 220, 104, 4, 162, 174, 160, 234, 133, 156, 33, 117, 139, 22, 112, 108, 214, 97, 178, 100, 191, 13, 177, 164, 30, 124, 48, 33, 118, 21, 137, 38, 59, 191, 13, 183, 5, 16, 245 }, - DP = new byte[] { 225, 112, 117, 117, 160, 191, 233, 136, 53, 153, 158, 94, 174, 225, 71, 104, 200, 75, 77, 229, 232, 148, 245, 46, 212, 93, 9, 142, 28, 90, 206, 187, 140, 40, 41, 87, 32, 130, 204, 169, 136, 135, 154, 237, 100, 227, 144, 229, 115, 102, 68, 21, 167, 28, 20, 128, 122, 210, 80, 148, 3, 139, 243, 97 }, - DQ = new byte[] { 133, 252, 100, 207, 232, 184, 92, 143, 157, 82, 115, 220, 65, 81, 118, 0, 228, 136, 153, 81, 219, 157, 160, 157, 218, 171, 47, 81, 41, 69, 12, 123, 136, 224, 159, 182, 40, 72, 119, 70, 210, 5, 137, 131, 25, 94, 55, 152, 157, 236, 115, 40, 43, 36, 54, 53, 39, 131, 97, 56, 153, 114, 206, 101 }, - InverseQ = new byte[] { 129, 119, 84, 118, 29, 35, 194, 186, 96, 169, 7, 7, 200, 22, 187, 34, 72, 131, 200, 246, 79, 120, 49, 242, 8, 220, 74, 114, 195, 95, 90, 108, 80, 2, 212, 71, 125, 100, 184, 77, 203, 236, 64, 122, 108, 212, 150, 129, 66, 248, 218, 3, 186, 71, 213, 236, 142, 66, 33, 196, 150, 216, 138, 114 }, - D = new byte[] { 94, 20, 94, 119, 18, 92, 141, 13, 17, 238, 92, 80, 22, 96, 232, 82, 128, 164, 115, 195, 191, 119, 142, 202, 135, 210, 103, 8, 10, 11, 51, 60, 208, 207, 168, 179, 253, 164, 250, 80, 245, 42, 201, 128, 97, 123, 108, 161, 69, 63, 47, 49, 24, 150, 165, 139, 105, 214, 154, 104, 172, 159, 86, 208, 64, 134, 158, 156, 234, 125, 140, 210, 3, 32, 60, 28, 62, 154, 198, 21, 132, 191, 236, 10, 158, 12, 247, 159, 177, 77, 178, 53, 238, 95, 165, 9, 200, 28, 148, 242, 35, 70, 189, 121, 169, 248, 97, 91, 111, 45, 103, 1, 167, 220, 67, 250, 175, 89, 122, 238, 192, 144, 142, 248, 198, 101, 96, 129 }, - }; #else [Obsolete("You must use a real key for a real app.", true)] public static readonly RSAParameters AuthorizationServerSigningPublicKey = new RSAParameters(); - +#endif
+
+ /// <summary>
+ /// An application memory cache of recent log messages.
+ /// </summary>
+ public static StringBuilder LogMessages = new StringBuilder();
+
+ /// <summary>
+ /// The logger for this sample to use.
+ /// </summary>
+ public static log4net.ILog Logger = log4net.LogManager.GetLogger("DotNetOpenAuth.OAuthResourceServer");
+
+#if SAMPLESONLY
+ /// <summary>
+ /// The FOR SAMPLE ONLY hard-coded private key used to decrypt access tokens intended for this resource server.
+ /// </summary>
+ /// <remarks>
+ /// In a real app, the resource server would likely use its own HTTPS certificate or some other certificate stored securely
+ /// on the server rather than hard-coded in compiled code for security and ease of changing the certificate in case it was compromised.
+ /// </remarks>
+ internal static readonly RSAParameters ResourceServerEncryptionPrivateKey = new RSAParameters {
+ Exponent = new byte[] { 1, 0, 1 },
+ Modulus = new byte[] { 166, 175, 117, 169, 211, 251, 45, 215, 55, 53, 202, 65, 153, 155, 92, 219, 235, 243, 61, 170, 101, 250, 221, 214, 239, 175, 238, 175, 239, 20, 144, 72, 227, 221, 4, 219, 32, 225, 101, 96, 18, 33, 117, 176, 110, 123, 109, 23, 29, 85, 93, 50, 129, 163, 113, 57, 122, 212, 141, 145, 17, 31, 67, 165, 181, 91, 117, 23, 138, 251, 198, 132, 188, 213, 10, 157, 116, 229, 48, 168, 8, 127, 28, 156, 239, 124, 117, 36, 232, 100, 222, 23, 52, 186, 239, 5, 63, 207, 185, 16, 137, 73, 137, 147, 252, 71, 9, 239, 113, 27, 88, 255, 91, 56, 192, 142, 210, 21, 34, 81, 204, 239, 57, 60, 140, 249, 15, 101 },
+ P = new byte[] { 227, 25, 96, 71, 220, 99, 11, 55, 15, 241, 153, 20, 32, 213, 68, 127, 246, 162, 153, 204, 98, 26, 10, 99, 46, 189, 35, 18, 162, 180, 184, 134, 230, 198, 156, 87, 52, 174, 74, 155, 163, 204, 252, 51, 232, 189, 135, 172, 88, 24, 52, 174, 72, 157, 81, 90, 118, 59, 142, 154, 152, 201, 62, 177 },
+ Q = new byte[] { 187, 229, 223, 233, 118, 20, 5, 251, 85, 8, 196, 3, 220, 232, 38, 159, 15, 95, 174, 162, 36, 13, 138, 239, 16, 85, 220, 104, 4, 162, 174, 160, 234, 133, 156, 33, 117, 139, 22, 112, 108, 214, 97, 178, 100, 191, 13, 177, 164, 30, 124, 48, 33, 118, 21, 137, 38, 59, 191, 13, 183, 5, 16, 245 },
+ DP = new byte[] { 225, 112, 117, 117, 160, 191, 233, 136, 53, 153, 158, 94, 174, 225, 71, 104, 200, 75, 77, 229, 232, 148, 245, 46, 212, 93, 9, 142, 28, 90, 206, 187, 140, 40, 41, 87, 32, 130, 204, 169, 136, 135, 154, 237, 100, 227, 144, 229, 115, 102, 68, 21, 167, 28, 20, 128, 122, 210, 80, 148, 3, 139, 243, 97 },
+ DQ = new byte[] { 133, 252, 100, 207, 232, 184, 92, 143, 157, 82, 115, 220, 65, 81, 118, 0, 228, 136, 153, 81, 219, 157, 160, 157, 218, 171, 47, 81, 41, 69, 12, 123, 136, 224, 159, 182, 40, 72, 119, 70, 210, 5, 137, 131, 25, 94, 55, 152, 157, 236, 115, 40, 43, 36, 54, 53, 39, 131, 97, 56, 153, 114, 206, 101 },
+ InverseQ = new byte[] { 129, 119, 84, 118, 29, 35, 194, 186, 96, 169, 7, 7, 200, 22, 187, 34, 72, 131, 200, 246, 79, 120, 49, 242, 8, 220, 74, 114, 195, 95, 90, 108, 80, 2, 212, 71, 125, 100, 184, 77, 203, 236, 64, 122, 108, 212, 150, 129, 66, 248, 218, 3, 186, 71, 213, 236, 142, 66, 33, 196, 150, 216, 138, 114 },
+ D = new byte[] { 94, 20, 94, 119, 18, 92, 141, 13, 17, 238, 92, 80, 22, 96, 232, 82, 128, 164, 115, 195, 191, 119, 142, 202, 135, 210, 103, 8, 10, 11, 51, 60, 208, 207, 168, 179, 253, 164, 250, 80, 245, 42, 201, 128, 97, 123, 108, 161, 69, 63, 47, 49, 24, 150, 165, 139, 105, 214, 154, 104, 172, 159, 86, 208, 64, 134, 158, 156, 234, 125, 140, 210, 3, 32, 60, 28, 62, 154, 198, 21, 132, 191, 236, 10, 158, 12, 247, 159, 177, 77, 178, 53, 238, 95, 165, 9, 200, 28, 148, 242, 35, 70, 189, 121, 169, 248, 97, 91, 111, 45, 103, 1, 167, 220, 67, 250, 175, 89, 122, 238, 192, 144, 142, 248, 198, 101, 96, 129 },
+ };
+#else
[Obsolete("You must use a real key for a real app.", true)] internal static readonly RSAParameters ResourceServerEncryptionPrivateKey= new RSAParameters(); -#endif - +#endif
+
/// <summary> /// Gets the transaction-protected database connection for the current request. /// </summary> diff --git a/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs b/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs index f98dc79..83446da 100644 --- a/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs +++ b/src/DotNetOpenAuth/OAuth2/AuthorizationServer.cs @@ -91,14 +91,34 @@ namespace DotNetOpenAuth.OAuth2 { var response = this.PrepareRejectAuthorizationRequest(authorizationRequest, callback); this.Channel.Send(response); - } - + }
+
+ /// <summary>
+ /// Checks the incoming HTTP request for an access token request and prepares a response if the request message was found.
+ /// </summary>
+ /// <param name="response">The formulated response, or <c>null</c> if the request was not found..</param>
+ /// <returns>A value indicating whether any access token request was found in the HTTP request.</returns>
+ /// <remarks>
+ /// This method assumes that the authorization server and the resource server are the same and that they share a single
+ /// asymmetric key for signing and encrypting the access token. If this is not true, use the <see cref="ReadAccessTokenRequest"/> method instead.
+ /// </remarks>
public bool TryPrepareAccessTokenResponse(out IDirectResponseProtocolMessage response) { return this.TryPrepareAccessTokenResponse(this.Channel.GetRequestFromContext(), out response); - } - + }
+
+ /// <summary>
+ /// Checks the incoming HTTP request for an access token request and prepares a response if the request message was found.
+ /// </summary>
+ /// <param name="httpRequestInfo">The HTTP request info.</param>
+ /// <param name="response">The formulated response, or <c>null</c> if the request was not found..</param>
+ /// <returns>A value indicating whether any access token request was found in the HTTP request.</returns>
+ /// <remarks>
+ /// This method assumes that the authorization server and the resource server are the same and that they share a single
+ /// asymmetric key for signing and encrypting the access token. If this is not true, use the <see cref="ReadAccessTokenRequest"/> method instead.
+ /// </remarks>
public bool TryPrepareAccessTokenResponse(HttpRequestInfo httpRequestInfo, out IDirectResponseProtocolMessage response) { - Contract.Requires<ArgumentNullException>(httpRequestInfo != null, "httpRequestInfo"); + Contract.Requires<ArgumentNullException>(httpRequestInfo != null, "httpRequestInfo");
+ Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn<IDirectResponseProtocolMessage>(out response) != null)); var request = this.ReadAccessTokenRequest(httpRequestInfo); if (request != null) { @@ -111,8 +131,13 @@ namespace DotNetOpenAuth.OAuth2 { response = null; return false; - } - + }
+
+ /// <summary>
+ /// Reads the access token request.
+ /// </summary>
+ /// <param name="requestInfo">The request info.</param>
+ /// <returns>The Client's request for an access token; or <c>null</c> if no such message was found in the request.</returns>
public AccessTokenRequestBase ReadAccessTokenRequest(HttpRequestInfo requestInfo = null) { if (requestInfo == null) { requestInfo = this.Channel.GetRequestFromContext(); @@ -121,8 +146,14 @@ namespace DotNetOpenAuth.OAuth2 { AccessTokenRequestBase request; this.Channel.TryReadFromRequest(requestInfo, out request); return request; - } - + }
+
+ /// <summary>
+ /// Prepares a response to inform the Client that the user has rejected the Client's authorization request.
+ /// </summary>
+ /// <param name="authorizationRequest">The authorization request.</param>
+ /// <param name="callback">The Client callback URL to use when formulating the redirect to send the user agent back to the Client.</param>
+ /// <returns>The authorization response message to send to the Client.</returns>
public EndUserAuthorizationFailedResponse PrepareRejectAuthorizationRequest(EndUserAuthorizationRequest authorizationRequest, Uri callback = null) { Contract.Requires<ArgumentNullException>(authorizationRequest != null, "authorizationRequest"); Contract.Ensures(Contract.Result<EndUserAuthorizationFailedResponse>() != null); @@ -204,8 +235,14 @@ namespace DotNetOpenAuth.OAuth2 { } return response; - } - + }
+
+ /// <summary>
+ /// Gets the redirect URL to use for a particular authorization request.
+ /// </summary>
+ /// <param name="authorizationRequest">The authorization request.</param>
+ /// <returns>The URL to redirect to. Never <c>null</c>.</returns>
+ /// <exception cref="ProtocolException">Thrown if no callback URL could be determined.</exception>
protected Uri GetCallback(EndUserAuthorizationRequest authorizationRequest) { Contract.Requires<ArgumentNullException>(authorizationRequest != null, "authorizationRequest"); Contract.Ensures(Contract.Result<Uri>() != null); diff --git a/src/DotNetOpenAuth/OAuth2/AuthorizationState.cs b/src/DotNetOpenAuth/OAuth2/AuthorizationState.cs index dda5f5e..75774c2 100644 --- a/src/DotNetOpenAuth/OAuth2/AuthorizationState.cs +++ b/src/DotNetOpenAuth/OAuth2/AuthorizationState.cs @@ -11,13 +11,14 @@ namespace DotNetOpenAuth.OAuth2 { using DotNetOpenAuth.Messaging; /// <summary> - /// A simple memory-only copy of an authorization state. + /// A simple in-memory copy of an authorization state. /// </summary> [Serializable] - public class AuthorizationState : IAuthorizationState { - /// <summary> - /// Initializes a new instance of the <see cref="AuthorizationState"/> class. - /// </summary> + public class AuthorizationState : IAuthorizationState {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AuthorizationState"/> class.
+ /// </summary>
+ /// <param name="scopes">The scopes of access being requested or that was obtained.</param>
public AuthorizationState(IEnumerable<string> scopes = null) { this.Scope = new HashSet<string>(OAuthUtilities.ScopeStringComparer); if (scopes != null) { diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessToken.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessToken.cs index de24428..17dee95 100644 --- a/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessToken.cs +++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/AccessToken.cs @@ -44,8 +44,14 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { /// </summary> /// <value>The lifetime.</value> [MessagePart] - internal TimeSpan? Lifetime { get; set; } - + internal TimeSpan? Lifetime { get; set; }
+
+ /// <summary>
+ /// Creates a formatter capable of serializing/deserializing an access token.
+ /// </summary>
+ /// <param name="signingKey">The authorization server's private key used to asymmetrically sign the access token.</param>
+ /// <param name="encryptingKey">The resource server's public key used to encrypt the access token.</param>
+ /// <returns>An access token serializer.</returns>
internal static IDataBagFormatter<AccessToken> CreateFormatter(RSAParameters signingKey, RSAParameters encryptingKey) { Contract.Ensures(Contract.Result<IDataBagFormatter<AccessToken>>() != null); diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/AuthorizationCode.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/AuthorizationCode.cs index 1ec6b41..7cb90d4 100644 --- a/src/DotNetOpenAuth/OAuth2/ChannelElements/AuthorizationCode.cs +++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/AuthorizationCode.cs @@ -52,6 +52,11 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { [MessagePart("cb")] private byte[] CallbackHash { get; set; } + /// <summary>
+ /// Creates a serializer/deserializer for this type.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server that will be serializing/deserializing this authorization code. Must not be null.</param>
+ /// <returns>A DataBag formatter.</returns> internal static IDataBagFormatter<AuthorizationCode> CreateFormatter(IAuthorizationServer authorizationServer) { Contract.Requires<ArgumentNullException>(authorizationServer != null, "authorizationServer"); Contract.Ensures(Contract.Result<IDataBagFormatter<AuthorizationCode>>() != null); diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/EndUserAuthorizationResponseTypeEncoder.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/EndUserAuthorizationResponseTypeEncoder.cs index 7ad6940..afcaee9 100644 --- a/src/DotNetOpenAuth/OAuth2/ChannelElements/EndUserAuthorizationResponseTypeEncoder.cs +++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/EndUserAuthorizationResponseTypeEncoder.cs @@ -8,8 +8,11 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { using System; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; - using DotNetOpenAuth.OAuth2.Messages; - + using DotNetOpenAuth.OAuth2.Messages;
+
+ /// <summary>
+ /// Encodes/decodes the OAuth 2.0 response_type argument.
+ /// </summary>
internal class EndUserAuthorizationResponseTypeEncoder : IMessagePartEncoder { /// <summary> /// Initializes a new instance of the <see cref="EndUserAuthorizationResponseTypeEncoder"/> class. diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/GrantTypeEncoder.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/GrantTypeEncoder.cs index f39eecd..7af6a83 100644 --- a/src/DotNetOpenAuth/OAuth2/ChannelElements/GrantTypeEncoder.cs +++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/GrantTypeEncoder.cs @@ -8,8 +8,11 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { using System; using DotNetOpenAuth.Messaging; using DotNetOpenAuth.Messaging.Reflection; - using DotNetOpenAuth.OAuth2.Messages; - + using DotNetOpenAuth.OAuth2.Messages;
+
+ /// <summary>
+ /// Encodes/decodes the OAuth 2.0 grant_type argument.
+ /// </summary>
internal class GrantTypeEncoder : IMessagePartEncoder { /// <summary> /// Initializes a new instance of the <see cref="GrantTypeEncoder"/> class. diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/IDataBagFormatter.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/IDataBagFormatter.cs index a9c1875..108b362 100644 --- a/src/DotNetOpenAuth/OAuth2/ChannelElements/IDataBagFormatter.cs +++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/IDataBagFormatter.cs @@ -1,54 +1,76 @@ -//----------------------------------------------------------------------- -// <copyright file="IDataBagFormatter.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.OAuth2.ChannelElements { - using System; - using System.Diagnostics.Contracts; - using DotNetOpenAuth.Messaging; - - /// <summary> - /// A serializer for <see cref="DataBag"/>-derived types - /// </summary> - /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam> - [ContractClass(typeof(IDataBagFormatterContract<>))] - internal interface IDataBagFormatter<T> where T : DataBag, new() { - string Serialize(T message); - - T Deserialize(IProtocolMessage containingMessage, string data); - } - - /// <summary> - /// Contract class for the IDataBagFormatter interface. - /// </summary> - /// <typeparam name="T">The type of DataBag to serialize.</typeparam> - [ContractClassFor(typeof(IDataBagFormatter<>))] - internal abstract class IDataBagFormatterContract<T> : IDataBagFormatter<T> where T : DataBag, new() { - /// <summary> - /// Prevents a default instance of the <see cref="IDataBagFormatterContract<T>"/> class from being created. - /// </summary> - private IDataBagFormatterContract() { - } - - #region IDataBagFormatter<T> Members - - string IDataBagFormatter<T>.Serialize(T message) { - Contract.Requires<ArgumentNullException>(message != null, "message"); - Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>())); - - throw new System.NotImplementedException(); - } - - T IDataBagFormatter<T>.Deserialize(IProtocolMessage containingMessage, string data) { - Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage"); - Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(data)); - Contract.Ensures(Contract.Result<T>() != null); - - throw new System.NotImplementedException(); - } - - #endregion - } +//-----------------------------------------------------------------------
+// <copyright file="IDataBagFormatter.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2.ChannelElements {
+ using System;
+ using System.Diagnostics.Contracts;
+ using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A serializer for <see cref="DataBag"/>-derived types
+ /// </summary>
+ /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam>
+ [ContractClass(typeof(IDataBagFormatterContract<>))]
+ internal interface IDataBagFormatter<T> where T : DataBag, new() {
+ /// <summary>
+ /// Serializes the specified message.
+ /// </summary>
+ /// <param name="message">The message to serialize. Must not be null.</param>
+ /// <returns>A non-null, non-empty value.</returns>
+ string Serialize(T message);
+
+ /// <summary>
+ /// Deserializes a <see cref="DataBag"/>.
+ /// </summary>
+ /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be nulll.</param>
+ /// <param name="data">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param>
+ /// <returns>The deserialized value. Never null.</returns>
+ T Deserialize(IProtocolMessage containingMessage, string data);
+ }
+
+ /// <summary>
+ /// Contract class for the IDataBagFormatter interface.
+ /// </summary>
+ /// <typeparam name="T">The type of DataBag to serialize.</typeparam>
+ [ContractClassFor(typeof(IDataBagFormatter<>))]
+ internal abstract class IDataBagFormatterContract<T> : IDataBagFormatter<T> where T : DataBag, new() {
+ /// <summary>
+ /// Prevents a default instance of the <see cref="IDataBagFormatterContract<T>"/> class from being created.
+ /// </summary>
+ private IDataBagFormatterContract() {
+ }
+
+ #region IDataBagFormatter<T> Members
+
+ /// <summary>
+ /// Serializes the specified message.
+ /// </summary>
+ /// <param name="message">The message to serialize. Must not be null.</param>
+ /// <returns>A non-null, non-empty value.</returns>
+ string IDataBagFormatter<T>.Serialize(T message) {
+ Contract.Requires<ArgumentNullException>(message != null, "message");
+ Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
+
+ throw new System.NotImplementedException();
+ }
+
+ /// <summary>
+ /// Deserializes a <see cref="DataBag"/>.
+ /// </summary>
+ /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be nulll.</param>
+ /// <param name="data">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param>
+ /// <returns>The deserialized value. Never null.</returns>
+ T IDataBagFormatter<T>.Deserialize(IProtocolMessage containingMessage, string data) {
+ Contract.Requires<ArgumentNullException>(containingMessage != null, "containingMessage");
+ Contract.Requires<ArgumentException>(!String.IsNullOrEmpty(data));
+ Contract.Ensures(Contract.Result<T>() != null);
+
+ throw new System.NotImplementedException();
+ }
+
+ #endregion
+ }
}
\ No newline at end of file diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ChannelBase.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ChannelBase.cs index 2a959ff..913bff0 100644 --- a/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ChannelBase.cs +++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ChannelBase.cs @@ -11,8 +11,11 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { using System.Text; using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OAuth2.Messages; - + using DotNetOpenAuth.OAuth2.Messages;
+
+ /// <summary>
+ /// The base messaging channel used by OAuth 2.0 parties.
+ /// </summary>
internal abstract class OAuth2ChannelBase : StandardMessageFactoryChannel { /// <summary> /// The messages receivable by this channel. diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ClientChannel.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ClientChannel.cs index 256a28d..76ccc25 100644 --- a/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ClientChannel.cs +++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/OAuth2ClientChannel.cs @@ -12,8 +12,11 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { using System.Net; using System.Web; - using DotNetOpenAuth.Messaging; - + using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// The messaging channel used by OAuth 2.0 Clients.
+ /// </summary>
internal class OAuth2ClientChannel : OAuth2ChannelBase { /// <summary> /// Initializes a new instance of the <see cref="OAuth2ClientChannel"/> class. @@ -43,8 +46,16 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { } return httpRequest; - } - + }
+
+ /// <summary>
+ /// Gets the protocol message that may be in the given HTTP response.
+ /// </summary>
+ /// <param name="response">The response that is anticipated to contain an protocol message.</param>
+ /// <returns>
+ /// The deserialized message parts, if found. Null otherwise.
+ /// </returns>
+ /// <exception cref="ProtocolException">Thrown when the response is not valid.</exception>
protected override IDictionary<string, string> ReadFromResponseCore(IncomingWebResponse response) { // The spec says direct responses should be JSON objects, but Facebook uses HttpFormUrlEncoded instead, calling it text/plain string body = response.GetResponseReader().ReadToEnd(); @@ -55,8 +66,15 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { } else { throw ErrorUtilities.ThrowProtocol("Unexpected response Content-Type {0}", response.ContentType.MediaType); } - } - + }
+
+ /// <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) { Logger.Channel.DebugFormat("Incoming HTTP request: {0} {1}", request.HttpMethod, request.UrlBeforeRewriting.AbsoluteUri); @@ -82,9 +100,21 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { } return (IDirectedProtocolMessage)this.Receive(fields, recipient); - } - + }
+
+ /// <summary>
+ /// Queues a message for sending in the response stream where the fields
+ /// are sent in the response stream in querystring style.
+ /// </summary>
+ /// <param name="response">The message to send as a response.</param>
+ /// <returns>
+ /// The pending user agent redirect based message to be sent as an HttpResponse.
+ /// </returns>
+ /// <remarks>
+ /// This method implements spec OAuth V1.0 section 5.3.
+ /// </remarks>
protected override OutgoingWebResponse PrepareDirectResponse(IProtocolMessage response) { + // Clients don't ever send direct responses. throw new NotImplementedException(); } } diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/RefreshToken.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/RefreshToken.cs index a88bf9f..6ce53c1 100644 --- a/src/DotNetOpenAuth/OAuth2/ChannelElements/RefreshToken.cs +++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/RefreshToken.cs @@ -34,8 +34,13 @@ namespace DotNetOpenAuth.OAuth2.ChannelElements { this.UtcCreationDate = authorization.UtcIssued; this.User = authorization.User; this.Scope.ResetContents(authorization.Scope); - } - + }
+
+ /// <summary>
+ /// Creates a formatter capable of serializing/deserializing a refresh token.
+ /// </summary>
+ /// <param name="symmetricSecret">The symmetric secret used by the authorization server to sign/encrypt refresh tokens. Must not be null.</param>
+ /// <returns>A DataBag formatter. Never null.</returns>
internal static IDataBagFormatter<RefreshToken> CreateFormatter(byte[] symmetricSecret) { Contract.Requires<ArgumentNullException>(symmetricSecret != null, "symmetricSecret"); diff --git a/src/DotNetOpenAuth/OAuth2/ChannelElements/UriStyleMessageFormatter.cs b/src/DotNetOpenAuth/OAuth2/ChannelElements/UriStyleMessageFormatter.cs index 2232b6c..a778564 100644 --- a/src/DotNetOpenAuth/OAuth2/ChannelElements/UriStyleMessageFormatter.cs +++ b/src/DotNetOpenAuth/OAuth2/ChannelElements/UriStyleMessageFormatter.cs @@ -1,305 +1,316 @@ -//----------------------------------------------------------------------- -// <copyright file="UriStyleMessageFormatter.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace DotNetOpenAuth.OAuth2.ChannelElements { - using System; - using System.Collections.Generic; - using System.Diagnostics.Contracts; - using System.Linq; - using System.Security.Cryptography; - using System.Text; - using System.Web; - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.Messaging.Bindings; - using DotNetOpenAuth.Messaging.Reflection; - - /// <summary> - /// A serializer for <see cref="DataBag"/>-derived types - /// </summary> - /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam> - internal class UriStyleMessageFormatter<T> : IDataBagFormatter<T> where T : DataBag, new() { - /// <summary> - /// The length of the nonce to include in tokens that can be decoded once only. - /// </summary> - private const int NonceLength = 6; - - /// <summary> - /// The message description cache to use for data bag types. - /// </summary> - private static readonly MessageDescriptionCollection MessageDescriptions = new MessageDescriptionCollection(); - - /// <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; - - /// <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; - - /// <summary> - /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> 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> - internal UriStyleMessageFormatter(bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) { - Contract.Requires<ArgumentException>(signed || decodeOnceOnly == null, "A signature must be applied if this data is meant to be decoded only once."); - Contract.Requires<ArgumentException>(maximumAge.HasValue || decodeOnceOnly == null, "A maximum age must be given if a message can only be decoded once."); - - this.signed = signed; - this.maximumAge = maximumAge; - this.decodeOnceOnly = decodeOnceOnly; - this.encrypted = encrypted; - this.compressed = compressed; - } - - /// <summary> - /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> 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> - internal UriStyleMessageFormatter(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) { - this.asymmetricSigning = new RSACryptoServiceProvider(); - this.asymmetricSigning.ImportParameters(signingKey.Value); - } - - if (encryptingKey.HasValue) { - this.asymmetricEncrypting = new RSACryptoServiceProvider(); - this.asymmetricEncrypting.ImportParameters(encryptingKey.Value); - } - - this.hasherForAsymmetricSigning = new SHA1CryptoServiceProvider(); - } - - /// <summary> - /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> 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> - internal UriStyleMessageFormatter(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 && !encrypted), "A secret is required when signing or encrypting is required."); - - if (symmetricSecret != null) { - this.symmetricHasher = new HMACSHA256(symmetricSecret); - } - - this.symmetricSecret = symmetricSecret; - } - - public string Serialize(T message) { - message.UtcCreationDate = DateTime.UtcNow; - - if (this.decodeOnceOnly != null) { - message.Nonce = MessagingUtilities.GetNonCryptoRandomData(NonceLength); - } - - if (this.signed) { - message.Signature = this.CalculateSignature(message); - } - - var fields = MessageSerializer.Get(message.GetType()).Serialize(MessageDescriptions.GetAccessor(message)); - string value = MessagingUtilities.CreateQueryString(fields); - - byte[] encoded = Encoding.UTF8.GetBytes(value); - - if (this.compressed) { - encoded = MessagingUtilities.Compress(encoded); - } - - if (this.encrypted) { - encoded = this.Encrypt(encoded); - } - - return Convert.ToBase64String(encoded); - } - - public T Deserialize(IProtocolMessage containingMessage, string value) { - var message = new T { ContainingMessage = containingMessage }; - byte[] data = Convert.FromBase64String(value); - - if (this.encrypted) { - data = this.Decrypt(data); - } - - if (this.compressed) { - data = MessagingUtilities.Decompress(data); - } - - value = Encoding.UTF8.GetString(data); - - // Deserialize into message newly created instance. - var serializer = MessageSerializer.Get(message.GetType()); - var fields = MessageDescriptions.GetAccessor(message); - serializer.Deserialize(HttpUtility.ParseQueryString(value).ToDictionary(), fields); - - if (this.signed) { - // Verify that the verification code was issued by message authorization server. - ErrorUtilities.VerifyProtocol(this.IsSignatureValid(message), Protocol.bad_verification_code); - } - - if (this.maximumAge.HasValue) { - // Has message verification code expired? - DateTime expirationDate = message.UtcCreationDate + this.maximumAge.Value; - if (expirationDate < DateTime.UtcNow) { - throw new ExpiredMessageException(expirationDate, containingMessage); - } - } - - // Has message verification code already been used to obtain an access/refresh token? - 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, Convert.ToBase64String(message.Nonce), message.UtcCreationDate)) { - Logger.OpenId.ErrorFormat("Replayed nonce detected ({0} {1}). Rejecting message.", message.Nonce, message.UtcCreationDate); - throw new ReplayedMessageException(containingMessage); - } - } - - ((IMessage)message).EnsureValidMessage(); - - return message; - } - - /// <summary> - /// Determines whether the signature on this instance is valid. - /// </summary> - /// <param name="message">The message whose signature is to be checked.</param> - /// <returns> - /// <c>true</c> if the signature is valid; otherwise, <c>false</c>. - /// </returns> - private bool IsSignatureValid(DataBag message) { - Contract.Requires<ArgumentNullException>(message != null, "message"); - - if (this.asymmetricSigning != null) { - byte[] bytesToSign = this.GetBytesToSign(message); - return this.asymmetricSigning.VerifyData(bytesToSign, this.hasherForAsymmetricSigning, message.Signature); - } else { - return MessagingUtilities.AreEquivalentConstantTime(message.Signature, this.CalculateSignature(message)); - } - } - - /// <summary> - /// Calculates the signature for the data in this verification code. - /// </summary> - /// <param name="message">The message whose signature is to be calculated.</param> - /// <returns>The calculated signature.</returns> - private byte[] CalculateSignature(DataBag message) { - Contract.Requires<ArgumentNullException>(message != null, "message"); - Contract.Requires<InvalidOperationException>(this.asymmetricSigning != null || this.symmetricHasher != null); - Contract.Ensures(Contract.Result<byte[]>() != null); - - byte[] bytesToSign = this.GetBytesToSign(message); - if (this.asymmetricSigning != null) { - return this.asymmetricSigning.SignData(bytesToSign, this.hasherForAsymmetricSigning); - } else { - return this.symmetricHasher.ComputeHash(bytesToSign); - } - } - - /// <summary> - /// Gets the bytes to sign. - /// </summary> - /// <param name="message">The message to be encoded as normalized bytes for signing.</param> - /// <returns>A buffer of the bytes to sign.</returns> - private byte[] GetBytesToSign(DataBag message) { - Contract.Requires<ArgumentNullException>(message != null, "message"); - - // Sign the data, being sure to avoid any impact of the signature field itself. - var fields = MessageDescriptions.GetAccessor(message); - var fieldsCopy = fields.ToDictionary(); - fieldsCopy.Remove("sig"); - - var sortedData = new SortedDictionary<string, string>(fieldsCopy, StringComparer.OrdinalIgnoreCase); - string value = MessagingUtilities.CreateQueryString(sortedData); - byte[] bytesToSign = Encoding.UTF8.GetBytes(value); - 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); - - if (this.asymmetricEncrypting != null) { - return this.asymmetricEncrypting.EncryptWithRandomSymmetricKey(value); - } else { - return MessagingUtilities.Encrypt(value, this.symmetricSecret); - } - } - - /// <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); - - if (this.asymmetricEncrypting != null) { - return this.asymmetricEncrypting.DecryptWithRandomSymmetricKey(value); - } else { - return MessagingUtilities.Decrypt(value, this.symmetricSecret); - } - } - } -} +//-----------------------------------------------------------------------
+// <copyright file="UriStyleMessageFormatter.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.OAuth2.ChannelElements {
+ using System;
+ using System.Collections.Generic;
+ using System.Diagnostics.Contracts;
+ using System.Linq;
+ using System.Security.Cryptography;
+ using System.Text;
+ using System.Web;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Bindings;
+ using DotNetOpenAuth.Messaging.Reflection;
+
+ /// <summary>
+ /// A serializer for <see cref="DataBag"/>-derived types
+ /// </summary>
+ /// <typeparam name="T">The DataBag-derived type that is to be serialized/deserialized.</typeparam>
+ internal class UriStyleMessageFormatter<T> : IDataBagFormatter<T> where T : DataBag, new() {
+ /// <summary>
+ /// The length of the nonce to include in tokens that can be decoded once only.
+ /// </summary>
+ private const int NonceLength = 6;
+
+ /// <summary>
+ /// The message description cache to use for data bag types.
+ /// </summary>
+ private static readonly MessageDescriptionCollection MessageDescriptions = new MessageDescriptionCollection();
+
+ /// <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;
+
+ /// <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;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> 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>
+ internal UriStyleMessageFormatter(bool signed = false, bool encrypted = false, bool compressed = false, TimeSpan? maximumAge = null, INonceStore decodeOnceOnly = null) {
+ Contract.Requires<ArgumentException>(signed || decodeOnceOnly == null, "A signature must be applied if this data is meant to be decoded only once.");
+ Contract.Requires<ArgumentException>(maximumAge.HasValue || decodeOnceOnly == null, "A maximum age must be given if a message can only be decoded once.");
+
+ this.signed = signed;
+ this.maximumAge = maximumAge;
+ this.decodeOnceOnly = decodeOnceOnly;
+ this.encrypted = encrypted;
+ this.compressed = compressed;
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> 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>
+ internal UriStyleMessageFormatter(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) {
+ this.asymmetricSigning = new RSACryptoServiceProvider();
+ this.asymmetricSigning.ImportParameters(signingKey.Value);
+ }
+
+ if (encryptingKey.HasValue) {
+ this.asymmetricEncrypting = new RSACryptoServiceProvider();
+ this.asymmetricEncrypting.ImportParameters(encryptingKey.Value);
+ }
+
+ this.hasherForAsymmetricSigning = new SHA1CryptoServiceProvider();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UriStyleMessageFormatter<T>"/> 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>
+ internal UriStyleMessageFormatter(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 && !encrypted), "A secret is required when signing or encrypting is required.");
+
+ if (symmetricSecret != null) {
+ this.symmetricHasher = new HMACSHA256(symmetricSecret);
+ }
+
+ this.symmetricSecret = symmetricSecret;
+ }
+
+ /// <summary>
+ /// Serializes the specified message.
+ /// </summary>
+ /// <param name="message">The message to serialize. Must not be null.</param>
+ /// <returns>A non-null, non-empty value.</returns>
+ public string Serialize(T message) {
+ message.UtcCreationDate = DateTime.UtcNow;
+
+ if (this.decodeOnceOnly != null) {
+ message.Nonce = MessagingUtilities.GetNonCryptoRandomData(NonceLength);
+ }
+
+ if (this.signed) {
+ message.Signature = this.CalculateSignature(message);
+ }
+
+ var fields = MessageSerializer.Get(message.GetType()).Serialize(MessageDescriptions.GetAccessor(message));
+ string value = MessagingUtilities.CreateQueryString(fields);
+
+ byte[] encoded = Encoding.UTF8.GetBytes(value);
+
+ if (this.compressed) {
+ encoded = MessagingUtilities.Compress(encoded);
+ }
+
+ if (this.encrypted) {
+ encoded = this.Encrypt(encoded);
+ }
+
+ return Convert.ToBase64String(encoded);
+ }
+
+ /// <summary>
+ /// Deserializes a <see cref="DataBag"/>.
+ /// </summary>
+ /// <param name="containingMessage">The message that contains the <see cref="DataBag"/> serialized value. Must not be nulll.</param>
+ /// <param name="value">The serialized form of the <see cref="DataBag"/> to deserialize. Must not be null or empty.</param>
+ /// <returns>The deserialized value. Never null.</returns>
+ public T Deserialize(IProtocolMessage containingMessage, string value) {
+ var message = new T { ContainingMessage = containingMessage };
+ byte[] data = Convert.FromBase64String(value);
+
+ if (this.encrypted) {
+ data = this.Decrypt(data);
+ }
+
+ if (this.compressed) {
+ data = MessagingUtilities.Decompress(data);
+ }
+
+ value = Encoding.UTF8.GetString(data);
+
+ // Deserialize into message newly created instance.
+ var serializer = MessageSerializer.Get(message.GetType());
+ var fields = MessageDescriptions.GetAccessor(message);
+ serializer.Deserialize(HttpUtility.ParseQueryString(value).ToDictionary(), fields);
+
+ if (this.signed) {
+ // Verify that the verification code was issued by message authorization server.
+ ErrorUtilities.VerifyProtocol(this.IsSignatureValid(message), Protocol.bad_verification_code);
+ }
+
+ if (this.maximumAge.HasValue) {
+ // Has message verification code expired?
+ DateTime expirationDate = message.UtcCreationDate + this.maximumAge.Value;
+ if (expirationDate < DateTime.UtcNow) {
+ throw new ExpiredMessageException(expirationDate, containingMessage);
+ }
+ }
+
+ // Has message verification code already been used to obtain an access/refresh token?
+ 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, Convert.ToBase64String(message.Nonce), message.UtcCreationDate)) {
+ Logger.OpenId.ErrorFormat("Replayed nonce detected ({0} {1}). Rejecting message.", message.Nonce, message.UtcCreationDate);
+ throw new ReplayedMessageException(containingMessage);
+ }
+ }
+
+ ((IMessage)message).EnsureValidMessage();
+
+ return message;
+ }
+
+ /// <summary>
+ /// Determines whether the signature on this instance is valid.
+ /// </summary>
+ /// <param name="message">The message whose signature is to be checked.</param>
+ /// <returns>
+ /// <c>true</c> if the signature is valid; otherwise, <c>false</c>.
+ /// </returns>
+ private bool IsSignatureValid(DataBag message) {
+ Contract.Requires<ArgumentNullException>(message != null, "message");
+
+ if (this.asymmetricSigning != null) {
+ byte[] bytesToSign = this.GetBytesToSign(message);
+ return this.asymmetricSigning.VerifyData(bytesToSign, this.hasherForAsymmetricSigning, message.Signature);
+ } else {
+ return MessagingUtilities.AreEquivalentConstantTime(message.Signature, this.CalculateSignature(message));
+ }
+ }
+
+ /// <summary>
+ /// Calculates the signature for the data in this verification code.
+ /// </summary>
+ /// <param name="message">The message whose signature is to be calculated.</param>
+ /// <returns>The calculated signature.</returns>
+ private byte[] CalculateSignature(DataBag message) {
+ Contract.Requires<ArgumentNullException>(message != null, "message");
+ Contract.Requires<InvalidOperationException>(this.asymmetricSigning != null || this.symmetricHasher != null);
+ Contract.Ensures(Contract.Result<byte[]>() != null);
+
+ byte[] bytesToSign = this.GetBytesToSign(message);
+ if (this.asymmetricSigning != null) {
+ return this.asymmetricSigning.SignData(bytesToSign, this.hasherForAsymmetricSigning);
+ } else {
+ return this.symmetricHasher.ComputeHash(bytesToSign);
+ }
+ }
+
+ /// <summary>
+ /// Gets the bytes to sign.
+ /// </summary>
+ /// <param name="message">The message to be encoded as normalized bytes for signing.</param>
+ /// <returns>A buffer of the bytes to sign.</returns>
+ private byte[] GetBytesToSign(DataBag message) {
+ Contract.Requires<ArgumentNullException>(message != null, "message");
+
+ // Sign the data, being sure to avoid any impact of the signature field itself.
+ var fields = MessageDescriptions.GetAccessor(message);
+ var fieldsCopy = fields.ToDictionary();
+ fieldsCopy.Remove("sig");
+
+ var sortedData = new SortedDictionary<string, string>(fieldsCopy, StringComparer.OrdinalIgnoreCase);
+ string value = MessagingUtilities.CreateQueryString(sortedData);
+ byte[] bytesToSign = Encoding.UTF8.GetBytes(value);
+ 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);
+
+ if (this.asymmetricEncrypting != null) {
+ return this.asymmetricEncrypting.EncryptWithRandomSymmetricKey(value);
+ } else {
+ return MessagingUtilities.Encrypt(value, this.symmetricSecret);
+ }
+ }
+
+ /// <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);
+
+ if (this.asymmetricEncrypting != null) {
+ return this.asymmetricEncrypting.DecryptWithRandomSymmetricKey(value);
+ } else {
+ return MessagingUtilities.Decrypt(value, this.symmetricSecret);
+ }
+ }
+ }
+}
diff --git a/src/DotNetOpenAuth/OAuth2/ClientBase.cs b/src/DotNetOpenAuth/OAuth2/ClientBase.cs index 845d24c..7a49f53 100644 --- a/src/DotNetOpenAuth/OAuth2/ClientBase.cs +++ b/src/DotNetOpenAuth/OAuth2/ClientBase.cs @@ -184,8 +184,13 @@ namespace DotNetOpenAuth.OAuth2 { } authorizationState.SaveChanges(); - } - + }
+
+ /// <summary>
+ /// Updates authorization state with a success response from the Authorization Server.
+ /// </summary>
+ /// <param name="authorizationState">The authorization state to update.</param>
+ /// <param name="authorizationSuccess">The authorization success message obtained from the authorization server.</param>
internal void UpdateAuthorizationWithResponse(IAuthorizationState authorizationState, EndUserAuthorizationSuccessAuthCodeResponse authorizationSuccess) { Contract.Requires<ArgumentNullException>(authorizationState != null, "authorizationState"); Contract.Requires<ArgumentNullException>(authorizationSuccess != null, "authorizationSuccess"); diff --git a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAssertionRequest.cs b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAssertionRequest.cs index 8c2ef2b..e708925 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAssertionRequest.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAssertionRequest.cs @@ -9,9 +9,17 @@ namespace DotNetOpenAuth.OAuth2.Messages { using System.Collections.Generic; using System.Linq; using System.Text; - using DotNetOpenAuth.Messaging; - - internal class AccessTokenAssertionRequest : AccessTokenRequestBase { + using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A request from a Client to an Authorization Server to exchange some assertion for an access token.
+ /// </summary>
+ internal class AccessTokenAssertionRequest : AccessTokenRequestBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AccessTokenAssertionRequest"/> class.
+ /// </summary>
+ /// <param name="tokenEndpoint">The Authorization Server's access token endpoint URL.</param>
+ /// <param name="version">The version.</param>
internal AccessTokenAssertionRequest(Uri tokenEndpoint, Version version) : base(tokenEndpoint, version) { } @@ -28,8 +36,12 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// </summary> /// <value>The assertion.</value> [MessagePart(Protocol.assertion, IsRequired = true, AllowEmpty = false)] - internal string Assertion { get; set; } - + internal string Assertion { get; set; }
+
+ /// <summary>
+ /// Gets the type of the grant.
+ /// </summary>
+ /// <value>The type of the grant.</value>
internal override GrantType GrantType { get { return GrantType.Assertion; } } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs index 559d557..35da4d0 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenAuthorizationCodeRequest.cs @@ -12,8 +12,11 @@ namespace DotNetOpenAuth.OAuth2.Messages { using System.Text; using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OAuth2.ChannelElements; - + using DotNetOpenAuth.OAuth2.ChannelElements;
+
+ /// <summary>
+ /// A request from a Client to an Authorization Server to exchange an authorization code for an access token.
+ /// </summary>
internal class AccessTokenAuthorizationCodeRequest : AccessTokenRequestBase, ITokenCarryingRequest { /// <summary> /// Initializes a new instance of the <see cref="AccessTokenAuthorizationCodeRequest"/> class. @@ -22,9 +25,12 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// <param name="version">The version.</param> internal AccessTokenAuthorizationCodeRequest(Uri tokenEndpoint, Version version) : base(tokenEndpoint, version) { - } - - + }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AccessTokenAuthorizationCodeRequest"/> class.
+ /// </summary>
+ /// <param name="authorizationServer">The authorization server.</param>
internal AccessTokenAuthorizationCodeRequest(AuthorizationServerDescription authorizationServer) : this(authorizationServer.TokenEndpoint, authorizationServer.Version) { @@ -51,8 +57,12 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// <summary> /// Gets or sets the authorization that the token describes. /// </summary> - IAuthorizationDescription ITokenCarryingRequest.AuthorizationDescription { get; set; } - + IAuthorizationDescription ITokenCarryingRequest.AuthorizationDescription { get; set; }
+
+ /// <summary>
+ /// Gets the type of the grant.
+ /// </summary>
+ /// <value>The type of the grant.</value>
internal override GrantType GrantType { get { return Messages.GrantType.AuthorizationCode; } } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenClientCredentialsRequest.cs b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenClientCredentialsRequest.cs index f0bc76d..4aab6a8 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenClientCredentialsRequest.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenClientCredentialsRequest.cs @@ -28,8 +28,12 @@ namespace DotNetOpenAuth.OAuth2.Messages { internal AccessTokenClientCredentialsRequest(Uri tokenEndpoint, Version version) : base(tokenEndpoint, version) { this.HttpMethods = HttpDeliveryMethods.PostRequest; - } - + }
+
+ /// <summary>
+ /// Gets the type of the grant.
+ /// </summary>
+ /// <value>The type of the grant.</value>
internal override GrantType GrantType { get { return Messages.GrantType.None; } } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRefreshRequest.cs b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRefreshRequest.cs index e325dd7..4c05b2d 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRefreshRequest.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRefreshRequest.cs @@ -61,8 +61,12 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// REQUIRED. The refresh token associated with the access token to be refreshed. /// </remarks> [MessagePart(Protocol.refresh_token, IsRequired = true, AllowEmpty = false)] - internal string RefreshToken { get; set; } - + internal string RefreshToken { get; set; }
+
+ /// <summary>
+ /// Gets the type of the grant.
+ /// </summary>
+ /// <value>The type of the grant.</value>
internal override GrantType GrantType { get { return Messages.GrantType.RefreshToken; } } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRequestBase.cs b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRequestBase.cs index 943308e..204df61 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRequestBase.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenRequestBase.cs @@ -33,8 +33,12 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// </summary> /// <value>The type of the grant.</value> [MessagePart(Protocol.grant_type, IsRequired = true, AllowEmpty = false, Encoder = typeof(GrantTypeEncoder))] - internal abstract GrantType GrantType { get; } - + internal abstract GrantType GrantType { get; }
+
+ /// <summary>
+ /// Gets the set of scopes the Client would like the access token to provide access to.
+ /// </summary>
+ /// <value>A set of scopes. Never null.</value>
[MessagePart(Protocol.scope, IsRequired = false, AllowEmpty = true, Encoder = typeof(ScopeEncoder))] internal HashSet<string> Scope { get; private set; } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs index 8faf510..9a8fc33 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/AccessTokenResourceOwnerPasswordCredentialsRequest.cs @@ -10,16 +10,25 @@ namespace DotNetOpenAuth.OAuth2.Messages { using System.Linq; using System.Text; - using DotNetOpenAuth.Messaging; - - internal class AccessTokenResourceOwnerPasswordCredentialsRequest : AccessTokenRequestBase { - /// <summary> - /// Initializes a new instance of the <see cref="AccessTokenResourceOwnerPasswordCredentialsRequest"/> class. - /// </summary> + using DotNetOpenAuth.Messaging;
+
+ /// <summary>
+ /// A request from a Client to an Authorization Server to exchange the user's username and password for an access token.
+ /// </summary>
+ internal class AccessTokenResourceOwnerPasswordCredentialsRequest : AccessTokenRequestBase {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="AccessTokenResourceOwnerPasswordCredentialsRequest"/> class.
+ /// </summary>
+ /// <param name="accessTokenEndpoint">The access token endpoint.</param>
+ /// <param name="version">The protocol version.</param>
internal AccessTokenResourceOwnerPasswordCredentialsRequest(Uri accessTokenEndpoint, Version version) : base(accessTokenEndpoint, version) { - } - + }
+
+ /// <summary>
+ /// Gets the type of the grant.
+ /// </summary>
+ /// <value>The type of the grant.</value>
internal override GrantType GrantType { get { return Messages.GrantType.BasicCredentials; } } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs index 41c7e68..060c12e 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAccessTokenResponse.cs @@ -43,22 +43,38 @@ namespace DotNetOpenAuth.OAuth2.Messages { ((IMessageWithClientState)this).ClientState = request.ClientState; } - [MessagePart(Protocol.access_token, AllowEmpty = false, IsRequired = true)] - internal string AccessToken { get; set; } - - #region ITokenCarryingRequest Members - + #region ITokenCarryingRequest Members
+
+ /// <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 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 authorization that the token describes.
+ /// </summary>
+ /// <value></value>
IAuthorizationDescription ITokenCarryingRequest.AuthorizationDescription { get; set; } - #endregion + #endregion
+
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ /// <value>The access token.</value>
+ [MessagePart(Protocol.access_token, AllowEmpty = false, IsRequired = true)]
+ internal string AccessToken { get; set; }
} } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs index 076fc42..4ac0ec0 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessAuthCodeResponse.cs @@ -5,12 +5,9 @@ //----------------------------------------------------------------------- namespace DotNetOpenAuth.OAuth2.Messages { - using System; - using System.Collections.Generic; - using System.Diagnostics.Contracts; - using System.Linq; - using System.Text; - + using System;
+ using System.Diagnostics.Contracts;
+
using DotNetOpenAuth.Messaging; using DotNetOpenAuth.OAuth2.ChannelElements; @@ -43,24 +40,43 @@ namespace DotNetOpenAuth.OAuth2.Messages { ((IMessageWithClientState)this).ClientState = request.ClientState; } - #region ITokenCarryingRequest Members - + #region ITokenCarryingRequest Members
+
+ /// <summary>
+ /// Gets or sets the verification code or refresh/access token.
+ /// </summary>
+ /// <value>The code or token.</value>
string ITokenCarryingRequest.CodeOrToken { get { return this.AuthorizationCode; } set { this.AuthorizationCode = value; } - } - + }
+
+ /// <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.AuthorizationCode; } - } - + }
+
+ /// <summary>
+ /// Gets or sets the authorization that the token describes.
+ /// </summary>
IAuthorizationDescription ITokenCarryingRequest.AuthorizationDescription { get; set; } - #endregion - + #endregion
+
+ /// <summary>
+ /// Gets or sets the authorization code.
+ /// </summary>
+ /// <value>The authorization code.</value>
[MessagePart(Protocol.code, AllowEmpty = false, IsRequired = true)] - internal string AuthorizationCode { get; set; } - + internal string AuthorizationCode { get; set; }
+
+ /// <summary>
+ /// Gets or sets the access token.
+ /// </summary>
+ /// <value>The access token.</value>
[MessagePart(Protocol.access_token, AllowEmpty = false, IsRequired = false)] internal string AccessToken { get; set; } } diff --git a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs index 305c0e6..2d02b87 100644 --- a/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs +++ b/src/DotNetOpenAuth/OAuth2/Messages/EndUserAuthorizationSuccessResponseBase.cs @@ -52,22 +52,22 @@ namespace DotNetOpenAuth.OAuth2.Messages { /// REQUIRED if the Client sent the value in the <see cref="EndUserAuthorizationRequest"/>. /// </remarks> [MessagePart(Protocol.state, IsRequired = false, AllowEmpty = true)] - string IMessageWithClientState.ClientState { get; set; } - - /// <summary> - /// Gets or sets the lifetime of the authorization. - /// </summary> - /// <value>The lifetime.</value> - [MessagePart(Protocol.expires_in, IsRequired = false, Encoder = typeof(TimespanSecondsEncoder))] - internal TimeSpan? Lifetime { get; set; } - + string IMessageWithClientState.ClientState { get; set; }
+
/// <summary> /// Gets the scope of the <see cref="AccessToken"/> if one is given; otherwise the scope of the authorization code. /// </summary> /// <value>The scope.</value> [MessagePart(Protocol.scope, IsRequired = false, AllowEmpty = true, Encoder = typeof(ScopeEncoder))] - public ICollection<string> Scope { get; private set; } - + public ICollection<string> Scope { get; private set; }
+
+ /// <summary> + /// Gets or sets the lifetime of the authorization. + /// </summary> + /// <value>The lifetime.</value> + [MessagePart(Protocol.expires_in, IsRequired = false, Encoder = typeof(TimespanSecondsEncoder))] + internal TimeSpan? Lifetime { get; set; }
+
/// <summary> /// Gets or sets the authorizing user's account name. /// </summary> |