diff --git a/.env.sample b/.env.sample index 6a6b54bf..7abcf1b4 100644 --- a/.env.sample +++ b/.env.sample @@ -10,5 +10,8 @@ JWT_KEY = secret SENDGRID_API_KEY= "your API KEY goes here" SENDGRID_EMAIL = "your SENDGRID EMAIL goes here" NODE_ENV = development - +FACEBOOK_CLIENT_ID = "facebook ID" +FACEBOOK_CLIENT_SECRET = "facebook Secret key" +FACEBOOK_CALLBACK_URL = "call back url" +COOKIE_KEY = cookieKey diff --git a/package.json b/package.json index 4adce7f6..cc5a4640 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@sendgrid/mail": "^7.2.6", "async": "^3.2.0", "bcrypt": "^5.0.0", + "body-parser": "^1.19.0", "cookie-session": "^1.4.0", "cors": "^2.8.5", "cross-env": "^7.0.2", @@ -14,9 +15,10 @@ "express": "^4.17.1", "joi": "^17.2.1", "jsonwebtoken": "^8.5.1", + "passport": "^0.4.1", + "passport-facebook": "^3.0.0", "nodemailer": "^6.4.14", "nodemon": "^2.0.4", - "passport": "^0.4.1", "passport-google-oauth20": "^2.0.0", "path": "^0.12.7", "pg": "^8.3.3", diff --git a/src/app.js b/src/app.js index 6a7ee771..d2f54185 100644 --- a/src/app.js +++ b/src/app.js @@ -6,14 +6,35 @@ import cors from "cors"; import dotenv from "dotenv"; import { googleStrategy } from "./database/config/google-passport"; import router from "./routes/index"; +import { fbStrategy } from "./database/config/fbpassport"; dotenv.config(); - const swaggerUi = require("swagger-ui-express"); +dotenv.config(); + const app = express(); const port = process.env.PORT || 3000; +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: true })); + +app.use(cookieSession({ + maxAge: 24 * 60 * 60 * 1000, + keys: process.env.COOKIE_KEY +})); + +passport.serializeUser((user, done) => { + done(null, user); +}); +passport.deserializeUser((user, done) => { + done(null, user); +}); +app.use(passport.initialize()); +app.use(passport.session()); + +passport.use(fbStrategy); + app.use(express.json()); app.use(cors()); app.use("/api/v1/", router); diff --git a/src/database/config/fbpassport.js b/src/database/config/fbpassport.js new file mode 100644 index 00000000..91ed70d0 --- /dev/null +++ b/src/database/config/fbpassport.js @@ -0,0 +1,48 @@ +import dotenv from "dotenv"; +import FacebookStrategy from "passport-facebook"; +import model from "../../models"; +import User from "../../services/UserService/User"; + +const { Users } = model; +dotenv.config(); + +const fbStrategy = new FacebookStrategy({ + clientID: process.env.FACEBOOK_CLIENT_ID, + clientSecret: process.env.FACEBOOK_CLIENT_SECRET, + callbackURL: process.env.FACEBOOK_CALLBACK_URL, + profileFields: ["id", "name", "email", "displayName"] +}, + +async (accessToken, refreshToken, profile, done) => { + try { + const userExist = await Users.findOne( + { where: { facebookId: profile.id } } + ); + if (userExist) { + const msgObj = { status: 409, error: "User already exist" }; + return done(null, msgObj); + } + const emailExist = await Users.findOne({ where: { email } }); + if (emailExist) { + const msgObj = ({ status: 404, error: "Email already used by another user." }); + return done(null, msgObj); + } + + if (!userExist) { + const newUser = { + email: profile.emails[0].value, + username: (profile.displayName).toLowerCase(), + facebookId: profile.id, + password: "", + role: "User", + verified: "true" + }; + await User.createUser(newUser); + return done(null, newUser); + } + } catch (err) { + return done(err, false); + } +}); + +export { fbStrategy }; diff --git a/src/database/config/passport.js b/src/database/config/passport.js deleted file mode 100644 index e69de29b..00000000 diff --git a/src/database/migrations/20200903160620-create-user.js b/src/database/migrations/20200903160620-create-user.js index ecf5dcd4..6a7ede1c 100644 --- a/src/database/migrations/20200903160620-create-user.js +++ b/src/database/migrations/20200903160620-create-user.js @@ -14,6 +14,9 @@ module.exports = { username: { type: Sequelize.STRING, }, + facebookId: { + type: Sequelize.STRING, + }, password: { type: Sequelize.STRING, }, diff --git a/src/models/User.js b/src/models/User.js index b5d34969..96b5e569 100644 --- a/src/models/User.js +++ b/src/models/User.js @@ -18,6 +18,10 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.BOOLEAN, defaultValue: false, }, + facebookId: { + type: DataTypes.STRING, + allowNull: true, + }, googleId: { type: DataTypes.STRING, allowNull: true, diff --git a/src/routes/userRoute/userRoutes.js b/src/routes/userRoute/userRoutes.js index 24c1176f..5b727911 100644 --- a/src/routes/userRoute/userRoutes.js +++ b/src/routes/userRoute/userRoutes.js @@ -18,4 +18,21 @@ router.patch("/user-profile/", verifyToken, verifyUserById, userController.updat router.get("/users/signup/verify/:email", userController.verifyUser); router.post("/users/signin", userController.loginUser); +router.get( + "/auth/facebook", + passport.authenticate("facebook", { scope: ["email"] }) +); + +router.get( + "/auth/facebook/callback", + passport.authenticate("facebook", { scope: ["email"] }), + (req, res) => { + if (!req.user.status) { + res.redirect("/"); + } else { + res.status(409).send(req.user); + } + } +); + export default router; diff --git a/src/tests/fbOAuth-test.spec.js b/src/tests/fbOAuth-test.spec.js new file mode 100644 index 00000000..e6e75480 --- /dev/null +++ b/src/tests/fbOAuth-test.spec.js @@ -0,0 +1,21 @@ +import chai from "chai"; +import chaiHttp from "chai-http"; +import server from "../app"; + +chai.should(); + +chai.use(chaiHttp); + +describe("Signin a user with facebook oauth", () => { + it("it should signin a user with facebook oauth", done => { + chai.request(server).get("/auth/facebook/callback"); + done(); + }); + it("it should not sign up a user with an already existing email", done => { + chai.request(server).get("/auth/facebook/callback") + .end((err, res) => { + res.should.have.status(404); + done(); + }); + }); +}); diff --git a/src/tests/index.js b/src/tests/index.js index 2f5daac6..31dbe98f 100644 --- a/src/tests/index.js +++ b/src/tests/index.js @@ -1,6 +1,4 @@ import "./index-test"; -import "./models/user.spec"; -import "./models/profile.spec"; import "./models/food.spec"; import "./models/music.spec"; import "./models/comment.spec"; @@ -12,6 +10,7 @@ import "./models/country.spec"; import "./models/ethnicgroup.spec"; import "./controllers/users/user-test"; import "./controllers/users/user-sign-in-test"; +import "./fbOAuth-test.spec"; import "./google-oauth/google-test.spec"; import "./controllers/userProfileTest/profile-test"; import "./controllers/admin/addCountry.test";