Source

backend/passportConfigs/passportSetup.js

/**
 * This module is in charge of settings up passport for our various authentication methods
 * 
 * Twitch_strategy - 
 * If twitch returns with a user with user id that already exists we - we sign the user in.
 * If it returns with a user id that we do not have - we create a new user.
 * Otherwise - indication that an error occured.
 * 
 * Google strategy 
 * If youtube returns with a user id that already exists we sign the user in.
 * If it returns with a new user id - we use the youtube API to retrieve the google account's linked youtube account.
 * We then create a new user
 * Otherwise - indication that an error occured.
 * 
 * Facebook Strategy   
 * If twitch returns with a user with user id that already exists we - we sign the user in.
 * If it returns with a user id that we do not have - we create a new user.
 * Otherwise - indication that an error occured.
 * 
 * @module PassportSetup
 * @category Backend
 */

const bcrypt = require("bcryptjs");
var youtubeController = require("../controllers/youtubeController");
var notificationController = require("../controllers/notificationController");
//all our strategies
const LocalStrategy = require("passport-local").Strategy;
const FacebookStrategy = require("passport-facebook").Strategy;
const TwitchStrategy = require("passport-twitch.js").Strategy;
const GoogleStrategy = require("passport-google-oauth20").Strategy;

//our keys
const passportConfigs = require("./passportConfigs.js");
const { response } = require("express");
const { json } = require("body-parser");
const { GOOGLE_CONFIG, FACEBOOK_CONFIG } = require("./passportConfigs.js");

// our Models
const { User, FriendsData, FollowData, Notification } = require("../models/user");
const { GoogleData } = require("../models/user");
const { TwitchData } = require("../models/user");
const { FacebookData } = require("../models/user");

const YOUTUBE_SCOPE = "https://www.googleapis.com/auth/youtube.readonly";

// Used for authenticating users, passport is passed to not create new instance
module.exports = function (passport) {
  // Local strategy
  passport.use(
    new LocalStrategy((username, password, done) => {
      User.findOne({ username: username }, (err, user) => {
        if (err) throw err;
        if (!user) return done(null, false);
        bcrypt.compare(password, user.password, (err, result) => {
          if (err) throw err;
          if (result === true) {
            return done(null, user);
          } else {
            return done(null, false);
          }
        });
      });
    })
  );

  // Twitch Strategy
  passport.use(
    new TwitchStrategy(
      {
        clientID: passportConfigs.TWITCH_CONFIG.clientID,
        clientSecret: passportConfigs.TWITCH_CONFIG.clientSecret,
        callbackURL: "/auth/twitch/callback",
      },
      // Twitch auth callback function
      function (accessToken, refreshToken, profile, done) {
        // We try to see if we have an existing user with this id
        User.findOne(
          { twitchId: profile.id },
          // findOne callback function
          async function (err, doc) {
            if (err) {
              console.log("error occured");
              throw err;
            }
            // If we find a user with the existing twitch id we just sign in
            if (doc) {
              console.log(profile.id);
              return done(null, doc);
            }
            //Otherwise we create a new user
            else {
              const newTwitchData = new TwitchData({
                login: profile.login,
                display_name: profile.display_name,
                description: profile.description,
                profile_image_url: profile.profile_image_url,
                view_count: profile.view_count,
              });
              const friendsData = new FriendsData({
                friendsList: [],
                receivedRequests: [],
                sentRequests: [],
              });
              const followData = new FollowData({
                followersList: [],
                followingList: [],
              });
              const newUser = new User({
                username: profile.display_name,
                twitchId: profile.id,
                twitchData: newTwitchData,
                friendsData: friendsData,
                followData: followData,
              });
              // we save and finish
              await newUser.save();
              const notificationData = new Notification({
                type: "welcomeNotification",
                clearable: true,
              });
              notificationController.addNotificationToUser(newUser._id,notificationData ,null);
              return done(null, newUser);
            }
          }
        );
      }
    )
  );

  // Google strategy
  passport.use(
    new GoogleStrategy(
      {
        clientID: GOOGLE_CONFIG.clientID,
        clientSecret: GOOGLE_CONFIG.clientSecret,
        callbackURL: "/auth/google/callback",
        scope: ["profile", "email", YOUTUBE_SCOPE],
      },
      // Google auth callback function
      async function (accessToken, refreshToken, profile, done) {
        // We try to see if we have an existing user with this id
        User.findOne(
          { googleId: profile.id },
          // findOne callback function
          async function (err, doc) {
            if (err) {
              console.log("error occured");
            }

            // If we find a user with the existing google id we just sign in
            if (doc) {
              // If we didnt have a youtube account we try and find one
              if(!doc.googleData.youtubeId){
                var channels = await youtubeController.getYoutubeChannelFromGoogle(
                  accessToken,
                  refreshToken
                );
                doc.googleData.youtubeId = (channels && (channels.length > 0)) ? channels[0].id : null;
                doc.googleData.youtubeName = (channels && (channels.length > 0))? channels[0].snippet.title : null;
                doc.markModified('googleData');
                await doc.save();
              }
              return done(null, doc);
            }
            //Otherwise we create a new user
            else {
              var channels = await youtubeController.getYoutubeChannelFromGoogle(
                accessToken,
                refreshToken
              );
              const newGoogleData = new GoogleData({
                displayName: profile.displayName,
                youtubeId: (channels && (channels.length > 0)) ? channels[0].id : null,
                youtubeName:
                (channels && (channels.length > 0))? channels[0].snippet.title : null,
                name: profile.name,
                emails: profile.emails,
                photos: profile.photos,
              });
              const friendsData = new FriendsData({
                friendsList: [],
                receivedRequests: [],
                sentRequests: [],
              });
              const followData = new FollowData({
                followersList: [],
                followingList: [],
              });
              const newUser = new User({
                username: profile.displayName,
                googleId: profile.id,
                googleData: newGoogleData,
                friendsData: friendsData,
                followData: followData,
              });
              // we save and finish
              await newUser.save();
              // Add notification about youtube missing
              if(!newUser.googleData.youtubeId){
                const notificationData = new Notification({
                  type: "noYoutubeAccount",
                  clearable: true,
                });
                notificationController.addNotificationToUser(newUser._id,notificationData ,null);
              }
              const notificationData = new Notification({
                type: "welcomeNotification",
                clearable: true,
              });
              notificationController.addNotificationToUser(newUser._id,notificationData ,null);
              return done(null, newUser);
            }
          }
        );
      }
    )
  );

  // Facebook strategy
  passport.use(
    new FacebookStrategy(
      {
        clientID: FACEBOOK_CONFIG.clientID,
        clientSecret: FACEBOOK_CONFIG.clientSecret,
        callbackURL: "/auth/facebook/callback",
        profileFields: [
          "id",
          "displayName",
          "name",
          "link",
          "picture.type(large)",
        ],
      },
      // Facebook auth callback function
      async function (accessToken, refreshToken, profile, done) {
        // We try to see if we have an existing user with this id
        User.findOne(
          { facebookId: profile.id },
          // findOne callback function
          async function (err, doc) {
            if (err) {
              console.log("error occured");
              throw err;
            }
            // If we find a user with the existing facebook id we just sign in
            if (doc) {
              return done(null, doc);
            }
            //Otherwise we create a new user
            else {
              const newFacebookData = new FacebookData({
                displayName: profile.displayName,
                profileUrl: profile.profileUrl,
                name: profile.name,
                photos: profile.photos,
              });
              const friendsData = new FriendsData({
                friendsList: [],
                receivedRequests: [],
                sentRequests: [],
              });
              const followData = new FollowData({
                followersList: [],
                followingList: [],
              });
              const newUser = new User({
                username: profile.displayName,
                facebookId: profile.id,
                facebookData: newFacebookData,
                friendsData: friendsData,
                followData: followData,
              });
              // we save and finish
              await newUser.save();
              const notificationData = new Notification({
                type: "welcomeNotification",
                clearable: true,
              });
              notificationController.addNotificationToUser(newUser._id,notificationData ,null);
              return done(null, newUser);
            }
          }
        );
      }
    )
  );

  passport.serializeUser((user, cb) => {
    cb(null, user.id);
  });

  passport.deserializeUser((id, cb) => {
    User.findOne({ _id: id }, (err, user) => {
      if (err || user == null) {
        return cb(err, null);
      }

      // We only return relevant data
      const userInformation = {
        _id: user._id,
        username: user.username,
        image: user.image,
        shortDescription: user.shortDescription,
        description: user.description,
        currentStream: user.currentStream,
        twitchId: user.twitchId,
        googleId: user.googleId,
        twitchData: user.twitchData,
        googleData: user.googleData,
        facebookData: user.facebookData,
        upcomingEvents: user.upcomingEvents,
        friendsData: user.friendsData,
        notifications: user.notifications,
        followData: user.followData,
        interests: user.interests,
      };
      cb(err, userInformation);
    });
  });
};