From 2cc29a8d922d71a0973a239474e1be5983aacec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Chalet?= Date: Tue, 8 Nov 2016 02:00:08 +0100 Subject: [PATCH] Remove the authorization endpoint resolution logic and update the sample to use FormValueRequiredAttribute --- .../Controllers/AuthorizationController.cs | 13 ++++---- .../Helpers/FormValueRequiredAttribute.cs | 33 +++++++++++++++++++ .../Views/Authorization/Authorize.cshtml | 4 +-- .../Infrastructure/OpenIddictProvider.cs | 14 +------- 4 files changed, 43 insertions(+), 21 deletions(-) create mode 100644 samples/Mvc.Server/Helpers/FormValueRequiredAttribute.cs diff --git a/samples/Mvc.Server/Controllers/AuthorizationController.cs b/samples/Mvc.Server/Controllers/AuthorizationController.cs index d6b793b6..f96d3af5 100644 --- a/samples/Mvc.Server/Controllers/AuthorizationController.cs +++ b/samples/Mvc.Server/Controllers/AuthorizationController.cs @@ -5,7 +5,6 @@ */ using System.Linq; -using System.Security.Claims; using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Server; @@ -14,6 +13,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http.Authentication; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Mvc.Server.Helpers; using Mvc.Server.Models; using Mvc.Server.ViewModels.Authorization; using Mvc.Server.ViewModels.Shared; @@ -37,7 +37,7 @@ namespace Mvc.Server { // Note: to support interactive flows like the code flow, // you must provide your own authorization endpoint action: - [Authorize, HttpGet, Route("~/connect/authorize")] + [Authorize, HttpGet("~/connect/authorize")] public async Task Authorize(OpenIdConnectRequest request) { // Retrieve the application details from the database. var application = await _applicationManager.FindByClientIdAsync(request.ClientId); @@ -57,7 +57,8 @@ namespace Mvc.Server { }); } - [Authorize, HttpPost("~/connect/authorize/accept"), ValidateAntiForgeryToken] + [Authorize, FormValueRequired("submit.Accept")] + [HttpPost("~/connect/authorize"), ValidateAntiForgeryToken] public async Task Accept(OpenIdConnectRequest request) { // Retrieve the profile of the logged in user. var user = await _userManager.GetUserAsync(User); @@ -75,7 +76,8 @@ namespace Mvc.Server { return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme); } - [Authorize, HttpPost("~/connect/authorize/deny"), ValidateAntiForgeryToken] + [Authorize, FormValueRequired("submit.Deny")] + [HttpPost("~/connect/authorize"), ValidateAntiForgeryToken] public IActionResult Deny() { // Notify OpenIddict that the authorization grant has been denied by the resource owner // to redirect the user agent to the client application using the appropriate response_mode. @@ -109,8 +111,7 @@ namespace Mvc.Server { // Note: to support non-interactive flows like password, // you must provide your own token endpoint action: - [HttpPost("~/connect/token")] - [Produces("application/json")] + [HttpPost("~/connect/token"), Produces("application/json")] public async Task Exchange(OpenIdConnectRequest request) { if (request.IsPasswordGrantType()) { var user = await _userManager.FindByNameAsync(request.Username); diff --git a/samples/Mvc.Server/Helpers/FormValueRequiredAttribute.cs b/samples/Mvc.Server/Helpers/FormValueRequiredAttribute.cs new file mode 100644 index 00000000..872f5964 --- /dev/null +++ b/samples/Mvc.Server/Helpers/FormValueRequiredAttribute.cs @@ -0,0 +1,33 @@ +using System; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.ActionConstraints; +using Microsoft.AspNetCore.Routing; + +namespace Mvc.Server.Helpers { + public sealed class FormValueRequiredAttribute : ActionMethodSelectorAttribute { + private readonly string _name; + + public FormValueRequiredAttribute(string name) { + _name = name; + } + + public override bool IsValidForRequest(RouteContext context, ActionDescriptor action) { + if (string.Equals(context.HttpContext.Request.Method, "GET", StringComparison.OrdinalIgnoreCase) || + string.Equals(context.HttpContext.Request.Method, "HEAD", StringComparison.OrdinalIgnoreCase) || + string.Equals(context.HttpContext.Request.Method, "DELETE", StringComparison.OrdinalIgnoreCase) || + string.Equals(context.HttpContext.Request.Method, "TRACE", StringComparison.OrdinalIgnoreCase)) { + return false; + } + + if (string.IsNullOrEmpty(context.HttpContext.Request.ContentType)) { + return false; + } + + if (!context.HttpContext.Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)) { + return false; + } + + return !string.IsNullOrEmpty(context.HttpContext.Request.Form[_name]); + } + } +} diff --git a/samples/Mvc.Server/Views/Authorization/Authorize.cshtml b/samples/Mvc.Server/Views/Authorization/Authorize.cshtml index 7dd66099..d9bee489 100644 --- a/samples/Mvc.Server/Views/Authorization/Authorize.cshtml +++ b/samples/Mvc.Server/Views/Authorization/Authorize.cshtml @@ -10,7 +10,7 @@ - - + + diff --git a/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.cs b/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.cs index 4847dc32..767db7c9 100644 --- a/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.cs +++ b/src/OpenIddict.Core/Infrastructure/OpenIddictProvider.cs @@ -4,23 +4,11 @@ * the license and the contributors participating to this project. */ -using System.Threading.Tasks; using AspNet.Security.OpenIdConnect.Server; -using JetBrains.Annotations; namespace OpenIddict.Infrastructure { public partial class OpenIddictProvider : OpenIdConnectServerProvider where TApplication : class where TAuthorization : class where TScope : class where TToken : class { - public override Task MatchEndpoint([NotNull] MatchEndpointContext context) { - // Note: by default, OpenIdConnectServerHandler only handles authorization requests made to AuthorizationEndpointPath. - // This context handler uses a more relaxed policy that allows extracting authorization requests received at - // /connect/authorize/accept and /connect/authorize/deny (see OpenIddictController.cs for more information). - if (context.Options.AuthorizationEndpointPath.HasValue && - context.Request.Path.StartsWithSegments(context.Options.AuthorizationEndpointPath)) { - context.MatchesAuthorizationEndpoint(); - } - - return Task.FromResult(null); - } + // Note: this class is split into specialized partial classes. } } \ No newline at end of file