Breeze JS errors with Content Security Policy - breeze

With Breeze JS and a strict Content Security Policy I get the error Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive.
Is there a way to fallback without 'unsafe-eval' the same way AngularJS does with https://docs.angularjs.org/api/ng/directive/ngCsp?

Breeze uses Function(string) in order to make constructor functions for entities that have the same name as the entity. This is purely to make debugging easier, and is not an essential feature.
It should be possible to remove the reliance on Function(string) in the next version of Breeze. In the meantime, you can patch your version using:
function createEmptyCtor(type) {
return function(){};
}
Or minified as in your comment above:
function t(e) { return function(){}; }

Related

Identity Server 4 refused to frame SignOutIframeUrl

I have IdenityServer4 application (.net core 2.1) and 2 client mvc applications (.net framework 452) using UseOpenIdConnectAuthentication middleware. I am using implicit flow and I am trying to implement Single Log Out via Front-Channel. I am using the Quickstart app and everything is set up to work I just have to return the LoggedOut.cshtml with SignOutIframeUrl present in the Model.
My problem is that even though the signout work and logs me out from both clients I am getting the following errors in the network tab and in the console tab.
I have tried to add Content-Security-Policy header with all kinds of settings in it in the SecurityHeadersAttribute action filter attribute in the IdentityServer but nothing seems to fix the problem.
Few examples:
var csp = "default-src 'self' 'unsafe-inline' http://localhost:*;"
"frame-ancestors 'self' http://localhost:*;" +
"frame-src 'self' http://localhost:*;" +
"child-src 'self' http://localhost:*;";
var csp = "default-src 'self' *.aspnetcdn.com 'unsafe-inline';" +
"frame-src 'self' *";
This is how my FrontChannelLogoutUri action looks like
public void ForceLogOut()
{
AuthenticationManager.SignOut(CookieAuthenticationDefaults.AuthenticationType);
AuthenticationManager.SignOut(OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
I have also tried adding Content-Security-Policy header in the response from ForceLogOut, also didnt work.
My question is how can I fix this error ? There is something(most probably many things) I dont understand, but everywhere I have tried to find info about the Refused to frame.. policy directive problem, people are saying that I have to set appropriate frame-src values.
In IdentityServer options there is a property RequireCspFrameSrcForSignout which you can set to false to stop this happening
If set, will require frame-src CSP headers being emitting on the end
session callback endpoint which renders iframes to clients for
front-channel signout notification. Defaults to true.
https://identityserver4.readthedocs.io/en/latest/reference/options.html
When there is a frame-src violation it is the CSP of the outer frame that prevents the inner frame from being displayed. If it is a frame-ancestors violation the inner frame prevents the outer frame from displaying it. As it seems like you are framing a page from IdentityServer it is most likely the CSP of the other apps that need to add your source of IdentityServer to frame-src.
Not entirely in context, but can be shaped to fit; I just struggled with this for a good hour or two while learning Identity Server 4 in a Dot Net Core 5.0 / React app I am working on. I am using Visual Studio Code for development of the client app (hosted on https://localhost:3000) and Visual Studio for the server-side element (hosted on e.g. https://localhost:41234). There was the obvious cross-site element to this being a problem when used in development, so to rid the error, I used the following:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
//add this method to the Configure method in startup.cs
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Content-Security-Policy", "frame-ancestors https://localhost:3000"); //modifies CSP to allow client url to frame content
await next();
});
...
}

Adding cookies with Dart server?

So I have a simple HttpServer like this:
import 'dart:io';
main() {
HttpServer
.bind("127.0.0.1", 80)
.then((server) {
server.listen((request) {
// Add cookie here how?
request.response.write("Howdy Darty cowboy!");
request.response.close();
});
});
}
Is there a way to add cookies to the HttpResponse in Dart?
I can see both HttpResponse and HttpRequest classes have cookies properties and I can access them, but I can't seem to figure out how to add a cookie.
Tried this:
request.response.cookies = ["name", "value"];
But got this error:
Error: The setter 'cookies' isn't defined for the class 'HttpResponse'.
So there are no predefined methods to work with cookies?
Do I have to add my own HTTP Headers to add cookies?
Again, I can see headers properties in both classes, but no setters or getters!
Just started playing around with Dart.
Note: Please don't link me external packages, I would like to do it with Dart's core libraries. Don't want to get into another npm hell! Moved away from Node.js cause of npm, but looks like pub is identical, just uses yaml.
request.response.cookies is a List<Cookie>, so you'll want to add to it (rather than assign it with equals).
Try:
request.response.cookies.add(Cookie('name', 'value'));

Manage URL when debugging from Visual Studio 2015

I have an asp.net mvc site to which I've added some knockoutjs. The knockout code makes ajax request for data from the controllers e.g.
$.getJSON(BASE_URL + 'MyTasks/GetDataPage', { userKey: vm.UserKey, pageSize: pageSize }, function (returnedPayload) {
data = returnedPayload.filter(function (item) {
return JSON.stringify(item).toLowerCase().indexOf(ft) != -1;
});
self.setPagingData(data,page,pageSize);
The BASE_URL constant I set in the <head> of my layout razor page as follows:
<script type="text/javascript">
var BASE_URL = '/bamportal/';
</script>
All works fine when the website is deployed. However, when I run the website from VS by hitting F5 then I get a 404 such as:
http://localhost:49601/bamportal/MyTasks/GetDataPage?userKey=2&pageSize=50 Failed to load resource
If it had tried to address "http://localhost:49601/MyTasks/GetDataPage" (without the "/bamportal/") it would work.
What's the best solution for this problem?
Quick and dirty:
<script type="text/javascript">
var BASE_URL = '#Constants.BaseUrl';
</script>
where Constants is a static class defined as:
public static class Constants {
#if DEBUG
public const string BaseUrl = "/";
#else
public const string BaseUrl = "/bamportal/";
#endif
}
So when you compile your application in debug configuration you will get /, while in release you will get /bamportal/.
As alternative, a more complex and versatile approach could be obtained using Configuration Transforms and appSettings from Web.config:
<script type="text/javascript">
var BASE_URL = '#System.Configuration.ConfigurationManager.AppSettings["BaseUrl"];';
</script>
This, of course, will be extremely useful in scenarios where you need to deploy front-end and back-end on different domains/urls.
Your code should never know where the site will be hosted, you should use the correct helpers to determine where the action/content is located, this will prevent any issues with paths. Use Url.Content and Url.Action they will generate the correct path/url in the code.
As an example your action needs to point to the "MyTasks", "GetDataPage"
In your razor code you should have something like
<div id="urls" data-url="#Url.Action("ActionMethodName","YourControllerName")"></div>
Then in your get code get that stored url
$.getJSON($("#urls").data("url"),
To elaborate further this code will work on any environment (production, debug, iis, any location) without any developer worry or tweaking with config files or any other process. Tomorrow if you need to host your site on "osportal" not "bamportal" no changes need to be made, this should not be part of your code base.
One of the biggest benefits is that if the controller or action ever change the compiler will let you know that the url does not exist and you can fix it. Hardcoding paths/urls/location is a very bad/unmaintainable practice.

Can't save the data to OData controller after updating to Microsoft.AspNet.WebApi.OData 5.3.x

Not sure what's changed exactly and causing this partial error (since it's not happening with retrieving the data) but after updating Microsoft.AspNet.WebApi.OData library to 5.3.x version, breeze has an issue with saving the data to OData controller. It's about the urls of the batch requests.
It can be reproduced with breeze's its own sample OData project as well;
http://www.breezejs.com/samples/breeze-web-api-odata
If you look at ExecuteRequestMessagesAsync method of the BatchHandler class, RequestUri property of the items contain OData route prefix two times.
Microsoft.AspNet.WebApi.OData library 5.2.2 url
http://localhost:55802/odata/TodoItems
Microsoft.AspNet.WebApi.OData library 5.3.1 url
http://localhost:55802/odata/odata/TodoItems
Any ideas how to solve this issue?
breeze version: 1.5.1
Oh joy. Microsoft has changed their Web API OData implementation ... again
Thanks for digging in, #coni2k, and identifying the problem.
Fortunately, you do NOT have to patch Breeze. We deliberately expose the getRoutePrefix method so you can change it externally yourself to meet your needs.
In the following example, I've incorporated your suggestion in the body of the method.
var adapter = breeze.config.getAdapterInstance('dataservice', 'webApiOdata');
adapter.getRoutePrefix = getRoutePrefix531; // plug-in alternative for THIS adapter instance.
function getRoutePrefix531(dataService) {
// Copied from breeze.debug and modified for Web API OData v.5.3.1.
if (typeof document === 'object') { // browser
var parser = document.createElement('a');
parser.href = dataService.serviceName;
} else { // node
parser = url.parse(dataService.serviceName);
}
// THE CHANGE FOR 5.3.1: Add '/' prefix to pathname
var prefix = parser.pathname;
if (prefix[0] !== '/') {
prefix = '/' + prefix;
} // add leading '/' (only in IE)
if (prefix.substr(-1) !== '/') {
prefix += '/';
} // ensure trailing '/'
return prefix;
};
As I write we're not sure how to detect which version of Web API OData you're talking to which makes it difficult for us to say a priori which version of getRoutePrefix is right for your application.
Hope to figure this out eventually. Don't dare change the default to this new version because that would break every existing app that has to talk to an older version of Web API OData. Not sure how we can win this game. We'll look at it. Frustrating for now.

How to route without a templateUrl?

Ok. I have a url setup to log a user out. On the server, there is no html. The session on the server simply gets destroyed, and then the user is redirected to an address.
This works fine with plain html, but with Angular i am having issues. I've been routing all main routes using $routeProvider.when('/foo', {templateUrl: '/foo.html', controller: 'Ctrl'}) and that works fine for normal templated routes.. however, if there is no template it will not work.
So, how do i support the route /logout in the same fashion as above, when there is no html template?
A workaround is to use template instead of templateUrl. From the Angular docs:
template – {string=} – html template as a string that should be used
by ngView or ngInclude directives. this property takes precedence over
templateUrl.
This can be used as follows:
$routeProvider.when("/foo", {template: " ", controller: "Ctrl"});
Note: You must use " " instead of an empty string "" because Angular uses an if (template) check before firing the controller, and an empty string evaluates to false.
-- EDIT --
A better way to do it is to use the resolve map. See the Angular Docs:
resolve - {Object.=} - An optional map of
dependencies which should be injected into the controller.
This can be used like this:
$routeProvider.when('/foo', {resolve: {redirect: 'RedirectService'}});
Note: I've changed it from "Ctrl" to "RedirectService", because what you're describing in the question isn't really a "controller" in the Angular sense. It doesn't set up scope for a view. Instead, it's more like a service, which ends up redirecting.
I am writing the solution based on the already accepted answer and the github issue mentioned in it's comments.
The approach I am using is a resolve parameter in the $routeProvider. In my case I was trying to create a nice solution to logout in my application, when user goes to /logout.
Example code of $routeProvider:
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
...
when('/logout', {
resolve: {
logout: ['logoutService', function (logoutService) {
logoutService();
}]
},
}).
...
}]);
In the resolve part you specify a service (factory) by name and later on you have to call it. Still it is the nicest solution around.
To make the example complete I present my logoutService:
angular.module('xxx').factory('logoutService', function ($location, Auth) {
return function () {
Auth.setUser(undefined);
$location.path('/');
}
});
Works great!

Resources