# OAuth with Google and Facebook
## OAuth
### What is OAuth?
OAuth is a technical standard for authorizing users. It passes authorization from one service to another without sharing the user credentials, such as a username and password.
With OAuth, a user can sign in on one platform and then be authorized to perform actions and view data on another platform.
OAuth makes it possible to pass authorization from one application to another regardless of what the two applications are. OAuth is one of the most common methods used to pass authorization from a single sign-on (SSO) service to another cloud application, but it could be used between any two applications.
Imagine a visitor comes to a house when the homeowner is not there, and instead of sending the visitor an actual house key, the owner sends them a temporary code to get into a lockbox that contains the key. OAuth works in a similar manner. In OAuth, one application sends another application an authorization token to give a user access, instead of sending the user's credentials.

### What is OAuth 2.0?
OAuth 2.0 is the latest version of OAuth. The first version of OAuth was published in 2010. OAuth 2.0 was published in 2012, and it fixed a number of vulnerabilities that were present in OAuth 1.0.
## Create Google and Facebook App
### Create a new app at [Goodle](https://console.cloud.google.com/)












### Create a new app at [Facebook](https://developers.facebook.com/)




## Implement Frontend
```
npm install react-facebook-login react-google-login
```
Put these variables into `.env`
```
REACT_APP_BACKEND_API="http://localhost:5000"
REACT_APP_FB_APP_ID=""
REACT_APP_GOOGLE_CLIENT_ID=""
```
Render buttons inside the body of `RegisterPage`
```
<FacebookLogin
appId={FB_APP_ID}
fields="name,email,picture"
callback={loginWithFacebook}
icon="fa-facebook"
onFailure={(err) => {
console.log("FB LOGIN ERROR:", err);
}}
containerStyle={{
textAlign: "center",
backgroundColor: "#3b5998",
borderColor: "#3b5998",
flex: 1,
display: "flex",
color: "#fff",
cursor: "pointer",
marginBottom: "3px",
}}
buttonStyle={{
flex: 1,
textTransform: "none",
padding: "12px",
background: "none",
border: "none",
}}
/>
<GoogleLogin
className="google-btn d-flex justify-content-center"
clientId={GOOGLE_CLIENT_ID}
buttonText="Login with Google"
onSuccess={loginWithGoogle}
onFailure={(err) => {
console.log("GOOGLE LOGIN ERROR:", err);
}}
cookiePolicy="single_host_origin"
/>
```
Define `oauthLogin` inside the body of App.
```
const loginWithFacebook = (response) => {
dispatch(authActions.loginFacebookRequest());
};
const loginWithGoogle = (response) => {
dispatch(authActions.loginGoogleRequest());
};
```
## Implement Backend
Installing necessary packages
```
npm install passport passport-facebook-token passport-google-token bcryptjs dotenv jsonwebtoken nodemon cors
```
Define variables in`.env` file
Here are all the variables we'll need, we get them from Google & Facebook.
```
PORT=5000
MONGODB_URI=mongodb://localhost:27017/ecommerce
JWT_SECRET_KEY="secret_of_jaguar"
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
FACEBOOK_APP_ID=
FACEBOOK_APP_SECRET=
```
Initialize Passport inside of `app.js`.
Passport is the package we'll use to manage authentication more easily.
```
const passport = require("passport");
require("./middlewares/passport");
app.use(passport.initialize());
```
Configure Passport middleware in ./middlewares/passport.js.
This is where we configure our authentication strategy.
We define how we'll parse the data coming from Facebook, Google and our frontend app using one of two strategies, `FacebookTokenStrategy`, `GoogleTokenStrategy`. [FacebookTokenStrategy](https://www.npmjs.com/package/passport-facebook-token), [GoogleTokenStrategy](https://www.npmjs.com/package/passport-google-token)
```
const passport = require("passport");
const FacebookTokenStrategy = require("passport-facebook-token");
const GoogleTokenStrategy = require("passport-google-token").Strategy;
const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID;
const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET;
const FACEBOOK_APP_ID = process.env.FACEBOOK_APP_ID;
const FACEBOOK_APP_SECRET = process.env.FACEBOOK_APP_SECRET;
const User = require("../models/User");
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (user, done) {
done(null, user);
});
passport.use(
new FacebookTokenStrategy(
{
fbGraphVersion: "v3.0",
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
},
function (_, _, profile, done) {
User.findOrCreate(
{
facebookId: profile.id,
name: profile.displayName,
email: profile.emails[0].value,
avatarUrl: profile.photos[0].value,
},
function (error, user) {
return done(error, user);
},
);
},
),
);
passport.use(
new GoogleTokenStrategy(
{
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
},
function (_, _, profile, done) {
User.findOrCreate(
{
googleId: profile.id,
name: profile.displayName,
email: profile.emails[0].value,
avatarUrl: profile._json.picture,
},
function (err, user) {
return done(err, user);
},
);
},
),
);
```
* Create route file `./routes/auth.api.js` and define three endpoints in it.
* POST /auth/login
* POST /auth/login/google
* POST /login/facebook
```
var express = require("express");
var router = express.Router();
const passport = require("passport");
const authController = require("../controllers/auth.controller");
router.post(
"/login/google",
passport.authenticate("google-token", { session: false }),
authController.loginWithFacebookOrGoogle,
);
router.post(
"/login/facebook",
passport.authenticate("facebook-token", { session: false }),
authController.loginWithFacebookOrGoogle,
);
module.exports = router;
```
Take note of the arguments send to router.post().
1. Path.
2. A middleware, Passport's authentication strategy; google-token or facebook-token.
3. The controller action/function we want fire when the endpoint is hit.
The different paths result in different authentication strategies. Passport's job is to normalize incoming data. In other words, it makes data consistent when it hits out backend endpoint. Notice all three paths hit the same controller action.
- Define controller endpoints in ./controllers/auth.controller.js.
We import `bcrypt` because we use this library to encrypt our users password when they create an account.
```
const bcrypt = require("bcryptjs");
const User = require("../models/User");
const authController = {};
authController.loginWithFacebookOrLogin = async ({ user }, res) => {
if (user) {
user = await User.findByIdAndUpdate(
user._id,
{ avatarUrl: user.avatarUrl },
{ new: true },
);
} else {
throw new Error("There is No User");
}
const accessToken = await user.generateToken();
res.status(200).json({ status: "success", data: { user, accessToken } });
};
module.exports = authController;
```
We'll use these packages to secure our app.
```
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const JWT_SECRET_KEY = process.env.JWT_SECRET_KEY;
```
- Implement `findOrCreate()` on the User model in ./server/models/User.js.
This function exists on User. Learn more [here](https://mongoosejs.com/docs/2.7.x/docs/methods-statics.html)
```
userSchema.statics.findOrCreate = function findOrCreate(profile, cb) {
const userObj = new this(); // create a new User class
this.findOne({ email: profile.email }, async function (err, result) {
if (!result) {
let newPassword =
profile.password || "" + Math.floor(Math.random() * 100000000);
const salt = await bcrypt.genSalt(10);
newPassword = await bcrypt.hash(newPassword, salt);
userObj.name = profile.name;
userObj.email = profile.email;
userObj.password = newPassword;
userObj.googleId = profile.googleId;
userObj.facebookId = profile.facebookId;
userObj.avatarUrl = profile.avatarUrl;
userObj.save(cb);
} else {
cb(err, result);
}
});
};
```