Reputation: 32726
I'm wondering if an approach like this
(it's a very trivial example)
could be a starting point to manage
angularjs + user authentication
<!DOCTYPE html>
<html>
<head>
<title>My auth test</title>
</head>
<body>
<div data-ng-app="myApp">
<div data-ng-view></div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.factory('Devs', function() {
var data = [{name:'Joe',auth:false},{name:'Whisher',auth:true}];
return data;
});
app.factory('Projects', function() {
var data = [{name:'Php'},{name:'Javascript'}];
return data;
});
app.config(function($routeProvider) {
$routeProvider.
when('/', {
controller: 'OneCtrl',
resolve: {
projects: function(Projects) {
return Projects;
}
},
templateUrl:'./view.html'
}).
when('/one', {
controller: 'OneCtrl',
resolve: {
projects: function(Projects) {
return Projects;
}
},
templateUrl:'./view.html'
}).
when('/two', {
controller: 'TwoCtrl',
resolve: {
projects: function(Projects) {
return Projects;
}
},
templateUrl:'./view.html'
})
.otherwise({redirectTo:'/'});
});
app.controller('OneCtrl',function($scope,Devs, projects) {
$scope.project = projects[0];
$scope.dev = Devs[0];
});
app.controller('TwoCtrl',function($scope,Devs, projects) {
$scope.project = projects[1];
$scope.dev = Devs[1];
});
app.directive('checkAuth',function($location){
return {
link:function(scope){
scope.$on('$routeChangeSuccess', function(next, current) {
if(!scope.dev.auth){
$location.path('/');
}
});
}
}
});
</script>
</body>
</html>
in the view
<div check-auth>
<p>{{project.name}}</p>
<div><a ng-href="./#/one">one</a></div>
<div><a ng-href="./#/two">two</a></div>
<div>
you could use the directive only within the views require authentication.
What do you think about ? I'm pleased with some links as well :)
Upvotes: 1
Views: 3583
Reputation: 889
I've created an AngularJS module for user authentication that supports protected/public routes, rerouting on login/logout, heartbeats for status checks, stores the session token in a cookie, directives for signup/login/logout, etc.
It's built for UserApp (a cloud-based user management API), but it could easily be attached to your own API. If you use UserApp, you won't have to write any server-side code for the user stuff. Take the course on Codecademy to try it out.
https://github.com/userapp-io/userapp-angular
Here are some examples of how it works:
How to specify which routes that should be public, and which route that is the login form:
$routeProvider.when('/login', {templateUrl: 'partials/login.html', public: true, login: true});
$routeProvider.when('/signup', {templateUrl: 'partials/signup.html', public: true});
$routeProvider.when('/home', {templateUrl: 'partials/home.html'});
The .otherwise()
route should be set to where you want your users to be redirected after login. Example:
$routeProvider.otherwise({redirectTo: '/home'});
Login form with error handling:
<form ua-login ua-error="error-msg">
<input name="login" placeholder="Username"><br>
<input name="password" placeholder="Password" type="password"><br>
<button type="submit">Log in</button>
<p id="error-msg"></p>
</form>
Signup form with error handling:
<form ua-signup ua-error="error-msg">
<input name="first_name" placeholder="Your name"><br>
<input name="login" ua-is-email placeholder="Email"><br>
<input name="password" placeholder="Password" type="password"><br>
<button type="submit">Create account</button>
<p id="error-msg"></p>
</form>
Log out link:
<a href="#" ua-logout>Log Out</a>
(Ends the session and redirects to the login route)
Access user properties:
User properties are accessed using the user
service, e.g: user.current.email
Or in the template: <span>{{ user.email }}</span>
Hide elements that should only be visible when logged in:
<div ng-show="user.authorized">Welcome {{ user.first_name }}!</div>
Show an element based on permissions:
<div ua-has-permission="admin">You are an admin</div>
And to authenticate to your back-end services, just use user.token()
to get the session token and send it with the AJAX request. At the back-end, use the UserApp API (if you use UserApp) to check if the token is valid or not.
Hope this answers your question. Let me know if you need any help :)
Upvotes: 1
Reputation: 1751
I am currently learning angular JS. I cooked this up one day:
app.factory("user", function($http, $q) {
var user = {};
// query logged in status on initial page load
$http.get("/loggedin").success(function() {
user.isLogged = true;
}).error(function() {
user.isLogged = false;
});
user.login = function(username, password) {
var defer = $q.defer();
if(user.isLogged) {
defer.resolve("Already logged in");
return defer.promise;
}
$http.post("/login", {username: username, password: password})
.success(function() {
user.isLogged = true;
defer.resolve("User login success");
})
.error(function() {
defer.reject("User login failed");
})
return defer.promise;
}
user.logout = function() {
var defer = $q.defer();
$http.post("/logout", {})
.success(function() {
// sucessfully logged out
user.isLogged = false;
defer.resolve();
}).error(function() {
// unable to logout for some reason
defer.reject();
});
return defer.promise;
}
return user;
});
And here's the server side in Node JS:
module.exports = function(app) {
// GET users
app.get('/users', function(req, res, next) {
async.parallel([
function(next) {
User.count(next);
},
function(next) {
// get all users (within specified query parameters)
var query = User.find({});
restUtil.setSort(query, req);
restUtil.setLimits(query, req);
query.exec(next);
}
], function(err, results) { // final callback
if(err) {
return next(err);
}
var count = results[0];
var users = results[1];
// add total number as header (for pagination, etc)
res.set('total', count);
res.json(users);
});
});
// POST a new user
app.post('/users', hashPassword, function(req, res, next) {
User.create(req.body, function(err, user) {
if(err) {
if(err.code===11000) {
res.send("Conflict", 409);
} else {
next(err);
}
}
res.json(user);
});
});
// POST a login request
app.post("/login", loadUserByUsername, function(req, res, next) {
bcrypt.compare(req.body.password, req.user.passwordHash, function(err, ok) {
if(ok) {
req.session.user = req.user; // logged in
res.send("Login ok", 200);
} else {
// incorrect password
res.send("Incorrect password", 400);
}
})
})
// POST a logout request
app.post("/logout", function(req, res, next) {
req.session.destroy();
res.send(200);
})
// GET logout status
app.get("/loggedIn", function(req, res, next) {
if(req.session.user) {
res.send("Logged in", 200);
} else {
res.send("Not Logged in", 400)
}
})
app.delete("/users/:_id", loadUserById, function(req, res, next) {
User.remove({ _id: req.params._id }, function(err) {
if(err) {
res.send("An error occurred", 404);
} else {
res.send("Delete Success", 200);
}
});
});
};
Haven't taken it further than that as of yet, but I like the client side code so far. Haven't given much thought to user roles etc, but this should get you started. It wouldn't be difficult to add further promises to resolve against. resolve: user.login
would cover a lot of needs.
You might also check out this Blog Post
Upvotes: 1