Self-Hosted SignalR app refusing CORS requests - c#

I am working on an MVC 5 application that uses a windows service to perform some processing; I am using signal R so that I can show if the windows service is working on the UI and also allow the user to manually start processing (as opposed to running on a schedule).
On the server side I have the following config:
public class SignalRStartup
{
public static IAppBuilder App = null;
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
EnableDetailedErrors = true,
};
map.RunSignalR(hubConfiguration);
});
}
}
Which is used like so:
SignalR = WebApp.Start<SignalRStartup>(_settings.LoaderServiceUrl);
Right now the loader service url is: http://localhost:8080
Then on the client side:
var adminHubProxy = $.connection.adminHub;
adminHubProxy.client.updateProcessing = function(status) {
if (status === true) {
$('#processing').show();
} else {
$('#processing').hide();
}
};
$.connection.hub.url = 'http://localhost:8080/signalr';
$.connection.hub.start();
$('#startProcessingLink').on('click', function() {
adminHubProxy.server.startProcessing();
});
And if it matters the code that includes the generated proxy:
<script src="http://localhost:8080/signalr/hubs"></script>
So the problem I'm having is that when I trigger the startProcessing function the server throws back this error message:
XMLHttpRequest cannot load http://localhost:8080/signalr/send?transport=serverSentEvents&connectionTok…Pp7JqCJOnkJEA%3D%3D&connectionData=%5B%7B%22name%22%3A%22adminhub%22%7D%5D.
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
From all the reading I've done my configuration should be resolving this issue by allowing all CORS requests but it isn't and I can't see why.
Edit
After some more debugging I pulled up the details of the response on the negotiate call and am seeing the following headers:
Access-Control-Allow-Credentials:true
Access-Control-Allow-Origin:http://localhost
I'm not sure why the credentials header is being added at all, but the origin header again makes me believe that this should be working.

I figured out the problem, first off the error message has absolutely nothing to do with what is going on.
TL;DR;
The problem was that the AdminHub could not be resolved on the server side because of my dependency injection setup
I am using Castle Windsor for dependency injection and originally the AdminHub looked like this:
public class AdminHub : Hub
{
private readonly IMyService _myService;
public AdminHub(IMyService myService)
{
_myService= myService;
_myService.OnProcessingUpdate += (sender, args) => UpdateProcessingStatus();
}
public void UpdateProcessingStatus()
{
Clients.All.updateProcessing(_myService.IsProcessing);
}
public void GetProcessingStatus()
{
Clients.Caller.updateProcessing(_myService.IsProcessing);
}
public void StartProcessing()
{
_myService.Process();
}
}
The default dependency resolver cannot resolve this as it requires a parameterless constructor. This answer both served to point out what was happening and provide the basis for a solution.

Related

Call API from AccountClaimsPrincipalFactory in Blazor WASM

I am using Blazor WASM with AzureB2C to call an API hosted in Azure Functions. I would like to call my API on a successful login to add/update user info into a database. I have been following this guide. When trying to inject my typed httpclient into the AccountClaimsPrincipalFactory I am met with a runtime error:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: ValueFactory attempted to access the Value property of this instance.
System.InvalidOperationException: ValueFactory attempted to access the Value property of this instance.
This shows in the browser, but the app compiles and runs just fine. The codes works great if I don't inject my PlatformServiceClient, but I need to make the API call to record the user. The following files are involved. I adjusted some things to simplify. This seems like the appropriate approach, but I have not seen examples where an api call was made in the claims factory.
CustomAccountFactory.cs
public class CustomAccountFactory: AccountClaimsPrincipalFactory<CustomUserAccount>
{
public IPlatformServiceClient client { get; set; }
public CustomAccountFactory(NavigationManager navigationManager,
IPlatformServiceClient platformServiceClient,
IAccessTokenProviderAccessor accessor) : base(accessor)
{
client = platformServiceClient;
}
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
CustomUserAccount account, RemoteAuthenticationUserOptions options)
{
var initialUser = await base.CreateUserAsync(account, options);
if (initialUser.Identity.IsAuthenticated)
{
//call the API here
await client.RegisterUserAsync();
}
return initialUser;
}
}
Program.cs excerpt
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
builder.Services.AddHttpClient<IPlatformServiceClient, PlatformServiceClient>(
client => client.BaseAddress = new Uri(builder.Configuration["PlatformServiceUrl"]))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
builder.Services.AddMsalAuthentication<RemoteAuthenticationState, CustomUserAccount>(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("openid");
options.ProviderOptions.DefaultAccessTokenScopes.Add("offline_access");
options.ProviderOptions.DefaultAccessTokenScopes.Add("access_as_user");
options.ProviderOptions.LoginMode = "redirect";
options.UserOptions.RoleClaim = "roles";
}).AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount, CustomAccountFactory>();
CustomAuthorizationMessageHandler.cs
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "http://localhost:7071" },
scopes: new[] { "access_as_user" });
}
}
I solved this by creating a named instance of the client and passing an IHttpClientFactory into the CustomAccountFactory.
builder.Services.AddHttpClient<PlatformServiceClient>("PlatformServiceClient",
client => client.BaseAddress = new Uri(builder.Configuration["PlatformServiceUrl"]))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
There I can create a client, but I have to setup my urls manually vs using the typed client where I have this work already done.
var client = factory.CreateClient("PlatformServiceClient");
var response = await client.GetAsync("/user/me");
I also registered the new client prior to calling AddMsalAuthenication
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("PlatformServiceClient"));
I did all of this following the code found here by Coding Flamingo. It is all working as expected.

SignalR Hub Clients not resolving correctly outside SignalR pipeline

Please help, this is driving me crazy!
My Server -> SignalR (JS) Client method execution works fine via the SignalR pipeline, but not when called via a background service. I'm using ASP.NET MVC / Castle Windsor DI
I use a custom Dependency Resolver which I register during Owin Startup
I observe that via the NotificationHub (SignalR pipeline), Clients is resolved to a HubConnectionContext class:
However, via my background service, Clients resolves to a HubConnectionContextBase class:
... so I'm pretty sure it's a DI issue. I just can't see what I'm doing wrong. Also any tips to Debug would be greatly appreciated. Thanks
Application Start:
bootstrapper = ContainerBootstrapper.Bootstrap();
this.container = bootstrapper.Container;
var resolverSignalR = new DependencyResolverSignalR(container);
GlobalHost.DependencyResolver = resolverSignalR;
OwinConfig:
app.MapSignalR(url, DependencyResolverSignalR.CreateHubConfiguration());
DependencyResolverSignalR:
public class DependencyResolverSignalR : DefaultDependencyResolver
{
public static HubConfiguration CreateHubConfiguration()
{
var signalrDependencyResolver = new DependencyResolverSignalR(_container);
var configuration = new HubConfiguration {
EnableDetailedErrors = true,
Resolver = signalrDependencyResolver
};
return configuration;
}
private static IWindsorContainer _container;
public DependencyResolverSignalR(IWindsorContainer container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
_container = container;
}
public override object GetService(Type serviceType)
{
return _container.Kernel.HasComponent(serviceType) ? _container.Resolve(serviceType) : base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return _container.Kernel.HasComponent(serviceType) ? _container.ResolveAll(serviceType).Cast<object>() : base.GetServices(serviceType);
}
}
NotificationService:
(runs via a loop every 10 seconds - AFTER client has connected)
// Not working
var hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
var clients = hubContext.Clients;
clients.All.receiveMessage(testMessage);
NotificationHub:
public override Task OnConnected()
{
var connectionId = Context.ConnectionId;
...
...
// Working fine
base.Clients.All.receiveMessage(testMessage);
return base.OnConnected();
}
Client:
omitted since it works fine via the signalr pipeline
I would expect the receiveMessage() client method called via the NotificationService to be executed on the client in exactly the same way it does when called via the SignalR pipeline. Instead nada. No error message, the call just silently does nothing.
I've even tried following the guide here (though it's geared towards Ninject) SignalR document to resolve the Clients (IHubConnectionContext) directly. Exactly the same result.
Code:
var resolverSignalR = new DependencyResolverSignalR(container);
container.Register(Component
.For<IHubConnectionContext<dynamic>>()
.UsingFactoryMethod(() =>
resolverSignalR.Resolve<IConnectionManager().GetHubContext<NotificationHub>().Clients));
Solved! It was an issue with the HubConfiguration - I'm not sure what exactly. But just bypassing it (which is possible because I'm already replacing the DependencyResolver in Application_Start()) fixed the issue.
Old OwinConfig:
app.MapSignalR(url, DependencyResolverSignalR.CreateHubConfiguration());
Replaced by:
app.MapSignalR();
but make sure you have something like this in Application_Start() (or wherever you initialise your DI container)
var resolverSignalR = new DependencyResolverSignalR(container);
GlobalHost.DependencyResolver = resolverSignalR;

OWIN-hosted web api: using windows authentication and allow anonymous access

I have a WebApi project self-hosted using OWIN.
I want to enable Windows Authentication on some of the controller's actions, but allow other actions to be called anonymously.
So, following some examples I found online, I setup my WebApi like this in my Statrup class:
public void Configuration(IAppBuilder appBuilder)
{
HttpListener listener = (HttpListener)appBuilder.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication | AuthenticationSchemes.Anonymous; //Allow both WinAuth and anonymous auth
//setup routes and other stuff
//...
//Confirm configuration
appBuilder.UseWebApi(config);
}
Then, in my controller, I created two actions:
[HttpGet]
[Authorize]
public HttpResponseMessage ProtectedAction()
{
//do stuff...
}
[HttpGet]
[AllowAnonymous]
public HttpResponseMessage PublicAction()
{
//do stuff...
}
This, however, does not work.
Calling the action marked AllowAnonymous works as expected, but calling the one marked Authorize always returns a 401 error and the following message:
{
"Message": "Authorization has been denied for this request."
}
even if the caller supports windows authentication, tested on browsers (Chrome and Edge) and Postman.
What am I missing here?
Well, I found a workaround for this in another question.
Instead of specifying multiple auth modes (which doesn't work), you can chose the auth mode for each request at runtime, by setting up an AuthenticationSchemeSelector method like this:
public void Configuration(IAppBuilder app)
{
HttpListener listener = (HttpListener)appBuilder.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemeSelectorDelegate = new
AuthenticationSchemeSelector(GetAuthenticationScheme);
}
private AuthenticationSchemes GetAuthenticationScheme(HttpListenerRequest httpRequest)
{
if(/* some logic... */){
return AuthenticationSchemes.Anonymous;
}
else{
return AuthenticationSchemes.IntegratedWindowsAuthentication;
}
}
While not ideal (you have to manually check the request URL or some other parameter of the request to decide which method to use) it works.
Since your description about the question is bit limited I have set-up a demo app, where I implemented OAuthAuthorizationServerProvider as Provider for OAuthAuthorizationServerOptions and override GrantResourceOwnerCredentials and ValidateClientAuthentication
public void Configuration(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new ApplicationOAuthBearerAuthenticationProvider()
});
app.Use<AuthenticationResponseMiddleware>();
var options = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/xxxx"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new OwinAuthorisationProvider()
};
app.UseOAuthAuthorizationServer(options);
}
also tried to have a custom AuthorizeAttribute and added as filters in the configuration class .Filters.Add(new AuthorizeAttribute());
In AuthenticationResponseMiddleware i inherited OwinMiddleware and in the public override async Task Invoke(IOwinContext context) method please inspect the flow of the request.
It hits OAuthBearerAuthenticationProvider first in RequestToken method then to OwinMiddleware class, before going to any DelegatingHandler pipelines,
mostly your authentication is implemented in this layer.
Please comment your findings after this check, parallelly I too modify the API and update you, hope it can help you.

Owin SelfHosted WebApp does not fulfill HEAD requests

I'm self hosting a web app using Microsoft.Owin.Hosting.WebApp, but after making a HEAD request to the server, it throws a 500 error. When trying to pull a JSON file, the error changes to 504.
I've seen many solutions, but none applying to WebApp. If hosting with NancyFX, I could set AllowChunkedEncoding to false to make it work. But that doesn't seems like a good option.
Code snippet:
var options = new StartOptions("http://localhost:8080")
{
ServerFactory = "Microsoft.Owin.Host.HttpListener"
};
WebApp.Start<Startup>(options);
Implementation of Startup:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseNancy();
}
}
Both calling the browser or using Fiddle causes a failure:
I haven't added the Nancy Module implementation here because it's not where the problem should be fixed, as I also want to serve static content, but allowing HEAD request on them.
Does anyone knows how to serve HEAD verbs from a Self Hosted OWIN?
I just ran into a very similar issue like this. I learned that HEAD method responses should be identical to GET responses but with no content.
Here's the relevant RFC: https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
Example I have for my self-hosted Web api app:
[HttpHead]
[HttpGet]
[ResponseType(typeof(string))]
public HttpResponseMessage LiveCheck(HttpRequestMessage request)
{
HttpResponseMessage response;
response = request.CreateResponse(HttpStatusCode.OK);
if (request.Method == HttpMethod.Get)
{
response.Content = new StringContent("OK", System.Text.Encoding.UTF8, "text/plain");
}
return response;
}
I had a similar issue with a self-hosted SignalR app where HEAD requests caused an app crash and returned error code 500. The solution I found was to write a custom OWIN middleware layer to intercept HEAD requests and return code 200.
Create a new class in your project called HeadHandler.cs
using Microsoft.Owin;
using System.Threading.Tasks;
namespace YourProject
{
public class HeadHandler : OwinMiddleware
{
public HeadHandler(OwinMiddleware next) : base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
if (context.Request.Method == "HEAD")
{
context.Response.StatusCode = 200;
}
else
{
await Next.Invoke(context);
}
}
}
}
In your OWIN Startup class, add a line before mapping any other middleware to use the new HeadHandler middleware.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use<HeadHandler>();
//The rest of your original startup class goes here
//app.UseWebApi()
//app.UseSignalR();
}
}

Disable pipeline for static files

I recently added OWIN to my existing ASP.NET MVC 5 project.
I'm using it to log the request and response data that comes to my server.
Everything is set up properly, and the logging works great, except for one issue that I'm not entirely thrilled about: it logs any static file requests.
How can I avoid logging .js/images/css/etc. requests using the OWIN pipeline?
One of my custom logs:
Request
Method: GET
Path: http://localhost:12345/content/stylesheets/site.css.map
Headers: ...
Body: ...
I find when I load one of my web pages, I could see 8 log entries get generated from all of the static file loads. I only care about the main request.
Now, I could go in and whitelist or blacklist request paths, but I thought before I do that, there had to be an easier way.
I want to avoid doing this:
_urlsToNotLog = new[]{
"content/stylesheets/site.css.map",
"Scripts/jquery.validate.unobtrusive.js",
.
.
};
if(_urlsToNotLog.Contains(environment["owin.RequestPath"]))
{
//log request
}
else
{
//Don't log
}
My code:
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Use<LoggingMiddleware>();
}
}
public class LoggingMiddleware
{
public LoggingMiddleware(AppFunc next)
{
_next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
LogRequest(environment);
await _next(environment);
LogResponse(environment);
}

Categories