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.
Related
I am trying to use the localStorageService in my CustomAuthStateProvider class so I can create a AuthenticationState based on a key in LocalStorage (Just to learn and to practice). Howevever, when I run my application I get an error telling me that no suitable constructor can be found for CustomAuthStateProvider. The error makes sense but I don't understand how I can fix it and haven't found much online.
Here is the error:
Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: A suitable constructor for type 'BlazorBattles.Client.CustomAuthStateProvider' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.
System.InvalidOperationException: A suitable constructor for type 'BlazorBattles.Client.CustomAuthStateProvider' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.
Here is my CustomAuthStateProvider implimenting AuthenticationStateProvider:
public class CustomAuthStateProvider : AuthenticationStateProvider
{
private readonly ILocalStorageService _localStorageService;
CustomAuthStateProvider(ILocalStorageService localStorageService)
{
_localStorageService = localStorageService;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
if (await _localStorageService.GetItemAsync<bool>("isAuthenticated"))
{
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, "Thomas")
}, "Test Authentication");
ClaimsPrincipal user = new ClaimsPrincipal(claimsIdentity);
AuthenticationState state = new AuthenticationState(user);
//Tell all the components that the Auth state has changed
NotifyAuthenticationStateChanged(Task.FromResult(state));
return (state);
}
//This will result in an unauthorised user because it does not have a claims identity
return (new AuthenticationState(new ClaimsPrincipal()));
}
}
Here is my Program.cs
using BlazorBattles.Client;
using BlazorBattles.Client.Services;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Blazored.Toast;
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components.Authorization;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddBlazoredToast();
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<IBananaService, BananaService>();
builder.Services.AddScoped<IUnitService, UnitService>();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();
await builder.Build().RunAsync();
I am using V4.3.0 for Blazored.LocalStorage and V6 for Microsoft.AspNetCore.Components.Authorization
Thanks.
It works as expected when I remove the constructor and references to LocalStorage but when I try to inject LocalStorage to use it then I get the error. I'm not sure how to make use of the constrctor correctly in this specific case?
Update:
The solution to my problem here is to add the public keyword for the constructor
Try to register CustomAuthStateProvider service like this:
// Make the same instance accessible as both AuthenticationStateProvider and CustomAuthStateProvider
builder.Services.AddScoped<CustomAuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(provider => provider.GetRequiredService<CustomAuthStateProvider>());
I think your main issue is your custom AuthenticationStateProvider inheritance.
Here is my "Pass Through" WASM provider that injects (but never uses) Local Storage. It just gets the user from the base code. Note it's inheritance.
public class CustomAuthenticationStateProvider
: RemoteAuthenticationService<RemoteAuthenticationState, RemoteUserAccount, MsalProviderOptions>
{
private readonly ILocalStorageService _localStorageService;
public CustomAuthenticationStateProvider(
IJSRuntime jsRuntime,
IOptionsSnapshot<RemoteAuthenticationOptions<MsalProviderOptions>> options,
NavigationManager navigation,
AccountClaimsPrincipalFactory<RemoteUserAccount> accountClaimsPrincipalFactory,
ILocalStorageService localStorageService
)
: base(jsRuntime, options, navigation, accountClaimsPrincipalFactory)
{
_localStorageService= localStorageService;
}
public async override Task<AuthenticationState> GetAuthenticationStateAsync()
{
var auth = await base.GetAuthenticationStateAsync();
return new AuthenticationState(auth.User ?? new ClaimsPrincipal());
}
}
For reference here's my Program using AzureAD for authentication.
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddHttpClient("Blazr.AzureOIDC.WASM.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
// Supply HttpClient instances that include access tokens when making requests to the server project
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("Blazr.AzureOIDC.WASM.ServerAPI"));
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("api://api.id.uri/access_as_user");
});
builder.Services.AddBlazoredLocalStorage();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();
await builder.Build().RunAsync();
The issue with my code above is that I had missed out the public keyword in my constructor and now it works as expected. A huge thank you to everyone who commented on my post and provided potential solutions, I appreciate the time you took to help me out!
Original code:
CustomAuthStateProvider(ILocalStorageService localStorageService)
{
_localStorageService = localStorageService;
}
Updated code:
public CustomAuthStateProvider(ILocalStorageService localStorageService)
{
_localStorageService = localStorageService;
}
I've a gRPC service written in .NET 6 that is running fine on localhost. I'm trying to consume it in a Blazor client for .NET 6. but, I'm getting System.NullReferenceException when creating that channel for my gRPC service.
SERVICE HELPER
public class ServiceHelper : IServiceHelper
{
public Server.ServerClient? ServerClient { get; set; }
public ServiceHelper()
{
try
{
GrpcChannel channel = GrpcChannel.ForAddress("https://localhost:7027");
ServerClient = new Server.ServerClient(channel);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
Program.cs for Client
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddAntDesign();
//var serviceAddress = builder.Configuration["ServiceAddress"];
builder.Services.AddSingleton<IServiceHelper, ServiceHelper>();
await builder.Build().RunAsync();
Using the gRPC Client
#inject IServiceHelper _serviceHelper
<div class="page elementFillSpaceY">
</div>
#code
{
public ServerStatus? ServerStatus { get; set; }
protected override async Task OnInitializedAsync()
{
await SetUpData();
}
private async Task SetUpData()
{
var client = _serviceHelper.ServerClient;
if (client != null)
{
var result = await client.GetServerStatusAsync(new Empty());
ServerStatus = result;
Debug.WriteLine(ServerStatus.Status);
}
}
}
Program.cs for gRPC Service
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddGrpc();
const string corsPolicy = "_corsPolicy";
/*builder.Services.AddCors(options =>
{
options.AddPolicy(name: corsPolicy,
policy =>
{
/*policy.WithOrigins("https://localhost:7075",
"http://localhost:5025")
policy.AllowAnyOrigin()
.WithExposedHeaders("grpc-status", "grpc-message")
.AllowAnyHeader()
.AllowAnyMethod();
});
});*/
var app = builder.Build();
//app.UseCors(corsPolicy);
//app.UseRouting();
//app.UseGrpcWeb();
// Configure the HTTP request pipeline.
app.MapGrpcService<GreeterService>();
app.MapGrpcService<ServerService>();
app.MapGet("/",
() =>
"Communication with gRPC endpoints must be made through a gRPC client.");
app.Run();
I've tried different ways of consuming gRPC service in client e.g. Dependency Injection for Channel and on gRPC side look at the commented code that i have tried but still getting same error. It just throws the null reference exception without any other detail.
Stack trace of exception
'at
Grpc.Net.Client.Balancer.Internal.BalancerHttpHandler..ctor(HttpMessageHandler
innerHandler, HttpHandlerType httpHandlerType, ConnectionManager
manager)\n at
Grpc.Net.Client.GrpcChannel.CreateInternalHttpInvoker(HttpMessageHandler
handler)\n at Grpc.Net.Client.GrpcChannel..ctor(Uri address,
GrpcChannelOptions channelOptions)\n at
Grpc.Net.Client.GrpcChannel.ForAddress(Uri address, GrpcChannelOptions
channelOptions)\n at Grpc.Net.Client.GrpcChannel.ForAddress(String
address, GrpcChannelOptions channelOptions)\n at
Grpc.Net.Client.GrpcChannel.ForAddress(String address)\n at
BusinessBlazor.Helpers.ServiceHelper..ctor() in ....'
I am not exactly sure why the exception is thrown. But, with a pretty high probability, it may be thrown because the GrpcChannel.ForAddress can not construct a valid HttpClient for the communication. Possible steps to debug and fix this issue
Ensure that your server is actually up and accessible via the URL https://localhost:7027
Try to construct and pass the HttpClient explicitly. See example here. Also, note that in the client service you registered the HttpClient as Scoped, so you won't be able to fetch it from DI container in the Singleton service.
Try to add the gRPC client via builder.Services.AddGrpcClient and get it from the DI. Example here
I really hope that it will help to solve your problem. Good luck!
Here are some artifacts to help understand the issue:
Sample Code - Github repo
Deployed Application - no longer available
Update: I have followed this YouTube video which I now believe to be the correct way of accessing information about the authenticated user in dependent services for a Blazor Server application: https://www.youtube.com/watch?v=Eh4xPgP5PsM.
I've updated the Github code to reflect that solution.
I have the following classes that I register using dependency injection in my ASP.NET MVC Core application.
public class UserContext
{
ClaimsPrincipal _principal;
public UserContext(ClaimsPrincipal principal) => _principal = principal;
public bool IsAuthenticated => _principal.Identity.IsAuthenticated;
}
public class WrapperService
{
UserContext _userContext;
public WrapperService(UserContext context) => _userContext = context;
public bool UserHasSpecialAccess()
{
return _userContext.IsAuthenticated;
}
}
The IoC dependency registrations are configured in Startup.cs
services.AddScoped<ClaimsPrincipal>(x =>
{
var context = x.GetRequiredService<IHttpContextAccessor>();
return context.HttpContext.User;
});
services.AddScoped<UserContext>();
services.AddScoped<WrapperService>();
I recently enabled Blazor in the MVC application and wanted to use my DI registered services from within my Blazor components.
I injected the service in a Blazor component in order to use it like so:
#inject WrapperService _Wrapper
However, when I attempt to use the service from a server side handler, the request fails with an exception complaining that the services could not be constructed - due to IHttpContext not existing on subsequent calls to the server.
<button #onclick="HandleClick">Check Access</button>
async Task HandleClick()
{
var hasPermission = _Wrapper.UserHasSpecialAccess(); // fails 😔
}
I think I understand why the use of IHttpContextAccessor is not working/recommended in Blazor Server apps. My question is, how can I access the claims I need in my services without it?
The odd thing to me is that this all works when I run it under IIS Express in my development environment, but fails when I deploy and attempt to run it from within an Azure AppService.
This is what work for me, writing a derived class for AuthenticationStateProvider.
public class AppAuthenticationStateProvider : AuthenticationStateProvider
{
private ClaimsPrincipal principal;
// Constructor, only needed when injections required
public AppAuthenticationStateProvider(/* INJECTIONS HERE */)
: base()
{
principal ??= new();
}
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
return Task.FromResult(new AuthenticationState(principal));
}
// Method called from login form view
public async Task LogIn(/* USER AND PASSWORD */)
{
// Create session
principal = new ClaimsPrincipal(...);
var task = Task.FromResult(new AuthenticationState(principal));
NotifyAuthenticationStateChanged(task);
}
// Method called from logout form view
public async Task LogOut()
{
// Close session
principal = new();
var task = Task.FromResult(new AuthenticationState(principal));
NotifyAuthenticationStateChanged(task);
}
Then, at program/startup you add these lines:
// Example for .Net 6
builder.Services.AddScoped<AuthenticationStateProvider, AppAuthenticationStateProvider>();
builder.Services.AddScoped<ClaimsPrincipal>(s =>
{
var stateprovider = s.GetRequiredService<AuthenticationStateProvider>();
var state = stateprovider.GetAuthenticationStateAsync().Result;
return state.User;
});
That's it. Now you can inject ClaimsPrincipal wherever you want.
You can inject AuthenticationStateProvider into your Service constructor and then use
var principal = await _authenticationStateProvider.GetAuthenticationStateAsync();
AuthenticationStateProvider is a Scoped service so yours has to be too.
Use CascadingAuthenticationState to access the claims principal
https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-5.0#expose-the-authentication-state-as-a-cascading-parameter-1
If you need to use your own logic, you will need to implement your own authentication state provider.
If you want to use a service to use ClaimsPrincipal you can do the following:
ClaimsPrincipalUserService.cs
ClaimsPrincipal claimsPrincipal;
void SetClaimsPrincipal(ClaimsPrincipal cp)
{
claimsPrincipal = cp;
// any logic + notifications which need to be raised when
// ClaimsPrincipal has changes
}
Inject this service as scoped in the startup.
In the layout
MainLayout.razor
#inject ClaimsPrincipalUserService cpus;
[CascadingParameter]
public Task<AuthenticationState> State {get;set;}
protected override async Task OnInitializedAsync()
{
var state = await State;
var user = state.User; // Get claims principal.
cpus.SetClaimsPrincipal(user);
}
I have my own authorization server built on top identityserver4 where I want to secure all apis on a host. System is just a simple mimic of google developers or facebook developers where application owners sign up and get client id and client secrets for access grant on apis.
So I followed client_credentials flow on identityserver4 samples. All working fine. I built a public UI for app owners to create apps and choose which apis to access from their apps. I make use of IConfigurationDbContext for CRUD procecces on internal tables of identityserver.
The problem is I couldn't find a way to secure apis based on app owners' choices, when a developer crate an app and choose a few logical endpoints to access, they still can reach all enpoints. What I have done is as follows;
Authorization Server Startup
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryCaching()
.AddOperationalStore(storeOpitons =>
{
storeOpitons.ConfigureDbContext = builder =>
builder.UseSqlServer(Configuration.GetConnectionString("Default"),
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddConfigurationStore(storeOptions =>
{
storeOptions.ConfigureDbContext = builder =>
builder.UseSqlServer(Configuration.GetConnectionString("Default"),
sql => sql.MigrationsAssembly(migrationsAssembly));
});
Saving Client Method
public IActionResult SaveApp(ClientViewModel model, List<SelectedApi> selectedApis)
{
//ommited for brevity
Client client = new Client
{
Description = model.Description,
ClientName = model.Name,
RedirectUris = new[] { model.CallBackUri }
};
client.AllowedScopes = selectedApis.Where(a => a.apiValue == "true").Select(a => a.apiName).ToList();
//e.g : client.AllowedScopes = {"employee_api"};
_isRepository.SaveClient(client, userApp);
}
Api Project Startup
services.AddAuthentication("Bearer").AddJwtBearer(opt => {
opt.Authority = "http://localhost:5000";
opt.Audience = "employee_api";
opt.RequireHttpsMetadata = false;
});
Api Sample Controller
[Authorize]
[Route("api/[controller]")]
[ApiController]
public class EmployeeController : BaseController
{
private readonly IEmployeeRepository _employeeRepoistory;
public EmployeeController(IServiceProvider provider, IEmployeeRepository employeeRepository) : base(provider)
{
_employeeRepoistory = employeeRepository;
}
[HttpGet]
public IActionResult GetEmployees([FromQuery] EmployeeResourceParameter parameter)
{
return Ok(_mapper.Map<IEnumerable<EmployeeModel>>(_employeeRepoistory.GetAll(parameter)));
}
[HttpGet("{id:int}")]
public IActionResult GetEmployeeById(int id)
{
var emp = _employeeRepoistory.GetById(id);
return Ok(_mapper.Map<EmployeeModel>(emp));
}
}
What I want is if a developer choose employee_api, they should just reach to EmployeeController's endpoints. However right now, they can reach all the apis no matter of what their choices are.
What are the steps to take for this on api side or auth server side?
Finally I get it done.. First of all, I realized that it is important to grasp the relation between ApiResource -> Scopes, Clients -> AllowedScopes. I suggest you to read the parts about them in the docs and here
When a client is registered to identityserver and then choose the api endpoints (eg: organization, employee, calender) they should be registered as allowedScopes of client (they live in ClientScopes table), I was doing it in right way. What I was doing wrong is I suppose all these scopes are ApiResources (for my case, because all my apis are living in the same host which I call it as CommonServiceApi, just one web api app). I redefined my ApiResources and its Scopes, as follows;
new ApiResource("commonserviceapi", "Common Service API")
{
Scopes = {
new Scope("calender_api", "Calender Api"),
new Scope("employee_api", "Employee Api"),
new Scope("organization_api", "Organization Api"),
}
}
On the api side, endpoints should be authorized with policies as indicated here.
Within the access token, allowed scopes of the requesting client are passed to the api app, so api grants accesses according to these values.
So Api Startup
services.AddAuthentication("Bearer").AddJwtBearer(opt =>
{
opt.Authority = "http://localhost:5000";
opt.Audience = "commonserviceapi";
opt.RequireHttpsMetadata = false;
});
services.AddAuthorization(options =>
{
options.AddPolicy("ApiEmployee", builder =>
{
builder.RequireScope("employee_api");
});
options.AddPolicy("ApiOrganization", builder =>
{
builder.RequireScope("organization_api");
});
});
And Api Controllers
[Authorize(Policy = "ApiEmployee")]
[Route("api/[controller]")]
[ApiController]
public class EmployeeController : BaseController
{
...
RequireScope is an extension method of IdentityServer4.AccessTokenValidation package by the way. You should include this package to your api project.
And lastly, this was a confusing point for me; while requesting an access token from server, scope parameter should be empty, as identityserver takes it from client's allowdScopes values. Almost all samples were filling this field, so you'd think it should be filled.
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.