Why is localhost next js GetStaticProps returning 500 error against localhost C# ASP.NET Core API route - c#

I have a C# / ASP.NET Core Web API project running on https://localhost:7001 and a next js app running on http://localhost:3000.
I can run the C# API from swagger and directly in the browser (https://localhost:7001/api/SourceSystems), but when I try to call it from the next js page using GetStaticProps, I get a 500 error.
Next.js code:
export default function Sourcesystem({systems}) {
return (
<ul>
{systems.map((system) => (
<li key={system.systemName}>{system.systemName} </li>
))}
</ul>
)
};
// This function gets called at build time
export async function getStaticProps() {
// Call an external API endpoint to get posts
const res = await fetch(
'https://localhost:7001/api/Schedules',
{
method:'GET',
}
)
const systems = await res.json()
console.log(systems);
return {
props: {
systems
},
}
}
I have added CORS to the c# code (I think) in
program.cs
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins(
"http://example.com",
"http://www.contoso.com",
"http://localhost:3000"
);
});
});
// services.AddResponseCaching();
builder.Services.AddControllers();
// add dbContext
builder.Services.AddDbContext<GdqcDevContext>(options => options.UseSqlServer("Data Source = RAZERPRO17; Initial Catalog = GDQC_dev; Integrated Security = True; Connect Timeout = 30; Encrypt = False; TrustServerCertificate = False; ApplicationIntent = ReadWrite; MultiSubnetFailover = False"));
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthorization();
app.MapControllers();
app.Run();
Next is saying the the fetch failed:
I am suspecting something still not set correctly for CORS but I have copied the CORS configuration and the middleware assignment order from documentation and other stackoverflow answers. NOTE: I am running the c# API code using the debug browser rather than directly from IIS. I have read somewhere about the OPTIONS but this is only for the full blown IIS
I have also added a CORS guard annotation to the c# controller with no success
namespace Overwatch_API.Controllers
{
[EnableCors("MyAllowSpecificOrigins")]
[Route("api/[controller]")]
[ApiController]
public class SourceSystemsController : ControllerBase
{
private readonly GdqcDevContext _context;
public SourceSystemsController(GdqcDevContext context)
{
_context = context;
}
// GET: api/SourceSystems
[HttpGet]
public async Task<ActionResult<IEnumerable<SourceSystem>>> GetSourceSystems()
...
UPDATE: It looks like Next is returning the following error message in the logging:
cause: Error: self-signed certificate
at TLSSocket.onConnectSecure (node:_tls_wrap:1538:34)
at TLSSocket.emit (node:events:513:28)
at TLSSocket._finishInit (node:_tls_wrap:952:8)
at ssl.onhandshakedone (node:_tls_wrap:733:12) {
code: 'DEPTH_ZERO_SELF_SIGNED_CERT'
I presume this is related to the SSL cert on the .net core 6 api code, as this is being called with https. How do I get next to accept a self signed cert, or build a propertly signed cert for the dev environment

This issue is common in nextjs.
I found two ways to fixed it.
1. If the issue occurs in dev environment. we can use NODE_TLS_REJECT_UNAUTHORIZED=0 to solve it.
① Overcome the DEPTH_ZERO_SELF_SIGNED_CERT on Node.js
② Self Signed SSL Certificate in NextJS
2. We also can use NODE_EXTRA_CA_CERTS to solve it. It can be used in Dev or Prod Environment.
Node TLS socket : DEPTH_ZERO_SELF_SIGNED_CERT error

Related

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.

Blazor cannot connect to ASP.NET Core WebApi (CORS)

I have a ASP.NET Core Server running on local IP https://192.168.188.31:44302 with Web API Enpoints.
I can connect to said server with VS Code REST Client.
Now I want to conenct to the Web API with Blazor WebAssembly running on https://192.168.188.31:5555.
My Blozor Code:
#page "/login"
#inject HttpClient Http
[ ... some "HTML"-Code ... ]
#code {
private async Task Authenticate()
{
var loginModel = new LoginModel
{
Mail = "some#mail.com",
Password = "s3cr3T"
};
var requestMessage = new HttpRequestMessage()
{
Method = new HttpMethod("POST"),
RequestUri = ClientB.Classes.Uris.AuthenticateUser(),
Content =
JsonContent.Create(loginModel)
};
var response = await Http.SendAsync(requestMessage);
var responseStatusCode = response.StatusCode;
var responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine("responseBody: " + responseBody);
}
public async void LoginSubmit(EditContext editContext)
{
await Authenticate();
Console.WriteLine("Debug: Valid Submit");
}
}
When I now trigger LoginSubmit I get the following error-message in the developer console of Chrome and Firefox: login:1 Access to fetch at 'https://192.168.188.31:44302/user/authenticate' from origin 'https://192.168.188.31:5555' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
I'm new to web development and found that you have to enable CORS on the server-side ASP.NET Core project, so I extended startup.cs with
readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<UserDataContext, UserSqliteDataContext>();
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
builder.WithOrigins("https://192.168.188.31:44302",
"https://192.168.188.31:5555",
"https://localhost:44302",
"https://localhost:5555")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddControllers();
services.AddApiVersioning(x =>
{
...
});
services.AddAuthentication(x =>
...
});
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddScoped<IViewerService, ViewerService>();
}
public void Configure(IApplicationBuilder app,
IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
Program.IsDevelopment = env.IsDevelopment();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCors(MyAllowSpecificOrigins);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
Log.Initialize();
}
But I still get above error message.
Am I doing something wrong with configuring CORS?
Why is it working as expected with the VS Code REST Client and how am I making the call wrong in the Blazor WASM application?
The issue causing the error message login:1 Access to fetch at 'https://192.168.188.31:44302/user/authenticate' from origin 'https://192.168.188.31:5555' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. was caused by HttpsRedirection.
To resolve the issue, either deactivate HttpsRedirection by removing the line app.UseHttpsRedirection(); in function Configure or add the proper ports for redirection in function ConfigureServices (recommended way).
In my case, I start my WebAPI at port 44302, so my solution looks like this (you have to adapt it to your port number):
if (Program.IsDevelopment)
{
services.AddHttpsRedirection(options =>
{
options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect;
options.HttpsPort = 44302;
});
}
else
{
services.AddHttpsRedirection(options =>
{
options.RedirectStatusCode = StatusCodes.Status308PermanentRedirect;
options.HttpsPort = 443;
});
}
Also note that it is sufficient to add the IP address of the requesting API to CORS like this:
services.AddCors(options =>
{
options.AddPolicy(name: specificOrigins,
builder =>
{
builder.WithOrigins("https://192.168.188.31:5555",
"http://192.168.188.31:5444")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
Step 1: Please add following code in your WebAPI's Startup.cs to allow CORS with specific origins:
services.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
builder.WithOrigins("https://localhost:44351")
.AllowAnyHeader()
.AllowAnyMethod());
});
Step 2: Now change "https://localhost:44351" in above code with your blazor web assembly application's URL. Refer below screen shot:
Step 3: Now add app.UseCors() in your WebAPI's Configure method after app.UseRouting() and before app.UseRouting(). Please refer below screen shot:
I was also facing same issue and it solved my problem. Hope it will also work for you.
Note: No changes required in Blazor web assembly code to fix the above issue.

.NET Core 2 WebAPI CORS problems with Angular 4 front end and Windows Auth

Yes I have countless articles and re-arranged add app.UserCors and other pieces of advice but am still stuck.
Using Visual Studio WebAPI Core 2 with Angular 2 web front end.
Using Windows authentication on remote server using IIS 8.5 (actually this doesn't work locally either).
WebAPI startup conf:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SomeContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SomeDB")));
//////// ********************
//////// Setup CORS
//////// ********************
var corsBuilder = new CorsPolicyBuilder();
corsBuilder.AllowAnyHeader();
corsBuilder.AllowAnyMethod();
corsBuilder.WithOrigins("*");
corsBuilder.AllowCredentials();
services.AddCors(options =>
{
options.AddPolicy("AllowAll", corsBuilder.Build());
});
services.AddMvc();
...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("AllowAll"); //<===
app.UseMvc();
...
When I run the WebAPI locally, or deploy it to a remote server, My Angular 4 front end gives me:
For a call to a WebAPI controller - 401 (Unauthorized) (even on controllers that have no [Authorize()] set.
When I try to use my local instance of the Angular app to access the remote WebAPI:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:62482' is therefore not allowed access. The response had HTTP status code 401.
I have tried re-arranged the order of adding services in startup.
I have manually added the respone headers in IIS8.5 (they get overwritten whenever I deploy new code).
I have remove all the CORS stuff completely from the WebAPI app.
I SWEAR this all worked at one time. Not sure what changed.
I all runs fine when I deploy both the WebAPI Core 2 app and the Angular web app to the remote server because origins are the same, but that makes local debugging impossible.
*** Angular REPO code per request:
import { Injectable, Inject } from '#angular/core';
import { Http } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { DocumentModel } from '../models/document.model';
import 'rxjs/add/observable/from';
import 'rxjs/add/operator/map';
import { map } from 'rxjs/operators/map';
import { IRepository } from './IRepository';
declare var API_URL: string;
#Injectable()
export class DocumentRepository implements IRepository<DocumentModel, number> {
GetByDocument(key: any): Observable<DocumentModel[]> {
throw new Error("Method not implemented.");
}
private httpService: Http;
private apiRootUrl: string;
constructor(http: Http) {
this.httpService = http;
this.apiRootUrl = API_URL;
}
Create(Model: DocumentModel): Observable<any> {
throw new Error("Method not implemented.");
}
Find(key: any): Observable<DocumentModel[]> {
return this.httpService.get(this.apiRootUrl +'documents/mine').map(result => result.json());
}
Get(id: any): Observable<DocumentModel> {
return this.httpService.get(this.apiRootUrl +'documents/' + id).map(result => result.json());
}
}
This works for me :
In Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
//app.UseHttpsRedirection();
app.UseCors(
options => options.AllowAnyOrigin().AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
);
app.UseMvc();
}
and that all, no other mention to Cors.
I use JWT Bearer Token and [Authorize] works as intended.
You can try with Postman first, just to see if your endpoint is working, then move to make an Angular http.get()
This is the angular part
const headers = new HttpHeaders().
set('Accept', 'application/json')
const url = 'http://localhost:5000/api/'
return this.http.get(
url + 'endpoint',
{ headers: headers }
);

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.

Is middleware neeeded to redirect to HTTPS in ASP.net and C#?

What is the recommend way to redirect to HTTPS all incoming requests that are not secure. Do I need to write a middleware component? If so, I couldn't figure out how to get the server name.
public class RedirectHttpMiddleware
{
RequestDelegate _next;
public RedirectHttpMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.IsSecure)
await _next(context);
else
{
var server = ""; // How do I get the server name?
context.Response.Redirect("https://" + server + context.Request.Path);
}
}
}
You can use your own middleware class, but typically I just do something like this in my Startup configuration:
app.Use(async (context, next) =>
{
if (context.Request.IsHttps)
{
await next();
}
else
{
var withHttps = Uri.UriSchemeHttps + Uri.SchemeDelimiter + context.Request.Uri.GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Scheme, UriFormat.SafeUnescaped);
context.Response.Redirect(withHttps);
}
});
What this does is just grab the entire URL, query string and all, and use GetComponents to get everything except the scheme in the URL. Then the HTTPS scheme gets prepended to the components URL.
This will work with the full .NET Framework, for ASP.NET Core, you can do something like this:
app.Use(async (context, next) =>
{
if (context.Request.IsHttps)
{
await next();
}
else
{
var withHttps = "https://" + context.Request.Host + context.Request.Path;
context.Response.Redirect(withHttps);
}
});
This appends the host and the path to the HTTPS scheme. You may want to add other components such as the query and hash, too.
For .NET Core 2.0 and lower (official docs for 2.0):
Use the [RequireHttps] attribute/filter.
You can either do this to your controllers:
[RequireHttps]
public class AccountController {
}
Or else add this in your Startup.cs in the ConfigureServices method:
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
}
Also, I just wanted to add that vcsjones' answer is also correct, but you need to be sure to add this code early on in your config, before any other Middleware/code that causes redirects. In my case, I added it just before I added the Identity Framework middleware.
The full answer is in number 1 but don't stop there setting up HTTPS, go the extra step:
1 - We then use the RequireHttpsAttribute to redirect to HTTPS and set the SSL port in the MVC options. We are also reading the SSL port from launchSettings.json but we only need this in Development mode.
2 - Use AddAntiforgery to require HTTPS on your anti-forgery tokens.
3 - Use the NWebsec.AspNetCore.Middleware NuGet package and UseHsts method to enable Strict Transport Security (HSTS) across the site. Don't forget to add the Preload below and submit your site to the HSTS Preload site. More information here and here.
4 - Use NWebsec.AspNetCore.Middleware NuGet package and the UseHpkp method to enable Public Key Pinning (HPKP) across the site. Note that if you make a mistake with this one you are essentially DoS'ing your site. More information here and here.
5 - Include the https scheme in any URL's used. Content Security Policy (CSP) HTTP header and Subresource Integrity (SRI) do not play nice when you imit the scheme in some browsers. It is better to be explicit about HTTPS. e.g.
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.4/bootstrap.min.js"></script>
6- Use the ASP.NET MVC Boilerplate Visual Studio project template to generate a project with all of this and much more built in. You can also view the code on GitHub.
After all of the above, your Startup class should look something like this:
public class Startup
{
private readonly int? sslPort;
public Startup(IHostingEnvironment hostingEnvironment)
{
if (hostingEnvironment.IsDevelopment())
{
var launchConfiguration = new ConfigurationBuilder()
.SetBasePath(hostingEnvironment.ContentRootPath)
.AddJsonFile(#"Properties\launchSettings.json")
.Build();
// During development we won't be using port 443.
this.sslPort = launchConfiguration.GetValue<int>("iisSettings:iisExpress:sslPort");
}
}
public void ConfigureServices(IServiceCollection services)
{
services
.AddAntiforgery(options =>
{
options.RequireSsl = true;
});
.AddMvc(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
options.SslPort = sslPort;
});
}
public void Configure(IApplicationBuilder application)
{
application
.UseHsts(options => options.MaxAge(days: 18 * 7).IncludeSubdomains().Preload())
.UseHpkp(options => options
.Sha256Pins(
"Base64 encoded SHA-256 hash of your first certificate e.g. cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=",
"Base64 encoded SHA-256 hash of your second backup certificate e.g. M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=")
.MaxAge(days: 18 * 7)
.IncludeSubdomains())
.UseCsp(options => options
.UpgradeInsecureRequests(this.sslPort.HasValue ? this.sslPort.Value : 443))
.UseMvc();
}
}
If you want to grab the port when in a DEV enviroment in .NET Core, look at env.IsDevelopment() and conditionally grab the SSL port from launchSettings.json.
if (env.IsDevelopment())
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile(#"Properties/launchSettings.json", optional: false, reloadOnChange: true);
var launchConfig = builder.Build();
sslPort = launchConfig.GetValue<int>("iisSettings:iisExpress:sslPort");
}
`
After grabing the SSL port, then you can incorporate the port into the solution posted by #vcsjones.
AlwaysHttpsMiddleware.cs, inspired by RequiresHttpsAttribute.
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
public class AlwaysHttpsMiddleware
{
private readonly RequestDelegate _next;
public AlwaysHttpsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.IsHttps)
{
await _next.Invoke(context);
}
else
{
var request = context.Request;
// only redirect for GET requests, otherwise the browser might
// not propagate the verb and request body correctly.
if (!string.Equals(request.Method, "GET", StringComparison.OrdinalIgnoreCase))
{
context.Response.StatusCode = StatusCodes.Status403Forbidden;
await context.Response.WriteAsync("This site requires HTTPS.");
}
else
{
var newUrl = string.Concat(
"https://",
request.Host.ToUriComponent(),
request.PathBase.ToUriComponent(),
request.Path.ToUriComponent(),
request.QueryString.ToUriComponent());
context.Response.Redirect(newUrl);
}
}
}
}
Startup.cs
public void Configure(IApplicationBuilder app)
{
if (_env.IsProduction())
{
app.UseMiddleware<AlwaysHttpsMiddleware>();
}
}
I have modified a bit #vcsjones answer to use custom port in dev environment. Also credits to #long2know.
app.Use(async (context, next) =>
{
var request = context.Request;
if (request.IsHttps)
{
await next();
}
else
{
var devPort = Configuration.GetValue<int>("iisSettings:iisExpress:sslPort");
var host = env.IsDevelopment() && devPort > 0
? new HostString(request.Host.Host, devPort)
: new HostString(request.Host.Host);
string newUrl = $"https://{host}{request.PathBase}{request.Path}{request.QueryString}";
context.Response.Redirect(newUrl, true);
}
});
This must appear before app.UseStaticFiles or app.UseMvc, otherwise it will be ignored.
Note that the port should be taken from launchSettings.json file so you should also add this file to ConfigurationBuilder in the Startup.cs:
.AddJsonFile(#"Properties/launchSettings.json", optional: false, reloadOnChange: true)
In ASP.NET Core 2.1 just use this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts(); // <-- Add this !!!!!
}
app.UseHttpsRedirection(); // <-- Add this !!!!!
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvc();
}
There are some great answers here, but I needed a solution that would work with or without IIS and also not change the protocol during local debug. I added this just after adding AD auth to the pipeline in the Startup.Configure method. This is for the full framework. Other solutions here outline how to rebuild the URL for Core.
app.Use(async (context, next) =>
{
if (context.Request.IsHttps || // Handles https straight to the server
context.Request.Headers["X-Forwarded-Proto"] == Uri.UriSchemeHttps || // Handles an IIS or Azure passthrough
context.Request.Host.ToString().StartsWith("localhost",true, System.Globalization.CultureInfo.InvariantCulture) || // Ignore for localhost
context.Request.Headers["X-Forwarded-Proto"].Contains( Uri.UriSchemeHttps )) // X-Forwarded-Proto can have multiple values if there are multiple proxies
{
await next();
}
else
{
var withHttps = Uri.UriSchemeHttps + Uri.SchemeDelimiter + context.Request.Host + context.Request.Path + context.Request.QueryString;
context.Response.Redirect(withHttps);
}
});
In order to have your DOTNet Core application to run under HTTPS, there are three steps to follow:
Navigate to your Application's launchSettings.json file and enter your desired https port from 44390-44399
Edit your Startup.cs file. Enter the following code:
services.Configure<MvcOptions>(options =>
{
options.SslPort = 44390;
options.Filters.Add(new RequireHttpsAttribute());
});
Right-Click on your Project Root Directory, from Solution Explorer and Select Properties. Check Enable SSL, Copy the SSL link and add it to the App URL area.
Launch the Application. It will always run under the HTTPS context.
One technique discussed at https://github.com/aspnet/KestrelHttpServer/issues/916 is adding this to your web.config:
<rewrite>
<rules>
<rule name="HTTP/S to HTTPS Redirect" enabled="true" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAny">
<add input="{SERVER_PORT_SECURE}" pattern="^0$" />
</conditions>
<action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
</rule>
</rules>
</rewrite>
If you need permanent redirect from http:// to https:// at .net core project, just add this code to Startup.cs
app.UseHttpsRedirection();
More info here https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-6.0&tabs=visual-studio
I like attribute with compiler directive
#if !DEBUG
[RequireHttps]
#endif
public class HomeController : Controller
{
}

Categories

Resources