I am trying to use angular-permission to implement permission-based authentication but I don't know where to define those permissions which are retrieved from my back-end via API which requires token-based access.
First, let me give a bit background about how my app looks like. On my back-end, my system portal, I define permissions to allow different APIs to be called. Permissions won't change all the time. Only when I add new features(APIs), new permissions will be added. For example.
permission1: api1,api2,api3
permission2:api4,api5,api6
permission3:api7,api8,api9
On the front-end, customers login the front-end web portal and create customized roles themselves which group some permissions together, for example:
admin: permission1,permission2,permission3
auditor:permission 3
The angular-permission doc says (https://github.com/Narzerus/angular-permission/blob/development/docs/1-manging-permissions.md#multiple-permissions) I can use PermissionStore.defineManyPermissions to define permissions which are retrieved from API after user login. That's all clear.
So I have two modules. One is the Authentication module which handles user login. The other one is the Permission module which handles the permission validation. On the Permission module .run() phase, I define the permissions like this:
var getPermissions = function () {
var deferred = $q.defer();
system.permissions.get(
function () {
return deferred.resolve(system.permissions._permissions);
},
function (error) {
console.log("error if can't load permissions");
console.log(error);
}
);
return deferred.promise;
};
var loadPermissions = function () {
var promise = getPermissions();
promise.then(function (permissions) {
var arrayPermissions = formatPermissionArray(permissions);
//var arrayPermissions=['viewSeed','viewAuthentication'];
PermissionStore.defineManyPermissions(arrayPermissions, checkPermission);
console.log("from permission run service");
console.log(arrayPermissions);
}, function (reason) {
console.log('Failed: ' + reason);
}, function (update) {
console.log('Got notification: ' + update);
});
};
loadPermissions();
var formatPermissionArray = function (sourceData) {
var formatedPermissionArray = [];
for (var i = 0; i < sourceData.length; i++) {
formatedPermissionArray.push(sourceData[i].permissionId);
};
return formatedPermissionArray;
};
But during the bootstrap of the app, this module already loaded and the arrayPermissions will be empty since user hasn't logged in yet.
I tried to use oclazyload to load the Permission module from the login controller of the Authentication module, that actually works but if user refresh/reload their page, the Permission module won't be loaded anymore.
I am new to web development and also new to AngularJs. Just a few months experience. I don't know if I am doing it in a complete wrong way.
My questions are:
The API for retrieving a permission list should require authentication? Since I will need to put those authentication on the UI-router routes. Anyone can see it anyway. If I should not protect that API, then my problem is solved.
If I should keep my api protected, how should I address the issues I described above and that is where to define the permissions for angular-permission and how to use API to retrieve the permissions.
I hope I have managed to describe my issues clearly. Any help or guidance are greatly appreciated.
Regards,
Lola
I'm using angular-permission with angular-satellizer. PermRoleStore or PermPermissionStore needs to be in run block. You can add data to JSON WEB TOKEN add use it at the run block like I did.
$auth.getPayload()This function returns payload from JWT in localStorage. And in that payload it has data with role key which I saved in backend. I hope this helps your issue.
.run(function (PermRoleStore, $auth, Yollar) {
PermRoleStore
.defineRole('ADMIN', function () {
if($auth.getPayload()) {
if ($auth.getPayload().data.role === 'ADMIN') {
return true;
}
else {
return false;
}
}
else {
return false;
}
});
PermRoleStore
.defineRole('MODERATOR', function () {
if($auth.getPayload()) {
if ($auth.getPayload().data.role === 'MODERATOR') {
return true;
}
else {
return false;
}
}
else {
return false;
}
});
})
Related
I am currently trying to create a remote method in loopback that will query a firebase database using the Firebase Admin SDK in NodeJS.
It works, but the issue I am having is that I am unable to make it realtime. It keeps crashing with an error pointing to the callback function being called more than once.
Here is a snippet of the code for my remote method:
'use strict';
module.exports = function(Scusers) {
var admin = require("firebase-admin");
Scusers.listItems = function(cb) {
// Get a database reference
var db = admin.database();
var ref = db.ref("users");
// Attach an asynchronous callback to read the data at our posts reference
var items = [];
// return list of users ordered by key and push each object into an array
ref.orderByKey().on("value", function(snapshot) {
snapshot.forEach(function(data) {
items.push(data.val());
});
// return array
cb(null, items);
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
};
}
If I change this line:
ref.orderByKey().on
for:
ref.orderByKey().once
It works, but on my front-end which is coded in AngularJS, it won't see the changes unless I manually call a refresh.
What should be the best approach to this? Sorry if it is unclear or my approach is wrong, I am so new at this. Thanks!
I want to refresh a URL upon successful login without a page refresh.
I am using the following code
$scope.Login = function (teacher, chkRememberMe) {
alert(chkRememberMe);
$http.post("/Teacher/Login", { teacher: teacher, chkRememberMe: chkRememberMe })
.success(function (result) {
if (result == "True") {
toastr.success("You are Login successfully!!!");
$timeout(function () { }, 5000);
$timeout(function () { }, 5000);
$location.path('Teacher/Index');
}
else {
toastr.error("The email and password you entered don't match.")
window.location = "/Teacher/Login";
}
})
.error(function (error) {
error.message
alert(error);
toastr.error("The email and password you entered don't match11")
})
}
The URL is not updating successfully.
You can use history.replaceState(data,title,url) to accomplish this. There's a bit of documentation at MDN. The history.js library may be of interest as well.
If you're trying to handle single-page authentication with angular, it can be a hassle. You may want to take a look at AngularJS: Basic example to use authentication in Single Page Application for more on this.
An aside: you may also want to consider taking steps to ensure that password managers function as you'd expect. There are a few stackoverflow questions which address these concerns (e.g., How to make Chrome remember password for an AJAX form?).
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.
So I have some code that authenticates the user to my app using google which works out fine. What I want to do is then save that user info to the firebase and then have that user be able add data specifically under their account that will then reload the next time they log in. What's the best way to do that? I'm getting very lost.
(function() {
'use strict';
angular.module('life-of-a-story')
.controller('UserController', function($scope, $firebaseAuth) {
var ref = new Firebase('https://life-of-a-story.firebaseio.com/');
// create an instance of the authentication service
var auth = $firebaseAuth(ref);
// login with Google
this.login = function() {
auth.$authWithOAuthPopup("google").then(function(authData) {
console.log(authData);
console.log("Logged in as:", authData.uid);
var user = {
'name': authData.google.displayName,
'image': authData.google.profileImageURL,
'uid': authData.uid
}
console.log(user);
}).catch(function(error) {
console.log("Authentication failed:", error);
});
};
});
})();
AngularFire is a (relatively) thin UI binding library on top of Firebase's regular JavaScript SDK. So when something is not explicitly documented in the AngularFire documentation, you can sometimes find the answer in the documentation for the regular Firebase JavaScript SDK.
Most Firebase Authentication developers store each user's data under a /users node. If that is what you're trying to do, you can read how to accomplish it in the section called Storing user data in the Firebase documentation for JavaScript.
The relevant code from there:
// we would probably save a profile when we register new users on our site
// we could also read the profile to see if it's null
// here we will just simulate this with an isNewUser boolean
var isNewUser = true;
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.onAuth(function(authData) {
if (authData && isNewUser) {
// save the user's profile into the database so we can list users,
// use them in Security and Firebase Rules, and show profiles
ref.child("users").child(authData.uid).set({
provider: authData.provider,
name: getName(authData)
});
}
});
// find a suitable name based on the meta info given by each provider
function getName(authData) {
switch(authData.provider) {
case 'password':
return authData.password.email.replace(/#.*/, '');
case 'twitter':
return authData.twitter.displayName;
case 'facebook':
return authData.facebook.displayName;
}
}
In my angular app I need to read some data from the server before I allow the rest of my application to run. For example, I need the user authenticated so I can verify their permission before I allow them access to anything. Since I need to do this on app start and I don't know what controller will be loaded first, I can't use the resolve feature of the $routeProvider.when method because I do not know which controller will be hit first, and it only occurs once on app start.
I keep steering towards doing something in the module.run method, but I can't find a way to prevent it from continuing until get the data back from the server. I found this but it suffers from the same problem: AngularJS : Initialize service with asynchronous data.
I can also do manual bootstrapping, but I can't find any good examples of how to do this. Any help you can provide would be appreciated.
The easiest way i can think of here would be to have a separate page for login. Once the user is authenticated then only allow him access to the main app (angular app).
Also you need to secure you service resources against unauthorized request. This would safeguard against your controller \ views loading unauthorized data.
Like the other answer already mentioned, you should take care of the security of your app.
Possible solutions are:
Check if the user is authenticated
Implement a authentication in your API. You can check if the user is logged in with a responseInterceptor:
.config(['$routeProvider','$httpProvider', function($routeProvider,$httpProvider) {
//setup your routes here...
var authChecker = ['$location', '$q', function($location, $q) {
//redirects the user to /login page if he's not authorized (401)
function success(response) {
return response;
}
function error(response) {
if(response.status === 401) {
$location.path('/login');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
return function(promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(authChecker);
}])
See $http docs for more details.
Workaround: run scope
Does not solve the security issues, but is working, too:
Get your data in the run block like this:
.run(function($http,$location,$rootScope) {
$rootScope.$on("$routeChangeStart", function (event, next, current) {
if($rootScope.gotData) {
$location.path(next.path);
} else {
$location.path('/loading');
}
});
$http.get('path/to/data').success(function() {
$rootScope.gotData = true;
$location.path('/home');
}).error(function() {
$location.path('/404');
})
})
see docs for further information.