2017-02-27 40 views
5

尝试使用IdentityServer4上的自检端点验证令牌。我一直得到401:未经授权。我的日志看起来是这样的:IdentityServer4自检端点API使用无效散列算法

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()) 
        }, 
       } 

我传递标题内容类型应用程序/ x-WWW窗体-urlencoded和授权为基础xxxxxxxxxxxxxxxxx其中x是我的base64编码验证字符串(myapi:TopSecret)。我的令牌在帖子正文中

我错过了什么?为什么我会收到“MyAPI API使用无效哈希算法”?如果它无效,什么是有效的哈希算法?

附加信息:我的资源包含在通过实体框架访问的SQL数据库中。具体来说,该设置与快速入门文档here中的相同。为了说明问题,我必须手动将API添加到ApiSecrets表中,并为其指定一个Type(SharedSecret)和一个值,这是一个Sha256密码。

在Startup.cs我COnfigureServices包括

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表中创建了一个ApiResourceId为1的记录,添加了描述和过期日期,将Type设置为“SharedSecret”,并使用各种格式(包括纯文本,sha256和base64)添加了Secret。

这是通话过程中的完整日志。也许它会有所帮助。我发现有一些关于持票人未被发现或类似的东西,但我不确定为什么会这样,以及是否影响程序的结果。

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 
+0

也许你可以发布你的整个VS解决方案,以便人们更容易调试。 – Evk

+0

很难说,但是根据您发布的代码,似乎您在一个应用程序中使用了API和IdentityServer。这里可能有混合吗? 另外你为什么不得不手动添加哈希的ApiSecret?那里有错误吗? 如果您遵循QS文档'InitializeDatabase(app);'只会填充您的数据库,如果它不包含数据,这可能是您的代码更改似乎不起作用的原因。 – user1336

回答

5

没有看到代码和数据库配置的每个小细节,都有点困难。另外,我没有看到实际调用Introspection端点的代码。你是用C#或Javascript还是邮递员来做这件事?

不管怎么说,这是我的评价......

Startup.cs

ConfigureServices方法看起来不错。所述问题不需要添加ResourceOwner密码验证器服务;要访问自检端点需要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()

阿比InMemory配置

正确使用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()) 
      } 
     } 
    }; 
} 

据透露,因为没有在指定new ApiResources正上方,则IdentityServer工具将自动生成每个ApiResource一个ApiScope作用域。在这种情况下,ApiScore获得与ApiResource相同的名称。一个ApiResource必须至少有一个ApiScope;但可能有很多。然后是ApiScopes,然后与ClientScopes表中的客户端相关联。

客户InMemory配置

看评论...

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应用程序中)。客户可以对此方法提出请求。

在这个方法里面你可以看到它调用了它的权威的内省端点来验证/解密access_token。在这个示例中,这不是必需的,因为我们已经将网络应用程序设置为app.UseIdentityServerAuthentication,这已经完成了。内省端点将用于Reference tokens或当Web应用程序本身无法验证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可能会导致无法使文本得到适当的解密怪异的复制/粘贴问题。

查看完整的解决方案:

https://github.com/travisjs/AspNetCore-IdentityServer-Instrospection

我希望这可以帮助得到这个问题的底部或至少刺激了新的思路。

+0

这是迄今为止最简洁的答案。不幸的是,我无法加载你的解决方案,因为我暂时不愿意使用VS2015,而且看起来你的解决方案使用了新的2017测试版。除此之外,我确实设法让你的代码工作。看来我的方法错了,为了适应你自己的用语,你激发了我的一些新想法。非常感谢,先生! – Rafe

+0

@Rafe是的,我刚刚重建我的机器,并决定不安装VS 2015;如果你将所有的代码放到VS 2015项目中,它应该可以工作 - project.json是唯一真正的区别。 –

+0

谢谢@travis.js。我为此奖励你的赏金。我想知道如果我直接通过电子邮件发送关于我在这个项目上遇到的相关问题的信息是否可以? – Rafe

0

(myapi:绝密)。我的令牌是在后

内省端点需要使用scope:apisecret,不name:apisecret一个基本的认证机构。

+0

我输错了,它实际上是MyAPI:TopSecret,其中MyAPI是范围。 – Rafe