SignalR failing to authenticate via cookies - authentication

I have a couple of projects:
Chat - SignalR backend (ChatHub)
Web - MVC project that hosts clientside scripts, including those that make calls to the Chat project
I'm in the process of migrating from .NET Framework to .NET Core and am moving to SignalR for Core as part of this work. Cookie authentication is working correctly on the old version, but upon migrating to SignalR for Core, I appear to be having problems.
Cookie authentication is enabled in both projects. The Web project works fine and the Auth cookie is correctly recognised and used for authentication. The Chat project, however, is not correctly authenticated against the cookie, despite the cookie being included at least in the negotiate request:
When I make a call to the ChatHub, Context.User.Identity.Name is empty. The same call returns a populated name when run on the Web project. If I decorate the ChatHub with [Authorize], the call fails with a 401.
Here's a minimalistic repro project showing the issue.
I assume the problem is related to the Authentication I have configured, or perhaps the cross-domain nature of the call?
This documentation is pretty unhelpful, and only says the following:
In a browser-based app, cookie authentication allows your existing user credentials to automatically flow to SignalR connections. When using the browser client, no additional configuration is needed. If the user is logged in to your app, the SignalR connection automatically inherits this authentication.
This appears under some very basic configuration, which basically only calls app.UseAuthentication(). Alas, that configuration does not work for me.
How do I set Cookies authentication in SignalR for Core so it works across two projects?

The issue is that by default, the Data Protection system that ASP.NET Core uses to encrypt the auth ticket isolates apps from one another.
You need to configure data protection on each project you wish to share protected payloads to use the same key ring and app identifier:
services.AddDataProtection()
.SetApplicationName("<appName>")
// as well as the following calls if your projects are to be deployed on different machines
.PersistKeysToAzureBlobStorage(new Uri("<blobUriWithSasToken>"))
.ProtectKeysWithAzureKeyVault(new Uri("<keyIdentifier>"), new DefaultAzureCredential());

Related

Blazor WASM (Client Side Only) - How to make HTTP Request to different APIs?

So I'm coming from Xamarin world trying to build a Blazor App. And I'm struggling with a high level understanding of why Blazor Apps ( client side only ) cannot make a basic HTTP get call to say google.com or any other http get/post call to different resources/urls?
Can someone break it down for me, am i crazy? how would i ever implement maps.google.com or other http request I'm going to need to make.
I do notice anything with a package, like SendGrid or B2C or Cosmos Nuget Packages seem to work fine... how do they get around the different domain names ?
Can i simply say on my webserver : (in English) - allow requests to google.com and someoneElsesApi.com
or would i have to contact google and have them allow my Blazor app to make calls?
Just really struggling with how to use Blazor Client Only PWA app if it cant connect or call to anything else on the web... seems pointless if a Blazor app cannot make any http calls to other services.
Ok, so yes... I found an Public Open API to test a request against, and it does work from a Blazor WASM (Client only). More specifically the below works just fine..
#inject HttpClient HttpClient
...
string responsString = await HttpClient.GetStringAsync("https://rickandmortyapi.com/api/character/5");
The problem i was having which seems confusing:
Both
Blazor WASM
Xamarin Forms apps
can both call an open public Web API just fine from HTTP Client.
But...
When I create an ASP.NET core API and publish it allowing anonymous access in azure,
Xamarin Forms can call that API
Blazor WASM cannot call it unless i specify CORS correctly in the Web API
So with my inexperience with Blazor WASM i assumed it could not do this.. while Xamarin can. So this changes to ... how is it that the Web API i created in Azure + ASP.net Core Web API - just allows the Xamarin App to call it (without CORS specification)... while CORS Must be set correctly for a Blazor WASM?

How can I use Azure AD B2C to authenticate users on the server before serving a hosted Blazor WebAssembly app?

I have a Blazor WebAssembly app. It's self contained and doesn't use any web API calls. I'd like to add authentication using my existing Azure AD B2C tenant so that only registered users can access the app.
Microsoft says that:
In Blazor WebAssembly apps, authorization checks can be bypassed because all client-side code can be modified by users.
So rather than host my Blazor app statically (the standalone model), I'd like to use the ASP.NET Core hosted model (so an ASP.NET Core app serves the Blazor app to clients). That way I can do the authentication/authorization on the server - if the user is registered and is allowed access to the app, the ASP.NET Core app serves them the Blazor app. If they aren't, they can be shown an error page or be redirected.
The issue is that I'm not sure how to implement this.
When I create a new Blazor WebAssembly Hosted app from the template in Visual Studio 2019, it creates three projects. A shared project, the Blazor project, and a Server project. The Server project has a reference to the Blazor project. This line appears at the end of the Configure method of Startup.cs:
endpoints.MapFallbackToFile("index.html");
This seems to be the thing that makes it serve the Blazor app to the client. I'm not sure how I can add authentication/authorization to this. I tried modifying this line to look like this:
endpoints.MapFallbackToFile("index.html").RequireAuthorization();
But the app still loads without redirecting to a login screen first.
There is some Microsoft documentation called "Secure an ASP.NET Core Blazor WebAssembly hosted app with Azure Active Directory B2C" which sounds like it's what I need, but it's actually still doing the authentication in the Client app while also securing API calls to the Server app (which I don't need).
Is there any way to accomplish what I'm trying to do - authenticating the user on the server before serving the Blazor app, rather than doing that authentication in the Blazor app itself?
The answer was really simple. Actually, the RequireAuthorization() call does work. If you're already logged in to B2C you won't be redirected to the login page first (and, in Edge at least, it doesn't even visibly redirect to B2C first before redirecting back to the app, hence the confusion). But if I load the page in a private browser window I am redirected to the B2C login page - so it must be working as intended.
For future reference, the answer to the title question is:
Add auth to the ASP.NET Core Server app if you haven't already.
In the Server app, add a project reference to the Blazor app.
Add the following line to the UseEndpoints call inside the Configure method in Startup.cs:
endpoints.MapFallbackToFile("index.html").RequireAuthorization();

Authentication Filter in .NET Core 2.x Web API ? What to use instead

We have configured windows authentication in a .NET Core web API project using the below.
services.AddAuthentication(IISDefaults.AuthenticationScheme);
And windows authentication works, we get the user identity. :-)
However I want to run some additional code on user authentication. In particular a database query/update, possible returning access denied. I would need the Identity from windows authentication as it contains the data needed for the database lookup.
I could add the code to a ClaimsTransformer however I don't think that's what the IClaimsTransformation interface should be used for, as I'm not going to map any claims.
How can I extend the in built windows authentication with some custom code?

ASP.Net Core Windows Auth - Cache Claims in Cookies

I'm porting a web application from ASP.Net to ASP.Net Core and need to support both Windows Integrated Authentication and Cookie Authentication with users stored in the apps database (the app is self-hosted by customers and different customers use different authentication methods and sometime migrate from one to the other).
In order to share as much code as possible between the authentication methods I'm using a ClaimsTranformer to add claims to the Windows Auth Users that match those that Cookie Auth (using ASP.Net Core Identity) gives me. However, I would like these claims to be stored in a cookie (in a secure way, like Identity does for the cookie auth) so that I don't have to be hitting the database on every request. Is there a way to do this?
In ASP.Net, I used to look up the user details for the Windows user on the first request and then SignIn with ASP.Net Identity cookie auth; The app would then just use cookie auth the same as if the user had been authenticated with user/password. Unfortunately in Core, it seems that as soon as I call services.AddIdentity in Startup.ConfigureServices, it disables Windows Integrated Authentication.
I'm using ASP.Net Core 2.1 on .Net Framework 4.7 (although we have plans to migrate to .Net Core in a future version, when we can remove some dependencies).

How to enable Windows Authentication in AspNetCore SignalR Alpha

How do I configure SignalR to Authenticate/Authorize to a server that is must be protected by Windows Authentication. There appears to be no way to make the JavaScript Client send Credentials with the request.
Windows authentication should be handled by the browser and you should not have to do anything special. The problem is that Chrome does not seem to handle Windows Authentication correctly. There is a thread on github that contains more details.
Also, SignalR MVC 5 Websocket no valid Credentials seems still to apply as the issue is browser related (i.e. external to SignalR).