ThreeMammals / Ocelot

.NET API Gateway
https://www.nuget.org/packages/Ocelot
MIT License
8.31k stars 1.63k forks source link

Does IdentityServer works in a stand alone WebApi? #92

Closed piyey closed 7 years ago

piyey commented 7 years ago

Hello, This is more a question than an issue. I need to implement Authentication and Authorisation in my services. I see that Ocelot have the functionality using IdentityServer, but I can't find how do I have to do it.

Do I have to create a WebApi for IdentityServer and call it from the Gateway? then, send the token in each request to my other services (webapi)?

Do I have to include IdentityServer inside my gateway? If anyone can provide me an arquitecture or schema it would be appreciated.

Client ------[request token]------> Gateway Gateway ------[authenticate]------> IdentityServer (WebApi) IdentityServer ------[response token]------>Gateway Gateway ------[response token]------>Client Client ------[Call the ExampleService service]------>Gateway Gateway ------[Redirect and send token]------>ExampleService ExampleService ------[Validate token]------>authentication and authorisation

I hope I have explained well :)

Thanks in advance. Regards!!!

TomPallister commented 7 years ago

Hi @piyey :)

I think it is better to have your identity server running in a separate process (another webapi, owin, kestrel etc).

You then register this IdentityServer in your Ocelot configuration and Ocelot will refer to your IdentityServer when validating tokens etc.

You can still route the calls to your IdentityServer to get tokens etc through Ocelot so you keep one single point of entry into your system!

Hope that helps!

piyey commented 7 years ago

Thanks for your answer Tom, I'm new with IdentityServer and I would like to implement it in my project. The point is, I want to create Microservices (let's talk about 10 services by now), so I created one project to my IdentityServer, one project to my gateway (ocelot), and 10 projects to my microservices. My question is, I am able to use this arquitecture to protect my services with Ocelot and IdentityServer?

Hope I explain well, and if you have some example of this I would apreciate you provide me one please.

Thanks for all Tom. I really like this project!! Regards!

TomPallister commented 7 years ago

@piyey this architecture is exactly what ocelot was designed for.

The code in these tests might help explain how this works...

https://github.com/TomPallister/Ocelot/blob/develop/test/Ocelot.AcceptanceTests/AuthenticationTests.cs

Maybe I need to draw a diagram to explain this!

piyey commented 7 years ago

Hello @TomPallister , Wow, this is very spectacular!!! :D I'm so excited with this. I have made run IdentityServer, this is the first time I'm using it. Now, when I try to call a method from the gateway it doesn't return nothing.

If I configure a ReRoute without IdentityServer it works pretty well, but if I configure a ReRoute with IdSrv i dont recibe any response.

This is my configuration.json

{
  "ReRoutes": [
    {
      "DownstreamPathTemplate": "/api/Clientes/BuscarPorId/{id}",
      "DownstreamScheme": "http",
      "DownstreamHost": "localhost",
      "DownstreamPort": 59293,
      "UpstreamPathTemplate": "/BuscarClientePorId/{id}",
      "UpstreamHttpMethod": "Get",
      "QoSOptions": {
        "ExceptionsAllowedBeforeBreaking": 3,
        "DurationOfBreak": 10,
        "TimeoutValue": 5000
      },
      "AuthenticationOptions": {
        "Provider": "IdentityServer",
        "ProviderRootUrl": "http://localhost:4999",
        "ApiName": "customAPI",
        "AllowedScopes": [
          "openid",
          "offline_access",
          "customAPI.read"
        ],
        "ApiSecret": "scopeSecret"
      },
      "RouteClaimsRequirement": {
        "UserType": "registered"
      },
      "RequestIdKey": "OcRequestId"
    },
    {
      "DownstreamPathTemplate": "/api/TramitesEmision/BuscarPorTramite/{id}",
      "DownstreamScheme": "http",
      "DownstreamHost": "localhost",
      "DownstreamPort": 59293,
      "UpstreamPathTemplate": "/BuscarTramiteEmisionPorID/{id}",
      "UpstreamHttpMethod": "Get"
    }
  ],

  "GlobalConfiguration": {
    "RequestIdKey": "OcRequestId",
    "AdministrationPath": "/admin"
  }
}

In the immediate windows I got these messages:

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:64977/BuscarClientePorId/348130  
Excepción producida: 'System.Net.Http.WinHttpException' en System.Net.Http.dll
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:4999/.well-known/openid-configuration  
IdentityServer4.Hosting.IdentityServerMiddleware:Information: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 20.5261ms 200 application/json
Excepción producida: 'System.Net.Http.WinHttpException' en System.Net.Http.dll
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:4999/.well-known/openid-configuration/jwks  
IdentityServer4.Hosting.IdentityServerMiddleware:Information: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration/jwks
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 15.1102ms 200 application/json
Excepción producida: 'Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException' en System.IdentityModel.Tokens.Jwt.dll
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: Failed to validate the token eyJhbGciOiJSUzI1NiIsImtpZCI6ImM4MjYxYmY2ZjYzMmRjODY4MzQ2OWQ2ZmVkYTE3MDgxIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0OTM5Mjk4NjUsImV4cCI6MTQ5MzkzMzQ2NSwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NTAwMCIsImF1ZCI6WyJodHRwczovL2xvY2FsaG9zdDo1MDAwL3Jlc291cmNlcyIsImN1c3RvbUFQSSJdLCJjbGllbnRfaWQiOiJvYXV0aENsaWVudCIsInNjb3BlIjpbImN1c3RvbUFQSS5yZWFkIl19.SJeR9Il-LsFJaQiIGwHbS7uXGQ6bxwYqG-TBDwQAmKmQgz3lMVkKNrUtS3TzZRaoHOModYPVzPPo4nll5zk0IP0xSTq00HPMykUWOZ8Zc5C657ebZwyCHkXxOSjIXOQ4ZxFxgJ3RWVvJT-B7bsczPZ0z_WemgtbpmJwMlywOvQXiVHJT9Q9ORDRriaAvoBjqWT0epO4HAxVk6uRMDaT9ZhxQFN5kVb3aDN-J9s1aP5a8nxdzTlguJk-OOJieNfefJlILt_P5Q3kbcgusxGo5LggY5F7IlC9vhqLX067qDWD5Vo3hn_Vqg6veCoYXe7W_MVlbn_RK_TvCjbEViRVtSA.

Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10501: Signature validation failed. Unable to match 'kid': 'c8261bf6f632dc8683469d6feda17081', 
token: '{"alg":"RS256","typ":"JWT","kid":"c8261bf6f632dc8683469d6feda17081"}.{"nbf":1493929865,"exp":1493933465,"iss":"https://localhost:5000","aud":["https://localhost:5000/resources","customAPI"],"client_id":"oauthClient","scope":["customAPI.read"]}'.
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
   at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
   at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__1.MoveNext()
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: Bearer was not authenticated. Failure message: IDX10501: Signature validation failed. Unable to match 'kid': 'c8261bf6f632dc8683469d6feda17081', 
token: '{"alg":"RS256","typ":"JWT","kid":"c8261bf6f632dc8683469d6feda17081"}.{"nbf":1493929865,"exp":1493933465,"iss":"https://localhost:5000","aud":["https://localhost:5000/resources","customAPI"],"client_id":"oauthClient","scope":["customAPI.read"]}'.
Ocelot.Authentication.Middleware.AuthenticationMiddleware:Error: Client has NOT been authenticated for /BuscarClientePorId/348130 and pipeline error set. Error Code: UnauthenticatedError Message: Request for authenticated route /BuscarClientePorId/348130 by  was unauthenticated : OcelotRequestId - not set
Ocelot.Responder.Middleware.ResponderMiddleware:Error: 1 pipeline errors found in ResponderMiddleware. Setting error response status code : OcelotRequestId - not set
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 751.6913ms 401 

BTW, to get this response from the IdSrv, I have to create a token first (calling the IdSrv service first) and then send it in the headers when calling the gateway. Is this correct?

Thank you for all your patience with me :)

piyey commented 7 years ago

I have solve last error, looks like It was by my certificate (refering to IdSrv).

Now I just have an Ocelot error.

Ocelot.Responder.Middleware.ResponderMiddleware:Error: 1 pipeline errors found in ResponderMiddleware. Setting error response status code : OcelotRequestId - not set

This is the full trace:

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:64977/BuscarClientePorId/348130  
Excepción producida: 'System.IO.IOException' en Microsoft.AspNetCore.Server.Kestrel.dll
Excepción producida: 'System.IO.IOException' en Microsoft.AspNetCore.Server.Kestrel.dll
Excepción producida: 'System.Net.Http.WinHttpException' en System.Net.Http.dll
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:4999/.well-known/openid-configuration  
IdentityServer4.Hosting.IdentityServerMiddleware:Information: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 56.4599ms 200 application/json
Excepción producida: 'System.Net.Http.WinHttpException' en System.Net.Http.dll
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:4999/.well-known/openid-configuration/jwks  
IdentityServer4.Hosting.IdentityServerMiddleware:Information: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration/jwks
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 16.9495ms 200 application/json
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: Successfully validated the token.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: HttpContext.User merged via AutomaticAuthentication from authenticationScheme: Bearer.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware:Information: AuthenticationScheme: Bearer was successfully authenticated.
IdentityModel.AspNetCore.ScopeValidation.ScopeValidationMiddleware:Information: Scopes found on current principal: scope: customAPI.read
Ocelot.Responder.Middleware.ResponderMiddleware:Error: 1 pipeline errors found in ResponderMiddleware. Setting error response status code : OcelotRequestId - not set
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 445.3075ms 403 

And finally, my last noob question, Do I need to create the token (and create a ReRoute from Ocelot to IdSrv) first before any petition?

Regards!!!

TomPallister commented 7 years ago

@piyey mmmmmmmmmmmmmmmmm I'm really not sure what this error could be. From the stack trace it looks like your request is getting through the IdentityServer middleware and then coming back to the Ocelot code. Seems the error is in the Ocelot code somewhere but I cannot work out where. If you set the logging level to trace you should get more information to show the problem.

I'm not sure why the errors are not being logged :(

I would create a re route from Ocelot to identity server for the /token endpoint so that I was always calling the API gateway.