IdentityServer4 Introspection Endpoint API 使用了无效的散列算法
IdentityServer4 Introspection Endpoint API uses invalid hashing algorithm
正在尝试使用 IdentityServer4 上的 Introspection Endpoint 验证令牌。我一直收到 401:Unauthorized。我的日志如下所示:
dbug: IdentityServer4.EntityFramework.Stores.ResourceStore[0]
Found MyAPI API resource in database
info: IdentityServer4.Validation.HashedSharedSecretValidator[0]
Secret: MyAPI API uses invalid hashing algorithm.
dbug: IdentityServer4.Validation.SecretValidator[0]
Secret validators could not validate secret
fail: IdentityServer4.Validation.ApiSecretValidator[0]
API validation failed.
fail: IdentityServer4.Endpoints.IntrospectionEndpoint[0]
API unauthorized to call introspection endpoint. aborting.
我的API配置如下:
new ApiResource
{
Name = "MyAPI",
DisplayName = "My API",
ApiSecrets =
{
new Secret("TopSecret".Sha256())
},
}
我将 headers 的内容类型传递为 application/x-www-form-urlencoded 并将授权传递为 Basic xxxxxxxxxxxxxxxxx,其中 x 是我的 base64 编码的身份验证字符串 (myapi:TopSecret)。我的 Token 在 post
的 body
我错过了什么?为什么我得到 "MyAPI API uses invalid hashing algorithm"?如果无效,什么是有效的哈希算法?
附加信息:我的资源包含在通过 Entity Framework 访问的 SQL 数据库中。具体来说,设置与 here 中的快速入门文档中的设置相同。为了达到我现在的目的,我必须手动将我的 API 添加到 ApiSecrets table 并给它一个类型 (SharedSecret) 和一个值,即 Sha256 密码。
在 Startup.cs 我的配置服务中包括
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryApiResources(Configurations.Scopes.GetApiResources())
.AddInMemoryClients(Configurations.Clients.GetClients())
.AddConfigurationStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.AddOperationalStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)));
// include the password validation routine
services.AddTransient<IResourceOwnerPasswordValidator, Configurations.ResourceOwnerPasswordValidator>();
services.AddTransient<IProfileService, Configurations.ProfileService>();
services.AddMvc();
配置下:
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
ApiSecret = "TopSecret",
AutomaticAuthenticate = true,
AutomaticChallenge = false,
ApiName = "MyAPI"
});
InitializeDatabase(app);
app.UseIdentityServer();
app.UseMvc();
请注意,我只是在开始遇到问题后才将 ApiSecret、AutomaticAuthenticate 和 AutomaticChallenge 添加到此部分以使其正常工作。
在我的 Scopes.cs 中,我概述了以下 API:
public static IEnumerable<ApiResource> GetApiResources()
{
return new[]
{
new ApiResource
{
Name = "MyAPI",
DisplayName = "My API",
ApiSecrets =
{
new Secret("TopSecret".Sha256()),
},
}
};
}
对于Clients.cs:
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientName = "My Client",
AlwaysSendClientClaims=true,
ClientId = "MyClient",
ClientSecrets = { new Secret("TopSecret".Sha256()) },
RequireClientSecret=false,
AllowAccessTokensViaBrowser =true,
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
AllowedScopes = { "MyAPI" },
RequireConsent = false,
AllowOfflineAccess = true,
},
这就是代码部分的全部内容。包含配置的数据库似乎覆盖了我所做的任何代码更改,所以我不确定这一切有多大用处。在数据库中,我在 ApiSecrets table 中创建了一条 ApiResourceId 为 1 的记录,添加了描述和到期日期,将类型设置为 "SharedSecret" 并使用各种格式添加了 Secret,包括纯文本、sha256 和base64。
这是通话期间的完整记录。也许它会有所帮助。我看到有一些关于 Bearer 未被发现或类似的事情,但我不确定为什么会这样,以及它是否会影响程序的结果。
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 29.4277ms 401
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 POST http://localhost:5000/connect/introspect application/x-www-form-urlencoded 762
info: IdentityServer4.AccessTokenValidation.Infrastructure.NopAuthenticationMiddleware[7]
Bearer was not authenticated. Failure message: No token found.
dbug: IdentityServer4.CorsPolicyProvider[0]
CORS request made for path: /connect/introspect from origin: chrome-extension://aicmkgpgakddgnaphhhpliifpcfhicfo but rejected because invalid CORS path
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware[7]
idsrv was not authenticated. Failure message: Unprotect ticket failed
dbug: IdentityServer4.Hosting.EndpointRouter[0]
Request path /connect/introspect matched to endpoint type Introspection
dbug: IdentityServer4.Hosting.EndpointRouter[0]
Mapping found for endpoint: Introspection, creating handler: IdentityServer4.Endpoints.IntrospectionEndpoint
info: IdentityServer4.Hosting.IdentityServerMiddleware[0]
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.IntrospectionEndpoint for /connect/introspect
dbug: IdentityServer4.Endpoints.IntrospectionEndpoint[0]
Starting introspection request.
dbug: IdentityServer4.Validation.BasicAuthenticationSecretParser[0]
Start parsing Basic Authentication secret
dbug: IdentityServer4.Validation.SecretParser[0]
Parser found secret: BasicAuthenticationSecretParser
dbug: IdentityServer4.Validation.SecretParser[0]
Secret id found: MyAPI
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (0ms) [Parameters=[@__name_0='?' (Size = 200)], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [apiResource].[Id], [apiResource].[Description], [apiResource].[DisplayName], [apiResource].[Enabled], [apiResource].[Name]
FROM [ApiResources] AS [apiResource]
WHERE [apiResource].[Name] = @__name_0
ORDER BY [apiResource].[Id]
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (0ms) [Parameters=[@__name_0='?' (Size = 200)], CommandType='Text', CommandTimeout='30']
SELECT [a3].[Id], [a3].[ApiResourceId], [a3].[Type]
FROM [ApiClaims] AS [a3]
INNER JOIN (
SELECT DISTINCT TOP(1) [apiResource].[Id]
FROM [ApiResources] AS [apiResource]
WHERE [apiResource].[Name] = @__name_0
ORDER BY [apiResource].[Id]
) AS [apiResource2] ON [a3].[ApiResourceId] = [apiResource2].[Id]
ORDER BY [apiResource2].[Id]
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (0ms) [Parameters=[@__name_0='?' (Size = 200)], CommandType='Text', CommandTimeout='30']
SELECT [a2].[Id], [a2].[ApiResourceId], [a2].[Description], [a2].[Expiration], [a2].[Type], [a2].[Value]
FROM [ApiSecrets] AS [a2]
INNER JOIN (
SELECT DISTINCT TOP(1) [apiResource].[Id]
FROM [ApiResources] AS [apiResource]
WHERE [apiResource].[Name] = @__name_0
ORDER BY [apiResource].[Id]
) AS [apiResource1] ON [a2].[ApiResourceId] = [apiResource1].[Id]
ORDER BY [apiResource1].[Id]
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (0ms) [Parameters=[@__name_0='?' (Size = 200)], CommandType='Text', CommandTimeout='30']
SELECT [a].[Id], [a].[ApiResourceId], [a].[Description], [a].[DisplayName], [a].[Emphasize], [a].[Name], [a].[Required], [a].[ShowInDiscoveryDocument]
FROM [ApiScopes] AS [a]
INNER JOIN (
SELECT DISTINCT TOP(1) [apiResource].[Id]
FROM [ApiResources] AS [apiResource]
WHERE [apiResource].[Name] = @__name_0
ORDER BY [apiResource].[Id]
) AS [apiResource0] ON [a].[ApiResourceId] = [apiResource0].[Id]
ORDER BY [apiResource0].[Id], [a].[Id]
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (0ms) [Parameters=[@__name_0='?' (Size = 200)], CommandType='Text', CommandTimeout='30']
SELECT [a0].[Id], [a0].[ApiScopeId], [a0].[Type]
FROM [ApiScopeClaims] AS [a0]
INNER JOIN (
SELECT DISTINCT [apiResource0].[Id], [a].[Id] AS [Id0]
FROM [ApiScopes] AS [a]
INNER JOIN (
SELECT DISTINCT TOP(1) [apiResource].[Id]
FROM [ApiResources] AS [apiResource]
WHERE [apiResource].[Name] = @__name_0
ORDER BY [apiResource].[Id]
) AS [apiResource0] ON [a].[ApiResourceId] = [apiResource0].[Id]
) AS [a1] ON [a0].[ApiScopeId] = [a1].[Id0]
ORDER BY [a1].[Id], [a1].[Id0]
dbug: IdentityServer4.EntityFramework.Stores.ResourceStore[0]
Found MyAPI API resource in database
info: IdentityServer4.Validation.HashedSharedSecretValidator[0]
Secret: MyAPI Secret uses invalid hashing algorithm.
info: IdentityServer4.Validation.HashedSharedSecretValidator[0]
Secret: MyAPI Secret uses invalid hashing algorithm.
dbug: IdentityServer4.Validation.SecretValidator[0]
Secret validators could not validate secret
fail: IdentityServer4.Validation.ApiSecretValidator[0]
API validation failed.
fail: IdentityServer4.Endpoints.IntrospectionEndpoint[0]
API unauthorized to call introspection endpoint. aborting.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 30.673ms 401
(myapi:TopSecret). My Token is in the body of the post
内省端点需要使用 scope:apisecret
而非 name:apisecret
的基本身份验证。
没有看到代码和数据库配置的每一个细节,这有点困难。此外,我没有看到您实际调用内省端点的代码。您是在 C# 中还是在 Javascript 或 Postman 中执行此操作?
无论如何,这是我的评论...
Startup.cs
ConfigureServices
方法看起来不错。对于所述问题,不需要添加 ResourceOwner 密码验证器服务;要访问 Introspection 端点,需要 ApiSecret,而不是 ResourceOwner 密码。我假设你出于一些不相关的原因把它放在那里,如果没有,那就把它拿出来。
在 Configure 方法下,您有 app.UseIdentityServerAuthentication
这意味着您使用 Web 应用程序不仅充当身份验证服务器(使用 IdentityServer4),而且您的 Web Api 应用程序回调身份验证服务器(在这种情况下它本身)以验证传入的令牌。
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "https://localhost:44388",
RequireHttpsMetadata = false,
ApiName = "MyAPI"
//ApiSecret = "TopSecret" not necessary to know the api secret for normal validation
//AutomaticAuthenticate = true, not necessary
//AutomaticChallenge = false not necessary
});
您可能还想 app.UseMvcWithDefaultRoute()
。
Api 内存配置
使用 new ApiResource("name", "display")
构造函数将正确设置数据库;但是像上面那样使用对象初始值设定项语法不会。这是一个报告给 GitHub 的问题:https://github.com/IdentityServer/IdentityServer4/issues/836
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
// this will incorrectly leave out the ApiScope record in the database, but will create the ApiResoure and ApiSecret records
new ApiResource
{
Name = "MyAPI",
DisplayName = "My API",
ApiSecrets =
{
new Secret("TopSecret".Sha256()),
}
},
// this will correctly create the ApiResource, ApiScope, and ApiSecret records in the database.
new ApiResource("MyAPI2", "My API2")
{
ApiSecrets =
{
new Secret("TopSecret2".Sha256())
}
}
};
}
Fyi,由于上面的 new ApiResources
中没有指定范围,IdentityServer 工具将为每个 ApiResource 自动生成一个 ApiScope。在这种情况下,ApiScore 与 ApiResource 同名。 ApiResource 必须至少有一个 ApiScope;但可以有很多。然后是 ApiScopes 与 ClientScopes table.
中的 Client 相关联
客户端内存配置
查看评论...
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientName = "My Client",
AlwaysSendClientClaims = true,
ClientId = "MyClient",
// changed the secret to make clear this is unrelated to the Api secret
ClientSecrets = { new Secret("TopSecretClientSecret".Sha256()) },
// RequireClientSecret might as well be true if you are giving this client a secret
RequireClientSecret = true,
AllowAccessTokensViaBrowser = true,
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
// Added MyAPI2 from my example above
AllowedScopes = { "MyAPI", "MyAPI2" },
RequireConsent = false,
AllowOfflineAccess = true
}
};
}
调用内省端点
以下代码来自 WebApi 控制器。 (请记住,在此讨论中,IdentityServer 权限和 ApiResource 托管在同一个 Web 应用程序中)。对此方法的请求将由客户端发出。
在此方法中,您可以看到它调用了其权限的内省端点 validate/decrypt access_token。在此示例中,这不是必需的,因为我们将 Web 应用程序设置为 app.UseIdentityServerAuthentication
,它已经这样做了。自省端点将用于 Reference tokens 或当网络应用程序本身无法验证 access_token.
时
[Route("api/[controller]/[action]")]
[Produces("application/json")]
public class DataController : Controller
{
[HttpGet]
[Authorize]
public async Task<IEnumerable<String>> Secure()
{
var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token");
var introspectionClient = new IntrospectionClient("https://localhost:44388/connect/introspect", "MyAPI", "TopSecret");
var response = await introspectionClient.SendAsync(new IntrospectionRequest { Token = accessToken });
var isActive = response.IsActive;
var claims = response.Claims;
return new[] { "secure1", "secure2", $"isActive: {isActive}", JsonConvert.SerializeObject(claims) };
}
}
此处,对 ApiScope "MyAPI" 使用 IntrospectionClient
会给出 401,因为数据库缺少 ApiScope 由于先前的对象初始化程序问题提到。
最后一件事
另一个可能的问题是,在数据库编辑器中手动添加散列的 ApiSecret 可能会导致奇怪的 copy/paste 问题,使文本无法正确解密。
查看完整解决方案:
https://github.com/travisjs/AspNetCore-IdentityServer-Instrospection
我希望这可以帮助弄清问题的真相,或者至少激发一个新的想法。
正在尝试使用 IdentityServer4 上的 Introspection Endpoint 验证令牌。我一直收到 401:Unauthorized。我的日志如下所示:
dbug: IdentityServer4.EntityFramework.Stores.ResourceStore[0]
Found MyAPI API resource in database
info: IdentityServer4.Validation.HashedSharedSecretValidator[0]
Secret: MyAPI API uses invalid hashing algorithm.
dbug: IdentityServer4.Validation.SecretValidator[0]
Secret validators could not validate secret
fail: IdentityServer4.Validation.ApiSecretValidator[0]
API validation failed.
fail: IdentityServer4.Endpoints.IntrospectionEndpoint[0]
API unauthorized to call introspection endpoint. aborting.
我的API配置如下:
new ApiResource
{
Name = "MyAPI",
DisplayName = "My API",
ApiSecrets =
{
new Secret("TopSecret".Sha256())
},
}
我将 headers 的内容类型传递为 application/x-www-form-urlencoded 并将授权传递为 Basic xxxxxxxxxxxxxxxxx,其中 x 是我的 base64 编码的身份验证字符串 (myapi:TopSecret)。我的 Token 在 post
的 body我错过了什么?为什么我得到 "MyAPI API uses invalid hashing algorithm"?如果无效,什么是有效的哈希算法?
附加信息:我的资源包含在通过 Entity Framework 访问的 SQL 数据库中。具体来说,设置与 here 中的快速入门文档中的设置相同。为了达到我现在的目的,我必须手动将我的 API 添加到 ApiSecrets table 并给它一个类型 (SharedSecret) 和一个值,即 Sha256 密码。
在 Startup.cs 我的配置服务中包括
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryApiResources(Configurations.Scopes.GetApiResources())
.AddInMemoryClients(Configurations.Clients.GetClients())
.AddConfigurationStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)))
.AddOperationalStore(builder =>
builder.UseSqlServer(connectionString, options =>
options.MigrationsAssembly(migrationsAssembly)));
// include the password validation routine
services.AddTransient<IResourceOwnerPasswordValidator, Configurations.ResourceOwnerPasswordValidator>();
services.AddTransient<IProfileService, Configurations.ProfileService>();
services.AddMvc();
配置下:
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
ApiSecret = "TopSecret",
AutomaticAuthenticate = true,
AutomaticChallenge = false,
ApiName = "MyAPI"
});
InitializeDatabase(app);
app.UseIdentityServer();
app.UseMvc();
请注意,我只是在开始遇到问题后才将 ApiSecret、AutomaticAuthenticate 和 AutomaticChallenge 添加到此部分以使其正常工作。
在我的 Scopes.cs 中,我概述了以下 API:
public static IEnumerable<ApiResource> GetApiResources()
{
return new[]
{
new ApiResource
{
Name = "MyAPI",
DisplayName = "My API",
ApiSecrets =
{
new Secret("TopSecret".Sha256()),
},
}
};
}
对于Clients.cs:
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientName = "My Client",
AlwaysSendClientClaims=true,
ClientId = "MyClient",
ClientSecrets = { new Secret("TopSecret".Sha256()) },
RequireClientSecret=false,
AllowAccessTokensViaBrowser =true,
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
AllowedScopes = { "MyAPI" },
RequireConsent = false,
AllowOfflineAccess = true,
},
这就是代码部分的全部内容。包含配置的数据库似乎覆盖了我所做的任何代码更改,所以我不确定这一切有多大用处。在数据库中,我在 ApiSecrets table 中创建了一条 ApiResourceId 为 1 的记录,添加了描述和到期日期,将类型设置为 "SharedSecret" 并使用各种格式添加了 Secret,包括纯文本、sha256 和base64。
这是通话期间的完整记录。也许它会有所帮助。我看到有一些关于 Bearer 未被发现或类似的事情,但我不确定为什么会这样,以及它是否会影响程序的结果。
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 29.4277ms 401
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 POST http://localhost:5000/connect/introspect application/x-www-form-urlencoded 762
info: IdentityServer4.AccessTokenValidation.Infrastructure.NopAuthenticationMiddleware[7]
Bearer was not authenticated. Failure message: No token found.
dbug: IdentityServer4.CorsPolicyProvider[0]
CORS request made for path: /connect/introspect from origin: chrome-extension://aicmkgpgakddgnaphhhpliifpcfhicfo but rejected because invalid CORS path
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware[7]
idsrv was not authenticated. Failure message: Unprotect ticket failed
dbug: IdentityServer4.Hosting.EndpointRouter[0]
Request path /connect/introspect matched to endpoint type Introspection
dbug: IdentityServer4.Hosting.EndpointRouter[0]
Mapping found for endpoint: Introspection, creating handler: IdentityServer4.Endpoints.IntrospectionEndpoint
info: IdentityServer4.Hosting.IdentityServerMiddleware[0]
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.IntrospectionEndpoint for /connect/introspect
dbug: IdentityServer4.Endpoints.IntrospectionEndpoint[0]
Starting introspection request.
dbug: IdentityServer4.Validation.BasicAuthenticationSecretParser[0]
Start parsing Basic Authentication secret
dbug: IdentityServer4.Validation.SecretParser[0]
Parser found secret: BasicAuthenticationSecretParser
dbug: IdentityServer4.Validation.SecretParser[0]
Secret id found: MyAPI
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (0ms) [Parameters=[@__name_0='?' (Size = 200)], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [apiResource].[Id], [apiResource].[Description], [apiResource].[DisplayName], [apiResource].[Enabled], [apiResource].[Name]
FROM [ApiResources] AS [apiResource]
WHERE [apiResource].[Name] = @__name_0
ORDER BY [apiResource].[Id]
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (0ms) [Parameters=[@__name_0='?' (Size = 200)], CommandType='Text', CommandTimeout='30']
SELECT [a3].[Id], [a3].[ApiResourceId], [a3].[Type]
FROM [ApiClaims] AS [a3]
INNER JOIN (
SELECT DISTINCT TOP(1) [apiResource].[Id]
FROM [ApiResources] AS [apiResource]
WHERE [apiResource].[Name] = @__name_0
ORDER BY [apiResource].[Id]
) AS [apiResource2] ON [a3].[ApiResourceId] = [apiResource2].[Id]
ORDER BY [apiResource2].[Id]
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (0ms) [Parameters=[@__name_0='?' (Size = 200)], CommandType='Text', CommandTimeout='30']
SELECT [a2].[Id], [a2].[ApiResourceId], [a2].[Description], [a2].[Expiration], [a2].[Type], [a2].[Value]
FROM [ApiSecrets] AS [a2]
INNER JOIN (
SELECT DISTINCT TOP(1) [apiResource].[Id]
FROM [ApiResources] AS [apiResource]
WHERE [apiResource].[Name] = @__name_0
ORDER BY [apiResource].[Id]
) AS [apiResource1] ON [a2].[ApiResourceId] = [apiResource1].[Id]
ORDER BY [apiResource1].[Id]
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (0ms) [Parameters=[@__name_0='?' (Size = 200)], CommandType='Text', CommandTimeout='30']
SELECT [a].[Id], [a].[ApiResourceId], [a].[Description], [a].[DisplayName], [a].[Emphasize], [a].[Name], [a].[Required], [a].[ShowInDiscoveryDocument]
FROM [ApiScopes] AS [a]
INNER JOIN (
SELECT DISTINCT TOP(1) [apiResource].[Id]
FROM [ApiResources] AS [apiResource]
WHERE [apiResource].[Name] = @__name_0
ORDER BY [apiResource].[Id]
) AS [apiResource0] ON [a].[ApiResourceId] = [apiResource0].[Id]
ORDER BY [apiResource0].[Id], [a].[Id]
info: Microsoft.EntityFrameworkCore.Storage.IRelationalCommandBuilderFactory[1]
Executed DbCommand (0ms) [Parameters=[@__name_0='?' (Size = 200)], CommandType='Text', CommandTimeout='30']
SELECT [a0].[Id], [a0].[ApiScopeId], [a0].[Type]
FROM [ApiScopeClaims] AS [a0]
INNER JOIN (
SELECT DISTINCT [apiResource0].[Id], [a].[Id] AS [Id0]
FROM [ApiScopes] AS [a]
INNER JOIN (
SELECT DISTINCT TOP(1) [apiResource].[Id]
FROM [ApiResources] AS [apiResource]
WHERE [apiResource].[Name] = @__name_0
ORDER BY [apiResource].[Id]
) AS [apiResource0] ON [a].[ApiResourceId] = [apiResource0].[Id]
) AS [a1] ON [a0].[ApiScopeId] = [a1].[Id0]
ORDER BY [a1].[Id], [a1].[Id0]
dbug: IdentityServer4.EntityFramework.Stores.ResourceStore[0]
Found MyAPI API resource in database
info: IdentityServer4.Validation.HashedSharedSecretValidator[0]
Secret: MyAPI Secret uses invalid hashing algorithm.
info: IdentityServer4.Validation.HashedSharedSecretValidator[0]
Secret: MyAPI Secret uses invalid hashing algorithm.
dbug: IdentityServer4.Validation.SecretValidator[0]
Secret validators could not validate secret
fail: IdentityServer4.Validation.ApiSecretValidator[0]
API validation failed.
fail: IdentityServer4.Endpoints.IntrospectionEndpoint[0]
API unauthorized to call introspection endpoint. aborting.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 30.673ms 401
(myapi:TopSecret). My Token is in the body of the post
内省端点需要使用 scope:apisecret
而非 name:apisecret
的基本身份验证。
没有看到代码和数据库配置的每一个细节,这有点困难。此外,我没有看到您实际调用内省端点的代码。您是在 C# 中还是在 Javascript 或 Postman 中执行此操作?
无论如何,这是我的评论...
Startup.cs
ConfigureServices
方法看起来不错。对于所述问题,不需要添加 ResourceOwner 密码验证器服务;要访问 Introspection 端点,需要 ApiSecret,而不是 ResourceOwner 密码。我假设你出于一些不相关的原因把它放在那里,如果没有,那就把它拿出来。
在 Configure 方法下,您有 app.UseIdentityServerAuthentication
这意味着您使用 Web 应用程序不仅充当身份验证服务器(使用 IdentityServer4),而且您的 Web Api 应用程序回调身份验证服务器(在这种情况下它本身)以验证传入的令牌。
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "https://localhost:44388",
RequireHttpsMetadata = false,
ApiName = "MyAPI"
//ApiSecret = "TopSecret" not necessary to know the api secret for normal validation
//AutomaticAuthenticate = true, not necessary
//AutomaticChallenge = false not necessary
});
您可能还想 app.UseMvcWithDefaultRoute()
。
Api 内存配置
使用 new ApiResource("name", "display")
构造函数将正确设置数据库;但是像上面那样使用对象初始值设定项语法不会。这是一个报告给 GitHub 的问题:https://github.com/IdentityServer/IdentityServer4/issues/836
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
// this will incorrectly leave out the ApiScope record in the database, but will create the ApiResoure and ApiSecret records
new ApiResource
{
Name = "MyAPI",
DisplayName = "My API",
ApiSecrets =
{
new Secret("TopSecret".Sha256()),
}
},
// this will correctly create the ApiResource, ApiScope, and ApiSecret records in the database.
new ApiResource("MyAPI2", "My API2")
{
ApiSecrets =
{
new Secret("TopSecret2".Sha256())
}
}
};
}
Fyi,由于上面的 new ApiResources
中没有指定范围,IdentityServer 工具将为每个 ApiResource 自动生成一个 ApiScope。在这种情况下,ApiScore 与 ApiResource 同名。 ApiResource 必须至少有一个 ApiScope;但可以有很多。然后是 ApiScopes 与 ClientScopes table.
客户端内存配置
查看评论...
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientName = "My Client",
AlwaysSendClientClaims = true,
ClientId = "MyClient",
// changed the secret to make clear this is unrelated to the Api secret
ClientSecrets = { new Secret("TopSecretClientSecret".Sha256()) },
// RequireClientSecret might as well be true if you are giving this client a secret
RequireClientSecret = true,
AllowAccessTokensViaBrowser = true,
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
// Added MyAPI2 from my example above
AllowedScopes = { "MyAPI", "MyAPI2" },
RequireConsent = false,
AllowOfflineAccess = true
}
};
}
调用内省端点
以下代码来自 WebApi 控制器。 (请记住,在此讨论中,IdentityServer 权限和 ApiResource 托管在同一个 Web 应用程序中)。对此方法的请求将由客户端发出。
在此方法中,您可以看到它调用了其权限的内省端点 validate/decrypt access_token。在此示例中,这不是必需的,因为我们将 Web 应用程序设置为 app.UseIdentityServerAuthentication
,它已经这样做了。自省端点将用于 Reference tokens 或当网络应用程序本身无法验证 access_token.
[Route("api/[controller]/[action]")]
[Produces("application/json")]
public class DataController : Controller
{
[HttpGet]
[Authorize]
public async Task<IEnumerable<String>> Secure()
{
var accessToken = await HttpContext.Authentication.GetTokenAsync("access_token");
var introspectionClient = new IntrospectionClient("https://localhost:44388/connect/introspect", "MyAPI", "TopSecret");
var response = await introspectionClient.SendAsync(new IntrospectionRequest { Token = accessToken });
var isActive = response.IsActive;
var claims = response.Claims;
return new[] { "secure1", "secure2", $"isActive: {isActive}", JsonConvert.SerializeObject(claims) };
}
}
此处,对 ApiScope "MyAPI" 使用 IntrospectionClient
会给出 401,因为数据库缺少 ApiScope 由于先前的对象初始化程序问题提到。
最后一件事
另一个可能的问题是,在数据库编辑器中手动添加散列的 ApiSecret 可能会导致奇怪的 copy/paste 问题,使文本无法正确解密。
查看完整解决方案:
https://github.com/travisjs/AspNetCore-IdentityServer-Instrospection
我希望这可以帮助弄清问题的真相,或者至少激发一个新的想法。