×
By the end of this chapter, you should be able to:
oAuth
standard isoAuth1
or oAuth2
express
application using oauth
So far we have seen how to authenticate using a local strategy (username/password), but what happens if we want to authenticate through another provider like Facebook, Twitter, LinkedIn, or GitHub? To do that we use a protocol called oAuth. There are currently two versions of oAuth, 1.0 and 2.0 and some providers use 1.0 while others use 2.0. They are not very different; here is how it works:
To set this up, we have to create a developer account with a provider (Facebook, Twitter, etc). Once we create an account we can create applications and receive a client_id, client secret key, and specify our callback URL (the url which we would like to tell the provider to go back to when the user has finished authenticating on the provider's website). These values - especially the secret key - should not be published on GitHub ever and should be hidden as environment variables.
passport-facebook
Let's create a sample application to authenticate through Facebook.
In order to successfully authenticate through Facebook, you will need to create a new application at https://developers.facebook.com/. You can create a new app, call it whatever you'd like, and choose whatever category you'd like. You can then head to your dashboard where you will see your App ID and App Secret (which will be hidden until you click show). You can head over to "Settings" and add localhost
to your App Domains and http://localhost:3000/auth/facebook/callback
to your Site URL.
Next, let's create our application. Here's some code to get you started:
const express = require("express"); const app = express(); const methodOverride = require("method-override"); const morgan = require("morgan"); const bodyParser = require("body-parser"); const passport = require("passport"); const findOrCreate = require("mongoose-findorcreate"); const FacebookStrategy = require("passport-facebook").Strategy; const mongoose = require("mongoose"); const session = require("cookie-session"); const flash = require("connect-flash"); mongoose.Promise = Promise; app.set("view engine", "pug"); app.use(express.static(__dirname + "/public")); app.use(morgan("tiny")); app.use(bodyParser.urlencoded({ extended: true })); app.use(methodOverride("_method")); app.use(session({ secret: process.env.SECRET_KEY })); app.use(passport.initialize()); app.use(passport.session()); app.use(flash()); // Redirect the user to Facebook for authentication. When complete, // Facebook will redirect the user back to the application at // /auth/facebook/callback app.get("/auth/facebook", passport.authenticate("facebook")); // Facebook will redirect the user to this URL after approval. Finish the // authentication process by attempting to obtain an access token. If // access was granted, the user will be logged in. Otherwise, // authentication has failed. app.get( "/auth/facebook/callback", passport.authenticate("facebook", { successRedirect: "/", failureRedirect: "/login" }) ); app.get("/", (req, res, next) => { // send the authenticated user res.send(req.user); }); app.get("/logout", (req, res, next) => { // send the authenticated user req.logout(); res.redirect("/"); }); var userSchema = new mongoose.Schema({ facebook_id: String }); userSchema.plugin(findOrCreate); // give us a findOrCreate method! var User = mongoose.model("User", userSchema); // instead of a username and password we will be using information that Facebook has given us when we create an application with them passport.use( new FacebookStrategy( { // these should ALL be values coming from a .env file clientID: process.env.FACEBOOK_APP_ID, clientSecret: process.env.FACEBOOK_APP_SECRET, // when you deploy your application you can add an environment variable for CALLBACK_URL, right now let's stick with localhost:3000/auth/facebook/callback callbackURL: process.env.CALLBACK_URL || "http://localhost:3000/auth/facebook/callback" }, // in the verify callback we will get an accessToken to make authenticated requests on the users behalf along with a refreshToken which is used in some authentication strategies to refresh an expired accessToken. We also are given an object called "profile" which has data on the authenticated user (accessToken, refreshToken, profile, done) => { User.findOrCreate({ facebook_id: profile.id }, (err, user) => { if (err) done(err); done(null, user); }); } ) ); // same process as before: passport.serializeUser((user, done) => { done(null, user.id); }); // same process as before: passport.deserializeUser((id, done) => { User.findById(id, (err, user) => { done(err, user); }); }); app.listen(3000, () => { console.log("Server is listening on port 3000"); });
You might be asking, should I store the access token in the database? You can read more about that here and here.
http://passportjs.org/docs/facebook
You can find a sample app with passport and facebook here.
When you're ready, move on to Introduction to NoSQL