1

System.InvalidOperationException:无法从 ASP.NET 上下文中检索 OpenID Connect 请求。确保在 'app.UseMvc()' 之前调用 'app.UseOpenIddict()' 并且操作路由对应于通过 'services.AddOpenIddict().Enable[...]Endpoint(... )'。在 OpenIddict.Mvc.OpenIddictModelBinder.BindModelAsync(ModelBindingContext 上下文)

我的启动.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddApplicationInsightsTelemetry(Configuration);
    services.AddDbContext<ApplicationUserDbContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddIdentity<ApplicationUser, ApplicationRole>()
           .AddEntityFrameworkStores<ApplicationUserDbContext>()
           .AddDefaultTokenProviders();

         services.AddMvc();



        .AddMvcBinders()

        .EnableAuthorizationEndpoint("/connect/authorize")
        .EnableLogoutEndpoint("/connect/logout")
        .EnableTokenEndpoint("/connect/token")
        .EnableUserinfoEndpoint("/Account/Userinfo")


        .AllowAuthorizationCodeFlow()
        .AllowPasswordFlow()
        .AllowRefreshTokenFlow()


        .RequireClientIdentification()

        // During development, you can disable the HTTPS requirement.
        .DisableHttpsRequirement()

        .AddEphemeralSigningKey();

    services.AddTransient<IEmailSender, AuthMessageSender>();
    services.AddTransient<ISmsSender, AuthMessageSender>();
}


public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    app.UseApplicationInsightsRequestTelemetry();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseApplicationInsightsExceptionTelemetry();

    app.UseStaticFiles();          

    app.UseCsp(options => options.DefaultSources(directive => directive.Self())
        .ImageSources(directive => directive.Self()
            .CustomSources("*"))
        .ScriptSources(directive => directive.Self()
            .UnsafeInline())
        .StyleSources(directive => directive.Self()
            .UnsafeInline()));

    app.UseXContentTypeOptions();

    app.UseXfo(options => options.Deny());

    app.UseXXssProtection(options => options.EnabledWithBlockMode());

    app.UseIdentity();

    // Add a middleware used to validate access
    // tokens and protect the API endpoints.
    app.UseOAuthValidation();

    app.UseGoogleAuthentication(new GoogleOptions
    {

    });    

    app.UseStatusCodePagesWithReExecute("/error");

    app.UseOpenIddict();

    app.UseMvcWithDefaultRoute();
}

更新

using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Mvc.Server.Models;
using Mvc.Server.ViewModels.Authorization;
using Mvc.Server.ViewModels.Shared;
using OpenIddict;

public class AuthorizationController : Controller {
    private readonly OpenIddictApplicationManager<OpenIddictApplication> _applicationManager;
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly UserManager<ApplicationUser> _userManager;

    public AuthorizationController(
        OpenIddictApplicationManager<OpenIddictApplication> applicationManager,
        SignInManager<ApplicationUser> signInManager,
        UserManager<ApplicationUser> userManager) {
        _applicationManager = applicationManager;
        _signInManager = signInManager;
        _userManager = userManager;
    }

    // Note: to support interactive flows like the code flow,
    // you must provide your own authorization endpoint action:

    [Authorize, HttpGet, Route("~/connect/authorize")]
    public async Task<IActionResult> Authorize(OpenIdConnectRequest request) {
        // Retrieve the application details from the database.
        var application = await _applicationManager.FindByClientIdAsync(request.ClientId);
        if (application == null) {
            return View("Error", new ErrorViewModel {
                Error = OpenIdConnectConstants.Errors.InvalidClient,
                ErrorDescription = "Details concerning the calling client application cannot be found in the database"
            });
        }

        // Flow the request_id to allow OpenIddict to restore
        // the original authorization request from the cache.
        return View(new AuthorizeViewModel {
            ApplicationName = application.DisplayName,
            RequestId = request.RequestId,
            Scope = request.Scope
        });
    }

    [Authorize, HttpPost("~/connect/authorize/accept"), ValidateAntiForgeryToken]
    public async Task<IActionResult> Accept(OpenIdConnectRequest request) {
        // Retrieve the profile of the logged in user.
        var user = await _userManager.GetUserAsync(User);
        if (user == null) {
            return View("Error", new ErrorViewModel {
                Error = OpenIdConnectConstants.Errors.ServerError,
                ErrorDescription = "An internal error has occurred"
            });
        }

        // Create a new authentication ticket.
        var ticket = await CreateTicketAsync(request, user);

        // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
        return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
    }

    [Authorize, HttpPost("~/connect/authorize/deny"), 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.
        return Forbid(OpenIdConnectServerDefaults.AuthenticationScheme);
    }

    // Note: the logout action is only useful when implementing interactive
    // flows like the authorization code flow or the implicit flow.

    [HttpGet("~/connect/logout")]
    public IActionResult Logout(OpenIdConnectRequest request) {
        // Flow the request_id to allow OpenIddict to restore
        // the original logout request from the distributed cache.
        return View(new LogoutViewModel {
            RequestId = request.RequestId
        });
    }

    [HttpPost("~/connect/logout"), ValidateAntiForgeryToken]
    public async Task<IActionResult> Logout() {
        // Ask ASP.NET Core Identity to delete the local and external cookies created
        // when the user agent is redirected from the external identity provider
        // after a successful authentication flow (e.g Google or Facebook).
        await _signInManager.SignOutAsync();

        // Returning a SignOutResult will ask OpenIddict to redirect the user agent
        // to the post_logout_redirect_uri specified by the client application.
        return SignOut(OpenIdConnectServerDefaults.AuthenticationScheme);
    }

    // Note: to support non-interactive flows like password,
    // you must provide your own token endpoint action:

    [HttpPost("~/connect/token")]
    [Produces("application/json")]
    public async Task<IActionResult> Exchange(OpenIdConnectRequest request) {
        if (request.IsPasswordGrantType()) {
            var user = await _userManager.FindByNameAsync(request.Username);
            if (user == null) {
                return BadRequest(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "The username/password couple is invalid."
                });
            }

            // Ensure the user is allowed to sign in.
            if (!await _signInManager.CanSignInAsync(user)) {
                return BadRequest(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "The specified user is not allowed to sign in."
                });
            }

            // Reject the token request if two-factor authentication has been enabled by the user.
            if (_userManager.SupportsUserTwoFactor && await _userManager.GetTwoFactorEnabledAsync(user)) {
                return BadRequest(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "The specified user is not allowed to sign in."
                });
            }

            // Ensure the user is not already locked out.
            if (_userManager.SupportsUserLockout && await _userManager.IsLockedOutAsync(user)) {
                return BadRequest(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "The username/password couple is invalid."
                });
            }

            // Ensure the password is valid.
            if (!await _userManager.CheckPasswordAsync(user, request.Password)) {
                if (_userManager.SupportsUserLockout) {
                    await _userManager.AccessFailedAsync(user);
                }

                return BadRequest(new OpenIdConnectResponse {
                    Error = OpenIdConnectConstants.Errors.InvalidGrant,
                    ErrorDescription = "The username/password couple is invalid."
                });
            }

            if (_userManager.SupportsUserLockout) {
                await _userManager.ResetAccessFailedCountAsync(user);
            }

            // Create a new authentication ticket.
            var ticket = await CreateTicketAsync(request, user);

            return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
        }

        return BadRequest(new OpenIdConnectResponse {
            Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
            ErrorDescription = "The specified grant type is not supported."
        });
    }

    private async Task<AuthenticationTicket> CreateTicketAsync(OpenIdConnectRequest request, ApplicationUser user) {
        // Create a new ClaimsPrincipal containing the claims that
        // will be used to create an id_token, a token or a code.
        var principal = await _signInManager.CreateUserPrincipalAsync(user);

        // Note: by default, claims are NOT automatically included in the access and identity tokens.
        // To allow OpenIddict to serialize them, you must attach them a destination, that specifies
        // whether they should be included in access tokens, in identity tokens or in both.

        foreach (var claim in principal.Claims) {
            // In this sample, every claim is serialized in both the access and the identity tokens.
            // In a real world application, you'd probably want to exclude confidential claims
            // or apply a claims policy based on the scopes requested by the client application.
            claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
                                  OpenIdConnectConstants.Destinations.IdentityToken);
        }

        // Create a new authentication ticket holding the user identity.
        var ticket = new AuthenticationTicket(
            principal, new AuthenticationProperties(),
            OpenIdConnectServerDefaults.AuthenticationScheme);

        // Set the list of scopes granted to the client application.
        // Note: the offline_access scope must be granted
        // to allow OpenIddict to return a refresh token.
        ticket.SetScopes(new[] {
            OpenIdConnectConstants.Scopes.OpenId,
            OpenIdConnectConstants.Scopes.Email,
            OpenIdConnectConstants.Scopes.Profile,
            OpenIdConnectConstants.Scopes.OfflineAccess,
            OpenIddictConstants.Scopes.Roles
        }.Intersect(request.GetScopes()));

        return ticket;
    }
}

改变:

 [Authorize, HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
        public async Task<IActionResult> Accept(OpenIdConnectRequest request) {
            // Retrieve the profile of the logged in user.
            var user = await _userManager.GetUserAsync(User);
            if (user == null) {
                return View("Error", new ErrorViewModel {
                    Error = OpenIdConnectConstants.Errors.ServerError,
                    ErrorDescription = "An internal error has occurred"
                });
            }

            // Create a new authentication ticket.
            var ticket = await CreateTicketAsync(request, user);

            // Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
            return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
        }
4

1 回答 1

2

OpenIddict 用于允许“子路由”喜欢/connect/authorize/accept或在指定/connect/authorize/deny时被识别为有效的授权端点路径,但最近删除了此功能/connect/authorize

使用最新的 OpenIddict 位,我们鼓励您对所有授权端点操作使用相同的路由模板。

[Authorize, HttpGet("~/connect/authorize")]
public async Task<IActionResult> Authorize(OpenIdConnectRequest request)
{
    // ...
}

[Authorize, FormValueRequired("submit.Accept")]
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
public async Task<IActionResult> Accept(OpenIdConnectRequest request)
{
    // ...
}

[Authorize, FormValueRequired("submit.Deny")]
[HttpPost("~/connect/authorize"), ValidateAntiForgeryToken]
public IActionResult Deny()
{
    // ...
}

您可以使用 Orchard 的[FormValueRequired]方法来区分您的行为:

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

不要忘记更新您的提交按钮:

<input class="btn btn-lg btn-success" name="submit.Accept" type="submit" value="Yes" />
<input class="btn btn-lg btn-danger" name="submit.Deny" type="submit" value="No" />
于 2016-11-11T12:47:39.067 回答