ServiceStack Metadata Redirect behind a Azure App Gateway not working - servicestack

My api is hosted on Azure as an App Service with an Azure App Gateway in front of that.
I have set the webhosturl in my startup and that is working as when I view the metadata page, i see the links pointing to the correct location. And those links work. However when I navigate to the base url for my api, it redirects me to the app service url.
Here is a snippet of my startup...
SetConfig(new HostConfig
{
WebHostUrl = "https://api-dev.hsawaknow.net/link/",
DefaultRedirectPath = "/metadata",
DebugMode = AppSettings.Get(nameof(HostConfig.DebugMode), false)
});
Please see the links below and see the differences.
https://api-dev.hsawaknow.net/link/metadata
vs
https://api-dev.hsawaknow.net/link/
You will get an https error as I am using a self signed cert, until I get this figured out. I have seen other posts that say to make this change and that it works, but not for me.
Please help!

I have this figured out. There were a couple things that I had to do.
First thing I had to do was setup the forwarded headers middleware to recognize and process the correct headers coming from the Azure Application Gateway.
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHostHeaderName = "X-ORIGINAL-HOST";
options.ForwardedHeaders = ForwardedHeaders.XForwardedHost | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedFor;
});
This allowed my site to work with links to the correct pages without setting the WebHostUrl. The only caveat about using the Azure App Gateway is that it uses X-ORIGINAL-HOST instead of the standard X-FORWARDED-HOST.
Next, I had to set the DefaultRedirectPath on the HostConfig dynamically based on settings in appsettings.json. In the case of the Azure App Gateway, my public url was https://api-dev.hsawaknow.net/link/, I had to set the redirect to /link/metadata, instead of just metadata, because of how the host header was getting set in the previous step.
It took a few tries, but this configuration works well, when hosting on Azure App Services fronted with an Azure Application Gateway.
Kudos to the mythz for the quick response, which pointed me in the right direction.

Enable the Request Logger so you can see what requests ServiceStack receives.
Does it work when not specifying a WebHostUrl?

Related

Azure Application Gateway does not process request but just redirect

My issue
I installed an Azure Application Gateway (AAG) in front of an App Service.
Amethystegw and amethysteweb1 repectively. The AAG is on the VNET1.
amethysteweb1 is a real .NET application, not just the default IIS page.
When browsing from the AAG IP, say 20.223.179.174, it redirect on the app service url:
https://amethysteweb1.azurewebsites.net/
So if I set an access restriction on Amethystegw for VNET1 I get a 403:
NOTE: I also tried to set only my public AAG IP
If I activate WAF rules it does not work because everything seem not to pass through AAG.
What I need
What can I do to have a normal behaviour?
Why Backend Health shows 307 code:
Other infos
Yes I tested the app service that works fine.
Standard V2 Tier
Listener type: Basic
No custom domain
HTTP (80) port
Rules:
Settings:
probe
I successefully tested it.
I read this that is quite similar to my issue:
Azure App Service behind Azure Application Gateway
You need to handle the redirect substitution in the application, at least for .net 5 or 6 we have done it like this in the Startup. That configuration value contains the desired redirect, something like "https://{your url from app gateway}/signin-oidc"
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme,
options => {
Task RedirectToIdentityProvider(RedirectContext ctx) {
var redirectUri = Configuration.GetValue<string>("AzureAdB2C:RedirectUri");
if (!string.IsNullOrWhiteSpace(redirectUri)) {
ctx.ProtocolMessage.RedirectUri = redirectUri;
}
return Task.FromResult(0);
}
var previousEvent = options.Events.OnRedirectToIdentityProvider;
options.Events.OnRedirectToIdentityProvider = (context) => { previousEvent(context); return RedirectToIdentityProvider(context); };
});
I found the solutions.
The web apps was a .NET application that forced an HTTP to HTTPS redirection.
I just removed:
app.UseHttpsRedirection();
And it is working now.
Thank you for all those helped me here.

Best practices for using Azure CDN in scenarios that require authentication to access?

I've never configured a CDN before and I'm looking for guidance for how to approach the configuration for my scenario.
For context, I'm currently using Azure Verizon Premium CDN and an Azure App service (.Net Core). My authentication platform is Azure Active Directory.
I have a static web app that has lots of static content (hundreds of files). Users hit the website at (www.mysite.azurewebsites.net/index.html). Index.html then fetches large amounts of static content (various, images, video, metadata). Initially, the requirement was to just serve the content publicly but now I have requirements around restricting access to some the content based on whether or not a user has a certain role & the user hits a certain path. I also need to be able to keep some content public and highly available to end users.
Before authorization requirements came in, I was able to successfully set up a CDN endpoint (www.mysite-cdn.azureedge.net) and point it to my app service no problem! I can hit the site and see the site content from the cdn endpoint without auth no issue.
Problems came when I added authentication to my site my CDN is redirected during the authentication flow back to the origin endpoint. The origin authentication middleware (Nuget: Microsoft.Identity.Web) automatically assembles the callback uri for AAD as "www.mysite.azurewebsites.net/signin-oidc". So the present flow for a user hitting the CDN endpoint returns them to an error page on the app service endpoint. Although if they manually redirect to "www.mysite.azurewebsites.net" they are logged in, I don't want to redirect the user back to origin, I want to keep them on www.mysite-cdn.azurewebsites.net.
Essentially, I want to be able to enable these flows:
Public End User -> CDN Endpoint + Public path -> (CDN request origin and caches) -> End user sees site at CDN endpoint
Internal End User -> CDN Endpoint + Private path -> (CDN request origin and has access) -> User is permitted into site at CDN endpoint
Internal End User -> CDN Endpoint + Private path -> (CDN request origin and DOESN’T have access) -> User is redirected to public CDN endpoint (or unauthorized page)
This is the auth check for each static file in OnPrepareResponse for static file options. This checks authentication before requesting a static asset in this folder on the server. This works fine without the CDN. It should be noted that I also do role checks and this has been simplified for the sake of the example as it repos with Authentication.
OnPrepareResponse = staticContext =>
{
// require authentication
if (authenticate &&
!staticContext.Context.User.Identity.IsAuthenticated)
{
// Redirect back to index sign in page all unauthorized requests
staticContext.Context.Response.Redirect(unauthenticatedRedirectPath);
}
},
I did find this stack overflow which seemed similar to my problem however I am using a different NuGet package (Microsoft.Identity.Web). I implemented a check to redirect however that did not seem to work and cause an infinite loop when trying to login.
Action<MicrosoftIdentityOptions> _configureMsOptions = (options) =>
{
options.Instance = Configuration["AzureAd:Instance"];
options.Domain = Configuration["AzureAd:Domain"];
options.ClientId = Configuration["AzureAd:ClientId"];
options.TenantId = Configuration["AzureAd:TenantId"];
options.CallbackPath = Configuration["AzureAd:CallbackPath"];
options.Events.OnRedirectToIdentityProvider = async (context) =>
{
// This check doesn’t work because the request host always is mysite.azurewebsites.net in this context
// if (context.Request.Host.Value.Contains("mysite-cdn"))
// {
context.ProtocolMessage.RedirectUri = "https://" + "mysite-cdn-dev.azureedge.net/" + Configuration["AzureAd:CallbackPath"];
//}
};
};
I've started looking into Azure Front door, as that seems to be more applicable to my use case but haven't set it up. It provides some level of caching/POP as well as security. It looks like it's also possible to use with Azure AD with some web server tweaking. It would be good to know from others if Azure Front Door sounds like a more sensible CDN solution approach vs Azure CDN.
I've also looked into Azure CDN Token authentication- but that seems to be something that also requires me to stamp each request with an Auth token. It changes my architecture such that I can no longer just 'point' my cdn at the web app and instead my app would give the browser a token so it could securely request each asset.
All that said, I'm looking for guidance on how best to configure an application using a CDN while also using authentication. Thanks!

How to fix warning: "Application evaluated unhealthy due to redirection." on Azure Health Check Feature

I ran "Diagnose and solve problems" on Azure dashboard inside one of the app services, and then I got this critical risk alert: "Application evaluated unhealthy due to redirection.".
Recommended actions is:
If the application has HTTP to HTTPS redirectoin, consider one of the following solutions.
a. Enable 'HTTPs Only' from the TLS/SSL settings blade for the respective App Service. This will force health check pings to over 443.
b. Modify the Application logic/ad a URL Rewrite rule so that requests from the user agents – ReadyForRequest/1.0+(HealthCheck) & HealthCheck/1.0 are not redirected.
I already enable 'HTTPs Only' as suggested on point (a), but I don't know how to do point (b). How to modify the Application logic/ad a URL Rewrite rule so that requests from the user agents – ReadyForRequest/1.0+(HealthCheck) & HealthCheck/1.0 are not redirected ?
Currently, I enable Health Check and set the Health Check path to /.
Thanks before for any help.
I download offical sample, and make some changes in code, hope it useful to you.
Sample Code --- .net core 3.X(HealthChecksSample)
Method 1.
Because the code is in .net core, I first recommend configuring rewrite in Startup.cs.
public void Configure(IApplicationBuilder app)
{
app.UseHealthChecks("/");
app.UseRouting();
app.MapWhen(
ctx => ctx.Request.Path.StartsWithSegments("/"),
appBuilder =>
{
appBuilder.UseMiddleware<HealthCheckMiddleware>();
}
);
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/");
endpoints.MapGet("/{**path}", async context =>
{
await context.Response.WriteAsync(
"Navigate to /health to see the health status.");
});
});
}
It works for me, both in local and azure web app.
I will add Method 2 in your another post.

Getting an infinite redirect loop on Azure App Service with ADB2C and a shared Auth Cookie

tl;dnr
In essence, my app was working fine until implementing a shared cookie. Now I have an infinite login redirect loop. I've spent hours mucking about with cookie configurations, testing locally, deploying to Azure, and failing. How can I force HTTPS inside an Azure App Service?
Like many others, I've run into the infinite login loop issue. My application was working fine with ASP.net Core 2.2 (on Dot Net 4.7.1) and then I moved to a "shared Auth cookie". In development, everything works fine from localhost, but as soon as I publish to an Azure App service (.azurewebsites.net domain) , I get the infinite login redirect loop.
To start things off, using an Azure Key Vault, I set up the "Data Protection" services following these instructions link.
and set my shared cookie as such for all applications:
services.AddDataProtection()
.SetApplicationName("custom-app")
.PersistKeysToAzureBlobStorage(cloudStorageAccount, azureBlobKeyStoragePath)
.ProtectKeysWithAzureKeyVault(keyVault, clientId, clientSecret);
var authCookie = new CookieBuilder() {
Name = ".AspNetCore.Auth.SharedCookie",
Path = "/",
Domain = ".mycustomdomain.com",
SecurePolicy = CookieSecurePolicy.SameAsRequest, // tried None as well without luck
SameSite = SameSiteMode.None, // I've tried Lax without any changes
HttpOnly = false,
IsEssential = true //changing this makes no difference
};
services
.AddAuthentication(sharedOptions => {
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultAuthenticateScheme = OpenIdConnectDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAdB2C(options => configuration.Bind("AzureAdB2C", options))
.AddCookie(options => { options.Cookie = authCookie; });
...
var corsOrigins = new[] {
"http://localhost",
"https://localhost",
"http://*.mycustomdomain.com",
"https://*.mycustomdomain.com",
"http://*.azurewebsites.net",
"https://*.azurewebsites.net",
"https://*.onmicrosoft.com",
"https://login.microsoftonline.com"
};
app.UseCors(cors => {
cors.WithOrigins(corsOrigins)
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowCredentials();
cors.AllowAnyHeader();
cors.AllowAnyMethod();
});
Other than configuring the "Data Protection" services and additional CORS domains, no other changes were made to the application code.
My application is configured for HTTPS ... https://learn.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-2.2
...
app.UseHsts();
...
app.UseHttpsRedirection();
...
And I also verified that my Azure App Service is set for HTTPS access on both the "Custom domains" and "SSL settings" options.
Based on all the posts I have come across, I understand that the main issue may have to do with HTTPS redirecting from the Azure ADB2C endpoints and the shared cookie not being stored/read properly. THe reverse proxy drops the HTTPS and only calls HTTP. I just can't seem to figure out which combination should work. I am using the OutofProcess host, and I can see that the internal calls are HTTP. How can I get these to be HTTPS?
Side Note: I tried changing the Correlation or Nonce cookie domains as well, but this results in Correlation Errors.
TIA, any direction pointing would be appreciated.
Short list of posts I've referenced:
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/azure-ad-b2c?view=aspnetcore-2.2
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-2.2
How can I share Cookie Authentication across apps in Azure with .Net Core?
Share Cookie authentication between ASP.NET Core 2.2 and ASP. NET MVC 5 (.NET Framework 4.6.1) without Microsoft.Identity
Azure AD login inifinite loop
AspNet Core Identity - cookie not getting set in production
.net core 2.0 cookie authentication getting stuck in infinite redirect loop when trying to access over https
ASP.NET Core 2.1 cookie authentication appears to have server affinity
Configure cors to allow all subdomains using ASP.NET Core (Asp.net 5, MVC6, VNext)
https://github.com/aspnet/Security/issues/219
https://github.com/aspnet/Security/issues/1231
https://github.com/aspnet/Security/issues/1583
https://blogs.msdn.microsoft.com/benjaminperkins/2017/11/30/how-to-make-an-azure-app-service-https-only/
After some time off studying my configuration and creating a test app from scratch ... I came across the following post:
ASP.NET Core Sharing Identity Cookie across azure web apps on default domain (*.azurewebsites.net)
My current assumption is that this is indeed the culprit, I will try a new test tonight with one of my custom domains and see what happens.

Not able to publish Azure Mobile App properly

I have implemented Azure Mobile App and Xamarin.Forms Client application. I want user to login using facebook from Phone and also want to fetch user's profile data. For this I have implemented the additional call/method into API controller in Azure Mobile App. I have followed steps and put the code as per your article but somehow get following error message when I run the Mobile App on localhost or trying to publish
Multiple types were found that match the controller named 'Home'. This can happen if the route that services this request ('') found multiple controllers defined with the same name but differing namespaces, which is not supported. The request for 'Home' has found the following matching controllers:
Microsoft.Azure.Mobile.Server.Controllers.HomeController Microsoft.WindowsAzure.Mobile.Service.Controllers.HomeController
I understand this is related config settings. I have following code in place
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.UseDefaultConfiguration()
.ApplyTo(config);
app.UseWebApi(config);
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = config.GetAppServiceTokenHandler()
});
If I remove the default configuration from above then exception message go away but in that case I don't see the app getting hosted properly i.e. it is showing blank page in browser instead of ready page shown once app is hosted properly.
What steps you followed, could you please show the link?
That exception is routing-related and very common, and can be fixed - for example, by use of areas. A lot of manuals are available, for example, here - http://blog.falafel.com/duplicate-controller-names-aspnet-mvc-areas/ .
You have added two different SDKs - one for Azure Mobile Services v1 and the other for Azure App Service Mobile Apps (which can be considered v2). You need to remove the reference to the older one.
Use the appropriate SDK for the service you are using, and delete the other one.

Resources