PassportJS, AngularJS, $rootScope function is not defined - angularjs

I’m building an app for 1 admin to be able to log in and edit content. When logged in the app will serve up a private html doc structure instead of those not logged in being presented the normal public docs. I’m using Angular-route, Session and Passport for authentication. I want to check if a user is logged in whenever they link to another page on the site. So, if they go somewhere they’re not authorized to be, they can’t edit content on the site.
I’m getting an $injector:modulerr when I run the app on a localhost. The ReferenceError says checkAuth is not defined.
I tried creating a global variable using $rootScope as suggested here:
app.run(['$rootScope', '$http', function($rootScope, $http){
$rootScope.checkAuth = function(){
console.log("rootScope things are happening");
$http.get('/login/loggedin').success(function(user){
if (user !== '0') {
console.log('Authenticated');
return true;
} else {
console.log('Not Authenticated');
return false;
}
});
};
}]);
So, if the user’s logged in, I should get a true and if they’re not logged in, I’ll get a false. But, I still get the checkAuth is not defined injector error and I never see any of my console logs.
The Angular documentation for $rootScope says to use this format:
$rootScope.Scope([providers], [instanceCache]);
So, I tried this:
$rootScope.Scope(['$rootScope', '$http', function($rootScope, $http){...
Unfortunately, that doesn’t work either.
This is an example of using Passport and Angular together which I found somewhat helpful, but they’re allowing multiple users to register (which I don’t want) and it still doesn’t solve the question of a global function for checking if a user is logged in across multiple pages in a website. It may help you to see a similar controller and route file though.
I know that you can use factories and services to do something similar as suggested in previous similar questions, but I was hoping to go this route before trying a different path. Obviously though, if that's the best way, I'll give it a go.

The provided rootscope declaration seems ok to me.
Assign a rootscope value.
app.run(function($rootScope) {
$rootScope.checkAuth = function(){
$http.get('/login/loggedin').success(function(user){
if (user !== '0') {
return true;
} else {
return false;
}
})
};
});
Use it like (don't forget $rootscope dependancy)
app.controller('myCtrl', function($scope, $rootScope) {
$scope.authenticated = function() {
return $rootScope.checkAuth ;
};
});
See live sample to compare: http://jsfiddle.net/TmPk5/6/

Related

Synchronous service using async service in Angular [duplicate]

This question already has answers here:
AngularJS : Initialize service with asynchronous data
(10 answers)
Closed 7 years ago.
I have a link generator service that is able to generate links to specific content types (users' details page, content items' details pages etc).
This service is really easy to use and has synchronous functions:
links.content(contentInstance); // /items/123
links.user(userInstance); // /users/234
I now have to introduce separate routing for logged in user to change from /users/id to /users/me.
The only change I'd need to add to my link generator service is to check whether userInstance.id == loggedInUser.id and return a different route URL. This is not a problem as long as my logged-in user's info would be synchronously available. but it's not...
I have a userService.getMyInfo() that returns a promise. The first time it's called it actually makes a server request but subsequent calls return a resolved promise with already cached data.
So how should I implement my user link URL generation in my link generator service?
Edit
Ok. So to see better what I have at the moment and where I'm having the problem. I'm pretty aware that async will stay async and that it can't be converted to synchronous (and it shouldn't be).
This is some more of my code, that will make it easier to understand.
linkGenerator.user
angular
.module("App.Globals")
.factory("linkGenerator", function(userService) {
...
user: function(userInstance) {
// I should be calling userService.getMyInfo() here
return "/users/{0}/{1}".format(userInstance.id, userInstance.name);
},
...
});
userService.getMyInfo
angular
.module("App.Globals")
.service("userService", function($q, cacheService, userResource) {
...
getMyInfo: function() {
if (cacheService.exists("USER_KEY"))
// return resolved promise
return $q.when(cacheService.get("USER_KEY"));
// get data
return userResource
.getCurrentUser()
.$promise
.then(function(userData) {
// cache it
cacheService.set("USER_KEY", userData);
});
},
...
});
Controller
angular
.module("App.Content")
.controller("ItemDetailsController", function(linkGenerator, ...) {
...
this.model = { ... };
this.helpers = {
...
links: linkGenerator,
...
};
...
});
View
View uses ItemDetailsController as context notation.
...
<a ng-href="{{::context.helpers.links(item.author)}}"
ng-bind="::item.author.name">
</a>
...
Notes
As you can see my view generates links to item authors. The problem is that my linkGenerator (as you can see from the code may not have the information yet whether it should generate one of the correct links to user details view.
I know I can't (and don't want to) change my async code to synchronous, but what would be the best way to make this thing work as expected?
One possible solution
For the time being I've come up with a solution that does the trick, but I don't really like it, as I have to supply my logged in user's ID to linkGenerator.user(userInstance, loggedInUserId) function. Then I set up my routing so that I add resolve to my route where I call userService.getMyInfo() which means that my controller is not being instantiated until all promises are resolved. Something along this line:
routeProvider
.when("...", {
templateUrl: "path/to/my/details/template",
controller: "ItemDetailsController".
controllerAs: "context",
resolve: {
myInfo: function(userService) {
return userService.getMyInfo();
}
}
})
...
Then I also add an additional helper to my controller
this.helpers = {
...
links: linkGenerator,
me: myInfo.id,
...
};
And then I also change link generator's function by adding the additional parameter that I then supply in the view.
linkGenerator.user = function(userInstance, loggedInUserId) {
if (userInstance.id === loggedInUserId)
return "users/me";
return "users/{0}/{1}".format(userInstance.id, userInstance.name);
}
and in the view
<a ng-href="{{::context.helpers.links.user(item.author, context.helpers.me)}}"...
And I don't to always supply logged in user's ID. I want my service to take care of this data on its own.
There is no way to make anything in JavaScript that is asynchronous at some point synchronous again. This is a ground rule of how concurrency works - no blocking for waiting for stuff is allowed.
Instead, you can make your new method return a promise and use the regular tools for waiting for it to resolve.
links.me = function(){
var info = userService.getMyInfo();
return info.then(info => { // or function(info){ if old browser
// generate link here
return `/users/${info.id}`; // or regular string concat if old browser
});
}
Which you'd have to use asynchronously as:
links.me().then(function(link){
// use link here
});

Angular-fullstack get user id

I'm using angular-fullstack to build a single page application, and in one of my controllers I'm trying to assign a user's id to a variable. The code
$scope.getCurrentUser = Auth.getCurrentUser;
returns
function () {
return currentUser;
}
This works fine for displaying in my view as my angular code can interpret the function and display the user id using {{getCurrentUser()._id}} which I'm assuming evaluates the promise and displays the code to the view.
My question is how do I assign $scope.getCurrentUser = Auth.getCurrentUser; to a variable in my controller? Whenever I do so, I get undefined variables. I've tried:
$scope.getId = Auth.getCurrentUser()._id;
console.log($scope.getId); //undefined
$scope.getId = Auth.getCurrentUser();
console.log($scope.getId._id); //undefined
I've read forum posts like this and this that explain that these methods are meant to be returned the way they are. Another post that is basically the same question as mine, but I'm still confused how to store the user id becuase the answer was hinting it was console.log that was the issue. Any help would be greatly appreciated, thanks.
Try the following and let me know how this works out:
angular.module('yourModuleName').controller('YourController', ['$scope', 'Authentication',
function($scope, Authentication) {
var authentication = Authentication;
$scope.getId = authentication.user._id;
console.log($scope.getId);
}
]);
Make sure Authentication is included in your controller.

Angularjs / Coldfusion - Identification module - retrieve user role before implementing routes controller and views

I develop an application with Angularjs. In order to access to the application, the user has to be identified thanks to an external system (not developed by myself).
When the user is correctly identified, this external system redirected to my application.
I have implemented a general remote service for retrieving data (thanks to Coldfusion and an Oracle database) about a user (thanks to the login). This service is equally used in other applications. It returns general user information: firstname, lastname,..., and the user role for the specific application.
I would like to authorize the access to the sections of my application when the user has the corresponding role.
Thus I have called once the function of this service before to implement the routes in order to avoid many calls to the functions in the controllers. It works and I retrieve the user role.
var app=angular.module('ContactsApp', ['ngRoute', 'RemoteService']);
// CALL the method from THE REMOTE SERVICE - used $http.get and retrieve JSON data
app.factory('RemoteServiceFunction', ['RemoteServiceFactory', function (RemoteServiceFactory) {
return RemoteServiceFactory.Auth_getUserFromLogin(userid)
}]);
app.config(function($routeProvider, $httpProvider){
// FUNCTION USING THE REMOTE SERVICE IN ORDER TO INJECT DATA IN THE ROUTES
var wait = ['RemoteServiceFunction', function(RemoteServiceFunction) {
return RemoteServiceFunction;
}];
$routeProvider.when('/all-contacts',
{
templateUrl: 'template/allContacts.html',
controller: 'ctrlContacts',
resolve: {
personInfo: wait
}
})
.when('/view-contacts/:contactId',
{
templateUrl: 'template/viewContact.html',
controller: 'ctrlViewContacts',
resolve: {
personInfo: wait
}
})
.when('/search-contacts',
{
templateUrl: 'template/fastSearch.html',
controller: 'ctrlContactSearch',
resolve: {
personInfo: wait
}
})
.when('/add-contacts',
{
templateUrl: 'template/manageContact.html',
controller: 'ctrlAddContacts',
resolve: {
personInfo: wait
}
})
.otherwise({redirectTo:'/all-contacts'});
});
//THE CONTROLERS WITH DEPENDANCIES INJECTED
app.controller('ctrlContacts', function ($scope, ContactService, personInfo){
// alert(personInfoRole.data["VALUES"][0]["ROLES"]); OK the role is retrieved
}
Now I would like to enable or not the access to the various sections ( for example the role "admin" is mandatory for example in order to add a contact: ctrlAddContacts) . I do not know if I have to add a condition or a function in the resolve part to give the access.
Could you please tell me how to do that? Or tell me if another solution is better for using roles with Angularjs.
Thanks
Your best bet is probably to "intercept" the route change, that is: listening to the $routeChangeStart event on your rootScope. There you can check against whatever model variable you store the user priviledge in, and stop the routeChange from happening or redirect to an authentication screen.
Keep in mind though that such client-side authentication in an angular (or any javascript) application is just a user interface thing, since the user's browser will have full access to all the resources, and can even rewrite the validating parts of the javascript code, bypassing any route restrictions you set up. Sensitive information shouldn't travel to the client in the first place without proper authentication.
edit: see a full code solution here:best way to limit access to 'logged in' users

angularJS unit testing where run contains a HTTP request?

I am fairly new to AngularJS and am trying to learn some best practices. I have things working, but would like to start adding some unit tests to my modules and controllers. The first one I am looking to tackle is my AuthModule.
I have an AuthModule. This Module registers a Factory called "AuthModule" and exposes things like "setAuthenticatedUser" and also fields like "isLoggedIn" and "currentUser". I think this is a fairly common pattern in an AngularJS application, with some variations on the specific implementation details.
authModule.factory(
'AuthModule',
function(APIService, $rootScope) {
var _currentUser = null;
var _isLoggedIn = false;
return {
'setAuthenticatedUser' : function(currentUser) {
_currentUser = currentUser;
_isLoggedIn = currentUser == null ? false : true;
$rootScope.$broadcast('event:authenticatedUserChanged',
_currentUser);
if (_isLoggedIn == false) {
$rootScope.$broadcast('event:loginRequired')
}
$rootScope.authenticatedUser = _currentUser;
$rootScope.isLoggedIn = _isLoggedIn;
},
'isLoggedIn' : _isLoggedIn,
'currentUser' : _currentUser
}
});
The module does some other things like register a handler for the event "loginRequired" to send the person back to the home screen. These events are raised by the AuthModule factory.
authModule.run(function($rootScope, $log, $location) {
$rootScope.$on("event:loginRequired", function(event, data) {
$log.info("sending him home. Login is required");
$location.path("/");
});
});
Finally, the module has a run block which will use an API service I have to determine the current logged in user form the backend.
authModule.run(
function(APIService, $log, AuthModule) {
APIService.keepAlive().then(function(currentUser) {
AuthModule.setAuthenticatedUser(currentUser.user);
}, function(response) {
AuthModule.setAuthenticatedUser(null);
});
});
Here are some of my questions:
My question is how would you setup tests for this? I would think that I would need to Mock out the APIService? I'm having a hard time because I keep getting unexpected POST request to my /keepalive function (called within APIService.keepAlive())?
Is there any way to use $httpBackend in order to return the right response to the actual KeepAlive call? This would prevent me from having to mock-out the API service?
Should I pull the .run() block out which obtains the current logged in user out of the AuthModule and put it into the main application? It seems no matter where I put the run() block, I can't seem to initialize the $httpbackend before I load the module?
Should the AuthModule even be its own module at all? or should I just use the main application module and register the factory there?
Run blocks are the closest thing in Angular to the main method. A run block is the code which needs to run to kickstart the application. It is executed after all of the service have been configured and the injector has been created. Run blocks typically contain code which is hard to unit-test, and for this reason should be declared in isolated modules, so that they can be ignored in the unit-tests.angularjs docs
I suggest you take a look at this authentication service, using a service is the way to go.
Hopefully this would help ... Good luck

AngularJS HTML5 mode degrade to full page reloads in lieu of hashbang

By enabling HTML5 mode in AngularJS, the $location service will rewrite URLs to remove the hashbang from them. This is a great feature that will help me with my application, but there is a problem with its fallback to hashbang mode. My service requires authentication, and I am forced to use an external authentication mechanism from my application. If a user attempts to go to a URL for my app with a hashbang in it, it will first redirect them to the authentication page (won't ever touch my service unless successfully authenticated), and then redirect them back to my application. Being that the hash tag is only seen from the client side, it will drop off whatever parts of the routes come after by the time they hit my server. Once they are authenticated, they may re-enter the URL and it will work, but its that one initial time that will cause a disruption to the user experience.
My question is then, is there any way to go from $location.html5Mode(true) to the fallback of full page reloads for un-supportive browsers, skipping the hashbang method of routing entirely in AngularJS?
The best comparison of available implementations of what I'm aiming for would be something such as browsing around folders on github.com. If the browser supports rewriting the URL without initiating a page refresh, the page will asynchronously load the necessary parts. If the browser does not support it, when a user clicks on a folder, a full-page refresh occurs. Can this be achieved with AngularJS in lieu of using the hashbang mode?
DON'T overwrite the core functionality.
Use Modernizr, do feature detection, and then proceed accordingly.
check for history API support
if (Modernizr.history) {
// history management works!
} else {
// no history support :(
// fall back to a scripted solution like History.js
}
Try to wrap $location and $routeProvider configuration in browser's HTML5 History API checking, like this:
if (isBrowserSupportsHistoryAPI()) {
$location.html5Mode(true)
$routeProvider.when(...);
}
Also may be you need to create a wrapper to $location if you use it to change path.
(Sorry for terrible english)
Why not handle the un-authenticated redirect on the client side for this situation? I'd need to know a bit more about exactly how your app works to give you a more specific solution but essentially something like:
User goes to a route handled by AngularJS, server serves up the AngularJS main template and javascript
User is not authenticated, AngularJS detects this and redirects to the authentication page
You could have something in the module's run block for when the AngularJS application starts:
module('app',[])
.configure(...yadda...yadda...yadda...)
.run(['$location', 'authenticationService', function($location, auth) {
if (!auth.isAuthenticated()) {
$location.url(authenticationUrl)
}
});
I've subbed in a service which would find out if you were authenticated somehow, up to you how, could be checking a session cookie, could be hitting your API to ask. Really depends on how you want to continue to check authentication as the client application runs.
You can try and override the functionality of the $location service. The general idea would be to rewrite the URL according to whether someone is already authenticated or not, or just use a single approach (without hashbangs) for all URLs, regardless to whether html5mode is on or not.
I'm not sure that I fully understand the use-case so I can't write the exact code that you need. Here is a sample implementation of how to overrides/implements and registers the $location service, just making sure that hashbang is always eliminated:
app.service('$location', [function() {
var DEFAULT_PORTS = {
ftp: 21,
http: 80,
https: 443
};
angular.extend(this, {
absUrl: function() {
return location.href;
},
hash: function(hash) {
return location.hash.substr(1);
},
host: function() {
return location.host;
},
path: function(path) {
if (!path) {
return location.pathname;
}
location.pathname = path;
return this;
},
port: function() {
return location.port ? Number(location.port) : DEFAULT_PORTS[this.protocol()] || null;
},
protocol: function() {
return location.protocol.substr(0, location.protocol.length - 1);
},
replace: function() {
return this;
},
search: function(search, paramValue) {
if (search || paramValue) {
return this;
}
var query = {};
location.search.substr(1).split("&").forEach(function(pair) {
pair = pair.split("="); query[pair[0]] = decodeURIComponent(pair[1]);
});
return query;
},
url: function(url, replace) {
return this.path();
}
});
}]);

Resources