SignalR in ASP.Net Core 401 Unauthorized - c#

I have a small problem with my UWP app. First, the UWP app has the following capabilities:
Enterprise Authentication
Shared user certificates
Private Networks
User Account Information
Now I want to connect to a SignalR-Hub in an ASP.NET Core 2.1 Web API. The hub looks like this:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;
namespace Test.Namespace
{
[Authorize]
public class SyncHub : Hub
{
public void SendUpdate()
{
Clients.All.SendAsync("Update");
}
}
}
And this is my Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Test.Namespace
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSignalR();
services.AddSingleton<IUserIdProvider, NameUserIdProvider>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseSignalR(routes =>
{
routes.MapHub<SyncHub>("/syncHub");
});
app.UseMvc();
}
}
}
The whole API runs on an IIS with Windows Authentication configured. The Active Directory runs on the same machine.
And this is how my UWP app calls the Service:
HubConnection connection = new HubConnectionBuilder().WithUrl("http://Server:81/syncHub", options => {
options.UseDefaultCredentials = true;
}).Build();
await connection.StartAsync();
This call always throws a 401.
What am I doing wrong? I work on this Problem for more than a week now and I can't figure out why it is not working.
Thanks to all who will help me :)
Edit: So I tried a few thinks today and found out, that this is not a problem of SignalR itself.I created a ASP.NET Core console app with the exact same call and everything works fine. It also works when I hardcode the credentials in the UWP app. It only doesn't work when I use "UseDefaultCredentials" in UWP. I am completly clueless. I have rechecked the capabilities but this doesn't help either.

It seems app.UseHttpsRedirection(); fail to redirect the client credentials.
Try to make a test with https url.
var hubConnectionBuilder = new HubConnectionBuilder();
var hubConnection = hubConnectionBuilder.WithUrl("https://localhost:44381/timeHub",options => {
options.UseDefaultCredentials = true;
}).Build();
await hubConnection.StartAsync();

Finally!
The answer was realy hard to find, but now it is working!
According to this site: https://support.microsoft.com/en-us/help/303650/intranet-site-is-identified-as-an-internet-site-when-you-use-an-fqdn-o
the app identifies the call as a call to the internet and therefore does not allow sending the default credentials. I must add this page manually to the local Intranet sites with the Internet Explorer and than it worked like a charm.
Thank to all who helped me with this :)

Related

.NET Identity not locking down static files after logging out

I am building a .NET Core 3.1 Web Application using .NET Identity to secure some static files (these are documentation files generated automatically that I do not want general users to be able to see). The application also uses MVC to serve some administration pages.
Things appear to work OK (if a new visitor to the site tries to go to the static pages, they get redirected to the 'Access Denied' page). If one logs in (and has the 'Admin' role set in the AspNet database), one can then see these files.
However, after such a user logs out, one can still get to the protected static files (although one can no longer get to the Admin pages served using MVC). Here is how the system is set up in the Startup.cs file:
using CoreMVC.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using System.IO;
namespace CoreMVC
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
/*
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
*/
// This gets the [Authorize(Roles = "Admin")] decoration working. N.B. reset RequireConfirmedAccount to false
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = false)
.AddRoles<IdentityRole>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddControllersWithViews();
services.AddRazorPages();
// Try putting the fallback authorization policy here
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireRole("Admin")
.Build();
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
// Keep this in (otherwise we lose access to required resources)
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
// New mapping for static files - Seems OK to call UseStaticFiles twice!
// Note: moved this after UseAuthentication and UseAuthorization
// Try <host>/Documents/index.html
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(env.ContentRootPath, "Documents")),
RequestPath = "/Documents"
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
}
}
Note that the protection for the static files is provided by the fallback policy set up by the call to services.AddAuthorization in the ConfigureServices method, so this is distinct from the methods used to secure the pages served using MVC. I guess the problem is here somewhere.
I have tried deleting all Cookies using the browser dev tools but to no effect. Cookies on the system whilst logged in are:
.AspNetCore.Identity.Application
ARRAffinitySameSite
.AspNetCore.Antiforgery.9fXoN5jHCXs
ARRAffinity
After logging out, the .AspNetCore.Identity.Application cookie disappears. There do not seem to be any session or local variables stored.
Can anyone explain why this access is still being allowed and how to prevent it? As mentioned above, I suspect it is something to do with how the fallback policy is applied but I do not understand the details.
Update: On further investigation it seems that access to the static pages is only granted on the pages visited whilst logged in. This suggests that the server is maintaining some kind of list - an IIS issue maybe?
Further update: Thanks to the suggestion below, I now realise that this is due to browser caching, so not really an issue. I have now tried sticking
<meta http-equiv="Cache-Control" content="private, no-store" />
into the head section of the HTML pages and this seems to have worked.

Add startup class in .Net Core Class Library

I am trying to add the OWIN startup class in a new .Net core class library project. I have installed the package Microsoft.AspNetCore.Owin package. But I still don't see the option to create OWIN Startup class in Add New Items wizard. It used to be there in .Net class library earlier. Is it different in .Net Core class library?
I basically want to create a separate project for my SingalR hub and use it from wherever I want by just referencing it.
This has to do with the tooling of Visual Studio. When you are working on a web project Visual Studio recognizes this and presents web options in the Add New Items Wizard. Since you are working in a class library project Visual Studio does not think you need web based options and thus does not present it. Luckily, the startup class you want is a plain class with some conventions. You should be able to add a class called startup to your class library project and give it the following definition to get what you want:
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace MyClassLibrary
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
}
}
}
Once I've created a ChatHub which derives from Microsoft.AspNetCore.SignalR.Hub<IChatClient>.
All components have been located in separate .net standard library.
IChatClient looks like (it's used for type safety):
public interface IChatClient
{
Task ReceiveChatMessage(string user, string message, DateTime sentAt, bool isMarkedAsImportant);
Task ReceiveChatActivity(string user, Activity activity, DateTime sentAt);
}
Finally I used that ChatHub in an ASP.net core project, where the hub is configured in Startup like this:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseCors(builder =>
{
builder.WithOrigins("https://localhost:3000")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
IdentityModelEventSource.ShowPII = true;
}
else
{
app.UseGlobalExceptionHandler();
app.UseHttpsRedirection();
app.NwebSecApiSetup();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<ChatHub>("/api/chat");
endpoints.MapHub<EventHub>("/api/events");
});
}
Additionally, I've configured something more for SignalR in the ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddControllers().AddControllersAsServices();
services.AddHttpContextAccessor();
services.AddConnections();
services.AddSignalR(options =>
{
options.EnableDetailedErrors = true;
})
.AddNewtonsoftJsonProtocol();
...
}
I suppose you can easily use such Hubs in other projects as well.

Asp.net core Identity Signs in user without password from any device

Let me explain the situation I'm running into as it seems I'm the first one in the whole internet to deal with it and I ran out of ideas. I have a pretty simple Razor Pages website that is running on asp.net core 3.0 It is hosted on the Azure App Service. I am using the most standard Idenity Framework Provided By Microsoft. Here is my Startup.cs:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using IIHF_Toolbox.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace IIHF_Toolbox
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSession();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Lax;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection"), sOptions =>
{
sOptions.EnableRetryOnFailure(20);
}), ServiceLifetime.Transient, ServiceLifetime.Transient);
services.AddDefaultIdentity<IdentityUser>()
.AddDefaultUI()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddCors(options =>
{
options.AddPolicy("AllowAllHeaders",
builder =>
{
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
//services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// services.AddControllers()
//.AddNewtonsoftJson();
services.AddRazorPages().AddNewtonsoftJson();
services.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSession();
app.UseAuthentication();
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("MYAPIKEY");
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
//app.UseMvc();
}
}
}
I believe it looks pretty standart.As you can see, I use the simplest login-password Sign-In mechanism with default IdentityUser. Now about the problem:
I login into my website with User-A, using it's credentials. Then, I open my website on a brand clean device that never previously visited the websites, nor logged into it, and I find out that somehow, that I'm signed in to the User-A without entering a password on a device that never visited this website before. This is more that a security bteach. This is basically the absence of security. And you've gussed it right - If I provide you with a link, and you follow it, you will basically enter website already logged into a User-A account.
N.B. The link to the website could be any - it is not something like if you follow "website.com/user-a" you'll get to his page with edit rights. The thing is that SignInManager.IsUserSignedIn(User) and User.Identity.IsAuthenticated both returns true. Maybe I'm misunderstanding that? But how in the world would brand new Session be injected with the same user as the last logged in one? I'm really running out of ideas. Any idea on what to check would be highly appreciated. Thanks.

How to fix the bot configuration when migrating settings form .bot file to appsettings

I ran into a problem when trying to deploy my bot to Azure. The following error was given when I tried to create the Azure resources: error: InvalidBotData, message: Version: Bot Version has an invalid value. I dug around a little bit and found the that my bot is version 4.3 while now you need 4.4 to deploy…
I found that Mircosoft already has a solution for the problem found here: https://learn.microsoft.com/bs-latn-ba/azure/bot-service/bot-file-basics?view=azure-bot-service-4.0&tabs=csharp I followed the steps, I also changed the way QnAmaker and Luis are called. But when I run the application I get the following error: System.InvalidOperationException: Unable to resolve service for type 'VacancyBot.VacancyBot.Services.BotServices' while attempting to activate 'VacancyBot.VacancyBotBot'.
I realized that the bot was not being added anywhere anymore so I tried adding it with services.AddSingleton<VacancyBotBot>() but that did not work. Adding it as an Transient also does not work.
The part that normally adds the bot is this:
var secretKey = Configuration.GetSection("botFileSecret")?.Value;
var botFilePath = Configuration.GetSection("botFilePath")?.Value;
var botConfig = BotConfiguration.Load(botFilePath ?? #".\nlp-with-luis.bot", secretKey);
services.AddSingleton(sp => botConfig ?? throw new InvalidOperationException($"The .bot config file could not be loaded. ({botConfig})"));
var connectedServices = new BotServices(botConfig);
services.AddSingleton(sp => connectedServices);
But this does not work anymore because, as aspected, the .\nlp-with-luis.bot cannot be found. (I did not delete the .bot file for real yet, but it doesn't use it anymore now I guess?).
I was wondering if anybody happens to know how to add the bot, or alter the BotConfiguration in a way that it works again. I really hope this is possible! If someone needs to see more code please say so and I will try to provide it (:
I forgot to add that I tried putting back "botFilePath": "VacancyBot.bot",
"botFileSecret": "", in the appsettings file, but results into getting the same error in Azure again...
The .bot file can still be used, but it looks like you're trying to use a combination of .bot file and appsettings.json. Let's get you straightened out.
Starting with appsettings.json: You no longer need botFilePath or botFileSecret. Instead, structure your appsettings.json like below:
{
"MicrosoftAppId": "",
"MicrosoftAppPassword": "",
"LuisAppId": "",
"LuisAPIKey": "",
"LuisAPIHostName": ""
}
The MicrosoftAppId and MicrosoftAppPassword are now pulled in through a ConfigurationCredentialProvider.cs file, which will later be added as a singleton in Startup.cs. The ConfigurationCredentialProvider should look like below:
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.Configuration;
namespace CoreBot1
{
public class ConfigurationCredentialProvider : SimpleCredentialProvider
{
public ConfigurationCredentialProvider(IConfiguration configuration)
: base(configuration["MicrosoftAppId"], configuration["MicrosoftAppPassword"])
{
}
}
}
Short, sweet and to the point. Finally, structure your startup.cs like below, to add both the bot and the ICredentialProvider:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Extensions.DependencyInjection;
using CoreBot1.Bots;
using CoreBot1.Dialogs;
namespace CoreBot1
{
public class Startup
{
public Startup()
{
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
// Create the credential provider to be used with the Bot Framework Adapter.
services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
// Create the Bot Framework Adapter with error handling enabled.
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
// Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.)
services.AddSingleton<IStorage, MemoryStorage>();
// The Dialog that will be run by the bot.
services.AddSingleton<MainDialog>();
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, DialogAndWelcomeBot<MainDialog>>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseDefaultFiles();
app.UseStaticFiles();
//app.UseHttpsRedirection();
app.UseMvc();
}
}
}

ASP.NET Core REST API with React SPA

I have an ASP.NET Core web application that hosts a REST API. I'm attempting to add a React-based Single-Page Application (SPA) to the server but the application is not using MVC.
When I enable a Router my app no longer loads. If I disable the Router and only host the static app, it loads correctly, but of course it can't communicate with the web API.
I've also sucessfully hosted my SPA using Python's built-in web server and using a simple file server written in Go, but, again, then it can't "talk" to my API without running separate services and or proxying/redirecting URLs. My goal is a single, C# application that can serve both the API and the static user interface.
Here are relevant portions of my MyStartup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
services.AddLogging();
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
app.UseDefaultFiles();
app.UseStaticFiles(new StaticFileOptions {
ServeUnknownFileTypes = true
});
app.UseRouter(buildRoutes(app));
}
private IRouter buildRoutes(IApplicationBuilder app)
{
// TODO: Figure out how to add a default (and versioned) API base URL to the Router...
string url(string path) {
return $"api/{path}";
}
RouteBuilder routeBuilder = new RouteBuilder(app, new RouteHandler(null));
/// Notify API
routeBuilder.MapGet(url("status"), GetControllerStatus);
routeBuilder.MapPost(url("heartbeat"), GotHeartbeat);
routeBuilder.MapGet(url("items/{id}"), httpCtx => {
string id = httpCtx.GetRouteValue("id").ToString();
return RetrieveItem(httpCtx, id);
});
// ... More routes ...
return routeBuilder.Build();
}
and my Main class contains this excerpt:
IWebHost host =
new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseUrls("http://*:11111")
.UseStartup<MyStartup>()
.Build();
host.Run();
I've investigated the following tools and tutorials, but they all seem to require different approaches using MVC:
https://github.com/aspnet/JavaScriptServices
https://learn.microsoft.com/en-us/aspnet/core/client-side/spa-services?view=aspnetcore-2.0#routing-helpers
https://github.com/bradymholt/aspnet-core-react-template/tree/master/api
https://github.com/aspnet/templating/blob/dev/src/Microsoft.DotNet.Web.Spa.ProjectTemplates/content/React-CSharp/Startup.cs
https://github.com/aspnet/JavaScriptServices/issues/973#issuecomment-303766597 (very relevant!)
https://github.com/aspnet/JavaScriptServices/issues/1150 (very relevant!)
Is it possible to host an SPA from an ASP.NET Core application that isn't using MVC?

Categories

Resources