2017-02-27 3 views
5

Versuch, ein Token mithilfe von Introspection-Endpunkt auf IdentityServer4 zu validieren. Ich bekomme immer eine 401: Nicht autorisiert. Mein Protokoll sieht wie folgt aus:IdentityServer4 Introspection-Endpunkt-API verwendet ungültigen Hashalgorithmus

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. 

Meine API ist wie so konfiguriert:

new ApiResource 
       { 
        Name = "MyAPI", 
        DisplayName = "My API", 
        ApiSecrets = 
        { 
         new Secret("TopSecret".Sha256()) 
        }, 
       } 

ich vorbei Header für Inhaltstyp als application/x-www-form-urlencoded und Zulassung als Grund XXXXXXXXXXXXXXXXX Dabei steht x für meine Base64-kodierte Auth-Zeichenfolge (myapi: TopSecret). Mein Token ist im Körper der Post

Was fehlt mir? Warum bekomme ich "MyAPI API verwendet ungültigen Hashalgorithmus"? Wenn es ungültig ist, was ist ein gültiger Hashing-Algorithmus?

Weitere Informationen: Meine Ressourcen sind in einer SQL-Datenbank enthalten, auf die über Entity Framework zugegriffen wird. Das Setup ist genau das gleiche wie in der Quickstart-Dokumentation here. Um zu dem Punkt zu kommen, an dem ich bin, musste ich meine API manuell zur ApiSecrets-Tabelle hinzufügen und ihr einen Typ (SharedSecret) und einen Wert geben, der ein Sha256-Passwort ist.

In Startup.cs meine COnfigureServices umfasst

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(); 

Unter Configure:

app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions 
     { 
      Authority = "http://localhost:5000", 
      RequireHttpsMetadata = false, 
      ApiSecret = "TopSecret", 
      AutomaticAuthenticate = true, 
      AutomaticChallenge = false, 

      ApiName = "MyAPI" 
     }); 

     InitializeDatabase(app); 

     app.UseIdentityServer(); 

     app.UseMvc(); 

Bitte beachte, dass ich die ApiSecret, AutomaticAuthenticate und AutomaticChallenge nur diesem Abschnitt hinzugefügt, nachdem ich begann ein Problem mit Anstrengung, damit es funktioniert.

In meinem Scopes.cs habe ich die folgende API beschrieben:

public static IEnumerable<ApiResource> GetApiResources() 
    { 
     return new[] 
     { 
      new ApiResource 
      { 
       Name = "MyAPI", 
       DisplayName = "My API", 
       ApiSecrets = 
       { 
        new Secret("TopSecret".Sha256()), 
       }, 

      } 
     };    
    } 

Für 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, 

     }, 

, dass mehr oder weniger alles, um den Code Teil besteht. Die Datenbank, in der sich die Konfiguration befindet, scheint alle Codeänderungen außer Kraft zu setzen, die ich mache, daher bin ich mir nicht sicher, wie nützlich das alles ist. In der DB habe ich einen Datensatz in der ApiSecrets-Tabelle mit einer ApiResourceId von 1 erstellt, eine Beschreibung und ein Ablaufdatum hinzugefügt, den Typ auf "SharedSecret" gesetzt und das Secret mit verschiedenen Formaten wie Klartext, sha256 und base64 hinzugefügt.

Hier ist das vollständige Protokoll während des Anrufs. Vielleicht wird es helfen. Ich sehe, dass es einige Dinge darüber gibt, dass Träger nicht gefunden wird oder so etwas, aber ich bin mir nicht sicher, warum das so sein sollte und ob es das Ergebnis des Verfahrens beeinflusst.

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

Vielleicht können Sie Ihre gesamte VS-Lösung veröffentlichen, so dass es für Menschen leichter zu debuggen ist. – Evk

+0

Es ist schwer zu sagen, aber basierend auf dem Code, den Sie gepostet haben, scheint es, dass Sie Ihre API und IdentityServer in einer Anwendung verwenden. Könnte es hier eine Verwechslung geben? Auch warum mussten Sie manuell eine gehashte ApiSecret hinzufügen? Ist da ein Fehler gemacht? Wenn Sie der QS-Dokumentation 'InitializeDatabase (app);' gefolgt haben, wird Ihre Datenbank nur gefüllt, wenn sie keine Daten enthält. Dies könnte der Grund sein, warum Ihr Code sich nicht ändert. – user1336

Antwort

5

Ohne jedes Detail der Code- und Datenbankkonfiguration zu sehen, ist es ein bisschen schwierig. Außerdem sehe ich nicht den Code, wo Sie tatsächlich den Introspektions-Endpunkt aufrufen. Machst du das in C# oder in Javascript oder mit Postman?

Sowieso ist hier meine Bewertung ...

Startup.cs

Die ConfigureServices Methode sieht gut aus. Das Hinzufügen eines ResourceOwner-Passwort-Validierungsdienstes ist für das angegebene Problem nicht erforderlich. Um auf den Introspektions-Endpunkt zuzugreifen, wird ein ApiSecret benötigt, kein ResourceOwner-Passwort. Ich nehme an, Sie haben es aus einem anderen Grund dort, wenn nicht, dann nehmen Sie es heraus.

Unter der Configure-Methode haben Sie app.UseIdentityServerAuthentication, was bedeutet, dass Sie die Web-App nicht nur als Authentifizierungsserver verwenden (mit IdentityServer4), sondern auch Ihre Web-API-Anwendung, die an den Authentifizierungsserver (selbst in In diesem Fall), um eingehende Token zu validieren.

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 
}); 

Sie könnten auch möchten app.UseMvcWithDefaultRoute().

Api InMemory Konfigurationen

Verwenden des new ApiResource("name", "display") Konstruktor wird das Setup die Datenbank korrekt; Aber die Objektinitialisierersyntax wird nicht wie oben verwendet. Dies ist ein Problem auf GitHub berichtet: 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, da es keine Scopes in den new ApiResources direkt oben angegeben ist, wird die IdentityServer Tooling einen ApiScope für jeden ApiResource automatisch generieren. Der ApiScore erhält in diesem Fall den gleichen Namen wie die ApiResource. Eine ApiResource muss mindestens ein ApiScope haben; aber könnte viele haben. Es sind die ApiScopes, die in der ClientScopes-Tabelle mit dem Client verknüpft sind.

Kunde InMemory Konfiguration

Kommentare ansehen ...

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 
     } 
    }; 
} 

Aufruf der Introspection Endpoint

Der folgende Code aus einem WebAPI Controller ist. (Denken Sie daran, dass die IdentityServer-Berechtigung und ApiResource in derselben Webanwendung in dieser Diskussion gehostet werden). Eine Anfrage an diese Methode würde von einem Kunden gestellt werden.

Innerhalb dieser Methode können Sie sehen, dass es an den Introspektionsendpunkt seiner Autorität aufruft, um das access_token zu validieren/entschlüsseln. Dies ist in diesem Beispiel nicht notwendig, da wir die Webanwendung auf app.UseIdentityServerAuthentication eingerichtet haben, was dies bereits tut. Ein Introspektionsendpunkt würde für Reference tokens verwendet werden oder wenn die Webanwendung selbst nicht in der Lage ist, das access_token zu validieren.

[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) }; 
    } 
} 

Hier ein IntrospectionClient für den ApiScope „MyApi“ verwenden würde ein 401 geben, weil die Datenbank die ApiScope aufgrund des Objektinitialisierer Problems fehlt zuvor erwähnt.

Eine letzte Sache

Ein weiteres mögliches Problem ist, dass ein Hash-ApiSecret in der Datenbank-Editor manuell hinzufügen in seltsamen Kopieren/Einfügen Problemen führen könnte der Text nicht in der Lage zu machen richtig entschlüsselt werden.

Komplette Lösung:

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

Ich hoffe, dass dies der Grund des Problems erhalten helfen kann oder zumindest einen neuen Gedanken anregen.

+0

Dies ist die bisher prägnanteste Antwort. Leider kann ich Ihre Lösung nicht laden, da ich für den Moment auf VS2015 angewiesen bin und es scheint, dass Ihre Lösung die neue Beta 2017 verwendet. Abgesehen davon habe ich es geschafft, dass ein Teil Ihres Codes funktioniert. Es scheint, dass ich die falsche Herangehensweise hatte, und um deine eigene Redewendung anzupassen, hast du einige neue Gedanken auf meiner Seite stimuliert. Viel Wertschätzung, Herr! – Rafe

+0

@Rafe Ja, ich habe gerade meine Maschine umgebaut und entschieden, VS 2015 nicht zu installieren; Wenn Sie den gesamten Code in ein VS 2015-Projekt fallen lassen, sollte es funktionieren - die project.json ist der einzige wirkliche Unterschied. –

+0

Danke @ travis.js. Ich gebe Ihnen das Kopfgeld dafür. Ich habe mich gefragt, ob es in Ordnung wäre, wenn ich dich direkt per E-Mail über ein damit verbundenes Problem, das ich bei diesem Projekt habe, benachrichtigt habe? – Rafe

0

(MyApi: TopSecret). Mein Token ist im Körper des Pfostens

Die Selbstbeobachtung Endpunkt braucht eine grundlegende Authentifizierung, die scope:apisecret verwendet, nicht name:apisecret.

+0

Ich falsch eingegeben, es ist eigentlich MyAPI: TopSecret, wo MyAPI der Bereich ist. – Rafe

Verwandte Themen