Browse Source

Remove the authorization endpoint resolution logic and update the sample to use FormValueRequiredAttribute

pull/261/head
Kévin Chalet 9 years ago
parent
commit
2cc29a8d92
  1. 13
      samples/Mvc.Server/Controllers/AuthorizationController.cs
  2. 33
      samples/Mvc.Server/Helpers/FormValueRequiredAttribute.cs
  3. 4
      samples/Mvc.Server/Views/Authorization/Authorize.cshtml
  4. 14
      src/OpenIddict.Core/Infrastructure/OpenIddictProvider.cs

13
samples/Mvc.Server/Controllers/AuthorizationController.cs

@ -5,7 +5,6 @@
*/ */
using System.Linq; using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions; using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server; using AspNet.Security.OpenIdConnect.Server;
@ -14,6 +13,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Authentication; using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Mvc.Server.Helpers;
using Mvc.Server.Models; using Mvc.Server.Models;
using Mvc.Server.ViewModels.Authorization; using Mvc.Server.ViewModels.Authorization;
using Mvc.Server.ViewModels.Shared; using Mvc.Server.ViewModels.Shared;
@ -37,7 +37,7 @@ namespace Mvc.Server {
// Note: to support interactive flows like the code flow, // Note: to support interactive flows like the code flow,
// you must provide your own authorization endpoint action: // you must provide your own authorization endpoint action:
[Authorize, HttpGet, Route("~/connect/authorize")] [Authorize, HttpGet("~/connect/authorize")]
public async Task<IActionResult> Authorize(OpenIdConnectRequest request) { public async Task<IActionResult> Authorize(OpenIdConnectRequest request) {
// Retrieve the application details from the database. // Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId); 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<IActionResult> Accept(OpenIdConnectRequest request) { public async Task<IActionResult> Accept(OpenIdConnectRequest request) {
// Retrieve the profile of the logged in user. // Retrieve the profile of the logged in user.
var user = await _userManager.GetUserAsync(User); var user = await _userManager.GetUserAsync(User);
@ -75,7 +76,8 @@ namespace Mvc.Server {
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme); 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() { public IActionResult Deny() {
// Notify OpenIddict that the authorization grant has been denied by the resource owner // 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. // 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, // Note: to support non-interactive flows like password,
// you must provide your own token endpoint action: // you must provide your own token endpoint action:
[HttpPost("~/connect/token")] [HttpPost("~/connect/token"), Produces("application/json")]
[Produces("application/json")]
public async Task<IActionResult> Exchange(OpenIdConnectRequest request) { public async Task<IActionResult> Exchange(OpenIdConnectRequest request) {
if (request.IsPasswordGrantType()) { if (request.IsPasswordGrantType()) {
var user = await _userManager.FindByNameAsync(request.Username); var user = await _userManager.FindByNameAsync(request.Username);

33
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]);
}
}
}

4
samples/Mvc.Server/Views/Authorization/Authorize.cshtml

@ -10,7 +10,7 @@
<input type="hidden" name="request_id" value="@Model.RequestId" /> <input type="hidden" name="request_id" value="@Model.RequestId" />
<input formaction="@Url.Action("Accept")" class="btn btn-lg btn-success" name="Authorize" type="submit" value="Yes" /> <input class="btn btn-lg btn-success" name="submit.Accept" type="submit" value="Yes" />
<input formaction="@Url.Action("Deny")" class="btn btn-lg btn-danger" name="Deny" type="submit" value="No" /> <input class="btn btn-lg btn-danger" name="submit.Deny" type="submit" value="No" />
</form> </form>
</div> </div>

14
src/OpenIddict.Core/Infrastructure/OpenIddictProvider.cs

@ -4,23 +4,11 @@
* the license and the contributors participating to this project. * the license and the contributors participating to this project.
*/ */
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Server; using AspNet.Security.OpenIdConnect.Server;
using JetBrains.Annotations;
namespace OpenIddict.Infrastructure { namespace OpenIddict.Infrastructure {
public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider public partial class OpenIddictProvider<TApplication, TAuthorization, TScope, TToken> : OpenIdConnectServerProvider
where TApplication : class where TAuthorization : class where TScope : class where TToken : class { where TApplication : class where TAuthorization : class where TScope : class where TToken : class {
public override Task MatchEndpoint([NotNull] MatchEndpointContext context) { // Note: this class is split into specialized partial classes.
// 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<object>(null);
}
} }
} }
Loading…
Cancel
Save