簡體   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