Open mkanakis opened 5 years ago
Definitely interested by it, that's the only issue in my architecture :-(
I guess the easiest way up till now, is to abstract SignalR from your service and deploy it as a separate one, without the use of a gateway, since .netcore provides token authentication for SignalR.
I do, however, I'm forced to have a second port open on the server, which is complicated on prod. Better have only the 443 to get through the firewall :-(
@zee85 I'm planning architecture for the next project and I want to use Ocelot and SignalR.
So you removed the GetUserId
method from your hub's and you are doing authentication logic inside the SignalR project?
Is your code available somewhere? I'd like to see Ocelot, SignalR, IdentityServer, and RabbitMQ working together.
@zee85 thank you for sharing 🙂 I plan to build my project on GitHub so it will be open-sourced as well :) Your code will help me a lot to get started with Ocelot, SignalR, and others. I always wanted to build a project based on microservices, now I have an opportunity.
I saw in ocelot docs that it doesn't handle authentication for SignalR (WebSockets). Hopefully, someday it will
Can anyone point me to how the identityserver and signal r authentication works? I thought signalr only supported basic auth? :o I might be behind the times as have not worked much in dotnet recently :)
Hi @TomPallister
Here is an example of what I have been using with my Hubs project:
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
// base-address of your identityserver
options.Authority = Configuration["IdentityServer:Url"];
// name of the API resource
options.ApiName = Configuration["IdentityServer:APIName"];
options.ApiSecret = Configuration["IdentityServer:APISecret"];
options.EnableCaching = true;
options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default
options.RequireHttpsMetadata = Convert.ToBoolean(Configuration["IdentityServer:RequireHttpsMetadata"]);
// For SignalR
options.TokenRetriever = new Func<HttpRequest, string>(req =>
{
var fromHeader = TokenRetrieval.FromAuthorizationHeader();
var fromQuery = TokenRetrieval.FromQueryString();
return fromHeader(req) ?? fromQuery(req);
});
});
Taken from here
Hope this helps.
Any updates?
Best, Marios.
@mkanakis because this feature is not available right now, and I want to implement a similar thing in my project, did you use any other methods to solve this (like using reverse proxy or other different tools?)
@hsnassaad you can use nginx as a reverse proxy, signalR is going through correctly
I managed to have my Websocket endpoint authenticated by copying some extension classes from the Ocelot project to my project in order to be able to include the Authentication and Authorization middlewares in the WebSocket middleware sub-pipeline
public static RequestDelegate BuildOcelotPipeline(this IApplicationBuilder app,
OcelotPipelineConfiguration pipelineConfiguration)
{
...
app.MapWhen(httpContext => httpContext.WebSockets.IsWebSocketRequest,
wenSocketsApp =>
{
wenSocketsApp.UseDownstreamRouteFinderMiddleware();
wenSocketsApp.UseMultiplexingMiddleware();
wenSocketsApp.UseDownstreamRequestInitialiser();
//added this here
wenSocketsApp.UseAuthenticationMiddleware();
wenSocketsApp.UseAuthorisationMiddleware();
wenSocketsApp.UseLoadBalancingMiddleware();
wenSocketsApp.UseDownstreamUrlCreatorMiddleware();
wenSocketsApp.UseWebSocketsProxyMiddleware(); // Notice that the request processing ends here!!!
});
...
}
Also, I need to introduce some custom middleware to return 401 on Authentication/Authorization errors because the current pipeline returns 200, which is obviously not correct.
I'm curious about why this functionality is not included as the Websocket endpoint is a regular HTTP endpoint. It might be that I'm looking at it with my specific case in mind and don't get the whole picture.
@alex-pollan please upload complete code
The code I'm working on below. Ocelot version 16.0.1.
ocelot.json
"Routes": [
{
"UpstreamPathTemplate": "/hub/update/{deviceName}",
"UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE", "OPTIONS" ],
"DownstreamScheme": "ws",
"DownstreamPathTemplate": "/update",
"DownstreamHostAndPorts": [ { "Host": "{SignalRService}" } ],
"RouteIsCaseSensitive": false,
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [ "the_scope" ]
},
"RouteClaimsRequirement": {
"client_id": "{deviceName}"
}
}]
Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseAuthentication();
app.UseAuthorization();
...
app.UseOcelotWebSockets();
app.UseOcelot().Wait();
}
Some extension methods class
public static IApplicationBuilder UseOcelotWebSockets(this IApplicationBuilder appBuilder)
{
appBuilder.UseWebSockets();
//Override Ocelot Websockets pipeline to support authentication and authorization
appBuilder.MapWhen(httpContext => httpContext.WebSockets.IsWebSocketRequest,
wenSocketsApp =>
{
wenSocketsApp.UseDownstreamContextMiddleware();
wenSocketsApp.UseExceptionHandlerMiddleware();
wenSocketsApp.UseResponderMiddleware();
wenSocketsApp.UseDownstreamRouteFinderMiddleware();
wenSocketsApp.UseMultiplexingMiddleware();
wenSocketsApp.UseHttpHeadersTransformationMiddleware();
wenSocketsApp.UseDownstreamRequestInitialiser();
wenSocketsApp.UseAuthenticationMiddleware();
wenSocketsApp.UseClaimsToClaimsMiddleware();
wenSocketsApp.UseAuthorisationMiddleware();
wenSocketsApp.UseClaimsToHeadersMiddleware();
wenSocketsApp.UseClaimsToQueryStringMiddleware();
wenSocketsApp.UseClaimsToDownstreamPathMiddleware();
wenSocketsApp.UseLoadBalancingMiddleware();
wenSocketsApp.UseDownstreamUrlCreatorMiddleware();
wenSocketsApp.UseWebSocketsProxyMiddleware();
});
return appBuilder;
}
Waiting for this feature. Please provide me any alternate as I need to pass access token to my signal R app.
@alex-pollan commented on Feb 2, 2022
Our current WS pipeline is quite short ))) No authentication at all :rofl: https://github.com/ThreeMammals/Ocelot/blob/d6eefc899f9b2d37814335c7d0bccd5f7a830bd0/src/Ocelot/Middleware/OcelotPipelineExtensions.cs#L37-L47
Could we convert your experience and solution to a PR please?
@mkanakis commented on Mar 30, 2020
Sorry! How can we re-use your solution in Ocelot if it is based on custom setup in AddIdentityServerAuthentication
method which is not a part of Ocelot code base?
This solution doesn't use Ocelot functionality at all.
Or, do you propose to wrap TokenRetriever
life hack into an useful extension method?
I just want to clarify that my solution above is for a pure APIGW with Ocelot that proxies the SignalR connection endpoint downstream to another service where I actually implement the SignalR server.
Notice that the authentication / authorization happens and it is terminated at the APIGW layer.
I capture the authenticated principal identity and pass it downstream as HTTP headers to the SignalR service to use it
Here are the final Ocelot routes I needed:
{
"UpstreamPathTemplate": "/signalr/negotiate",
"UpstreamHttpMethod": [ "POST" ],
"DownstreamScheme": "http",
"DownstreamPathTemplate": "/devices-hub/negotiate",
"DownstreamHostAndPorts": [ { "Host": "{DevicesHubApiService}" } ],
"RouteIsCaseSensitive": false,
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [ "myscope" ]
}
},
{
"UpstreamPathTemplate": "/signalr",
"UpstreamHttpMethod": [ "GET", "POST", "PUT", "DELETE", "OPTIONS" ],
"DownstreamScheme": "ws",
"DownstreamPathTemplate": "/devices-hub",
"DownstreamHostAndPorts": [ { "Host": "{DevicesHubSignalRService}" } ],
"RouteIsCaseSensitive": false,
"AuthenticationOptions": {
"AuthenticationProviderKey": "Bearer",
"AllowedScopes": [ "myscope" ]
}
},
@alex-pollan commented on Mar 20
Your JSON looks like a life hack to forward anonymous traffic to downstream and be authorized on Ocelot's side. I propose to developers always to define 2 routes to allow authentication on service's side.
Your previous comments with a solution is good. And I will figure out how to reuse it when started working on #1707
is this implemented? If not then I'm going to do id.
@hogwartsdeveloper You are assigned!
@hogwartsdeveloper, best of luck to you! Just so you know, I've assigned the issue to the Summer'24 milestone as I'm uncertain if it can be completed for the Annual 2023 release, which is currently underway. The final decision regarding its delivery will depend on the readiness of the PR.
@hogwartsdeveloper FYI #720 #718
@hogwartsdeveloper FYI #674 #998
Thanks for the good work!
New Feature
Is authentication for signalR on the plans? We are using signalR with IdentityServer4, to send data over to authenticated users, but I don't think Ocelot is supporting it currently, as stated in documentation.
Motivation for New Feature
I am not sure of people who would use signalR without user authentication.