Windows Authentication using Blazor with .Net Core WebApi - asp.net-core

I have the task to develop a Blazor Webassembly App. This app is only used within the companys network and for security reasons we would like to use the existing AD and NTLM Authentication.
So far I have a minimal Blazor App configured and running in IIS. Its configured to use Windows Authentication and that works so far. When I open the app in Browser I get asked for my credentials.
The app should also communicate with a .net core webapi which is also secured by windows authentication. This webapi security too works as it should. When I open an URL to it in Browser I get asked for my credentials and the page loads as it should.
Now the Problem: When I call the same url (that works in Browser) from my Blazor app with HttpClient.GetAsync I get an "401: Not Authorized" error. Even though the app itself is loaded with the same authentication. Is there something I have to do?
The MSDN Documenations a gigantic and I couldnt find a Solution.
In the WebApi Startup.cs in ConfigureServices() I added
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddAuthorization();
and in Configure()
app.UseCors(x => x.AllowAnyMethod().AllowAnyHeader().SetIsOriginAllowed(origin => true).AllowCredentials());
app.UseAuthentication();
app.UseAuthorization();
PS:
The Blazor App uses .NET Core 3.1
The WebApi uese .NET 5.0

I recommend checking Blazor WebAssembly additional security scenarios (all the examples are there)
To authorize your request, you can use HttpClient with BaseAddressAuthorizationMessageHandler:
builder.Services.AddHttpClient("ServerAPI",
client => client.BaseAddress = new Uri("https://www.example.com/base"))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
BaseAddressAuthorizationMessageHandler will automatically add Authorize header to all outgoing requests.
Note: This works only with Blazor Wasm, not Server.

Related

OpenIddict: Share authentication between ASP.NET Core application and WEB.API Application

I have an asp.net core web application that using OpenIddict. It works fine and users can login.
Now I want to add web.api hosted on separate subdomain, but because the user is already authenticated I want to somehow share that authentication.
Is it possible? How can I do it? I see in the examples for OpenIddict user have to authenticate again, but I do not want that.
My Setup:
ASP.NET Core Web App: app.domain.com
OpenIddict server: login.domain.com
Web API .net Core App: api.domain.com (that is new requirement)
My web application has views that display data from controllers, I want to add extra javascript logic, and I can have web.api as part of the asp.net core domain: app.domain.com/api/
in that case, it works, the authenticated shared between web application and API,
but my goal is to have web.api on a separate subdomain: api.domain.com
I know I have to create a Bearer token, but it is not clear how to do it without authenticating the user again. So I want somehow share ASP.NET Core web application authentication to access api.domain.com
Can you please point me in the right direction? Where to start looking?

SignalR failing to authenticate via cookies

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());

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();

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).