diff options
author | Andrew Arnott <andrewarnott@gmail.com> | 2010-04-29 06:40:59 -0700 |
---|---|---|
committer | Andrew Arnott <andrewarnott@gmail.com> | 2010-04-29 06:40:59 -0700 |
commit | 6d234b8df2a2eee625b1f52f458c2e28706343ca (patch) | |
tree | 4097f4233d6d2b93df036a185c6e7740c9c613ef | |
parent | dc5c6421e4fbc243ed3c6e2485cc1b2974ca76ec (diff) | |
download | DotNetOpenAuth-origin/dimebrain.zip DotNetOpenAuth-origin/dimebrain.tar.gz DotNetOpenAuth-origin/dimebrain.tar.bz2 |
An adaptation of @dimebrain's more MVC-ish way of doing OAuth SP.origin/dimebrain
We probably have a bit more work to do in this area before merging back.
9 files changed, 152 insertions, 104 deletions
diff --git a/projecttemplates/MvcRelyingParty/Code/OAuthAuthorizeAttribute.cs b/projecttemplates/MvcRelyingParty/Code/OAuthAuthorizeAttribute.cs new file mode 100644 index 0000000..28cc463 --- /dev/null +++ b/projecttemplates/MvcRelyingParty/Code/OAuthAuthorizeAttribute.cs @@ -0,0 +1,16 @@ +namespace MvcRelyingParty.Code { + using System.Web.Mvc; + using RelyingPartyLogic; + + public class OAuthAuthorize : AuthorizeAttribute { + public override void OnAuthorization(AuthorizationContext filterContext) { + var authorization = OAuthServiceProvider.ServiceProvider.ReadProtectedResourceAuthorization(); + if (authorization != null) { + filterContext.HttpContext.User = OAuthServiceProvider.ServiceProvider.CreatePrincipal(authorization); + } else { + // Doesn't authorize with OAuth; defer to other schemes + base.OnAuthorization(filterContext); + } + } + } +}
\ No newline at end of file diff --git a/projecttemplates/MvcRelyingParty/Code/OAuthEndpointAttribute.cs b/projecttemplates/MvcRelyingParty/Code/OAuthEndpointAttribute.cs new file mode 100644 index 0000000..33ba960 --- /dev/null +++ b/projecttemplates/MvcRelyingParty/Code/OAuthEndpointAttribute.cs @@ -0,0 +1,37 @@ +namespace MvcRelyingParty.Code { + using System.Web.Mvc; + using DotNetOpenAuth.Messaging; + using RelyingPartyLogic; + + public abstract class OAuthEndpointAttribute : ActionFilterAttribute { + internal MessageReceivingEndpoint GetRequestAsEndpoint(ActionExecutingContext filterContext) { + return new MessageReceivingEndpoint( + filterContext.HttpContext.Request.Url, + MessagingUtilities.GetHttpDeliveryMethod(filterContext.HttpContext.Request.HttpMethod)); + } + } + + public class OAuthRequestTokenEndpoint : OAuthEndpointAttribute { + public override void OnActionExecuting(ActionExecutingContext filterContext) { + var endpoint = GetRequestAsEndpoint(filterContext); + OAuthServiceProvider.RequestTokenEndpoint = endpoint; + base.OnActionExecuting(filterContext); + } + } + + public class OAuthAccessTokenEndpoint : OAuthEndpointAttribute { + public override void OnActionExecuting(ActionExecutingContext filterContext) { + var endpoint = GetRequestAsEndpoint(filterContext); + OAuthServiceProvider.AccessTokenEndpoint = endpoint; + base.OnActionExecuting(filterContext); + } + } + + public class OAuthUserAuthorizationEndpoint : OAuthEndpointAttribute { + public override void OnActionExecuting(ActionExecutingContext filterContext) { + var endpoint = GetRequestAsEndpoint(filterContext); + OAuthServiceProvider.UserAuthorizationEndpoint = endpoint; + base.OnActionExecuting(filterContext); + } + } +} diff --git a/projecttemplates/MvcRelyingParty/Controllers/AccountController.cs b/projecttemplates/MvcRelyingParty/Controllers/AccountController.cs index a322f7d..3d3e1aa 100644 --- a/projecttemplates/MvcRelyingParty/Controllers/AccountController.cs +++ b/projecttemplates/MvcRelyingParty/Controllers/AccountController.cs @@ -1,4 +1,6 @@ -namespace MvcRelyingParty.Controllers { +using MvcRelyingParty.Code; + +namespace MvcRelyingParty.Controllers { using System; using System.Collections.Generic; using System.Globalization; @@ -20,7 +22,7 @@ [HandleError] public class AccountController : Controller { - [Authorize] + [OAuthAuthorize] public ActionResult Edit() { return View(GetAccountInfoModel()); } @@ -35,7 +37,7 @@ /// <remarks> /// This action accepts PUT because this operation is idempotent in nature. /// </remarks> - [Authorize, AcceptVerbs(HttpVerbs.Put), ValidateAntiForgeryToken] + [OAuthAuthorize, AcceptVerbs(HttpVerbs.Put), ValidateAntiForgeryToken] public ActionResult Update(string firstName, string lastName, string emailAddress) { Database.LoggedInUser.FirstName = firstName; Database.LoggedInUser.LastName = lastName; @@ -49,6 +51,9 @@ } [Authorize] + [OAuthUserAuthorizationEndpoint] + [ActionName("authorize")] + [AcceptVerbs(HttpVerbs.Get)] public ActionResult Authorize() { if (OAuthServiceProvider.PendingAuthorizationRequest == null) { return RedirectToAction("Edit"); @@ -111,6 +116,27 @@ return PartialView("AuthorizedApps", GetAccountInfoModel()); } + [OAuthRequestTokenEndpoint] + [AcceptVerbs(HttpVerbs.Post)] + [ActionName("request_token")] + public ActionResult GetRequestToken() + { + var serviceProvider = OAuthServiceProvider.ServiceProvider; + var requestMessage = serviceProvider.ReadTokenRequest(); + var response = serviceProvider.PrepareUnauthorizedTokenMessage(requestMessage); + return serviceProvider.Channel.PrepareResponse(response).AsActionResult(); + } + + [OAuthAccessTokenEndpoint] + [ActionName("access_token")] + [AcceptVerbs(HttpVerbs.Post)] + public ActionResult GetAccessToken() { + var serviceProvider = OAuthServiceProvider.ServiceProvider; + var requestMessage = serviceProvider.ReadAccessTokenRequest(); + var response = serviceProvider.PrepareAccessTokenMessage(requestMessage); + return serviceProvider.Channel.PrepareResponse(response).AsActionResult(); + } + private static AccountInfoModel GetAccountInfoModel() { var authorizedApps = from token in Database.DataContext.IssuedTokens.OfType<IssuedAccessToken>() where token.User.UserId == Database.LoggedInUser.UserId diff --git a/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj b/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj index 540b702..21f7196 100644 --- a/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj +++ b/projecttemplates/MvcRelyingParty/MvcRelyingParty.csproj @@ -70,6 +70,8 @@ <ItemGroup> <Compile Include="Code\Extensions.cs" /> <Compile Include="Code\FormsAuthenticationService.cs" /> + <Compile Include="Code\OAuthAuthorizeAttribute.cs" /> + <Compile Include="Code\OAuthEndpointAttribute.cs" /> <Compile Include="Code\OpenIdRelyingPartyService.cs" /> <Compile Include="Controllers\AccountController.cs" /> <Compile Include="Controllers\AuthController.cs" /> @@ -83,9 +85,6 @@ </Compile> <Compile Include="Models\AccountAuthorizeModel.cs" /> <Compile Include="Models\AccountInfoModel.cs" /> - <Compile Include="OAuth.ashx.cs"> - <DependentUpon>OAuth.ashx</DependentUpon> - </Compile> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Setup.aspx.cs"> <DependentUpon>Setup.aspx</DependentUpon> @@ -175,7 +174,6 @@ </ItemGroup> <ItemGroup> <Content Include="Content\images\infocard_23x16.png" /> - <Content Include="OAuth.ashx" /> <Content Include="Views\Account\AuthenticationTokens.ascx" /> <Content Include="Views\Account\Authorize.aspx" /> <Content Include="Views\Account\AuthorizeApproved.aspx" /> diff --git a/projecttemplates/MvcRelyingParty/OAuth.ashx b/projecttemplates/MvcRelyingParty/OAuth.ashx deleted file mode 100644 index 81b3d52..0000000 --- a/projecttemplates/MvcRelyingParty/OAuth.ashx +++ /dev/null @@ -1 +0,0 @@ -<%@ WebHandler Language="C#" CodeBehind="OAuth.ashx.cs" Class="MvcRelyingParty.OAuth" %> diff --git a/projecttemplates/MvcRelyingParty/OAuth.ashx.cs b/projecttemplates/MvcRelyingParty/OAuth.ashx.cs deleted file mode 100644 index b9051c1..0000000 --- a/projecttemplates/MvcRelyingParty/OAuth.ashx.cs +++ /dev/null @@ -1,66 +0,0 @@ -//----------------------------------------------------------------------- -// <copyright file="OAuth.ashx.cs" company="Andrew Arnott"> -// Copyright (c) Andrew Arnott. All rights reserved. -// </copyright> -//----------------------------------------------------------------------- - -namespace MvcRelyingParty { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Web; - using System.Web.SessionState; - using DotNetOpenAuth.Messaging; - using DotNetOpenAuth.OAuth; - using DotNetOpenAuth.OAuth.Messages; - using RelyingPartyLogic; - - /// <summary> - /// Responds to incoming OAuth Service Provider messages. - /// </summary> - public class OAuth : IHttpHandler, IRequiresSessionState { - /// <summary> - /// Initializes a new instance of the <see cref="OAuth"/> class. - /// </summary> - public OAuth() { - } - - /// <summary> - /// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance. - /// </summary> - /// <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> - public void ProcessRequest(HttpContext context) { - var serviceProvider = OAuthServiceProvider.ServiceProvider; - var requestMessage = serviceProvider.ReadRequest(new HttpRequestInfo(context.Request)); - - UnauthorizedTokenRequest unauthorizedTokenRequestMessage; - AuthorizedTokenRequest authorizedTokenRequestMessage; - UserAuthorizationRequest userAuthorizationRequest; - if ((unauthorizedTokenRequestMessage = requestMessage as UnauthorizedTokenRequest) != null) { - var response = serviceProvider.PrepareUnauthorizedTokenMessage(unauthorizedTokenRequestMessage); - serviceProvider.Channel.Send(response); - } else if ((authorizedTokenRequestMessage = requestMessage as AuthorizedTokenRequest) != null) { - var response = serviceProvider.PrepareAccessTokenMessage(authorizedTokenRequestMessage); - serviceProvider.Channel.Send(response); - } else if ((userAuthorizationRequest = requestMessage as UserAuthorizationRequest) != null) { - // This is a browser opening to allow the user to authorize a request token, - // so redirect to the authorization page, which will automatically redirect - // to have the user log in if necessary. - OAuthServiceProvider.PendingAuthorizationRequest = userAuthorizationRequest; - HttpContext.Current.Response.Redirect("~/Account/Authorize"); - } else { - throw new InvalidOperationException(); - } - } - } -} diff --git a/projecttemplates/MvcRelyingParty/Web.config b/projecttemplates/MvcRelyingParty/Web.config index 7a867bb..1310bd6 100644 --- a/projecttemplates/MvcRelyingParty/Web.config +++ b/projecttemplates/MvcRelyingParty/Web.config @@ -209,7 +209,6 @@ <httpModules> <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> - <add name="OAuthAuthenticationModule" type="RelyingPartyLogic.OAuthAuthenticationModule, RelyingPartyLogic"/> <add name="Database" type="RelyingPartyLogic.Database, RelyingPartyLogic"/> </httpModules> </system.web> @@ -238,7 +237,6 @@ <remove name="UrlRoutingModule"/> <add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/> - <add name="OAuthAuthenticationModule" type="RelyingPartyLogic.OAuthAuthenticationModule, RelyingPartyLogic"/> <add name="Database" type="RelyingPartyLogic.Database, RelyingPartyLogic"/> </modules> <handlers> diff --git a/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs b/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs index 807da2d..357464a 100644 --- a/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs +++ b/projecttemplates/RelyingPartyLogic/OAuthServiceProvider.cs @@ -107,17 +107,23 @@ namespace RelyingPartyLogic { /// Initializes the <see cref="serviceProvider"/> field if it has not yet been initialized. /// </summary> private static void EnsureInitialized() { - if (serviceProvider == null) { + if (serviceProvider == null || serviceDescription == null) { lock (initializerLock) { if (serviceDescription == null) { - var postEndpoint = new MessageReceivingEndpoint(new Uri(Utilities.ApplicationRoot, "OAuth.ashx"), HttpDeliveryMethods.PostRequest); - var getEndpoint = new MessageReceivingEndpoint(postEndpoint.Location, HttpDeliveryMethods.GetRequest); + //var postEndpoint = new MessageReceivingEndpoint(new Uri(Utilities.ApplicationRoot, "OAuth.ashx"), HttpDeliveryMethods.PostRequest); + //var getEndpoint = new MessageReceivingEndpoint(postEndpoint.Location, HttpDeliveryMethods.GetRequest); + serviceDescription = new ServiceProviderDescription { TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() }, - RequestTokenEndpoint = postEndpoint, - AccessTokenEndpoint = postEndpoint, - UserAuthorizationEndpoint = getEndpoint, + RequestTokenEndpoint = _requestTokenEndpoint, + AccessTokenEndpoint = _accessTokenEndpoint, + UserAuthorizationEndpoint = _userAuthorizationEndpoint, }; + + if (tokenManager == null) { + tokenManager = new OAuthServiceProviderTokenManager(); + } + serviceProvider = new ServiceProvider(serviceDescription, tokenManager); } if (tokenManager == null) { @@ -130,5 +136,39 @@ namespace RelyingPartyLogic { } } } + + private static MessageReceivingEndpoint _requestTokenEndpoint; + private static MessageReceivingEndpoint _accessTokenEndpoint; + private static MessageReceivingEndpoint _userAuthorizationEndpoint; + + public static MessageReceivingEndpoint RequestTokenEndpoint { + get { + return _requestTokenEndpoint; + } + set { + _requestTokenEndpoint = value; + serviceDescription = null; + } + } + + public static MessageReceivingEndpoint AccessTokenEndpoint { + get { + return _accessTokenEndpoint; + } + set { + _accessTokenEndpoint = value; + serviceDescription = null; + } + } + + public static MessageReceivingEndpoint UserAuthorizationEndpoint { + get { + return _userAuthorizationEndpoint; + } + set { + _userAuthorizationEndpoint = value; + serviceDescription = null; + } + } } } diff --git a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs index 231637a..ab9ddc1 100644 --- a/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs +++ b/src/DotNetOpenAuth/Messaging/MessagingUtilities.cs @@ -193,6 +193,28 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> + /// Gets the <see cref="HttpDeliveryMethods"/> enum value for a given HTTP verb. + /// </summary> + /// <param name="httpVerb">The HTTP verb.</param> + /// <returns>A <see cref="HttpDeliveryMethods"/> enum value that is within the <see cref="HttpDeliveryMethods.HttpVerbMask"/>.</returns> + /// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception> + public static HttpDeliveryMethods GetHttpDeliveryMethod(string httpVerb) { + if (httpVerb == "GET") { + return HttpDeliveryMethods.GetRequest; + } else if (httpVerb == "POST") { + return HttpDeliveryMethods.PostRequest; + } else if (httpVerb == "PUT") { + return HttpDeliveryMethods.PutRequest; + } else if (httpVerb == "DELETE") { + return HttpDeliveryMethods.DeleteRequest; + } else if (httpVerb == "HEAD") { + return HttpDeliveryMethods.HeadRequest; + } else { + throw ErrorUtilities.ThrowArgumentNamed("httpVerb", MessagingStrings.UnsupportedHttpVerb, httpVerb); + } + } + + /// <summary> /// Sends a multipart HTTP POST request (useful for posting files) but doesn't call GetResponse on it. /// </summary> /// <param name="request">The HTTP request.</param> @@ -683,28 +705,6 @@ namespace DotNetOpenAuth.Messaging { } /// <summary> - /// Gets the <see cref="HttpDeliveryMethods"/> enum value for a given HTTP verb. - /// </summary> - /// <param name="httpVerb">The HTTP verb.</param> - /// <returns>A <see cref="HttpDeliveryMethods"/> enum value that is within the <see cref="HttpDeliveryMethods.HttpVerbMask"/>.</returns> - /// <exception cref="ArgumentException">Thrown if the HTTP request is something we can't handle.</exception> - internal static HttpDeliveryMethods GetHttpDeliveryMethod(string httpVerb) { - if (httpVerb == "GET") { - return HttpDeliveryMethods.GetRequest; - } else if (httpVerb == "POST") { - return HttpDeliveryMethods.PostRequest; - } else if (httpVerb == "PUT") { - return HttpDeliveryMethods.PutRequest; - } else if (httpVerb == "DELETE") { - return HttpDeliveryMethods.DeleteRequest; - } else if (httpVerb == "HEAD") { - return HttpDeliveryMethods.HeadRequest; - } else { - throw ErrorUtilities.ThrowArgumentNamed("httpVerb", MessagingStrings.UnsupportedHttpVerb, httpVerb); - } - } - - /// <summary> /// Gets the HTTP verb to use for a given <see cref="HttpDeliveryMethods"/> enum value. /// </summary> /// <param name="httpMethod">The HTTP method.</param> |