在客户端中进行身份验证后,出现错误:
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler 消息包含错误:“”,错误说明:“ error_description is null”,error_uri:“ error_uri为null”。
失败: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler [52] 消息包含错误:“(null)”,error_description:“ error_description为空”,error_uri:“ error_uri为空”,状态 代码“ 500”。失败: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler [17] 处理消息时发生异常。 Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolException: 消息包含错误:“”,错误说明:“ error_description is null”,error_uri:“ error_uri为null”。在 Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.RedeemAuthorizationCodeAsync(OpenIdConnectMessage tokenEndpointRequest) Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.HandleRemoteAuthenticateAsync() 失败:eCleverShopSolution.WebApp.Helpers.ErrorWrappingMiddleware [0] 处理远程登录时遇到错误。 System.Exception:处理遥控器时遇到错误 登录。 ---> Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolException: 消息包含错误:“”,错误说明:“ error_description is null”,error_uri:“ error_uri为null”。在 Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.RedeemAuthorizationCodeAsync(OpenIdConnectMessage tokenEndpointRequest) Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.HandleRemoteAuthenticateAsync() ---内部异常堆栈跟踪的结尾---在Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync() 在 Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext 上下文) eCleverShopSolution.WebApp.Helpers.ErrorWrappingMiddleware.Invoke(HttpContext 上下文)在D:\ CODE \ Web中 开发人员\ ASP.NET \ eCleverShopSolution \ src \ eCleverShopSolution.WebApp \ Helpers \ ErrorWrappingMiddleware.cs:line 23
这是客户端项目中的代码:
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>();
}
这是API项目中的配置:
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"
}
},
}
}
任何人都可以帮助我。非常感谢。
答案 0 :(得分:0)
如果您使用的是IdentityServer4 4.0x版,则应更改此代码
public static IEnumerable<ApiResource> Apis =>
new ApiResource[]
{
new ApiResource("api.eclevershop", "EClever Shop API")
};
要改为使用ApiScopes,您可以阅读有关API范围here的更多信息。 在V4.0x中,您要求客户端提供ApiScope,而不是ApiResources。
答案 1 :(得分:0)
当 OIDC 客户端尝试交换代码以获取访问令牌但 IdentityServer4 引发内部服务器错误时,这是一个非常奇怪的错误消息。它需要您检查 IdentityServer4 日志文件以查看实际错误是什么。