Add custom headers to ViewEngine response pages in ServiceStack - servicestack

I am using ServiceStack with SharpPages to render dynamic content. For "reasons", I need to set the CORS headers Access-Control-Allow-Origin and Access-Control-Allow-Credentials, supporting multiple subdomains.
My SharpPages feature is enabled with :
var pagesFeature = new SharpPagesFeature()
{
ScriptMethods = { new UrlScriptMethods(), new DbScriptsAsync() },
};
pagesFeature.Args[ServiceStack.Script.ScriptConstants.DefaultDateFormat] = "MM/dd/yyyy hh:mm";
pagesFeature.Args[ServiceStack.Script.ScriptConstants.DefaultDateTimeFormat] = "MM/dd/yyyy hh:mm";
Plugins.Add(pagesFeature);
I'm hosting on IIS, so I could use web.config like below, but I can only specify one domain this way. If I specify multiple, XMLHttpRequest calls complain there are multiple domains set for that header.
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="https://subdomain.domain.com" />
</customHeaders>
</httpProtocol>
</system.webServer>
Likewise, I could have used the ServiceStack HostConfig property GlobalResponseHeaders, but same deal.
I've even tried ServiceStack PreRequestFilters, but those aren't called unless a service method is called. Here is my filter:
this.PreRequestFilters.Add((httpReq, httpResp) =>
{
var origin = httpReq.Headers.Get(HttpHeaders.Origin);
if (!string.IsNullOrWhiteSpace(origin))
{
httpResp.AddHeader(HttpHeaders.AllowOrigin, origin);
httpResp.AddHeader(HttpHeaders.AllowCredentials, "true");
}
});
Finally, StaticFileHandler.ResponseFilter won't work, since I'm using a view engine and not static files.
So, how can I add custom response headers to View Pages (SharpPages in particular, possibly Razor pages as well) in ServiceStack?
The raw request is below. Interesting that I'm requesting https://computer.domain but FireFox translates that to localhost. Regardless, the favicon.ico request DOES get trapped by the filter. The request below DOES NOT.
GET /forms/newsletter HTTP/1.1
Host: localhost:44308
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: ss-pid=wCR4INmjLXpBnbsBoe2n
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache
The raw response is :
HTTP/2.0 200 OK
cache-control: private
content-type: text/html
content-encoding: gzip
vary: Accept-Encoding
server: Microsoft-IIS/10.0
x-aspnet-version: 4.0.30319
x-sourcefiles: =?UTF-8?B?QzpcVXNlcnNcamtsZW1tYWNrXFNvdXJjZVxSZXBvc1xPQlJDX0JNU1xCTVMuV2ViLkJvdHRsZURyb3BDZW50ZXJzXEJNUy5XZWIuQm90dGxlRHJvcENlbnRlcnNcZm9ybXNcbmV3c2xldHRlcg==?=
x-powered-by: ASP.NET
access-control-allow-origin: *
date: Tue, 11 Jun 2019 16:28:34 GMT
content-length: 862
X-Firefox-Spdy: h2

The PreRequestFilters should now be fired for all Razor and Sharp Pages requests from the latest v5.5.1+ that's now available on MyGet.

In the meantime, I've created a portmanteau. For a particular sub-folder, I'm using web.config to allow all remote origins. For Service-based calls, I'm using a hand-rolled version of CorsFeature (a small amount of custom logic won't work).
<location path="views/subfolder"> <-- applies the ACAO header for specific view pages
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>

Related

Issue with Azure AD B2C oauth/OpenIdConnect

I have an odd issue with azure authentication. It works in one scenario (logging in with an angular app using adal) but not in an other (loading the asp.net web api up and logging in via the swagger ui authorise mechanism). I have narrowed it down to a post to the:
https://login.microsoftonline.com/te/{tenantid}/oauth2/authresp
endpoint which rather unhelpfully returns:
302
<html>
<head>
<title>Object moved</title></head>
<body>
<h2>Object moved to here.</h2>
</body>
</html>
Basically telling me an exception has occurred.
error_description = AADB2C An exception has occured.
CorrelationID = 1816d2f8-aa74-4433-a7c0-d9c8fabebdb0
Timestamp = 2017-10-27 13:46:08
The angular app, does exactly the same post but that however returns a link with the token id:
302
<html>
<head>
<title>Object moved</title>
</head>
<body>
<h2>Object moved to <a href="http://localhost:4200/#state={state -
ommitted}&id_token={id token - omitted}">here</a>.
</h2>
</body>
</html>
I can't see any differences in the posts. This one does not work (initiated from swagger ui on web api project)
POST https://login.microsoftonline.com/te/{tenant - omitted}/oauth2/authresp HTTP/1.1
Host: login.microsoftonline.com
Connection: keep-alive
Content-Length: 1595
Cache-Control: max-age=0
Origin: https://login.microsoftonline.com
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Referer: https://login.microsoftonline.com/{tenant - omitted}/oauth2/authorize?client_id={clientid - omitted}&redirect_uri=https:%2f%2flogin.microsoftonline.com%2fte%2f{tenant - omitted}%2foauth2%2fauthresp&response_type=id_token&response_mode=form_post&nonce={nonce}&state=StateProperties
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en;q=0.9,de-CH;q=0.8,de;q=0.7,fr-CH;q=0.6,fr;q=0.5,it-CH;q=0.4,it;q=0.3
Cookie: {cookie - omitted}=; x-ms-gateway-slice=005; stsservicecookie=cpim_te
id_token={omitted}
But this post does:
POST https://login.microsoftonline.com/te/{tenant - omitted}/oauth2/authresp HTTP/1.1
Host: login.microsoftonline.com
Connection: keep-alive
Content-Length: 1590
Cache-Control: max-age=0
Origin: https://login.microsoftonline.com
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
DNT: 1
Referer: https://login.microsoftonline.com/{tenant - omitted}/oauth2/authorize?client_id={clientid - omitted}&redirect_uri=https:%2f%2flogin.microsoftonline.com%2fte%2f{tenant - omitted}%2foauth2%2fauthresp&response_type=id_token&response_mode=form_post&nonce={nonce}&state=StateProperties
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en;q=0.9,de-CH;q=0.8,de;q=0.7,fr-CH;q=0.6,fr;q=0.5,it-CH;q=0.4,it;q=0.3
Cookie: {cookie - omitted}; x-ms-gateway-slice=003; stsservicecookie=cpim_te
id_token={omitted}
As the posts are pretty much identical the issue must lie somewhere in either the cookie or the id token (which I cannot supply for security reasons). Pasting the ID tokens from both requests into http://www.jwt.io gives me same information with the correct claims present in both. So I am a little stuck and hoping someone from the azure team can step in and help?
Many thanks
Based on the referrer in your request samples, it looks like you're not calling /v2.0/ B2C endpoint. For example, in the following request, the path should be /{tenant}/oauth2/v2.0/authorize...
https://login.microsoftonline.com/{tenant}/oauth2/authorize?client_id={clientid}&redirect_uri=https:%2f%2flogin.microsoftonline.com%2fte%2f{tenant}%2foauth2%2fauthresp&response_type=id_token&response_mode=form_post&nonce={nonce}&state=StateProperties
Due to this, you're getting unexpected behavior across both your apps. This also means that the applications you are using were not created using B2C portal (we verified this via our offline chat as well).
As of now, B2C ONLY supports applications created through the B2C portal. So, if you can kindly create an application through the B2C portal, and try again, you should have this issue resolved.

Azure CDN adding pragma:no-cache header

I've set up an Azure CDN using the Standard Akamai tier. My origin is a Web App. I've set up the cache-control header in my web.config as follows:
<clientCache cacheControlMode="UseMaxAge" cacheControlCustom="public" cacheControlMaxAge="30.00:00:00" />
In my markup, I'm hitting my image file with the following code:
<img src="https://xxxx.azureedge.net/Content/Images/Turtle.jpg?v=1.0.0.27987">
When I do so, I get the following response headers:
cache-control:public, max-age=2591903
content-length:2321435
content-type:image/jpeg
date:Mon, 03 Apr 2017 19:34:23 GMT
etag:"2e7a1f1690a9d21:0"
last-modified:Thu, 30 Mar 2017 19:59:05 GMT
pragma:no-cache
server:Microsoft-IIS/8.0 status:200 vary:Accept-Encoding x-powered-by:ASP.NET
Notice the pragma:no-cache. I have NO IDEA where that is coming from. This is definitely NOT in my origin's response to load the cache. If I hit the origin, I see the following headers sent for the same image:
Accept-Ranges:bytes
Cache-Control:public,max-age=2592000
Content-Length:2321435
Content-Type:image/jpeg
Date:Mon, 03 Apr 2017 19:41:50 GMT
ETag:"2e7a1f1690a9d21:0"
Last-Modified:Thu, 30 Mar 2017 19:59:05 GMT
Server:Microsoft-IIS/8.0
X-Powered-By:ASP.NET
This means that when hitting the CDN, instead of serving this image from http cache, it's sending the ETag and I'm wasting a HTTP roundtrip. I'm assuming that the culprit is the pragma:no-cache header that is sent, which is overriding the cache-control header. My questions are:
Why is the Azure CDN adding this pragma:no-cache header when serving my image?
Is the pragma:no-cache the reason we're seeing the 304/ETag validation instead of serving from http cache?
Thanks!
Edit: I've also tried removing the tag from the web.config. I'm still seeing an ETag transmitted for the image instead of serving it from the browser http cache.

Remove Access-Control-Allow-Origin Response Header

I want to remove Access-Control-Allow-Origin: * vulnerability from Response Header.
I added the following line of codes into web.config which I found from this website.
I also apply some of the method that given in Stack Overflow.
<customHeaders>
<add name="Access-Control-Allow-Origin" value="domain" />
</customHeaders>
But it does not remove Access-Control-Allow-Origin: * instead of adding Access-Control-Allow-Origin: domain in the Response Header.
I have no idea how to remove this response header, as when I google for the solution. Most of the posts were trying to enable this header.
Please let me know if need more information.
Thanks in advance.
P/S: The website is build on PHP and running on IIS 7.
use a <remove> tag.
<remove name="Access-Control-Allow-Origin"/>
If you want to add a more restrictive Access-Control-Allow-Origin, you can then put it after the <remove>.

Overriding Cache-Control Header on Azure Websites

I have a Ghost blog hosted on Microsoft Azure. I want to make sure that I can set the Cache-Control header myself, because right now it has a max-age set to 0.
The way I am approaching this problem is through dropping a custom web.config (since Azure Websites uses IIS). Here is what I have:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="false">
</modules>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="1.00:00:00" />
</staticContent>
<httpProtocol>
<customHeaders>
<remove name="Cache-Control" />
<add name="Cache-Control" value="public, max-age=99" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
However, this does not actually remove the original Cache-Control header, but rather appends my value to it:
Any insights on what I am missing?
The solution to this problem is more trivial than I originally thought. While I was properly setting the headers in web.config, the server node.js configuration was enforcing its own Cache-Control rule, that was overriding mine.
In \core\server\middleware there is a file called cache-control.js. Inside it there is a snippet:
cacheControl = function cacheControl(options) {
/*jslint unparam:true*/
var profiles = {
public: 'public, max-age=0',
private: 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'
},
output;
All I needed to do is tweak 'public, max-age=0' to the value I wanted.
This eliminated the need for web.config altogether.

Varnish 4 Cache does not cache dynamic page

It is possible cache of dynamic pages, especially the home page?
For plan to reduce access to the database .
I can do static file cache. perfect.
Response headers
Accept-Ranges:bytes
Age:0
Cache-Control:no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection:keep-alive
Content-Type:text/html; charset=utf-8
Date:Tue, 09 Dec 2014 17:07:13 GMT
Expires:Thu, 19 Nov 1981 08:52:00 GMT
Pragma:no-cache
Transfer-Encoding:chunked
Via:1.1 varnish-v4
x-Cache:uncached
X-Varnish:295421
File default vcl: http://notepad.cc/vaokodde9
Your backend is screamming it doesn't want anyone to cache the page:
Cache-Control:no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Expires:Thu, 19 Nov 1981 08:52:00 GMT
Pragma:no-cache
It's setting all the Cache-Control headers to disallow cache, it's setting an Expires header in the past (way back), and a Pragma: no-cache. You either have to fix the backend to send headers that allow caching, or if you are sure that you won't break anything start to workaround this headers in vcl.
I would choose the first option, and work the Cookies issue later.
I think Varnish by default does not cache pages with Cookies. Maybe that is your problem (it looks like you have a PHPSESSID and some other stuff)?
See the Varnish documentation: https://www.varnish-cache.org/trac/wiki/VCLExampleCacheCookies
Try configuring your webserver to not set any cookies, or configure Varnish to ignore them (note that that may not make sense, and break your website!)
Thank you all . I managed to solve the problem by studying a little about the functioning of the cookie in the varnish .

Resources