简体   繁体   中英

Error redirect when authen with IdentityServer4

After authentication in the client i get the error:

Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler Message contains error: '', error_description: 'error_description is null', error_uri: 'error_uri is null'.

fail: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[52] Message contains error: '(null)', error_description: 'error_description is null', error_uri: 'error_uri is null', status code '500'. fail: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[17] Exception occurred while processing message. Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolException: Message contains error: '', error_description: 'error_description is null', error_uri: 'error_uri is null'. at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.RedeemAuthorizationCodeAsync(OpenIdConnectMessage tokenEndpointRequest) at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.HandleRemoteAuthenticateAsync() fail: eCleverShopSolution.WebApp.Helpers.ErrorWrappingMiddleware[0] An error was encountered while handling the remote login. System.Exception: An error was encountered while handling the remote login. ---> Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolException: Message contains error: '', error_description: 'error_description is null', error_uri: 'error_uri is null'. at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.RedeemAuthorizationCodeAsync(OpenIdConnectMessage tokenEndpointRequest) at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.HandleRemoteAuthenticateAsync() --- End of inner exception stack trace --- at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync() at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at eCleverShopSolution.WebApp.Helpers.ErrorWrappingMiddleware.Invoke(HttpContext context) in D:\\CODE\\Web Developer\\ASP.NET\\eCleverShopSolution\\src\\eCleverShopSolution.WebApp\\Helpers\\ErrorWrappingMiddleware.cs:line 23

Here this is code in client project:

    public void ConfigureServices(IServiceCollection services)
    {     
        services.AddHttpClient("BackendApi").ConfigurePrimaryHttpMessageHandler(() =>
        {
            var handler = new HttpClientHandler();
            var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");

            //if (environment == Environments.Development)
            //{
            //    handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
            //}
            handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
            return handler;
        });
        services.AddSession(options =>
        {
            options.IdleTimeout = TimeSpan.FromMinutes(30);
            options.Cookie.HttpOnly = true;
        });
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
        })
            .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
            {
                options.Events = new CookieAuthenticationEvents
                {
                    // this event is fired everytime the cookie has been validated by the cookie middleware,
                    // so basically during every authenticated request
                    // the decryption of the cookie has already happened so we have access to the user claims
                    // and cookie properties - expiration, etc..
                    OnValidatePrincipal = async x =>
                     {
                        // since our cookie lifetime is based on the access token one,
                        // check if we're more than halfway of the cookie lifetime
                        var now = DateTimeOffset.UtcNow;
                         var timeElapsed = now.Subtract(x.Properties.IssuedUtc.Value);
                         var timeRemaining = x.Properties.ExpiresUtc.Value.Subtract(now);

                         if (timeElapsed > timeRemaining)
                         {
                             var identity = (ClaimsIdentity)x.Principal.Identity;
                             var accessTokenClaim = identity.FindFirst("access_token");
                             var refreshTokenClaim = identity.FindFirst("refresh_token");

                            // if we have to refresh, grab the refresh token from the claims, and request
                            // new access token and refresh token
                            var refreshToken = refreshTokenClaim.Value;
                             var response = await new HttpClient().RequestRefreshTokenAsync(new RefreshTokenRequest
                             {
                                 Address = Configuration["Authorization:AuthorityUrl"],
                                 ClientId = Configuration["Authorization:ClientId"],
                                 ClientSecret = Configuration["Authorization:ClientSecret"],
                                 RefreshToken = refreshToken
                             });

                             if (!response.IsError)
                             {
                                // everything went right, remove old tokens and add new ones
                                identity.RemoveClaim(accessTokenClaim);
                                 identity.RemoveClaim(refreshTokenClaim);

                                 identity.AddClaims(new[]
                                 {
                                    new Claim("access_token", response.AccessToken),
                                    new Claim("refresh_token", response.RefreshToken)
                                  });

                                // indicate to the cookie middleware to renew the session cookie
                                // the new lifetime will be the same as the old one, so the alignment
                                // between cookie and access token is preserved
                                x.ShouldRenew = true;
                             }
                         }
                     }
                };
            })
             .AddOpenIdConnect("oidc", options =>
             {
                 options.Authority = Configuration["Authorization:AuthorityUrl"];
                 options.RequireHttpsMetadata = false;
                 options.GetClaimsFromUserInfoEndpoint = true;

                 options.ClientId = Configuration["Authorization:ClientId"];
                 options.ClientSecret = Configuration["Authorization:ClientSecret"];
                 options.ResponseType = "code";

                 options.SaveTokens = true;

                 options.Scope.Add("openid");
                 options.Scope.Add("profile");
                 options.Scope.Add("offline_access");
                 options.Scope.Add("api.eclevershop");

                 options.TokenValidationParameters = new TokenValidationParameters
                 {
                     NameClaimType = "name",
                     RoleClaimType = "role"
                 };
                 options.Events = new OpenIdConnectEvents
                 {
                     // that event is called after the OIDC middleware received the auhorisation code,
                     // redeemed it for an access token and a refresh token,
                     // and validated the identity token
                     OnTokenValidated = x =>
                      {
                         // store both access and refresh token in the claims - hence in the cookie
                         var identity = (ClaimsIdentity)x.Principal.Identity;
                          identity.AddClaims(new[]
                          {
                            new Claim("access_token", x.TokenEndpointResponse.AccessToken),
                            new Claim("refresh_token", x.TokenEndpointResponse.RefreshToken)
                          });

                         // so that we don't issue a session cookie but one with a fixed expiration
                         x.Properties.IsPersistent = true;

                         // align expiration of the cookie with expiration of the
                         // access token
                         var accessToken = new JwtSecurityToken(x.TokenEndpointResponse.AccessToken);
                          x.Properties.ExpiresUtc = accessToken.ValidTo;

                          return Task.CompletedTask;
                      }
                 };
             });

        var builder = services.AddControllersWithViews();

        var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
        if (environment == Environments.Development)
        {
            builder.AddRazorRuntimeCompilation();
        }

        //Declare DI containers
        services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    }

And here this is config in API project:

public class Config
{
    public static IEnumerable<IdentityResource> Ids =>
      new IdentityResource[]
      {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile()
      };

    public static IEnumerable<ApiResource> Apis =>
        new ApiResource[]
        {
            new ApiResource("api.eclevershop", "EClever Shop API")
        };

    public static IEnumerable<Client> Clients =>
        new Client[]
        {
            new Client
            {
                ClientId = "webportal",
                ClientSecrets = { new Secret("secret".Sha256()) },

                AllowedGrantTypes = GrantTypes.Code,
                RequireConsent = false,
                RequirePkce = true,
                AllowOfflineAccess = true,

                // where to redirect to after login
                RedirectUris = { "https://localhost:5002/signin-oidc" },

                // where to redirect to after logout
                PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },

                AllowedScopes = new List<string>
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    IdentityServerConstants.StandardScopes.OfflineAccess,
                    "api.eclevershop"
                }
             },
        }
   }

Anyone can help me. Thank you so much.

if you are using IdentityServer4, version 4.0x, you should change this code

public static IEnumerable<ApiResource> Apis =>
    new ApiResource[]
    {
        new ApiResource("api.eclevershop", "EClever Shop API")
    };

To use ApiScopes instead, you can read more about API Scopes here . In V4.0x you ask for ApiScopes in your client, not ApiResources.

This is a very weird error message when an OIDC client tries to exchange the code to have the access token but the IdentityServer4 throws an internal server error. It requires you to check the IdentityServer4 log file to see what the actual error is.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM