本文介绍了IdentityServer 4的自定义登录UI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在做一个类似于IdentityServer4.Quickstart.UI的项目,但是将Login UI和IdentityServer分开了.更具体地说,IdentityServer本身将是纯WebApi,而登录UI可以是使用Angualr或React或...构建的任何SPA.当前,我遇到的问题是Identity.Application was not authenticated. Failure message: Unprotect ticket failed.

I am doing a project that is similar to IdentityServer4.Quickstart.UI, but separate the Login UI and IdentityServer. More specifically, the IdentityServer itself will be a pure WebApi and the Login UI can be any SPA built with Angualr or React or .... Currently, I am facing an issue Identity.Application was not authenticated. Failure message: Unprotect ticket failed.

更详细的过程如下:单击SPA页面中的登录按钮后,以下代码

The more detailed process is as: after clicking the login button in the SPA page, the following code

var result = await this._signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);

将在WebApi(IdentityServer)中运行,并且result成功(用户已登录,可以在IdentityServer代码的UserSession中看到).

will be run in the WebApi(IdentityServer) and the result is succeeded (user is logged in, which can be seen in UserSession in IdentityServer code).

按照IdentityServer4.Quickstart.UI中的过程进行操作,UI将被重定向到/connect/authorize/callback端点.但是,当我将UI重定向到/connect/authorize/callback时,该用户不再登录.错误是Identity.Application was not authenticated. Failure message: Unprotect ticket failed.请帮忙看看有什么问题.有人说这是因为身份验证cookie,但我不知道该怎么做.

Following the procedure in IdentityServer4.Quickstart.UI, the UI will be redirected to /connect/authorize/callback endpoint. However when I redirect the UI to /connect/authorize/callback, the user is no longer logged in. The error is Identity.Application was not authenticated. Failure message: Unprotect ticket failed. Please help to see what could be wrong. Some people said it is because the authentication cookie but I have no idea how to do it.

详细日志如下所示

info: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[0]
      User profile is available. Using 'C:\Users\Admin\AppData\Local\ASP.NET\DataProtection-Keys' as key repository and Windows DPAPI to encrypt keys at rest.
info: IdentityServer4.Startup[0]
      You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation.
dbug: IdentityServer4.Startup[0]
      Custom IssuerUri set to http://localhost:5000
dbug: IdentityServer4.Startup[0]
      Using Identity.Application as default ASP.NET Core scheme for authentication
dbug: IdentityServer4.Startup[0]
      Using Identity.External as default ASP.NET Core scheme for sign-in
dbug: IdentityServer4.Startup[0]
      Using Identity.External as default ASP.NET Core scheme for sign-out
dbug: IdentityServer4.Startup[0]
      Using Identity.Application as default ASP.NET Core scheme for challenge
dbug: IdentityServer4.Startup[0]
      Using Identity.Application as default ASP.NET Core scheme for forbid
Hosting environment: Development
Content root path: C:\Users\Admin\Downloads\Programming\IDS\Server
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 OPTIONS http://localhost:5000/Account/Login?returnUrl=http://localhost:5000/connect/authorize/callback?client_id=ce&redirect_uri=http%3A%2F%2Flocalhost%3A5050&response_type=id_token%20token&scope=openid%20profile%20CE&nonce=N0.90507026110735561540718014359&state=15407087903260.1454827167180146
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
      Policy execution successful.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 20.8366ms 204
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 POST http://localhost:5000/Account/Login?returnUrl=http://localhost:5000/connect/authorize/callback?client_id=ce&redirect_uri=http%3A%2F%2Flocalhost%3A5050&response_type=id_token%20token&scope=openid%20profile%20CE&nonce=N0.90507026110735561540718014359&state=15407087903260.1454827167180146 application/json 61
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
      Policy execution successful.
dbug: IdentityServer4.Hosting.CorsPolicyProvider[0]
      CORS request made for path: /Account/Login from origin: http://localhost:5001
dbug: IdentityServer4.Hosting.CorsPolicyProvider[0]
      CorsPolicyService allowed origin: http://localhost:5001
info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
      Policy execution successful.
dbug: IdentityServer4.Startup[0]
      Login Url: http://localhost:5001/login
dbug: IdentityServer4.Startup[0]
      Login Return Url Parameter: ReturnUrl
dbug: IdentityServer4.Startup[0]
      Logout Url: /Account/Logout
dbug: IdentityServer4.Startup[0]
      ConsentUrl Url: /consent
dbug: IdentityServer4.Startup[0]
      Consent Return Url Parameter: returnUrl
dbug: IdentityServer4.Startup[0]
      Error Url: /home/error
dbug: IdentityServer4.Startup[0]
      Error Id Parameter: errorId
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Route matched with {action = "Login", controller = "Account"}. Executing action Server.Controllers.AccountController.Login (Server)
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
      Executing action method Server.Controllers.AccountController.Login (Server) with arguments (Server.Dto.Login) - Validation state: Valid
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 2.1.4-rtm-31024 initialized 'DbContext' using provider 'Pomelo.EntityFrameworkCore.MySql' with options: None
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (88ms) [Parameters=[@__normalizedUserName_0='?' (Size = 256)], CommandType='Text', CommandTimeout='30']
      SELECT `u`.`Id`, `u`.`AccessFailedCount`, `u`.`ConcurrencyStamp`, `u`.`Email`, `u`.`EmailConfirmed`, `u`.`LockoutEnabled`, `u`.`LockoutEnd`, `u`.`NormalizedEmail`, `u`.`NormalizedUserName`, `u`.`PasswordHash`, `u`.`PhoneNumber`, `u`.`PhoneNumberConfirmed`, `u`.`SecurityStamp`, `u`.`TwoFactorEnabled`, `u`.`UserName`
      FROM `AspNetUsers` AS `u`
      WHERE `u`.`NormalizedUserName` = @__normalizedUserName_0
      LIMIT 1
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (6ms) [Parameters=[@__user_Id_0='?' (Size = 255)], CommandType='Text', CommandTimeout='30']
      SELECT `uc`.`Id`, `uc`.`ClaimType`, `uc`.`ClaimValue`, `uc`.`UserId`
      FROM `AspNetUserClaims` AS `uc`
      WHERE `uc`.`UserId` = @__user_Id_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (30ms) [Parameters=[@__userId_0='?' (Size = 255)], CommandType='Text', CommandTimeout='30']
      SELECT `role`.`Name`
      FROM `AspNetUserRoles` AS `userRole`
      INNER JOIN `AspNetRoles` AS `role` ON `userRole`.`RoleId` = `role`.`Id`
      WHERE `userRole`.`UserId` = @__userId_0
dbug: IdentityServer4.Hosting.IdentityServerAuthenticationService[0]
      Augmenting SignInContext
dbug: IdentityServer4.Hosting.IdentityServerAuthenticationService[0]
      Adding idp claim with value: local
dbug: IdentityServer4.Hosting.IdentityServerAuthenticationService[0]
      Adding amr claim with value: pwd
dbug: IdentityServer4.Hosting.IdentityServerAuthenticationService[0]
      Adding auth_time claim with value: 1540720363
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[10]
      AuthenticationScheme: Identity.Application signed in.
info: Server.Controllers.AccountController[0]
      User logged in.
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action method Server.Controllers.AccountController.Login (Server), returned result Microsoft.AspNetCore.Mvc.OkResult in 25685.0508ms.
info: Microsoft.AspNetCore.Mvc.StatusCodeResult[1]
      Executing HttpStatusCodeResult, setting HTTP status code 200
info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
      Executed action Server.Controllers.AccountController.Login (Server) in 26016.4477ms
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 26239.7052ms 200
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
      Request starting HTTP/1.1 GET http://localhost:5000/connect/authorize/callback?client_id=ce&redirect_uri=http%3A%2F%2Flocalhost%3A5050&response_type=id_token%20token&scope=openid%20profile%20CE&nonce=N0.90507026110735561540718014359&state=15407087903260.1454827167180146
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[7]
      Identity.Application was not authenticated. Failure message: Unprotect ticket failed
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[7]
      Identity.Application was not authenticated. Failure message: Unprotect ticket failed
dbug: IdentityServer4.Hosting.EndpointRouter[0]
      Request path /connect/authorize/callback matched to endpoint type Authorize
dbug: IdentityServer4.Hosting.EndpointRouter[0]
      Endpoint enabled: Authorize, successfully created handler: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint
info: IdentityServer4.Hosting.IdentityServerMiddleware[0]
      Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint for /connect/authorize/callback
dbug: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint[0]
      Start authorize callback request
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[7]
      Identity.Application was not authenticated. Failure message: Unprotect ticket failed
dbug: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint[0]
      No user present in authorize request
dbug: IdentityServer4.Validation.AuthorizeRequestValidator[0]
      Start authorize request protocol validation
dbug: IdentityServer4.Stores.ValidatingClientStore[0]
      client configuration validation for client ce succeeded.
dbug: IdentityServer4.Validation.AuthorizeRequestValidator[0]
      Calling into custom validator: IdentityServer4.Validation.DefaultCustomAuthorizeRequestValidator
info: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint[0]
      ValidatedAuthorizeRequest
      {
        "ClientId": "ce",
        "ClientName": "ce",
        "RedirectUri": "http://localhost:5050",
        "AllowedRedirectUris": [
          "http://localhost:5050"
        ],
        "SubjectId": "anonymous",
        "ResponseType": "id_token token",
        "ResponseMode": "fragment",
        "GrantType": "implicit",
        "RequestedScopes": "openid profile CE",
        "State": "15407087903260.1454827167180146",
        "Nonce": "N0.90507026110735561540718014359",
        "Raw": {
          "client_id": "ce",
          "redirect_uri": "http://localhost:5050",
          "response_type": "id_token token",
          "scope": "openid profile CE",
          "nonce": "N0.90507026110735561540718014359",
          "state": "15407087903260.1454827167180146"
        }
      }
info: IdentityServer4.ResponseHandling.AuthorizeInteractionResponseGenerator[0]
      Showing login: User is not authenticated
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
      Request finished in 970.0804ms 302

这是StartUp.cs的关键部分

Here is the key part of StartUp.cs

services.AddDbContext<DbContext>(options =>
            options.UseMySql("server=10.1.1.228;database=IdentityService;user=admin;password=admin"));

        services.AddCors(options =>
        {
            options.AddPolicy("default", policy =>
            {
                policy.AllowAnyOrigin()
                    .AllowAnyHeader()
                    .AllowAnyMethod();
            });
        });

        services.AddIdentity<SsoUser, IdentityRole>(options =>
        {
            options.Tokens.ChangePhoneNumberTokenProvider = "Phone";

            options.Password.RequiredLength = 8;
            options.Password.RequiredUniqueChars = 0;
            options.Password.RequireDigit = false;
            options.Password.RequireLowercase = false;
            options.Password.RequireUppercase = false;
            options.Password.RequireNonAlphanumeric = false;
        })
            .AddEntityFrameworkStores<DbContext>()
            .AddDefaultTokenProviders();

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        services.AddIdentityServer(x =>
            {
                x.IssuerUri = "http://localhost:5000";
                x.UserInteraction.LoginUrl = "http://localhost:5001/login";
                x.Cors.CorsPaths.Add(new PathString("/Account/Login"));
            }
        ).AddDeveloperSigningCredential()
            .AddAspNetIdentity<SsoUser>()
            .AddInMemoryIdentityResources(InMemoryConfiguration.GetIdentityResources())
            .AddInMemoryApiResources(InMemoryConfiguration.ApiResources())
            .AddInMemoryClients(InMemoryConfiguration.Clients())
            .AddTestUsers(InMemoryConfiguration.Users().ToList())
            .AddCorsPolicyService<CorsPolicyService>();

        services.AddAuthentication().AddCookie();

推荐答案

主要原因是Angular没有将cookie发送到IdentityServer.

The main reason is that Angular did not send a cookie to IdentityServer.

在用户界面中,将withCredentials: true添加到httpClient选项.

In the UI, add withCredentials: true to the httpClient option.

然后在IdentityServer的Startup.cs中添加:

And in IdentityServer's Startup.cs, add:

services.AddCors(options =>
        {
            options.AddPolicy("default", policy =>
            {
                policy.WithOrigins("UI Domain")
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials();
            });
        });

(答案从问题中移出到CW帖子中)

(answer moved out of the question to a CW post)

这篇关于IdentityServer 4的自定义登录UI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-23 20:23