繁体   English   中英

使用 IdentityServer4 时如何在客户端检查 access_denied?

[英]How to check access_denied on client when using IdentityServer4?

我有一个登录页面,当用户单击“取消”按钮时我需要将他重定向到客户端应用程序上的拒绝页面。

内部登录操作:

 if (button != "login")
        {
            // the user clicked the "cancel" button
            var context = await interaction.GetAuthorizationContextAsync(model.ReturnUrl);
            if (context != null)
            {
                // if the user cancels, send a result back into IdentityServer as if they 
                // denied the consent (even if this client does not require consent).
                // this will send back an access denied OIDC error response to the client.
                await interaction.GrantConsentAsync(context, ConsentResponse.Denied);

                // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
                return Redirect(model.ReturnUrl);
            }
        }

在客户端(MVC)我配置了以下事件:

options.Events = new OpenIdConnectEvents
                {
                    OnRemoteFailure = context => 
                    {
                        // here it's returned as 200 ok in case I denied 
                        // consent should'nt be 401 access denined??
                        var statusCode=context.Response.StatusCode;
                        context.Response.Redirect("/");
                        context.HandleResponse();

                        return Task.FromResult(0);
                    }
                };

但我的问题是:我怎么知道 IdentityServer4 失败是因为用户单击了“取消”按钮(access_denied),或者是否有其他问题导致了该失败?

在 IdentityServer 端:

基本表单有 2 个按钮: logincancel 如果没有按下login 这是一个cancel

否则它是一个验证错误,您可以显示它。 cancel您应该重定向回有意义的页面。

在MVC方面:

您可以使用额外的参数重定向。 这些可以被获取并用于显示错误。 请记住,很多错误处理,例如无效的用户名/密码,都停留在 IdentityServer 端。

使用 IdentityServer4 时如何在客户端检查 access_denied?

经过一些检查,事件的context.Failure.Data是一种ListDictionaryInternal ,其中包含您可能正在寻找的条目:

  • error
  • error_description
  • error_uri

因此,通过使用context.Failure.Data["error"]您可以获得您正在寻找的“access_denied”值。


另外一个选项

以 Identity Server 4 的演示代码为例,如果我们转到 Login Post Account Controller 操作,您会感兴趣的部分是处理取消按钮按下的部分:

// check if we are in the context of an authorization request
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);

// the user clicked the "cancel" button
if (button != "login")
{
    if (context != null)
    {
        // RATHER THAN USING THE ORIGINAL STRATEGY:
        //await _interaction.GrantConsentAsync(context, ConsentResponse.Denied);

        // ...MANUALLY BUILD THE RETURN URL YOURSELF 
        // THEN REDIRECT TO THAT:
        model.ReturnUrl = BuildCancelReturnUrl(context);

        if (await _clientStore.IsPkceClientAsync(context.ClientId))
        {
            return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl });
        }

        return Redirect(model.ReturnUrl);
    }
    else
    {
        // since we don't have a valid context, then we just go back to the home page
        return Redirect("~/");
    }
}

如您所见,您无需使用对GrantConsentAsync的调用来响应access_denied代码(不包含任何error_descriptionerror_uri值),而是可以简单地将返回 url 替换为 redirect_uri,自己添加错误参数。 这里的技巧是,由于 error_description 和 error_uri 参数是完全可选的,您可以利用它并传递一些有意义的东西,以便每个客户端知道此访问被拒绝,因为用户取消了表单 然后,在重定向中使用该返回 url。

这是一个关于如何“重新构建”返回 url 的示例。 你可以有这样的功能:

private string BuildCancelReturnUrl(AuthorizationRequest context)
{
    var RedirectUri = new UriBuilder(context.RedirectUri);
    var Query = HttpUtility.ParseQueryString(string.Empty);

    Query.Add("error", "access_denied");
    Query.Add("error_description", "some_meaningful_code_here");
    // The state IS MEGA IMPORTANT:
    Query.Add("state", context.Parameters["state"]);

    RedirectUri.Query = Query.ToString();

    return RedirectUri.ToString();
}

// Use like:  model.ReturnUrl = BuildCancelReturnUrl(context);
// See above extract from Identity Server's demo code

那么,在客户端,你可以像这样创建一些扩展:

public static class OpenIdConnectOptionsExtensions {
    public static OpenIdConnectOptions UseRedirectionOnLoginCancel(
        this OpenIdConnectOptions options
        , string RedirectTo = "/")
    {
        options.Events.OnAccessDenied = context =>
        {
            NameValueCollection RequestQuery = HttpUtility.ParseQueryString(context.Request.QueryString.Value);

            string descriptionField = "error_description";
            string cancelledCode = "your_meaningful_description_code_here";

            bool descriptionIncluded = RequestQuery.AllKeys.Contains(descriptionField);

            if (descriptionIncluded && RequestQuery[errorDescriptionField].Equals(cancelledCode))
            {
                context.Response.Redirect(RedirectTo);
                context.HandleResponse();
            }

            return Task.CompletedTask;
        };

        return options;
    }
}

最后,每个客户端都可以配置为重定向到您在Startup.cs指定的位置:

services.AddOpenIdConnect(<challenge_name>, config =>
{
    // ...
    config.UseRedirectionOnLoginCancel(<final path to redirect to>);
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM