299 | ```
300 |
301 | Alright we're almost there! Let's add our main angular logic to the `app.js` file inside of the `js` directory:
302 |
303 | ```js
304 | var app = angular.module("PassportApp", ["ngRoute"]);
305 |
306 | app.config(function($routeProvider) {
307 | $routeProvider
308 | .when('/home', {
309 | templateUrl: 'views/home.html'
310 | })
311 | .when('/login', {
312 | templateUrl: 'views/login.html',
313 | controller: 'LoginCtrl'
314 | })
315 | .when('/signup', {
316 | templateUrl: 'views/signup.html',
317 | controller: 'SignUpCtrl'
318 | })
319 | .when('/profile', {
320 | templateUrl: 'views/profile.html',
321 | resolve: {
322 | logincheck: checkLoggedin
323 | }
324 | })
325 | .otherwise({
326 | redirectTo: '/home'
327 | })
328 | });
329 |
330 | var checkLoggedin = function($q, $timeout, $http, $location, $rootScope) {
331 | var deferred = $q.defer();
332 |
333 | $http.get('/loggedin').success(function(user) {
334 | $rootScope.errorMessage = null;
335 | //User is Authenticated
336 | if (user !== '0') {
337 | $rootScope.currentUser = user;
338 | deferred.resolve();
339 | } else { //User is not Authenticated
340 | $rootScope.errorMessage = 'You need to log in.';
341 | deferred.reject();
342 | $location.url('/login');
343 | }
344 | });
345 | return deferred.promise;
346 | }
347 | ```
348 |
349 | And finally, put the following code into the `controllers.js` file, also living inside of the `js` folder:
350 |
351 | ```js
352 | app.controller("NavCtrl", function($rootScope, $scope, $http, $location) {
353 | $scope.logout = function() {
354 | $http.post("/logout")
355 | .success(function() {
356 | $rootScope.currentUser = null;
357 | $location.url("/home");
358 | });
359 | }
360 | });
361 |
362 | app.controller("SignUpCtrl", function($scope, $http, $rootScope, $location) {
363 | $scope.signup = function(user) {
364 |
365 | if (user.password == user.password2) {
366 | $http.post('/signup', user)
367 | .success(function(user) {
368 | $rootScope.currentUser = user;
369 | $location.url("/profile");
370 | });
371 | }
372 | }
373 | });
374 |
375 | app.controller("LoginCtrl", function($location, $scope, $http, $rootScope) {
376 | $scope.login = function(user) {
377 | $http.post('/login', user)
378 | .success(function(response) {
379 | $rootScope.currentUser = response;
380 | $location.url("/profile");
381 | });
382 | }
383 | });
384 | ```
385 |
386 | That's it! Now fire up your server and give it a try, you should now have a working local authentication stratey in place. Don't forget to run `mongod` in a separate tab before starting your server.
387 |
388 | # [Facebook strategy tutorial](./facebook.md)
389 |
390 | -------------------------
391 | ## Sources
392 |
393 | - [Express and Passport - Scotch.io](https://scotch.io/tutorials/easy-node-authentication-setup-and-local)
394 | - [Angular, Express and Passport - YouTube tutorial](https://www.youtube.com/watch?v=jtaSRzP0i30&feature=youtu.be)
395 | - [Source code from tutorial above](https://github.com/soaresdiogo/authenticatePassport)
396 |
397 | --------------------------
398 |
--------------------------------------------------------------------------------
/app.js:
--------------------------------------------------------------------------------
1 | //Express
2 | var express = require('express');
3 | var app = express();
4 | app.use(express.static(__dirname + '/public'));
5 |
6 | //Passport
7 | var passport = require('passport');
8 | require('./config/passport')(passport); // pass passport for configuration
9 |
10 | //Cookie and session
11 | var cookieParser = require('cookie-parser');
12 | var session = require('express-session');
13 | app.use(session({
14 | secret: 'this is the secret'
15 | }));
16 | app.use(cookieParser());
17 | app.use(passport.initialize());
18 | app.use(passport.session());
19 |
20 | //Body-parser
21 | var bodyParser = require('body-parser');
22 | app.use(bodyParser.json()); //for parsing application/json
23 | app.use(bodyParser.urlencoded({
24 | extended: true
25 | }));
26 |
27 | //Load .env file
28 | var dotenv = require('dotenv');
29 | dotenv.load();
30 |
31 | // routes ======================================================================
32 | require('./routes/auth.js')(app, passport); // load our routes and pass in our app and fully configured passport
33 |
34 |
35 | app.listen(3000);
--------------------------------------------------------------------------------
/config/oauth.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | facebookAuth: {
3 | clientID: process.env.FB_CLIENT_ID,
4 | clientSecret: process.env.FB_SECRET,
5 | callbackURL: process.env.FB_CALLBACK_URL,
6 | enableProof: false
7 | }
8 | }
--------------------------------------------------------------------------------
/config/passport.js:
--------------------------------------------------------------------------------
1 | // load all the things we need
2 | var LocalStrategy = require('passport-local').Strategy;
3 | var FacebookStrategy = require('passport-facebook').Strategy;
4 | var configAuth = require('./oauth');
5 | // load up the user model
6 | var User = require('../models/user');
7 |
8 | // expose this function to our app using module.exports
9 | module.exports = function(passport) {
10 |
11 | passport.serializeUser(function(user, done) {
12 | done(null, user);
13 | });
14 |
15 | passport.deserializeUser(function(user, done) {
16 | done(null, user);
17 | });
18 |
19 | passport.use('local-login', new LocalStrategy(
20 | function(username, password, done) {
21 | User.findOne({
22 | username: username.toLowerCase()
23 | }, function(err, user) {
24 | // if there are any errors, return the error before anything else
25 | if (err)
26 | return done(err);
27 |
28 | // if no user is found, return the message
29 | if (!user)
30 | return done(null, false);
31 |
32 | // if the user is found but the password is wrong
33 | if (!user.validPassword(password))
34 | return done(null, false);
35 |
36 | // all is well, return successful user
37 | return done(null, user);
38 | });
39 | }
40 | ));
41 |
42 | // Facebook strategy
43 | passport.use(new FacebookStrategy({
44 |
45 | // pull in our app id and secret from our auth.js file
46 | clientID : configAuth.facebookAuth.clientID,
47 | clientSecret : configAuth.facebookAuth.clientSecret,
48 | callbackURL : configAuth.facebookAuth.callbackURL
49 |
50 | },
51 |
52 | // facebook will send back the token and profile
53 | function(token, refreshToken, profile, done) {
54 | // asynchronous
55 | process.nextTick(function() {
56 |
57 | // find the user in the database based on their facebook id
58 | User.findOne({ 'facebook.id' : profile.id }, function(err, user) {
59 |
60 | // if there is an error, stop everything and return that
61 | // ie an error connecting to the database
62 | if (err)
63 | return done(err);
64 |
65 | // if the user is found, then log them in
66 | if (user) {
67 | return done(null, user); // user found, return that user
68 | } else {
69 | // if there is no user found with that facebook id, create them
70 | var newUser = new User();
71 |
72 | // set all of the facebook information in our user model
73 | newUser.facebook.id = profile.id; // set the users facebook id
74 | newUser.facebook.token = token; // we will save the token that facebook provides to the user
75 | newUser.facebook.name = profile.displayName; // look at the passport user profile to see how names are returned
76 |
77 | // save our user to the database
78 | newUser.save(function(err) {
79 | if (err)
80 | throw err;
81 |
82 | // if successful, return the new user
83 | return done(null, newUser);
84 | });
85 | }
86 |
87 | });
88 | });
89 |
90 | }));
91 | };
92 |
--------------------------------------------------------------------------------
/facebook.md:
--------------------------------------------------------------------------------
1 | # Add facebook auth to your app with passport
2 |
3 | First thing we'll want to do is add a few more dependencies to our `package.json`
4 |
5 | ```
6 | "dotenv": "^1.2.0",
7 | "passport-facebook": "^2.0.0"
8 | ```
9 |
10 | Be sure to run `npm install`
11 |
12 | We'll use `passport-facebook` for our passport strategy configuration and `dotenv` to hide our API's secret key and client ID.
13 |
14 | Now open up your backend's `app.js` file from your app's root directory and add the following after your `body-parser` code:
15 |
16 | ```js
17 | //Load .env file
18 | var dotenv = require('dotenv');
19 | dotenv.load();
20 | ```
21 |
22 | Now visit the [Facebook developer's portal](https://developers.facebook.com/) and where it says `My Apps` select `Add a New App` from the dropdown. Select `WWW` from the app options, enter a name for your app, click through and enter a category, click through again, scroll down to app configuration and enter your site url `http://localhost:3000/auth/facebook/callback`.
23 |
24 | Scroll to the bottom and select `skip to the developer dashboard`, this is where you'll get access to your Client ID and Secret Key.
25 |
26 | Take that information and enter it into a `.env` file inside of your app's root directory. (First be sure to add `.env` to your `.gitignore` file)
27 |
28 | ```
29 | FB_CLIENT_ID=123456789
30 | FB_SECRET=a1b2c3d4e5f6g7h8j9k0
31 | FB_CALLBACK_URL=http://localhost:3000/auth/facebook/callback
32 | ```
33 |
34 | Replace everything after the `=` signs with your information, the FB_CALLBACK_URL should remain the same.
35 |
36 | _Note: This should work fine, but if your app still won't recognize the ENV variables, try exporting them manually from the command line, e.g., open up your terminal and type: `export FB_CLIENT_ID=123456789` and do the same for your FB_SECRET and FB_CALLBACK_URL_
37 |
38 | Now let's configure oath, create a file called `oath.js` inside of your `config` directory that lives inside of app's root directory. Add the following:
39 |
40 | ```js
41 | module.exports = {
42 | facebookAuth: {
43 | clientID: process.env.FB_CLIENT_ID,
44 | clientSecret: process.env.FB_SECRET,
45 | callbackURL: process.env.FB_CALLBACK_URL,
46 | enableProof: false
47 | }
48 | }
49 | ```
50 |
51 | Now we can modify our passport configuration file to add the facebook strategy.
52 | Open `app_name -> config -> passport.js` and add the following below where you require local-strategy:
53 |
54 | ```js
55 | var FacebookStrategy = require('passport-facebook').Strategy;
56 | var configAuth = require('./oauth');
57 | ```
58 |
59 | Now add this to the bottom of your app before the closing `}` bracket from the `module.exports = function(passport) {`:
60 |
61 | ```js
62 | // Facebook strategy
63 | passport.use(new FacebookStrategy({
64 |
65 | // pull in our app id and secret from our auth.js file
66 | clientID : configAuth.facebookAuth.clientID,
67 | clientSecret : configAuth.facebookAuth.clientSecret,
68 | callbackURL : configAuth.facebookAuth.callbackURL
69 |
70 | },
71 |
72 | // facebook will send back the token and profile
73 | function(token, refreshToken, profile, done) {
74 | // asynchronous
75 | process.nextTick(function() {
76 |
77 | // find the user in the database based on their facebook id
78 | User.findOne({ 'facebook.id' : profile.id }, function(err, user) {
79 |
80 | // if there is an error, stop everything and return that
81 | // ie an error connecting to the database
82 | if (err)
83 | return done(err);
84 |
85 | // if the user is found, then log them in
86 | if (user) {
87 | return done(null, user); // user found, return that user
88 | } else {
89 | // if there is no user found with that facebook id, create them
90 | var newUser = new User();
91 |
92 | // set all of the facebook information in our user model
93 | newUser.facebook.id = profile.id; // set the users facebook id
94 | newUser.facebook.token = token; // we will save the token that facebook provides to the user
95 | newUser.facebook.name = profile.displayName; // look at the passport user profile to see how names are returned
96 |
97 | // save our user to the database
98 | newUser.save(function(err) {
99 | if (err)
100 | throw err;
101 |
102 | // if successful, return the new user
103 | return done(null, newUser);
104 | });
105 | }
106 |
107 | });
108 | });
109 |
110 | }));
111 | ```
112 |
113 | Okay, the facebook-strategy is in place, but we need to update our user model to reflect the code in our passport configuration. Open up `app_name -> models -> user.js` and replace your user schema with:
114 |
115 | ```js
116 | // define the schema for our user model
117 | var userSchema = mongoose.Schema({
118 | username: String,
119 | password: String,
120 | email: String,
121 | firstName: String,
122 | lastName: String,
123 | facebook: {
124 | id: String,
125 | token: String,
126 | name: String
127 | }
128 | });
129 | ```
130 |
131 | We're almost done, now let's go add the backend routes that allow us to grant access from facebook and redirect to our app. Open up `app_name -> routes -> auth.js` and add the following code before the closing `};` bracket from `module.exports = function(app, passport) {`:
132 |
133 | ```js
134 |
135 | // Facebook auth routes
136 | app.get('/auth/facebook', function authenticateFacebook (req, res, next) {
137 | req.session.returnTo = '/#' + req.query.returnTo;
138 | next ();
139 | },
140 | passport.authenticate ('facebook'));
141 |
142 | app.get('/auth/facebook/callback', function (req, res, next) {
143 | var authenticator = passport.authenticate ('facebook', {
144 | successRedirect: req.session.returnTo,
145 | failureRedirect: '/'
146 | });
147 |
148 | delete req.session.returnTo;
149 | authenticator (req, res, next);
150 | })
151 | ```
152 |
153 | Let's add a facebook login button to both our `login` and `signup` pages (inside of `app_name -> public -> views`:
154 |
155 | ```html
156 |
Login or Register with:
157 |
158 | Facebook
159 | ```
160 |
161 | You can see how the query string from the end of the anchor tag's href will allow us to redirect to a route being used by the angular router, thus bridging us from the backend routing to the front end.
162 |
163 | One last thing, let's make sure the `profile` page will display the user's name when they log in with facebook.
164 |
165 | Open up `app_name -> public -> views -> profile.html` and replace the code there with:
166 |
167 | ```html
168 |