从外部 URL 重定向回导致无限循环

Redirect back from external URL resulting in endless loop

我有一个调用身份验证服务器的 Blazor 服务器端应用程序。我拥有 auth 服务器(也是一个 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}");
    }
}

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

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] Request starting 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 policy execution successful. 2021-02-04 16:56:19.629 +01:00 [INF] Executing endpoint '/_Host' 2021-02-04 16:56:19.629 +01:00 [INF] Route matched with {page = "/_Host", action = "", controller = ""}. Executing page /_Host 2021-02-04 16:56:19.639 +01:00 [INF] Antiforgery token validation failed. The required antiforgery request token was not provided in either form field "__RequestVerificationToken" or header value "RequestVerificationToken". Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException: The required antiforgery request token was not provided in either form field "__RequestVerificationToken" or header value "RequestVerificationToken". at Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateRequestAsync(HttpContext httpContext) at Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter.OnAuthorizationAsync(AuthorizationFilterContext context) 2021-02-04 16:56:19.671 +01:00 [INF] Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.AutoValidateAntiforgeryTokenAuthorizationFilter'. 2021-02-04 16:56:19.674 +01:00 [INF] Executing HttpStatusCodeResult, setting HTTP status code 400 2021-02-04 16:56:19.674 +01:00 [INF] Executed page /_Host in 45.2108ms 2021-02-04 16:56:19.674 +01:00 [INF] Executed endpoint '/_Host' 2021-02-04 16:56:19.675 +01:00 [INF] Request finished HTTP/2 POST https://localhost:44379/api/Login/Callback application/x-www-form-urlencoded 91 - 400 - - 47.2169ms

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

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

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

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

更新 1:

此代码:navigationManager.NavigateTo(url);

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

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

注意:以下是我关于 OIDC、server-side Blazor 等的回答的链接。我建议您阅读它们,从中复制代码等。您可以从中获益良多,因为他们都经过我的测试。我有很多与 Blazor 中的身份验证、授权相关的答案,寻找它们并帮助自己。它们可以让你在丛林中的生活更加舒适...

See this and this and this