ASP.Net Core 2.1 Vue.JS SPA - Cookie Auth and the Vue.JS Dev Server - c#

I have an application which is essentially a Vue.JS SPA sat within a dotnet core 2.1 app, providing API services. When I start up the app, with my current Startup.cs configuration, it starts up 3 windows. 1 of these windows is the actual ASP.Net Core app root - the other 2 (don't ask me why 2) are copies of the Vue.js dev server that you get when you do npm run serve.
The problem that I have is that, whilst authentication is working fine if I use the instance running in the first window, if I try to log in using the Vue.JS server windows then I just get a 401 back.
My first thought was CORS, so I set up a CORS policy purely for when running in development mode but this hasn't rectified the issue. Developing without the Vue.JS server isn't really viable as without it I have no HMR and will need to reload the entire application state every time I make a design change.
I have had this setup working before with a .net Framework 4.6 API back end with no problem at all so I can only think it's a modern security enhancement that's blocking the proxying which I need to configure to be off in Development or something.
Any suggestions on how I can solve this are welcome.
Here's my current Startup.cs Configure...
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory,
ApplicationDbContext context, RoleManager<IdentityRole> roleManager, UserManager<ApplicationUser> userManager)
{
if (env.IsDevelopment())
{
app
.UseDeveloperExceptionPage()
.UseDatabaseErrorPage()
.UseCors("DevelopmentPolicy");
}
else
{
app
.UseExceptionHandler("/Home/Error")
.UseHsts();
}
app
.UseAuthentication()
.UseHttpsRedirection()
.UseStaticFiles()
.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "VueApp";
if (env.IsDevelopment())
{
spa.UseVueCliServer("serve");
}
});
DbInitializer.Initialize(context, roleManager, userManager, env, loggerFactory);
}
...and ConfigureServices...
public void ConfigureServices(IServiceCollection services)
{
services
.AddLogging(builder => builder
.AddConsole()
.AddDebug());
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
.AddJsonOptions(options => options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver());
services
.AddCors(options =>
{
options.AddPolicy("DevelopmentPolicy",
policy => policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
});
// In production, the Vue files will be served from this directory
services.AddSpaStaticFiles(configuration => { configuration.RootPath = "wwwroot"; });
services.AddAuthentication();
services
.ConfigureEntityFramework(Configuration)
.ConfigureEntityServices()
.ConfigureIdentityDependencies()
.ConfigureDomainServices()
.ConfigureApplicationCookie(config =>
{
config.SlidingExpiration = true;
config.Events = new CookieAuthenticationEvents
{
OnRedirectToLogin = cxt =>
{
cxt.Response.StatusCode = 401;
return Task.CompletedTask;
},
OnRedirectToAccessDenied = cxt =>
{
cxt.Response.StatusCode = 403;
return Task.CompletedTask;
},
OnRedirectToLogout = cxt => Task.CompletedTask
};
});
}
...and Identity gets lumped in with EF configuration here...
public static IServiceCollection ConfigureEntityFramework(this IServiceCollection services, string connectionString)
{
services
.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString))
.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
// Password settings
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = true;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
// User settings
options.User.RequireUniqueEmail = true;
})
.AddRoleManager<RoleManager<IdentityRole>>()
.AddSignInManager<SignInManager<ApplicationUser>>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
return services;
}
My vue.config.js looks like this...
const baseUrl = ''
module.exports = {
publicPath: baseUrl + '/',
// place our built files into the parent project where they will be copied
// into the distribution for deployment
outputDir: '../wwwroot',
filenameHashing: true, //process.env.NODE_ENV === 'production',
lintOnSave: 'error',
css: {
modules: false,
sourceMap: process.env.NODE_ENV !== 'production',
loaderOptions: {
sass: {
data: `
$fa-font-path: ${process.env.NODE_ENV !== 'production' ? '"~/fonts"' : '"' + baseUrl + '/fonts"'};
#import "#/scss/base/index.scss";
#import "#/scss/helpers/index.scss";
`
}
}
},
devServer: {
host: 'localhost',
port: 8080,
hot: true,
open: true,
openPage: '',
overlay: true,
disableHostCheck: true,
proxy: {
// Proxy services to a backend API
'/api': {
target: process.env.PROXY || 'https://localhost:44368',
secure: false,
changeOrigin: true
}
}
},
// these node_modules use es6 so need transpiling for IE
transpileDependencies: [
]
}

I solved this in the end by configuring the app so that it only forces HTTPS when not in development mode. HTTPS was killing the underlying Webpack Development Server and therefore Hot Module replacement - I can now debug successfully in the main window!
Happy to up vote better solutions than that though to get it working without killing SSL (if possible).

Related

How to include Access-Control-Allow-Credentials in ASP .Net Core app?

Are there an explicit "Access-Control-Allow-Credentials" attribute to explicitly allow credentials to be sent with a request to a specific client site?
I have tried the following
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("http://my-account-name.github.io",
"http://my-account-name.github.io/My-repository",
"https://my-account-name.github.io",
"https://my-account-name.github.io/My-repository");
});
});
...
app.UseCors(MyAllowSpecificOrigins);
Not working solution source
And I get the "CORS" policy restriction in Chrome console nevertheless for those request that include credential headers and / or authorize cookies. Other requests (that don't include credentials) are passing fine with cors fetch() from JS.
The complete solution to the CORS Cookie Authorization is the SameSite = None; cookie policy (which you have to tell the browser explicitly from your server)
// Controller.cs
[EnableCors("_allowSpecific")] // !!!
public class YourController : Controller
// ...
// Startup.cs (.Net 5) / Program.cs (.Net 6-7 +)
string MyAllowSpecificOrigins = "_allowSpecific";
public void ConfigureServices(IServiceCollection services)
{
services.AddSession(options =>
{
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None; // !!!
});
services.AddCors(options => {
options.AddPolicy(name: MyAllowSpecificOrigins,
policy => { policy.WithOrigins( "http://your-domain.your-site.com", "https://your-domain.your-site.io", "http://your-domain.your-site.io"
#if DEBUG // Use this for debugging CORS in NPM on another localhost port
, "http://localhost:8081", "https://localhost:8081", "http://127.0.0.1:8081", "http://192.168.1.64:8081"
#endif
).AllowAnyHeader().AllowAnyMethod().AllowCredentials();
});
});
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.None; // !!!
});
// ADD your services, DbContexts, Identity, Configure<IdentityOptions>, AddDefaultIdentity<IdentityUser>, AddRoles<IdentityRole>, AddEntityFrameworkStores<ApplicationDbContext>, ConfigureApplicationCookie, AddDatabaseDeveloperPageExceptionFilter, AddSingleton<IHttpContextAccessor>, AddRazorPages, AddControllersWithViews, AddLogging,
services.ConfigureApplicationCookie(options => {
// Cookie settings
options.Cookie.SameSite = SameSiteMode.None; // !!!
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(10000);
options.SlidingExpiration = true;
});
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, IWebHostEnvironment env, Microsoft.Extensions.Hosting.IHostApplicationLifetime appLifetime)
{
// ...
app.UseCookiePolicy(new CookiePolicyOptions {
MinimumSameSitePolicy = SameSiteMode.None // !!!
});
// app.UseRouting(); ...
app.UseCors(MyAllowSpecificOrigins); // ? 1) not sure whether you need to use both, but it works
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(MyAllowSpecificOrigins); // ? 2) not sure if you need to use both, but it works
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.RequireCors(MyAllowSpecificOrigins); // !!!
endpoints.MapRazorPages().RequireCors(MyAllowSpecificOrigins); // !!!
endpoints.MapControllers().RequireCors(MyAllowSpecificOrigins); // !!!
});
}
//...
Not only this setup will allow you to use CORS autherization (using cookie headers), this will also allow Chrome to block any request from other than "_allowSpecific" origins.
I had to spend almost a year to figure this out.

Asp.net Blazor server app fails to redirect in kubernetes with OIDC

We have a .NET 5 (Blazor Server) app running in Azure Kubernetes that uses OpenID Connect to authenticate with a 3rd party. The app is running behind Ingress. Ingress uses https. The app is only http. After we authenticate with OIDC and get redirected back to /signin-oidc, we get a .NET error that we haven't been able to solve.
warn: Microsoft.AspNetCore.Http.ResponseCookies[1]
The cookie '.AspNetCore.OpenIdConnect.Nonce.CfDJ8EYehvsxFBVNtGDsitGDhE8K9FHQZVQwqqr1YO-zVntEtRgpfb_0cHpxfZp77AdGnS35iGRKYV54DTgx2O6ZO_3gq98pbP_XcbHnJmBDtZg2g5hhPakTrRirxDb-Qab0diaLMFKdmDrNTqGkVmqiGWpQkSxcnmxzVGGE0Cg_l930hk6TYgU0qmkzSO9WS16UBOYiub32GF4I9_qPwIiYlCq5dMTtUJaMxGlo8AdAqknxTzYz4UsrrPBi_RiWUKaF6heQitbOD4V-auHmdXQm4LE' has set 'SameSite=None' and must also set 'Secure'.
warn: Microsoft.AspNetCore.Http.ResponseCookies[1]
The cookie '.AspNetCore.Correlation.MMrYZ2WKyYiV4hMC6bhQbGZozpubcF2tYsKq748YH44' has set 'SameSite=None' and must also set 'Secure'.
warn: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[15]
'.AspNetCore.Correlation.MMrYZ2WKyYiV4hMC6bhQbGZozpubcF2tYsKq748YH44' cookie not found.
fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
An unhandled exception has occurred while executing the request.
System.Exception: An error was encountered while handling the remote login.
---> System.Exception: Correlation failed.
--- End of inner exception stack trace ---
at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
public class Startup
{
private static readonly object refreshLock = new object();
private IConfiguration Configuration;
private readonly IWebHostEnvironment Env;
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Console.WriteLine($"LogQAApp Version: {Assembly.GetExecutingAssembly().GetName().Version}");
// We apparently need to set a CultureInfo or some of the Razor pages dealing with DateTimes, like LogErrorCountByTime fails with JavaScript errors.
// I wanted to set it to CultureInvariant, but that wouldn't take. Didn't complain, but wouldn't actually set it.
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en-US");
Configuration = configuration;
Env = env;
}
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // Needed for 1252 code page encoding.
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("");
services.AddSignalR(e =>
{
e.MaximumReceiveMessageSize = 102400000;
});
services.AddBlazoredSessionStorage();
services.AddCors();
services.AddSyncfusionBlazor();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddHttpContextAccessor();
ServiceConfigurations.LoadFromConfiguration(Configuration);
#region Authentication
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(
options =>
{
options.Events = GetCookieAuthenticationEvents();
}
)
.AddOpenIdConnect("SlbOIDC", options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = Configuration["SlbOIDC:Authority"];
if (Env.IsDevelopment())
{
options.ClientId = Configuration["SlbOIDC:ClientID"];
options.ClientSecret = Configuration["SlbOIDC:ClientSecret"];
}
else
{
options.ClientId = Configuration.GetValue<string>("slbclientid");
options.ClientSecret = Configuration.GetValue<string>("slbclientsecret");
}
options.ResponseType = OpenIdConnectResponseType.Code;
options.UsePkce = true;
options.SaveTokens = true;
options.ClaimsIssuer = "SlbOIDC";
// Azure is communicating to us over http, but we need to tell SLB to respond back to us on https.
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = context =>
{
Console.WriteLine($"Before: {context.ProtocolMessage.RedirectUri}");
context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http://", "https://");
Console.WriteLine($"After: {context.ProtocolMessage.RedirectUri}");
return Task.FromResult(0);
}
};
});
services.AddSession(options =>
{
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.IsEssential = true;
});
#endregion
services.AddScoped<BrowserService>();
services.AddSingleton<ConcurrentSessionStatesSingleton>();
services.AddSingleton<URLConfiguration>();
services.AddScoped<CircuitHandler>((sp) => new CircuitHandlerScoped(sp.GetRequiredService<ConcurrentSessionStatesSingleton>(), sp.GetRequiredService<BrowserService>(), sp.GetRequiredService<IJSRuntime>()));
services.AddScoped<SessionServiceScoped>();
services.AddScoped<LogEditorScoped>();
services.AddSingleton<ModalService>();
services.AddFlexor();
services.AddScoped<ResizeListener>();
services.AddScoped<ApplicationLogSingleton>();
services.AddScoped<LogChartsSingleton>();
services.AddScoped<CurveNameClassificationSingleton>();
services.AddScoped<HubClientSingleton>();
services.AddScoped((sp) => new LogAquisitionScopedService(
sp.GetRequiredService<URLConfiguration>(),
sp.GetRequiredService<HubClientSingleton>(),
sp.GetRequiredService<ApplicationLogSingleton>(),
sp.GetRequiredService<IConfiguration>(),
sp.GetRequiredService<SessionServiceScoped>(),
sp.GetRequiredService<AuthenticationStateProvider>(),
sp.GetRequiredService<IHttpContextAccessor>(),
sp.GetRequiredService<IJSRuntime>()
)
);
services.AddScoped<UnitSingleton>();
services.AddServerSideBlazor().AddCircuitOptions(options => { options.DetailedErrors = true; });
services.AddScoped<TimeZoneService>();
services.AddHostedService<ExcelBackgroundService>();
services.AddHostedService<LogEditorBackgroundService>();
}
// 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();
}
else
{
app.UseExceptionHandler("/Error");
//app.UseHsts();
}
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseCookiePolicy();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseAuthentication();
if (!Env.IsDevelopment())
{
app.UseTrafficManager();
}
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
private CookieAuthenticationEvents GetCookieAuthenticationEvents()
{
return new CookieAuthenticationEvents()
{
OnValidatePrincipal = context =>
{
lock (refreshLock)
{
if (context.Properties.Items.ContainsKey(".Token.expires_at"))
{
DateTime expire = DateTime.Parse(context.Properties.Items[".Token.expires_at"]);
if (expire.AddMinutes(-20) < DateTime.Now)
{
try
{
CloudAuthentication cloudAuthentication = new CloudAuthentication();
TokenResponse tokenResponse = cloudAuthentication.GetRefreshToken(context.Properties.Items[".Token.refresh_token"]);
context.Properties.Items[".Token.access_token"] = tokenResponse.access_token;
context.Properties.Items[".Token.refresh_token"] = tokenResponse.refresh_token;
context.Properties.Items[".Token.expires_at"] = DateTime.Now.AddSeconds(tokenResponse.expires_in).ToString();
context.ShouldRenew = true;
}
catch (Exception ex)
{
context.RejectPrincipal();
}
}
}
return Task.FromResult(0);
}
}
};
}
}
It's a good question - there are a couple of interesting points here that I've expanded on since they are related to SameSite cookies.
REVERSE PROXY SETUP
By default the Microsoft stack requires you to run on HTTPS if using cookies that require an SSL connection. However, you are providing SSL via a Kubernetes ingress, which is a form of reverse proxy.
The Microsoft .Net Core Reverse Proxy Docs may provide a solution. The doc suggests that you can inform the runtime that there is an SSL context, even though you are listening on HTTP:
app.Use((context, next) =>
{
context.Request.Scheme = "https";
return next();
});
I would be surprised if Microsoft did not support your setup, since it is a pretty mainstream hosting option. If this does not work then you can try:
Further searching around Blazor and 'reverse proxy hosting'
Worst case you may have to use SSL inside the cluster for this particular component, as Johan indicates
WIDER INFO - API DRIVEN OAUTH
Many companies want to develop Single Page Apps, but use a website based back end in order to manage the OAuth security. Combining serving of web content with OAuth security adds complexity. It is often not understood that the OAuth SPA security works better if developed in an API driven manner.
The below resources show how the SPA code can be simplified and in this example the API will issue cookies however it is configured. This would enable it to listen over HTTP inside the cluster (if needed) but to also issue secure cookies:
API driven OpenID Connect code
Curity Blog Post
WIDER INFO: SAMESITE COOKIES
It is recommended to use SameSite=strict as the most secure option, rather than SameSite=none. There are sometimes usability problems with the strict option however, which can cause cookies to be dropped after redirects or navigation from email links.
This can result in companies downgrading their web security to a less secure SameSite option. These problems do not occur when an API driven solution is used, and you can then use the strongest SameSite=strict option.

OIDC correlation failed in MS Teams Blazor App but works in Browser

Im coding a Blazor App (ASP.NET Core 3.1) for MS Teams.
In the App user have to authentificate themself via an authentification popup.
The app is hostet via internal IIS on localhost and is tunneled via ngrok in MS Teams.
In Blazor there are Blazor Components which are saved via #attribute [Authorize] flag which is discribed in Blazor Security.
The App is registeres at Azure AD Portal correctly.
When i try to authentificate via Azure AD in the App which is hosted in MS Teams the App reads the user credentials and send them to Azure.
I think at the redirect the Error An unhandled exception occurred while processing the request. Exception: Correlation failed. Unknown location Exception: An error was encountered while handling the remote login. Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler<TOptions>.HandleRequestAsync() is thrown.
The call is looking like this:
https://login.microsoftonline.com/[tenantid]/oauth2/authorize?client_id=[clientId]&redirect_uri=https%3A%2F%2Flocalhost%3A44302%2Fsignin-oidc&response_type=id_token&scope=openid%20profile&response_mode=form_post&nonce=[nonce]&state=[state]&x-client-SKU=ID_NETSTANDARD2_0&x-client-ver=5.5.0.0
Also there is no cookie created.
The interesting thing is in the Browser (Chrome) the authentificaion flow works fine and also the cookie is created correctly. Also the Blazor AuthentificationStateProvider gets the UserState correctly from the popup.
My Login Page with the Authentification Popup:
function login() {
$(".auth-data").hide();
microsoftTeams.authentication.authenticate({
url: window.location.origin + "/AzureAD/Account/SignIn",
width: 600,
height: 535,
successCallback: function (result) {
window.location.replace(window.location.origin + "/");
},
failureCallback: function (reason) {
console.log("Login failed: " + reason);
if (reason === "CancelledByUser" || reason === "FailedToOpenWindow") {
console.log("Login was blocked by popup blocker or canceled by user.");
}
// At this point we have to get the user involved, so show the login button
$("#btnLogin").show();
$(".auth-data").hide();
$("#divError").text(reason).show();
}
});
}
My Startup Page:
private void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
if (options.SameSite == SameSiteMode.None)
{
var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
// TODO: Use your User Agent library of choice here.
if (userAgent.Contains("Chrome"))
{
options.SameSite = SameSiteMode.Unspecified;
}
}
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.OnAppendCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext =>
CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
options.HttpOnly = HttpOnlyPolicy.None;
options.Secure = CookieSecurePolicy.Always;
});
services
.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
services.Configure<JwtBearerOptions>(
AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.TokenValidationParameters.NameClaimType = "name";
});
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddBlazoredLocalStorage();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCookiePolicy();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
As you can see i used the method which is mentioned on another stack overflow question Stack Overflow
this dont work for my task.
I could need help if there is something wrong configured in my Startup or somewhere else beacause authentification via Teams dont work for me just via the Browser.

Aurelia Windows Authentication - Post 401 Unauthorized

I'm totally stuck on implementing Windows authentication for one of my .NET Core apps that uses Aurelia for client side.
The Aurelia application is hosted on port:9000 and the .NET WebAPI is hosted on port:9001.
The idea is to serve static pages from my .NET app once the app is published but now in development I use port:9000 because of the BrowserSync provided by Aurelia.
When I use port:9000 it's all fine and dandy and I have no issues posting or getting.
If I switch to port:9001 I can still get but not post. Posting results in 401 Unauthorized.
If we look at the headers for port:9000 requests..
Get(success):
Post(failed):
You can see that there are multiple headers missing in the post for some reasons, most importantly the authentication cookie..
Base-Repo.js
import {inject} from 'aurelia-framework';
import {HttpClient, json} from 'aurelia-fetch-client';
import {AppSettings} from '../infrastructure/app-settings';
#inject(HttpClient, AppSettings)
export class BaseRepo {
constructor(http, appSettings) {
http.configure(config => {
config
.withDefaults({
credentials: 'include',
headers: {
'Accept': 'application/json'
}
})
.withInterceptor({
request(request) {
console.log(`Requesting ${request.method} ${request.url}`);
return request;
},
response(response) {
console.log(`Received ${response.status} ${response.url}`);
return response;
}
})
});
this.http = http;
this.baseUrl = appSettings.api;
}
get(url) {
console.log('BaseRepo(get): ' + url);
return this.http.fetch(this.baseUrl + url)
.then(response => { return response.json(); })
.then(data => { return data; });
}
post(url, data) {
console.log('BaseRepo(post): ' + url, data);
return this.http.fetch(this.baseUrl + url, {
method: 'post',
body: json(data)
})
.then(response => response.json())
.then(data => { return data; });
}
}
Why is GET working but not POST when using BrowserSync port?
Edit 1
Post(success) for port:9001:
Edit 2
Console message post error:
OPTIONS http://localhost:9001/api/MYURLS 401 (Unauthorized)
Fetch API cannot load
http://localhost:9001/api/MYURLS.
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:9000' is therefore not allowed
access. The response had HTTP status code 401. If an opaque response
serves your needs, set the request's mode to 'no-cors' to fetch the
resource with CORS disabled.
Edit 3
Startup.cs
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
env.ConfigureNLog("nlog.config");
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
services.AddMemoryCache();
services.AddMvc();
services.InjectWebServices();
services.AddOptions();
//call this in case you need aspnet-user-authtype/aspnet-user-identity
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IConfiguration>(Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors("CorsPolicy");
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMvc();
app.UseDefaultFiles();
app.UseStaticFiles();
//add NLog to ASP.NET Core
loggerFactory.AddNLog();
//add NLog.Web
app.AddNLogWeb();
}
}
I enabled "Enable Anonymous Authentication" in project properties and voila...
Before I only had "Enable Windows Authenticaiton" enabled, now both ports work!
When application is deployed this wont be enabled anyway because by then I will use the real IIS.
Update 1
After upgrading to .net core 2.0 I was no longer able to enable both Windows Authentication and Anonymous Authentication.
After some research I found out you have to add:
services.AddAuthentication(IISDefaults.AuthenticationScheme);
in your startup.cs in order for it to work.
More info can be found in comment section and docs.
Update 2
You need Microsoft.AspNetCore.Authentication package for authentication builder.
You will need to enable CORS in your ASP.NET Core project. There's information on how to do this here: https://learn.microsoft.com/en-us/aspnet/core/security/cors.
You need to call AddCors in ConfigureServices:
services.AddCors();
And then UseCors in Configure:
// Shows UseCors with CorsPolicyBuilder.
app.UseCors(builder => builder.WithOrigins("http://example.com"));
When you're using port 9000, you're on a different origin to the API, but with 9001, you're on the same origin and therefore CORS will not apply.
The OPTIONS requests are known as "preflighting". There's more information on those here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests.

How to configure AspNetCore Identity and Auth0

I created a sample project within VS 2017 using ASP.NET Core Templates 1.1 (Web Application) changing the authentication to be Individual User Accounts.
From there I followed the instructions at
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/
and
https://auth0.com/docs/quickstart/webapp/aspnet-core/01-login
The Startup.cs is essentially
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(
options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
});
services.AddMvc(options=>
{
options.SslPort = 44321;
options.Filters.Add(new RequireHttpsAttribute());
});
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
services.AddOptions();
services.Configure<Auth0Settings>(Configuration.GetSection("Auth0"));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IOptions<Auth0Settings> auth0Settings)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentity();
// Add external authentication middleware below. To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
var options = new OpenIdConnectOptions("Auth0")
{
Authority = $"https://{auth0Settings.Value.Domain}",
ClientId = auth0Settings.Value.ClientId,
ClientSecret = auth0Settings.Value.ClientSecret,
AutomaticAuthenticate = false,
AutomaticChallenge = false,
SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme,
ResponseType = "code",
CallbackPath = new PathString("/signin-auth0"),
// Configure the Claims Issuer to be Auth0
ClaimsIssuer = "Auth0",
};
options.Scope.Clear();
options.Scope.Add("openid");
app.UseOpenIdConnectAuthentication(options);
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
I register a user, navigate to manage your account and click, Manage link next to External Logins.
I click Auth0 button and successfully login. I get redirected to /Manage/LinkLoginCallback where the following method returns null.
private Task<ApplicationUser> GetCurrentUserAsync()
{
return _userManager.GetUserAsync(HttpContext.User);
}
Causing the following to appear
Error. An error occurred while processing your request. Development
Mode Swapping to Development environment will display more detailed
information about the error that occurred.
Development environment should not be enabled in deployed
applications, as it can result in sensitive information from
exceptions being displayed to end users. For local debugging,
development environment can be enabled by setting the
ASPNETCORE_ENVIRONMENT environment variable to Development, and
restarting the application.
I'm not sure what i'm doing wrong with respect to configuring Auth0 and Identity. In addition, if you know how to address the error above when the launchSettings.json does have ASPNETCORE_ENVIRONMENT as Development, that would also be much appreciated.

Categories

Resources