带有 IS4 的 azure .net 核心应用程序:web api 调用失败 "Bearer error=invalid_token The issuer is invalid"
azure .net core app with IS4: web api call fails with "Bearer error=invalid_token The issuer is invalid"
我有一个 .net Core 3.1 应用程序 运行 在用于容器 (linux) 服务的 Azure Web 应用程序中。此应用程序是一个 Web api 项目,具有 angular 9 前端。它使用 身份服务器 4 进行授权。
作为参考,我正在为我的项目使用这个 clean architecture framework template(添加 docker 支持 pr)。
服务中的应用 运行 没问题。前端工作正常,我可以使用 ID4“登录”,我可以看到在登录时返回了授权令牌。
问题:
当我从前端 angular 客户端应用程序调用后端 Web api 时,出现以下错误:
HTTP/1.1 401 Unauthorized
Server: Kestrel
WWW-Authenticate: Bearer error="invalid_token", error_description="The issuer 'https://*********.azurewebsites.net' is invalid"
我很想为 IssuerUri 添加手动设置,但 identity server 4 docs 建议不要这样做,所以我做了不是。也许在 docker 中加入这个 运行 会有所不同。
我确实必须添加转发支持 headers 才能让 IS4 在 startup.cs configureServices 中正常工作,根据 these docs for proxy load balancers. 我有将 ASPNETCORE_FORWARDEDHEADERS_ENABLED=true 添加到我的应用程序设置
当我比较请求的提琴手结果时,我可以看到 AspNetCore.Antiforgery 对于登录和 Web api 调用是相同的,但是 .AspNetCore.Identity.Application值不同.
我正在使用 nSwag 自动生成 api 对 angular 的服务调用,如果有区别的话。
问题:
谁能帮我弄清楚为什么我可以登录但所有网络 api 请求都因上述未经授权的错误而失败?
提前致谢。
JK
编辑 1
我使用 fiddler 获取请求的授权令牌并使用 jwt.io 对其进行解析。 iss 值与 app/web api:
相同
"iss": "https://******.azurewebsites.net",
IS4 使用此域登录并且工作正常。如果该值正确,是否还有其他可能错误的地方?
编辑 2:
只是为了更多的上下文。
我的应用在 statup.cs 配置:
app.UseHsts();
app.UseHttpsRedirection();
因此,我需要添加以下代码以确保 headers 在应用服务处理 TSL、加载 balancer/proxy 和我的 docker 之间的请求中转发容器(starup.cs 配置服务):
// the following is documented here:
// https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.1#forward-the-scheme-for-linux-and-non-iis-reverse-proxies-1
// it is needed to run kestrel in azure app service in http with header forwarding
if (string.Equals(
Environment.GetEnvironmentVariable("ASPNETCORE_FORWARDEDHEADERS_ENABLED"),
"true", StringComparison.OrdinalIgnoreCase))
{
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto;
// Only loopback proxies are allowed by default.
// Clear that restriction because forwarders are enabled by explicit
// configuration.
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
}
我在日志中收到以下错误,确认上述错误与发行者不匹配
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidIssuerException:
IDX10205: Issuer validation failed. Issuer: '[PII is hidden. For more
details, see https://aka.ms/IdentityModel/PII.]'. Did not match:
validationParameters.ValidIssuer ...
我正在为 Jwt 令牌使用以下默认设置:
services.AddAuthentication().AddIdentityServerJwt();
如果我导航到 https://*******.azurewebsites.net/.well-known/openid-configuration/jwks 我会得到以下内容 JSON 我的 OIDC 设置的设置:
{
"issuer": "https://*******.azurewebsites.net",
"jwks_uri": "https://*******.azurewebsites.net/.well-known/openid-configuration/jwks",
"authorization_endpoint": "https://*******.azurewebsites.net/connect/authorize",
"token_endpoint": "https://*******.azurewebsites.net/connect/token",
"userinfo_endpoint": "https://*******.azurewebsites.net/connect/userinfo",
"end_session_endpoint": "https://*******.azurewebsites.net/connect/endsession",
"check_session_iframe": "https://*******.azurewebsites.net/connect/checksession",
"revocation_endpoint": "https://*******.azurewebsites.net/connect/revocation",
"introspection_endpoint": "https://*******.azurewebsites.net/connect/introspect",
"device_authorization_endpoint": "https://*******.azurewebsites.net/connect/deviceauthorization",
"frontchannel_logout_supported": true,
"frontchannel_logout_session_supported": true,
"backchannel_logout_supported": true,
"backchannel_logout_session_supported": true,
"scopes_supported": [
"openid",
"profile",
"CleanArchitecture.WebUIAPI",
"offline_access"
],
"claims_supported": [
"sub",
"name",
....
"updated_at"
],
"grant_types_supported": [
"authorization_code",
"client_credentials",
"refresh_token",
"implicit",
"password",
"urn:ietf:params:oauth:grant-type:device_code"
],
"response_types_supported": [
"code",
"token",
"id_token",
"id_token token",
"code id_token",
"code token",
"code id_token token"
],
"response_modes_supported": ["form_post", "query", "fragment"],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"id_token_signing_alg_values_supported": ["RS256"],
"subject_types_supported": ["public"],
"code_challenge_methods_supported": ["plain", "S256"],
"request_parameter_supported": true
}
我比较了本文档中的发行人,它们与上面解码的令牌中的发行人相同。
我仍然不确定如何调试它以找出发行者不匹配的位置。
注意:我已经缩小了范围。所有对内置 in/default IS4 端点的调用都有效。只有我在我的控制器中定义的自定义 webAPI 端点没有正确验证令牌。
任何具有 [Authorize] 属性的 webAPI 端点因无效的颁发者而失败
编辑 3:
感谢@d_f 的评论,我将 IS4 文档用于 adding local API
我在 startu.ca 配置服务中将以下调用添加到我的服务初始化中:
services.AddIdentityServer().AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddLocalApiAuthentication(); // I added this line after the above line
然后我将我的 webAPI 控制器顶部的 [Authorize] 属性更改为:
//[Authorize]
[Authorize(IdentityServerConstants.LocalApi.PolicyName)]
但是,我仍然遇到同样的错误。只有在我的自定义 webAPI 端点上,IS4 端点才能正常工作。登录有效,但不适用于具有 [Authorize] 属性的任何 Web api 端点。
编辑 4:
我删除了上述设置并将我的 services.AddAUthentication() 更改为以下内容:
services.AddAuthentication()
.AddIdentityServerJwt()
.AddLocalApi(options =>
options.ExpectedScope = "IdentityServer4");
我也试过:
services.AddAuthentication()
.AddIdentityServerJwt()
.AddLocalApi();
我使用策略名称“IdentityServer4”,因为它似乎是 IS4 中的默认策略
完整上下文如下所示:
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt()
.AddLocalApi(options =>
options.ExpectedScope = "IdentityServer4");
所有这些变体都可以在我的机器上本地运行。就在 运行 在 azure web 应用程序的容器内时,我的自定义 webAPI 端点出现发行者失败。
解决方案:
感谢这里的所有帮助,我找到了解决方案。开箱即用的 IS4 会尝试自动设置 ISS / Issuer。这在本地有效,但在我的生产环境中,我的容器 运行 在用于容器的 Azure Web 应用程序中。 Azure 将我的容器放在另一个容器中以加载 balancing/proxy 来处理 https 加密。因此,我的容器中自动检测到的 IS4 发行者与 Azure Web 应用程序之间存在差异 URL。
通过在我的代码中手动设置发行者,错误消失了,一切正常。
您可以在两个地方执行此操作
- 在你的 appsettings.jsson 赞中:
"IdentityServer": {
"IssuerUri": "https://yourapp.azurewebsites.net",
- 或者像这样的代码:
services.AddIdentityServer(options =>
{
options.IssuerUri = "https://your.azurewebsites.net/";
})
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
希望这对其他人有帮助再次感谢所有在这里提供帮助的人
您需要捕获您的令牌并使用 https://jwt.ms 来解析它。
根据您的错误信息:invalid token The issuer is invalid
,因此您应该检查令牌中的 iss
声明以确保它与 API 中预期的发行者匹配。参见 here。
解决方案:
感谢这里的所有帮助,我找到了解决方案。开箱即用的 IS4 会尝试自动设置 ISS / Issuer。这在本地有效,但在我的生产环境中,我的容器 运行 在用于容器的 Azure Web 应用程序中。 Azure 将我的容器放在另一个容器中以加载 balancing/proxy 来处理 https 加密。因此,我的容器中自动检测到的 IS4 发行者与 Azure Web 应用程序之间存在差异 URL。
通过在我的代码中手动设置发行者,错误消失了,一切正常。
您可以在两个地方执行此操作
- 在你的 appsettings.jsson 中:
"IdentityServer": {
"IssuerUri": "https://yourapp.azurewebsites.net",
- 或者像这样的代码:
services.AddIdentityServer(options =>
{
options.IssuerUri = "https://your.azurewebsites.net/";
})
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
希望这对其他人有所帮助,再次感谢所有在这里提供帮助的人
我有一个 .net Core 3.1 应用程序 运行 在用于容器 (linux) 服务的 Azure Web 应用程序中。此应用程序是一个 Web api 项目,具有 angular 9 前端。它使用 身份服务器 4 进行授权。
作为参考,我正在为我的项目使用这个 clean architecture framework template(添加 docker 支持 pr)。
服务中的应用 运行 没问题。前端工作正常,我可以使用 ID4“登录”,我可以看到在登录时返回了授权令牌。
问题:
当我从前端 angular 客户端应用程序调用后端 Web api 时,出现以下错误:
HTTP/1.1 401 Unauthorized
Server: Kestrel
WWW-Authenticate: Bearer error="invalid_token", error_description="The issuer 'https://*********.azurewebsites.net' is invalid"
我很想为 IssuerUri 添加手动设置,但 identity server 4 docs 建议不要这样做,所以我做了不是。也许在 docker 中加入这个 运行 会有所不同。
我确实必须添加转发支持 headers 才能让 IS4 在 startup.cs configureServices 中正常工作,根据 these docs for proxy load balancers. 我有将 ASPNETCORE_FORWARDEDHEADERS_ENABLED=true 添加到我的应用程序设置
当我比较请求的提琴手结果时,我可以看到 AspNetCore.Antiforgery 对于登录和 Web api 调用是相同的,但是 .AspNetCore.Identity.Application值不同.
我正在使用 nSwag 自动生成 api 对 angular 的服务调用,如果有区别的话。
问题:
谁能帮我弄清楚为什么我可以登录但所有网络 api 请求都因上述未经授权的错误而失败?
提前致谢。 JK
编辑 1
我使用 fiddler 获取请求的授权令牌并使用 jwt.io 对其进行解析。 iss 值与 app/web api:
相同"iss": "https://******.azurewebsites.net",
IS4 使用此域登录并且工作正常。如果该值正确,是否还有其他可能错误的地方?
编辑 2:
只是为了更多的上下文。
我的应用在 statup.cs 配置:
app.UseHsts();
app.UseHttpsRedirection();
因此,我需要添加以下代码以确保 headers 在应用服务处理 TSL、加载 balancer/proxy 和我的 docker 之间的请求中转发容器(starup.cs 配置服务):
// the following is documented here:
// https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.1#forward-the-scheme-for-linux-and-non-iis-reverse-proxies-1
// it is needed to run kestrel in azure app service in http with header forwarding
if (string.Equals(
Environment.GetEnvironmentVariable("ASPNETCORE_FORWARDEDHEADERS_ENABLED"),
"true", StringComparison.OrdinalIgnoreCase))
{
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor |
ForwardedHeaders.XForwardedProto;
// Only loopback proxies are allowed by default.
// Clear that restriction because forwarders are enabled by explicit
// configuration.
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
}
我在日志中收到以下错误,确认上述错误与发行者不匹配
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidIssuerException: IDX10205: Issuer validation failed. Issuer: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'. Did not match: validationParameters.ValidIssuer ...
我正在为 Jwt 令牌使用以下默认设置:
services.AddAuthentication().AddIdentityServerJwt();
如果我导航到 https://*******.azurewebsites.net/.well-known/openid-configuration/jwks 我会得到以下内容 JSON 我的 OIDC 设置的设置:
{
"issuer": "https://*******.azurewebsites.net",
"jwks_uri": "https://*******.azurewebsites.net/.well-known/openid-configuration/jwks",
"authorization_endpoint": "https://*******.azurewebsites.net/connect/authorize",
"token_endpoint": "https://*******.azurewebsites.net/connect/token",
"userinfo_endpoint": "https://*******.azurewebsites.net/connect/userinfo",
"end_session_endpoint": "https://*******.azurewebsites.net/connect/endsession",
"check_session_iframe": "https://*******.azurewebsites.net/connect/checksession",
"revocation_endpoint": "https://*******.azurewebsites.net/connect/revocation",
"introspection_endpoint": "https://*******.azurewebsites.net/connect/introspect",
"device_authorization_endpoint": "https://*******.azurewebsites.net/connect/deviceauthorization",
"frontchannel_logout_supported": true,
"frontchannel_logout_session_supported": true,
"backchannel_logout_supported": true,
"backchannel_logout_session_supported": true,
"scopes_supported": [
"openid",
"profile",
"CleanArchitecture.WebUIAPI",
"offline_access"
],
"claims_supported": [
"sub",
"name",
....
"updated_at"
],
"grant_types_supported": [
"authorization_code",
"client_credentials",
"refresh_token",
"implicit",
"password",
"urn:ietf:params:oauth:grant-type:device_code"
],
"response_types_supported": [
"code",
"token",
"id_token",
"id_token token",
"code id_token",
"code token",
"code id_token token"
],
"response_modes_supported": ["form_post", "query", "fragment"],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"id_token_signing_alg_values_supported": ["RS256"],
"subject_types_supported": ["public"],
"code_challenge_methods_supported": ["plain", "S256"],
"request_parameter_supported": true
}
我比较了本文档中的发行人,它们与上面解码的令牌中的发行人相同。
我仍然不确定如何调试它以找出发行者不匹配的位置。
注意:我已经缩小了范围。所有对内置 in/default IS4 端点的调用都有效。只有我在我的控制器中定义的自定义 webAPI 端点没有正确验证令牌。
任何具有 [Authorize] 属性的 webAPI 端点因无效的颁发者而失败
编辑 3:
感谢@d_f 的评论,我将 IS4 文档用于 adding local API 我在 startu.ca 配置服务中将以下调用添加到我的服务初始化中:
services.AddIdentityServer().AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddLocalApiAuthentication(); // I added this line after the above line
然后我将我的 webAPI 控制器顶部的 [Authorize] 属性更改为:
//[Authorize]
[Authorize(IdentityServerConstants.LocalApi.PolicyName)]
但是,我仍然遇到同样的错误。只有在我的自定义 webAPI 端点上,IS4 端点才能正常工作。登录有效,但不适用于具有 [Authorize] 属性的任何 Web api 端点。
编辑 4:
我删除了上述设置并将我的 services.AddAUthentication() 更改为以下内容:
services.AddAuthentication()
.AddIdentityServerJwt()
.AddLocalApi(options =>
options.ExpectedScope = "IdentityServer4");
我也试过:
services.AddAuthentication()
.AddIdentityServerJwt()
.AddLocalApi();
我使用策略名称“IdentityServer4”,因为它似乎是 IS4 中的默认策略
完整上下文如下所示:
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt()
.AddLocalApi(options =>
options.ExpectedScope = "IdentityServer4");
所有这些变体都可以在我的机器上本地运行。就在 运行 在 azure web 应用程序的容器内时,我的自定义 webAPI 端点出现发行者失败。
解决方案:
感谢这里的所有帮助,我找到了解决方案。开箱即用的 IS4 会尝试自动设置 ISS / Issuer。这在本地有效,但在我的生产环境中,我的容器 运行 在用于容器的 Azure Web 应用程序中。 Azure 将我的容器放在另一个容器中以加载 balancing/proxy 来处理 https 加密。因此,我的容器中自动检测到的 IS4 发行者与 Azure Web 应用程序之间存在差异 URL。
通过在我的代码中手动设置发行者,错误消失了,一切正常。
您可以在两个地方执行此操作
- 在你的 appsettings.jsson 赞中:
"IdentityServer": {
"IssuerUri": "https://yourapp.azurewebsites.net",
- 或者像这样的代码:
services.AddIdentityServer(options =>
{
options.IssuerUri = "https://your.azurewebsites.net/";
})
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
希望这对其他人有帮助再次感谢所有在这里提供帮助的人
您需要捕获您的令牌并使用 https://jwt.ms 来解析它。
根据您的错误信息:invalid token The issuer is invalid
,因此您应该检查令牌中的 iss
声明以确保它与 API 中预期的发行者匹配。参见 here。
解决方案:
感谢这里的所有帮助,我找到了解决方案。开箱即用的 IS4 会尝试自动设置 ISS / Issuer。这在本地有效,但在我的生产环境中,我的容器 运行 在用于容器的 Azure Web 应用程序中。 Azure 将我的容器放在另一个容器中以加载 balancing/proxy 来处理 https 加密。因此,我的容器中自动检测到的 IS4 发行者与 Azure Web 应用程序之间存在差异 URL。
通过在我的代码中手动设置发行者,错误消失了,一切正常。
您可以在两个地方执行此操作
- 在你的 appsettings.jsson 中:
"IdentityServer": {
"IssuerUri": "https://yourapp.azurewebsites.net",
- 或者像这样的代码:
services.AddIdentityServer(options =>
{
options.IssuerUri = "https://your.azurewebsites.net/";
})
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
希望这对其他人有所帮助,再次感谢所有在这里提供帮助的人