【问题标题】:Redirect back from external URL resulting in endless loop从外部 URL 重定向回来导致无限循环
【发布时间】:2021-05-08 07:30:44
【问题描述】:

我有一个调用身份验证服务器的 Blazor 服务器端应用程序。我拥有身份验证服务器(也是一个 ASP.NET Core WebApp),它基于 OpenIdDict。我已经用 oidcdebugger.com 测试过,可以成功生成授权码。

授权服务器重定向回应用程序,以便应用程序完成授权码流程,用户可以访问应用程序。

我做的第一件事是在MainLayout.razor:

@inherits LayoutComponentBase

<AuthorizeView>
    <NotAuthorized>
        <GetAuthorizationCode/>
    </NotAuthorized>
    <Authorized>
        <Header/>
        <NavMenu/>  
    </Authorized>
</AuthorizeView>

@code {

}

GetAuthorizationCode 是一个简单的组件,它是:

public class GetAuthorizationCode: ComponentBase
{
    [Inject]
    protected IAuthService AuthService { get; set; }

    protected override void OnInitialized()
    {
        base.OnInitialized();
        AuthService.GetAuthorizationCode();
    }
}

服务准备发送到认证服务器的请求:

public void GetAuthorizationCode()
{
    var url = "urltoauthserver";

    var parameters = new Dictionary<string, string>()
    {
        { "client_id", "hereclientid" },
        { "redirect_uri", "hereredirecturi" },
        { "scope", "openid email profile" },
        { "response_type", "code" },
        { "response_mode", "form_post" },
        { "state", Guid.NewGuid().ToString() },
        { "nonce", "herenonce" }
    };

    url = QueryHelpers.AddQueryString(url, parameters);
    navigationManager.NavigateTo(url);
}

重定向 uri 是 Blazor 应用程序本身中的一个 api 控制器(不是另一个外部 api)。我需要使用 api 控制器,因为重定向 uri 是一个 POST,看起来像这样:

[Route("api/[controller]")]
[ApiController]
[AllowAnonymous]
public class LoginController : ControllerBase
{
    [HttpPost]
    public IActionResult Callback([FromForm] string code, [FromForm] string state)
    {
        return Ok($"Login callback. Access token: {code}");
    }
}

流程应该是一旦身份验证服务器对用户进行身份验证并将授权代码发送回重定向 uri,上述操作应该创建一个带有授权代码的请求以发送到身份验证服务器并接收访问令牌。

startup.cs我还配置了OpenIdConnect如下:

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
    options.ClientId = "hereclientid";
    options.ClientSecret = "hereclientsecret";
    options.RequireHttpsMetadata = false;
    options.GetClaimsFromUserInfoEndpoint = true;
    options.SaveTokens = true;
    options.ResponseType = OpenIdConnectResponseType.Code;
    options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
    options.Authority = "authserver";
    options.Scope.Add("email");
    options.Scope.Add("profile");
    options.SecurityTokenValidator = new JwtSecurityTokenHandler
    {
        InboundClaimTypeMap = new Dictionary<string, string>()
    };
    options.TokenValidationParameters.NameClaimType = "name";
});

问题是永远无法到达该操作,因为在重定向期间,GetAuthorizationCode 被一遍又一遍地调用。

编辑 如果重定向是 GET,则会发生无限循环。如果重定向是 POST,我会收到 400 Bad Request,这是日志:

2021-02-04 16:56:19.628 +01:00 [INF] 请求开始 HTTP/2 POST https://localhost:44379/api/Login/Callback application/x-www-form-urlencoded 91 2021-02-04 16:56:19.628 +01:00 [INF] CORS 策略执行成功。 2021-02-04 16:56:19.629 +01:00 [INF] 执行端点'/_Host' 2021-02-04 16:56:19.629 +01:00 [INF] 路由与 {page = "/_Host", action = "", controller = ""} 匹配。执行页面/_Host 2021-02-04 16:56:19.639 +01:00 [INF] 防伪令牌验证失败。表单字段“__RequestVerificationToken”或标头值“RequestVerificationToken”中均未提供所需的防伪请求令牌。 Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException:未在表单字段“__RequestVerificationToken”或标头值“RequestVerificationToken”中提供所需的防伪请求令牌。 在 Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext) 在 Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext 上下文) 2021-02-04 16:56:19.671 +01:00 [INF] 过滤器“Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.AutoValidateAntiforgeryTokenAuthorizationFilter”的请求授权失败。 2021-02-04 16:56:19.674 +01:00 [INF] 执行 HttpStatusCodeResult,设置 HTTP 状态码 400 2021-02-04 16:56:19.674 +01:00 [INF] 在 45.2108 毫秒内执行页面 /_Host 2021-02-04 16:56:19.674 +01:00 [INF] 执行端点'/_Host' 2021-02-04 16:56:19.675 +01:00 [INF] 请求完成 HTTP/2 POST https://localhost:44379/api/Login/Callback application/x-www-form-urlencoded 91 - 400 - - 47.2169ms

我为 POST 操作添加了[IgnoreAntiForgeryToken],但它不起作用

【问题讨论】:

    标签: asp.net-core blazor blazor-server-side


    【解决方案1】:

    当用户尝试访问受 Authorize 属性保护的资源时,组件会被渲染。组件中的代码导航到外部身份验证服务器,我认为该服务器会生成授权代码,无论它是什么......

    外部身份验证服务器应该重定向回调用代码,将授权代码传递给它...这意味着您应该使用您希望他发回授权代码的地址 (returnUrl) 调用外部 url。我不确定您所说的授权码是什么意思,但说它是 Jwt 令牌;因此,您应该在 GetAuthorizationCode 中包含接受 Jwt 令牌的代码(通常在服务而不是组件中完成),并将其存储在本地存储中(以备将来使用)。请记住,用户正在尝试访问受保护的资源,例如他正在尝试访问 FetchData 页面......在这种情况下,FetchData 页面中的代码应该从本地存储读取 Jwt 令牌(授权代码)并将其添加到授权用于从 Web Api 端点请求数据的 HttpRequestMessage 对象的标头。您的 Web Api 端点必须使用 Authorize 属性进行保护,该属性调用授权服务以检查用户是否有权访问此资源...

    外部 url 永远不应重定向到 Web Api 端点。它应该重定向到调用它的代码,并传递给它授权代码(Jwt 令牌)。 获取此代码后,您的 Blazor 代码可能会调用 Web Api 端点,向其传递授权代码(Jwt 令牌)

    更新 1:

    此代码:navigationManager.NavigateTo(url);

    我认为应该是: navigationManager.NavigateTo(url, forceLoad:true);

    注意:第二个参数强制 Blazor 导航出 SPA 空间。尝试上面的代码并报告更改...这可能被证明是有意义的。

    注意:以下是我关于 OIDC、服务器端 Blazor 等的答案的链接。我建议您阅读它们,从中复制代码等。您可以从中获得很多,因为它们都是经我测试。我有很多与 Blazor 中的身份验证、授权相关的答案,请自行查找并帮助自己。他们可以让你在这片丛林中的生活更能忍受......

    See thisthisthis

    【讨论】:

    • 我正在使用授权代码流,第一个回调返回代码,然后我需要交换令牌。该组件只需调用构建请求并调用navigationto 的服务。由于无限循环,我从未到达回调
    • 您的意思是 API 控制器生成令牌以换取授权码吗?如果是这样,Api 控制器应该对用户进行身份验证,生成令牌,然后将其传递回 Blazor 调用代码。这意味着,外部身份验证服务器应将返回 URL 传递给 Web Api。注意:如果您停止在谜语中说话,并详细解释您拥有什么以及您想要实现的生活,那么所有参与者都会更容易。您能不能提供更多信息,例如关于外部身份验证服务器、它是什么、谁拥有它等等。
    • 在我使用令牌之前,您从未使用过令牌这个词...您的意思是 Jwt 令牌吗? Web Api 是否归您所有?您是否有验证用户并生成 Jwt 令牌的代码?等等。 ?请说详细点...
    • 我编辑了这个问题,希望您满意。
    猜你喜欢
    • 2014-02-14
    • 1970-01-01
    • 2011-06-04
    • 1970-01-01
    • 2019-04-01
    • 2013-01-25
    • 2015-11-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多