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

时间:2018-11-21 09:05:24

标签: c# identityserver4 oidc

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

内部登录操作:

 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)而失败,或者是否有其他问题导致该失败?

2 个答案:

答案 0 :(得分:1)

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

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

  • 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值),您可以简单地将return url替换为redirect_uri,自己添加error参数。这里的技巧是,由于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>);
});

答案 1 :(得分:0)

在IdentityServer端:

基本表单具有2个按钮:logincancel。如果未按下login;这是cancel

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

在MVC方面:

您可以使用其他参数进行重定向。这些可以被获取并用于显示错误。请记住,很多错误处理(例如无效的用户名/密码)都位于IdentityServer端。