Angular 11, HMR, wrong schema set, when used behind a asp.net core app with UseSpa - asp.net-core

I have an asp.net core app that uses:
app.UseSpa(x =>
{
if (env.IsDevelopment())
{
x.Options.SourcePath = "ClientApp";
x.UseProxyToSpaDevelopmentServer("http://localhost:4200");
}
else
{
// x.Options.SourcePath
x.Options.DefaultPageStaticFileOptions = new StaticFileOptions
{
OnPrepareResponse = context =>
{
context.Context.Response.Headers.Add("Cache-Control", "no-cache, no-store");
context.Context.Response.Headers.Add("Expires", "-1");
}
};
}
});
angular is started with:
ng serve which then serves the site on http://localhost:4200
The --public-host does not seem to respect the schema.
The docs says: The URL that the browser client (or live-reload client, if enabled) should use to connect to the development server. Use for a complex dev server setup, such as one with reverse proxies.
My API is hosted in the asp.net core app.
The browser tries to connect to: https://localhost:4200/sockjs-node/info?t=1605229799939 ... which is not running. Notice the https, which is gets from the asp.net core app which is being served over https.
I can get it to work if I start the angular dev server with --ssl, trust the certicate in my certificate store in Windows, so .NET trusts that certificate. The issues is that there is no override for the UseSpa as I have found, to trust self signed certificates, when running in development.
I want the setup to be as clean as possible, with the least amount of hacks, like trusting self signed certs on local machine.
Is there any way to let the HMR/nodejs/angular-cli know what it needs to connect to?

Well ... setting --public-host to localhost:5000 solved the issue.
Then it's just going though the asp.net core app which acts as a proxy. Not sure why I didn't think of that before creating this issue.

Related

ASP.Net Identity Login Redirect Enforce Protocol (Https) Part 2 (.Net 6++)

Prior reference (.Net Framework/ASP.Net MVC): ASP.Net Identity Login Redirect Enforce Protocol (Https)
It seems this is still an "issue" in .Net 6+. There are cases where the return url constructed by the infrastructure results in an http scheme/protocol instead of https for oauth/external logins (Google, etc). This obviously fails because it must be https.
While I haven't gone deep into things, because I haven't found the source code for it (yet?), it's likely the same "issue" - at the app level, it doesn't "see" a https request (because SSL is offloaded somewhere) and therefore the url created "matches" the scheme/protocol, resulting in an http redirect url.
End of day, whatever hosting infrastrucutre/configuration my host has is in place is beyond my control. Therefore, the ultimate goal is to force https (hard code, skip/override whatever scheme/protocol check/eval in place).
There's nothing special in my setup and it's working fine in local/dev (https) testing. It's only when the application is finally hosted (production):
In startup program.cs this is the only related code I have for external login (along with the scaffolding/templates of the identity package):
builder.Services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<my_db_context>();
builder.Services.AddAuthentication().AddGoogle(goog =>
{
goog.ClientId = builder.Configuration["GoogleAuthClientId"];
goog.ClientSecret = builder.Configuration["GoogleAuthClientSecret"];
});
The issue:
the origin is https
the redirect uri sent to Google Auth is http - this will always fail
Can anyone point me to relevant docs/source on how to add/override options in .Net 6 and above? (similar to prior implementations in .Net Framework/MVC)?
The answer is in the comment by #Tratcher:
Official Ref: Configure ASP.NET Core to work with proxy servers and load balancers
Essentially: ForwardedHeadersMiddleware
For my specific case:
In some cases, it might not be possible to add forwarded headers to
the requests proxied to the app. If the proxy is enforcing that all
public external requests are HTTPS, the scheme can be manually set
before using any type of middleware:
...
app.Use((context, next) => {
context.Request.Scheme = "https";
return next(context); });
...

Microsoft.Identity.Web.UI works locally but not in App Service

I've been trying to add auth to my web app following: https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/1-WebApp-OIDC/1-1-MyOrg
If I run locally with appsettings.Development.json via dotnet run, I can log in using my organization credentials as expected. When I containerize and deploy to my Web App in Azure, I do not get logged in successfully. The url in the browser stays at /signin-oidc and goes to the Error page from the default Razor pages app.
The App Service logs have messages saying .AspNetCore.Correlation.OpenIdConnect.[key?] cookie not found
[Update] The auth flow works on my phone but not on desktop.
Why would the same code work locally but not deployed?
Why isn't the cookie found?
Why does it work on iOS but not Windows?
Try to remove app.UseCookiePolicy(); in startup class.
Take a look here
Or the problem was IdentityServer was still using AddDeveloperSigningCredential
You can add a certificate in your code it will work perfectly
Refer here for more info
tl;dr - Setting the same-site cookie options in the authentication settings fixed this.
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(options =>
{
Configuration.Bind("AzureAd", options);
options.NonceCookie.SameSite = SameSiteMode.Unspecified;
options.CorrelationCookie.SameSite = SameSiteMode.Unspecified;
});
This seems to be because the App Service enforces TLS-only and handles the TLS termination. Because of this, requests to the application are always HTTP even though the browser URL is HTTPS. This causes a number of problems, the first was that the redirect URL never matched since the Identity library uses the request scheme to form the redirect URL. I had "fixed" this by injecting a redirect URL rewrite:
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(options =>
{
Configuration.Bind("AzureAd", options);
options.Events ??= new OpenIdConnectEvents();
options.Events.OnRedirectToIdentityProvider += _fixRedirect;
});
...
private async Task _fixRedirect(RedirectContext context)
{
context.Request.Scheme = "https";
if(!context.ProtocolMessage.RedirectUri.StartsWith("https"))
context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http", "https");
await Task.CompletedTask;
}
However, the correlation cookie also seems to use the same HTTP request so that when the redirect comes back, the HTTPS doesn't match. Relaxing the SameSiteMode explicitly tells the browser(?) to allow the differing schemes. This is why my phone, with a different browser with different cookie policy, worked while desktop did not. Running locally using the dev-certs allowed the schemes to match since the app was terminating the TLS rather than the App Service.
I hooked into the CookiePolicyOptions.OnAppendCookie event to inspect the cookies while debugging to find this out.

How to republish an ASP.NET Core 5 web application in IIS. Changes not taking effect

I've published an ASP.NET Core 5 Web API to IIS (using VS 2019 webdeploy), and it is running fine. Now I've made some minor changes and republished.
The changes take affect on my local machine using IIS Express, but I do not see them when I publish to my IIS server. I can see that the exe and dlls are updated, but the webapi behaviour does change.
Something as simple as setting the swaggerdoc title doesn't take affect.
Is there something else I need to do to refresh the app? I've tried recycling the app pool and doing an iisreset, but no luck. I've read that IIS is merely acting as a reverse proxy to Kestrel which is actually hosting the api. If so how do I recycle that?
As a test, I delete all the files in my web api directory. I browse to the web api and get the following error:
HTTP Error 500.31 - Failed to load ASP.NET Core runtime Common
solutions to this issue: The specified version of
Microsoft.NetCore.App or Microsoft.AspNetCore.App was not found.
Troubleshooting steps:
Check the system event log for error messages
Enable logging the application process' stdout messages Attach a
debugger to the application process and inspect For more information
visit: https://go.microsoft.com/fwlink/?LinkID=2028526
I then re-publish the web api and browse to it, but it still has the old behavior. I'm really confused, because I know the published dll for my service is correct.
If I use ILSpy I can see the following code:
public void ConfigureServices(IServiceCollection services)
{
IConfigurationSection appSettingsSection = Configuration.GetSection("ApplicationSettings");
ApplicationSettings appSettings = appSettingsSection.Get<ApplicationSettings>();
services.AddSingleton(appSettingsSection.Get<ApplicationSettings>());
services.AddControllers()
.AddXmlSerializerFormatters()
.AddJsonOptions(delegate(JsonOptions o)
{
o.JsonSerializerOptions.PropertyNamingPolicy = null;
o.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
services.AddSwaggerGen(delegate(SwaggerGenOptions c)
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "My Demo Service (" + appSettings.Environment + ")",
Version = "v1"
});
});
}
When I browse to the swagger page of my API on my local dev box the title matches code, and the property naming is what I want. When I browse to it on the server, the title of the swagger page is the old value, and the property naming policy is camel case, so I'm pretty sure I'm publishing the new version of my web api, but it's not being executed when I make http requests to the api. Something must be cached somewhere.

UrlHelper returning http links on Azure App Service

I have a service that when deployed on Azure App Services returns http links instead of https links when using UrlHelper. When testing on my development machine it returns https links as expected, and the service is available and accessed through https requests.
An example of the type of route from my startup I'm trying to use is:
routes.MapRoute(
"FooBar",
"api/Foo/{Id}/Bar");
The link is then constructed using:
IUrlHelper _urlHelper = // Injected into class via service registration
int id = 42; // Arbitrary value for example
_urlHelper.Link("FooBar", new {Id = id});
When running on my local machine using Docker on Windows from Visual Studio I get a link of https://localhost:1234/api/Foo/42/Bar, but on my deployed Linux Container App Service on Azure I get http://my-app-name.azurewebsites.net/api/Foo/42/Bar.
I don't know what I'm doing wrong to get an http link instead of an https link, and would appreciate any advice/pointing in the right direction.
So I found the solution was with the configuration of the ASP.Net Core app itself. I performed the following modifications and then everything worked correctly:
Added app.UseForwardedHeaders(); to the request pipeline.
Added the following snippet to service container registration:
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
The KnownNetworks and KnownProxies need to be cleared as they default to assuming an IIS hosting environment. For extra security you can add the known proxy/network IPs instead of clearing them here.

XSRF-TOKEN not updated when using IISExpress and localhost

.Net Core api layer and .Net Core MVC w/ Angular2 front end. Locally, they are running in different website (localhost:xxx1 and localhost:xxx2) and published, the api is running in a sub directory of the frontend.
I've set up the .Net Core Antiforgery like so:
in the ConfigureServices section:
services.AddAntiforgery(options =>
{
options.HeaderName = "X-XSRF-TOKEN";
});
in the Configure section:
app.Use(next => context =>
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false });
return next(context);
});
When i initially launch the sites and browse to the website in Chrome, I do get the 2 Antiforgery tokens (.AspNetCore.Antiforgery.xxxx and the XSRF-TOKEN) and when I make a get/post/etc call I see the x-xsrf-token header is added to the call.
The problem is on each call, the api returns a new XSRF-TOKEN cookie but locally my cookie is not updated, it always contains the original value. When published online, this doesn't happen, the cookie updates every time.
I've tried setting the sites up locally to use localhost.somedomain.com but that didn't work.
Any suggestions as to what I'm doing wrong or how to get it working locally?
The issue on my local machine was that I was running each app in its own IIS Express (localhost:xxx1 and localhost:xxx2).
I ended up setting IIS up to host .net core following this article (https://learn.microsoft.com/en-us/aspnet/core/publishing/iis) and i'm getting the expected result.