Electron: how to execute AngularJS in Electron without CSRF protection? - angularjs

Recently I started working on my first electron app and and bumped into a problem, hope someone may be able to help me out.
I would like to use Electron to quickly create a cool looking app by using a Admin Backend AngluarJS HTML theme.
The AngluarJS theme works fine from a http:// source but not when I load the theme from a local drive like the C:\ drive, it breaks the theme because (I think) of CSRF protection policies in chrome.
Now I googled my ass off but I can't find a good solution. I thought that running a http-server in electron might be a solution so I tried the npm http-server package. Problem with this approach is that the nodejs code is not working anymore in this case beceause the http-server will only proces static files.
Is there maybe another solution?
Thanks for thinking with me!

Electron has no problem running an AngularJs app.
I have built a few Electron apps using Angular with no CSRF problems.
One way to get around this is to create a simple server running inside electron, like shown below:
// <YOUR-ENTRY-FILE>.js
app.on('ready', function() {
mainWindow = new BrowserWindow({
width: 800,
height: 600
});
var server = http.createServer(requestHandler).listen(9527);
mainWindow.loadUrl('http://localhost:9527/index.html');
mainWindow.webContents.on('did-finish-load', function() {
mainWindow.setTitle(app.getName());
});
mainWindow.on('closed', function() {
mainWindow = null;
server.close();
});
});
function requestHandler(req, res) {
var
file = req.url == '/' ? '/index.html' : req.url,
root = __dirname + '/www',
page404 = root + '/404.html';
getFile((root + file), res, page404);
};
function getFile(filePath, res, page404) {
fs.exists(filePath, function(exists) {
if(exists) {
fs.readFile(filePath, function(err, contents) {
if(!err) {
res.end(contents);
} else {
console.dir(err);
}
});
} else {
fs.readFile(page404, function(err, contents) {
if(!err) {
res.writeHead(404, {'Content-Type': 'text/html'});
res.end(contents);
} else {
console.dir(err);
}
});
}
});
};
There really should not be a need for you to do this, check your paths and have this as the last option.

quite late, but i found a solution to this, maybe this helps someone someday https://stackoverflow.com/a/60452312/1284216

Related

How to serve two AngularJS apps with one Express server?

I've searched all around and i've found a few ways of how to do this, but none seem to specifically fit my needs and i can't get them to work. My latest attempt is below, using express-subdomain with express and trying to server two separate AngularJS apps based on the incoming sub domain. This code currently seems to serve the correct app, but none of the Angular modules are included from what i can see. The browser console has many many errors of 'Uncaught SyntaxError: Unexpected token <'. Which from previous experience i believe means AngularJS was not loaded correctly. Keep in mind, i have no issue running only one AngularJS app, it is only when i try to bring in sub-domain and serve that second static app.
I've already tried vhost with similar failed results.
server.js
app.use(subdomain('app1',express.static(__dirname + '/public/app1')));
app.use(subdomain('app2',express.static(__dirname + '/public/app2')));
require('./app/routes')(app); // configure our routes
app.listen(port);
routes.js
app.get('*', function(req, res) {
var firstIndex = req.get('host').indexOf('.');
var subdomain = req.get('host').substr(0,firstIndex).toLowerCase();
if (subdomain === 'app1'){
res.sendFile(root + '/public/app1/views/index.html');
}else if (subdomain === 'app2'){
res.sendFile(root + '/public/app2/views/index.html');
}else{
res.sendFile(root + '/public/app1/views/notfound.html');
}
});
if (subdomain === 'app1'){
res.sendFile(root + '/public/app1/views/index.html');
This seems wrong.
This will send index.html to any request, i.e. request for js file.
Should be i.e.:
if (subdomain === 'app1' && req.url.indexOf('.') === -1){
} else if (subdomain === 'app2' && req.url.indexOf('.') === -1){
} else { /* return static asset */ }
...
If you create a general json whit diferent instance whit the diferents aplicacitions and create all the call, you can emulate this. This is the example:
const routes = {
routeApp1: {
appTest1:{
url: '/appTest1/appTest1',
file: './mock/appTest1.json',
},
appTest2:{
url: '/appTest1/appTest2',
file: './mock/appTest2.json',
},
}
routeApp2: {
appTest1:{
url: '/appTest2/appTest1',
file: './mock/appTest1.json',
},
appTest2:{
url: '/appTest2/appTest2',
file: './mock/appTest2.json',
},
}
};
app.get(routes.routeApp1.appTest1.url, (req, res) => {
res.send(JSON.stringify(require(routes.routeApp1.appTest1.file)));
});
app.get(routes.routeApp2.appTest2.url, (req, res) => {
res.send(JSON.stringify(require(routes.routeApp2.appTest2.file)));
});
app.listen(3000, () => {
console.log(`Mocks app listening on port ${port}!`);
});

Cordova app using angular & ADAL

I'm building my first mobile app using Cordova. The back-end services live on Azure so I'm trying to get authentication working by using the ADAL plugin for Cordova.
First of all I found out that the library does not do intercepts as the ADAL library for Angular does. I'm using Angular within my Cordova app, paired with material design directives for the look-and-feel. Would have been nice to have interception, but as I understood it's just not there at the moment (should find out how hard it is to implement).
So instead I now wrote a service which will take care of sending REST api requests to Azure, including the correct authentication token. It's based on the sample found here.
This is what I came up with:
var request = function(url)
{
createContext()
.then(function () {
getAuthToken().then(
function(token) {
sendRequest(token, url);
})
},
function (err) {
$log.error("Failed to create a context.");
});
};
First it will create the authentication context:
function createContext () {
return $q(function (resolve, reject) {
var authenticationContext = Microsoft.ADAL.AuthenticationContext;
authenticationContext.createAsync(authority)
.then(function (context) {
authContext = context;
$log.log("Created authentication context for authority URL: " + context.authority);
resolve();
}, function (err) {
$log.error("Failed to create authentication context: " + pre(err))
reject();
});
});
};
The using the context it should get the authentication token:
function getAuthToken()
{
if (authContext == null) {
$log.error('Authentication context isn\'t created yet. Create context first');
return;
}
return $q(function (resolve, reject) {
authContext.acquireTokenAsync(resourceUrl, appId, redirectUrl)
.then(function (authResult) {
resolve(authResult.accessToken);
}, function (err) {
$log.error("Failed to acquire token: " + pre(err));
reject();
});
});
}
And afterwards it should send the request but I'll leave that part out since it never gets there anyway. I feel the need to re-emphasize that I'm a complete n00b at this stuff, so please be easy on me and especially on the code. There's probably a lot of room for improvement, I get that.
When I actually run this, it pops up a window where I need to login using my Microsoft account, cool. I even got two factor authentication first time I tried this, very nice! So I log in and I get returned to the code. But now the authresult variable has a status of "Failed" and there's no access token in the result. Unfortunately there's also no indication of what went wrong. So first part of the question is; what could have gone wrong here?
Now we get to the second part of the question; how do you properly debug these kinds of things? On my desktop I'd run Fiddler to check out the communication, but I don't know how to do that for Android. I'm debugging on my device btw, cause for some reason all of the emulators available to me are extremely slow (VS and Google) even though my hardware specs should support them just fine.
Thanks for any pointers!
Update 03-02-2016
Fiddling around with the code a bit, I decided to pack things in a login function which gives a somewhat shorter sample:
var createContext = function () {
if (authContext == null) {
authContext = new Microsoft.ADAL.AuthenticationContext(authority);
}
};
var getAuthToken = function () {
if (authContext == null) {
$log.error('Authentication context isn\'t created yet. Create context first');
return;
}
return $q(function (resolve, reject) {
authContext.acquireTokenAsync(endpointUrl, appId, redirectUrl)
.then(function (authResult) {
resolve(authResult.accessToken);
}, function (err) {
$log.error("Failed to acquire token: " + pre(err));
reject();
});
});
}
var login = function () {
createContext();
getAuthToken();
}
This code runs on the following input vars:
var authority = 'https://login.windows.net/[tenantid]';
var resourceUrl = 'https://graph.windows.net/';
var appId = '1ef41b17-0943-4359-bc12-014f4fd2d841';
var redirectUrl = 'http://MyApp';
I now used chrome://inspect to see what is going over the wire. And to my big surprise, I see a valid SAML token returned from Azure. It has got my name in it and everything, which I'd recon they wouldn't send after a failed authentication. So it seems that even though the response is ok, the ADAL library doesn't give me a proper response (Status = Failed). Again no clue on how to proceed :S
I just solved it. And like one would expect, the remedy is as simple as they get. Configuring the application in Azure AD, I chose the "web application" type application, since this is a web application with Angular and all. Now I guess since Cordova translates things to native code, that's not the correct option to chose. As soon as I created a new application as "native application" instead and used the client ID of that one, everything started working.... Sincerely hope this will help someone else in the future...!
I had a very similar issue where I was trying to access a web api from a Cordova app. I was using the App ID Uri for the web api I wanted to access as the resouceURL when calling acquireTokenAsync. When I changed this to the client Id of the Web Api instead it worked.

'AWS is not defined' when using aws-sdk-js in angular

Following this tutorial, implementing the AWS sdk with angular, I'm getting AWS is not defined from jshint (using grunt to serve the app).
I've installed the sdk with bower install aws-sdk-js --save, and it correctly appears in my index.html file.
This is my controller:
angular.module('myApp')
.controller('S3uploadCtrl', function ($scope) {
console.log(AWS);
$scope.creds = {
bucket: 'myBucket',
accessKey: 'accKey',
secretKey: 'secKey'
};
$scope.upload = function() {
// Configure The S3 Object
AWS.config.update({ accessKeyId: $scope.creds.accessKey, secretAccessKey: $scope.creds.secretKey });
AWS.config.region = 'us-west-2';
var bucket = new AWS.S3({ params: { Bucket: $scope.creds.bucket } });
if($scope.file) {
var params = { Key: $scope.file.name, ContentType: $scope.file.type, Body: $scope.file, ServerSideEncryption: 'AES256' };
bucket.putObject(params, function(err, data) {
if(err) {
// There Was An Error With Your S3 Config
alert(err.message);
return false;
}
else {
// Success!
alert('Upload Done');
}
})
.on('httpUploadProgress',function(progress) {
// Log Progress Information
console.log(Math.round(progress.loaded / progress.total * 100) + '% done');
});
}
else {
// No File Selected
alert('No File Selected');
}
};
function alert(msg) {
console.alert(msg);
}
});
There isn't much about this on google. I found one other SO question which I've tried to follow to no avail. (changed the order of my <script> tags etc.)
It's a JSHint error. JSHint makes sure you're accessing defined variables, and has no idea that an AWS global variable exists et runtime. So you need to tell JSHint that this globa variable exists and that you allow your code to access this global variable (although you probably should hide it behind an angular service, to make your code testable).
Edit your .jshintrc file (it might have another name: check your build configuration), and add (or modify) the following rule:
"globals": { "AWS" : false }
If you are just getting a JSHint error, it might be because AWS is not recognised as a variable. Create a .jshintrc file in the root of your project, and put this config in it:
"globals": {
"AWS": false
}
'AWS is not defined' this error occurs when you forgot to define js ,
After "bower install aws-sdk-js"
you need to define "aws-sdk.min.js" and "aws-sdk.js" to your index.html in script tag like
<script src="bower_components/aws-sdk/dist/aws-sdk.min.js"></script>
<script src="bower_components/aws-sdk/dist/aws-sdk.js"></script>

NavBar address loading angular template but not root shell

I am using Node.JS with Express, Angular.JS and the node module connect-roles for ACL. I want to allow a user with user.status of "Platinum" to access "Platinum" but not "Gold" and vice versa.
I have the ACL part working, if I enter /Platinum into the navigation bar I can't access /Gold, but when I try to access /Platinum I only get the template but not the root shell, so what comes up is this:
You made it!
You have the {{status}} status!
If I click on a link in angular to /Platinum, everything works as it should. If I enter any neutral address in the navigation bar, everything works as it should.
This should be an easy fix, but I've not figured it out.
Here is the code that sets up authorizations, I'm pretty sure everything here is okay.
ConnectRoles = require('connect-roles')
var user = new ConnectRoles({
failureHandler: function(req, res, action){
var accept = req.headers.accept || '';
res.status(403);
if(accept.indexOf('html')) {
res.render('access-denied', {action: action});
} else {
res.send('Access Denied - You don\'t have permission to: ' + action);
}
}
});
var app = express();
app.use(user.middleware());
// Setting up user authorizations,
// i.e. if req.user.status = "Platinum", they are given Platinum status
user.use('Platinum', function(req) {
if (req.user.status == 'Platinum') {
return true;
}
});
user.use('Gold', function(req) {
if (req.user.status == 'Gold') {
return true;
}
});
user.use('Admin', function(req) {
if (req.user.status == 'Admin') {
return true;
}
});
That sets up authorizations, now the problem lies below with the routing.
app.post('/login', passport.authenticate('local',
{ successRedirect: '/', failureRedirect: '/login' }));
app.get('/Platinum', user.is('Platinum'), function(req, res) {
//Obviously the code below is wrong.
res.render('templates/support/Platinum');
});
app.get('/Gold', user.is('Gold'), function(req, res) {
res.render('templates/support/Gold');
});
The way you are configuring your routes on server side (using express) is not correct. For a single page app like AngularJS, you need to do all of the routing for pages on the client (i.e. in Angular). The server still defines routes for API requests (e.g. getting and posting data) and static resources (index.html, partial HTML files, images, javascript, fonts, etc), though.
Thus the following code is wrong in your server side JS:
app.get('/Platinum', user.is('Platinum'), function(req, res) {
//Obviously the code below is wrong.
res.render('templates/support/Platinum');
});
app.get('/Gold', user.is('Gold'), function(req, res) {
res.render('templates/support/Gold');
});
Just remove those lines.
Instead, you need to define the routes that the server will handle, such as your /login post one first, and how to get static files (I suggest prefixing them all with /pub in the URL). Then you need to do something like the technique in this answer to return your index.html page if no routes are matched.
That way, when a user types http://localhost:port/Gold, express will see there is no route defined for /Gold, so it will return index.html, which will load AngularJS, run your Angular app, which will then look at the URL and see if that matches any of the routes your AngularJS app has configured, and if so, fetch the partial for that page and insert it into your ng-view (if using the core router).

Cordova FileTransfer: upload image to AWS s3

Am using ng-cordova file-Transfer plugin to upload images to my AWS s3 bucket.
but i run into two problems first it didn't work, second i have no idea how to debug the problem while the App running on the emulater.
here is my code:
.controller('newItemCtrl', function($scope, $http, API_URL, me, $cordovaFileTransfer) {
var s3URI = encodeURI("https://mybucketname.s3.amazonaws.com/"),
policyBase64 = "MY_BASE64_ENCODED_POLICY_FILE",
signature = "MY_BASE64_ENCODED_SIGNATURE",
awsKey = 'my AWSAccessKeyId',
acl = "public-read";
var options = {
fileKey: "avatar",
fileName: "image.png",
chunkedMode: false,
mimeType: "image/png"
// params = {
// "key": fileName,
// "AWSAccessKeyId": awsKey,
// "acl": acl,
// "policy": policyBase64,
// "signature": signature,
// "Content-Type": "image/png"
// }
};
var imageURI = '../img/ionic.png';
$scope.upload = function($cordovaFileTransfer) {
$cordovaFileTransfer.upload(s3URI, imageURI, options)
.then(function(result) {
console.log("SUCCESS: " + JSON.stringify(result.response));
}, function(err) {
console.log("ERROR: " + JSON.stringify(err));
}, function(progress) {
// constant progress updates
});
}
})
I also left the params code to ask another question it's commented, but before i run my app and it gives me an error with the params but my question why i got the error even before invoke the template assosiated with that controller
I had a similar problem, to debug I used the live server logs to check and see if the file upload hit the server at all, some errors I noticed:
my server was expecting a different file key
the Access-Control-Allow-Origin header wasnt being sent properly in the server's response
Then, I also installed the cordova native notifications plugin (link here) and sprinkled alerts throughout the file transfer callbacks to see where things were getting stuck
Anyway probably not the best way to debug, but it worked.
Hope that helps.
...one more thing the params part of "options" seems to work best when applied in this format:
var options = {
fileKey: "avatar",
fileName: "image.jpg",
/*params: {
"value1":"value1",
"value2": "value2"
}*/
};
var params = new Object();
params.value1 = "value1";
params.value2 = "value2";
options.params = params;
from the cordova docs "params: A set of optional key/value pairs to pass in the HTTP request. (Object)" so passing in a dictionary may be subtly different, I'm not sure, all I know is that it worked once I made that change.
To debug on emulator I use this from my app directory: ionic emulate ios -lc
That shows me errors or logs into the console.

Resources