Blazor 服务器托管 - 使用 AWS Cognito 配置 OpenId Connect

Blazor Server Hosted - Configure OpenId Connect with AWS Cognito

我正在尝试使用 AWS Cognito 设置 Blazor(服务器端 - 预览版 6)。不幸的是,我对 ASP.NET Core 或 OAuth/OpenId 没有太多经验。目前,我可以单击 OpenIdConnect 按钮并进行用户身份验证。但这是我看到的结果(一旦 Cognito 重定向到 https://localhost:44385/signin-oidc):

所以这让我抓狂。 "Error loading external login information" 是什么意思?这与 permissions/the 允许的范围有关吗?我确实尝试过 options.Scope.Add() 和 "email"、"profile" and/or "openid" - 但似乎没有帮助。

这是我的 Startup.cs 文件中的内容:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<ApplicationDbContext>( options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")) );
        services.AddDefaultIdentity<IdentityUser>().AddEntityFrameworkStores<ApplicationDbContext>();
        services.AddRazorPages();
        services.AddServerSideBlazor();
        services.AddSingleton<WeatherForecastService>();

        services.Configure<OpenIdConnectOptions>(Configuration.GetSection("Authentication:Cognito"));

        var serviceProvider = services.BuildServiceProvider();
        var authOptions = serviceProvider.GetService<IOptions<OpenIdConnectOptions>>();

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
        .AddCookie()
        .AddOpenIdConnect(options =>
        {
            options.ResponseType = OpenIdConnectResponseType.Code;
            options.MetadataAddress = authOptions.Value.MetadataAddress;
            options.ClientId = authOptions.Value.ClientId;
            options.ClientSecret = authOptions.Value.ClientSecret;
            options.GetClaimsFromUserInfoEndpoint = true;
            options.SaveTokens = authOptions.Value.SaveTokens;
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = authOptions.Value.TokenValidationParameters.ValidateIssuer
            };
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseAuthentication();
        app.UseAuthorization();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapBlazorHub();
            endpoints.MapFallbackToPage("/_Host");
        });
    }
}

这是输出的内容 window:

Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 POST https://localhost:44385/Identity/Account/ExternalLogin?returnUrl=%2F application/x-www-form-urlencoded 248
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executing endpoint '/Account/ExternalLogin'
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Route matched with {page = "/Account/ExternalLogin", area = "Identity"}. Executing page /Account/ExternalLogin
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Executing handler method Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.ExternalLoginModel.OnPost - ModelState is Invalid
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Executed handler method OnPost, returned result Microsoft.AspNetCore.Mvc.ChallengeResult.
Microsoft.AspNetCore.Mvc.ChallengeResult: Information: Executing ChallengeResult with authentication schemes (OpenIdConnect).
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler: Information: AuthenticationScheme: OpenIdConnect was challenged.
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Executed page /Account/ExternalLogin in 113.68560000000001ms
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executed endpoint '/Account/ExternalLogin'
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 302.9229ms 302 
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 GET https://localhost:44385/signin-oidc?code=ffbac0f8-e1e6-46fc-a64e-cd7ece7b4dd8&state=CfDJ8NccaQdck19Fie6EgKf0wAIZI23G5O9M52tXkPEptmR-6XW3ZWJQxlTYSHItlOdqzfZf7ZfscXMZg4Pew0gG0ybmyy_pOocBL--CC4j3deAsKtUM4bqUE7KyiKYqMpanwbCEShZBQZa1I32U-5F4jgHRS9Ott56PhEDAFgmOk6WmceSpCO058lYWQnVMtc1vUQ5M1_Shhv4y4jUJRYpVdVqsRqF5vVtQTvrMYlJlCsclALjQZmuEs_UO15Nq-7Q0VZhsypc4OmXGVVAfwL65uHMX1Q2JbVhb21unxcotUphXPEv5VYJBsqpq7qLA-9rl19XzOmJoq2SSx6g0N_AC-nmntuNVeUyIVh3OMTju8Qb6YJOMpE5p2zK0PgnpGxsA57kTH6laJbD_B-EIE2Bk_1rRCtczlmtaAx2wCnMwVsDM  
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler: Information: AuthenticationScheme: Cookies signed in.
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 644.9236000000001ms 302 
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 GET https://localhost:44385/Identity/Account/ExternalLogin?returnUrl=%2F&handler=Callback  
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executing endpoint '/Account/ExternalLogin'
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Route matched with {page = "/Account/ExternalLogin", area = "Identity"}. Executing page /Account/ExternalLogin
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Executing handler method Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.ExternalLoginModel.OnGetCallbackAsync - ModelState is Valid
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Executed handler method OnGetCallbackAsync, returned result Microsoft.AspNetCore.Mvc.RedirectToPageResult.
Microsoft.AspNetCore.Mvc.RedirectToRouteResult: Information: Executing RedirectToPageResult, redirecting to ./Login.
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Executed page /Account/ExternalLogin in 11.1302ms
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executed endpoint '/Account/ExternalLogin'
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 30.221500000000002ms 302 
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 GET https://localhost:44385/Identity/Account/Login?ReturnUrl=%2F  
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executing endpoint '/Account/Login'
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Route matched with {page = "/Account/Login", area = "Identity"}. Executing page /Account/Login
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Executing handler method Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.LoginModel.OnGetAsync - ModelState is Valid
Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler: Information: AuthenticationScheme: Identity.External signed out.
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Executed handler method OnGetAsync, returned result .
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Executing an implicit handler method - ModelState is Invalid
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Executed an implicit handler method, returned result Microsoft.AspNetCore.Mvc.RazorPages.PageResult.
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker: Information: Executed page /Account/Login in 42.6662ms
Microsoft.AspNetCore.Routing.EndpointMiddleware: Information: Executed endpoint '/Account/Login'
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 157.9035ms 200 text/html; charset=utf-8
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 GET https://localhost:44385/Identity/css/site.css  
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 GET https://localhost:44385/Identity/lib/bootstrap/dist/css/bootstrap.css  
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware: Information: The file /Identity/css/site.css was not modified
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware: Information: The file /Identity/lib/bootstrap/dist/css/bootstrap.css was not modified
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 115.28320000000001ms 304 text/css
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 125.4239ms 304 text/css
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 GET https://localhost:44385/Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js  
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 GET https://localhost:44385/Identity/lib/jquery-validation/dist/jquery.validate.js  
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 GET https://localhost:44385/Identity/js/site.js  
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware: Information: The file /Identity/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js was not modified
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware: Information: The file /Identity/lib/jquery-validation/dist/jquery.validate.js was not modified
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 109.1367ms 304 application/javascript
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 117.52770000000001ms 304 application/javascript
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware: Information: The file /Identity/js/site.js was not modified
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 GET https://localhost:44385/Identity/lib/bootstrap/dist/js/bootstrap.bundle.js  
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 212.7227ms 304 application/javascript
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware: Information: The file /Identity/lib/bootstrap/dist/js/bootstrap.bundle.js was not modified
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request starting HTTP/2.0 GET https://localhost:44385/Identity/lib/jquery/dist/jquery.js  
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 169.02100000000002ms 304 application/javascript
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware: Information: The file /Identity/lib/jquery/dist/jquery.js was not modified
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished in 146.98080000000002ms 304 application/javascript

里面没有明显的错误。这是我的 Cognito 配置:

有没有我遗漏的东西。有人能指出我正确的方向吗?

更新

如果我点击主页 link,我可以看到我 登录。所以在从 Cognito 回调后重定向有些不对劲?

事实证明,错误消息的原因来自此配置:

services.AddAuthentication(options =>
{
  options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
  options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
  options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options => { ... } );

从 GitHub 上的 comment 得到提示:

The UseIdentity call is registering two cookie middlewares, one for the ApplicationCookie and one for the ExternalApplicationCookie. These are set to different authentication schemes by default. It should just work if you remove all of these explicit authentication scheme settings and rely on the default values...

我将其简化为:

services.AddAuthentication()
  .AddCookie()
  .AddOpenIdConnect(options => { ... } );

调用 https://localhost:5001/Identity/Account/ExternalLogin?returnUrl=%2F&handler=Callback 时发生错误。由于 mis-configuration,它会抛出错误,"Error loading external login information"。

使用新配置后,login/logout 现在可以正常工作了!