Skip to content

Instantly share code, notes, and snippets.

@gaboelnuevo
Created June 21, 2021 21:03
Show Gist options
  • Save gaboelnuevo/86a22c883466b431771c2bde2227609b to your computer and use it in GitHub Desktop.
Save gaboelnuevo/86a22c883466b431771c2bde2227609b to your computer and use it in GitHub Desktop.
social login
import User, { UserModel } from "../models/users.model";
import Patient, { PatientModel } from "../models/patient";
import passport from "passport";
import * as passportLocal from "passport-local";
import RequestData, { RequestModel } from "../models/request.model";
import { SocialSignInStrategies } from "./SocialLogin";
import moment from "moment";
import * as _ from "lodash";
import UserSession from "../models/UserSession";
const MobileDetect = require("mobile-detect");
const LocalStrategy = passportLocal.Strategy;
export const getUsernameRegex = (username: String) => {
const regex = { $regex: new RegExp("^" + username.trim().toLowerCase().replace("+", "\\+") + "$", "i") };
return regex;
};
passport.use(
"local"
, new LocalStrategy(
(username, password, done) => {
const regex = getUsernameRegex(username);
User.findOne({ username: regex, enabled: true })
.populate({ path: "affiliate", populate: { path: "affiliateType" } })
.populate("userType")
.select("+password +hash +salt +auth")
.exec()
.then((user: UserModel) => {
if (!user) {
return done(undefined, false, { message: "error.notExists" });
}
if (!user.active) {
return done(undefined, false, { message: "error.inactive" });
}
user.isValidPassword(password, (err: Error & any, isMatch: boolean): any => {
if (err) {
return done(err);
}
if (isMatch) {
return done(undefined, user);
}
return done(undefined, false, { message: "error.userAndPassword" });
});
})
.catch((err: Error & any) => {
done(err);
});
}
));
passport.use(
"local-patient"
, new LocalStrategy(
(username, password, done) => {
const regex = getUsernameRegex(username);
Patient.findOne({ username: regex, enabled: true })
.select("+password +hash +salt +auth")
.populate("userType")
.exec()
.then((user: PatientModel) => {
console.log({ user });
if (!user) {
return done(undefined, false, { message: "err.notExists" });
}
if (!user.active) {
return done(undefined, false, { message: "error.inactive" });
}
user.isValidPassword(password, (err: Error & any, isMatch: boolean): any => {
if (err) {
return done(err);
}
if (isMatch) {
return done(undefined, user);
}
return done(undefined, false, { message: "error.userAndPassword" });
});
})
.catch((err: Error & any) => {
done(err);
});
}
));
passport.use(
"local-token"
, new LocalStrategy(
(username, password, done): any => {
let request_: any;
RequestData.findOne({ token: username }).exec()
.then((request: RequestModel): any => {
request_ = request;
if (!request) {
throw ({ message: "error.noRequest" });
}
if (!request.active) {
throw ({ message: "error.usedRequest" });
}
return User.findById(request.data.user)
.populate({ path: "affiliate", populate: { path: "affiliateType" } })
.populate("userType")
// .populate("countryDetails")
.exec();
})
.then((user: UserModel): any => {
// if (!user.affiliate.approved) {
// }
if (!user) {
throw ({ message: "error.notExists" });
}
// if (!user.active && !user.affiliate.approved) {
// return done(undefined, false, { message: "error.notapproved" });
// }
if (user.active) {
throw ({ message: "error.usedRequest" });
}
if (!user.enabled) {
throw ({ message: "error.deactivated" });
}
request_.active = false;
request_.save();
return done(undefined, user);
})
.catch((err: Error & any) => {
done(undefined, false, err);
});
}
));
export const addSocialSignInStrategies = (app: any) => {
Object.keys(SocialSignInStrategies).forEach((key: string) => {
passport.use(key, SocialSignInStrategies[key]);
app.get(`/auth/${key}`, (req: any, res: any, next: Function) => {
const state = {
secret: "" + Math.random().toString(36).substr(2, 20),
country: req.query.country,
metadata: req.query.metadata && JSON.parse(req.query.metadata)
};
return passport.authenticate(key, { state: Buffer.from(JSON.stringify(state)).toString("base64") })(req, res, next);
});
app.get(
`/auth/${key}/callback`,
function (req: any, res: any, next: any) {
// call passport authentication passing the "local" strategy name and a callback function
passport.authenticate(key, { failureRedirect: `/auth/${key}-error`, session: false }, function (error, user, info) {
// this will execute in any case, even if a passport strategy will find an error
// log everything to console
console.log({ error, user, info });
if (error) {
res.status(401).send(error);
} else if (!user) {
res.status(401).send(info);
} else {
next();
}
res.status(401).send(info);
})(req, res);
},
async (req: any, res: any) => {
const user = req.user;
const state = req.state || {};
if (!user) {
console.log({ state });
return res.status(401).json({ error: "No user found." });
}
const metadata = Object.assign({}, state.metadata || {}, {
ipAddress: req.headers["x-forwarded-for"] || req.connection.remoteAddress,
userAgent: req.headers["user-agent"],
});
const responseToken: any = {
token: user.generateJwt(user),
language: user.preferences ? user.preferences.language : "es"
};
const session = new UserSession({
_id: responseToken.token,
...metadata,
userId: user._id,
model: "Patient",
exp: moment().add(365, "days").toDate()
});
user.auth = { ...user.auth, token: responseToken.token, exp: session.exp };
session.save().then(() => {
return user.save()
.then((savedUser: any) => {
const user_ = { ...savedUser.toJSON() };
delete user_.hash;
delete user_.salt;
delete user_.password;
delete user_.auth;
responseToken.user = user_;
if (req.query.json == "true") {
return res.status(200).json(responseToken);
}
const md = new MobileDetect(req.headers["user-agent"]);
if (md.mobile()) {
return res.redirect("myapp://auth/" + "?response=" + Buffer.from(JSON.stringify(responseToken)).toString("base64"));
}
});
}).catch((err: Error & any) => {
return res.status(403).json({ error: true, message: err });
});
}
);
});
};
export default passport;
import UserIdentity from "../models/userIdentity";
import Patient, { PatientMethods } from "../models/patient";
import FacebookStrategy from "passport-facebook";
import GoogleStrategy from "passport-google-oauth20";
import fs from "fs";
const AppleSignInStrategy = require("passport-apple");
export const AppUrlScheme = "myapp://";
export const ENVIRONMENT = process.env.NODE_ENV;
const prod = ENVIRONMENT === "production"; // Anything else is treated as 'dev'
export const API_URL = prod ? process.env["API_URL"] : process.env["API_URL_DEV"];
export const facebook = {
clientID: process.env.FACEBOOK_ID || "INSERT-CLIENT-ID-HERE",
clientSecret: process.env.FACEBOOK_SECRET || "INSERT-CLIENT-SECRET-HERE",
callbackURL: `${API_URL}/auth/facebook/callback`,
profileFields: ["id", "name", "displayName", "picture", "email"],
scope: ["email"]
};
export const google = {
clientID: process.env.GOOGLE_ID || "INSERT-CLIENT-ID-HERE",
clientSecret: process.env.GOOGLE_SECRET || "INSERT-CLIENT-SECRET-HERE",
callbackURL: `/auth/google/callback`,
};
export const apple = {
clientID: "com.myapp",
teamID: process.env.APNS_TEAM_ID,
keyID: "XXXXXX",
privateKeyLocation: "./certs/AuthKey_.p8",
callbackURL: `${API_URL}/auth/apple/callback`,
};
const transformFacebookProfile = async (profile: any, state: any) => {
const email = profile.email || profile._json.email;
const country = state && state.country && state.country.toUpperCase();
const data = {
username: `${profile.id}@facebook.com`,
firstName: [profile.name.givenName, profile.name.middleName || ""].join(" ").trim(),
lastName: profile.name.familyName,
email: [email],
country,
auth: {
facebookId: profile.id
}
// avatar: profile.picture.data.url,
};
console.log({ data });
return data;
};
// Transform Google profile into user object
const transformGoogleProfile = async (profile: any, state: any) => {
const country = state && state.country && state.country.toUpperCase();
const data = {
fullName: profile.displayName,
email: [profile.email],
country,
auth: {
googleId: profile.id
}
};
return data;
// avatar: profile.image.url,
};
// Transform Google profile into user object
const transformAppleProfile = async (profile: any, state: any) => {
console.log({ profile });
const country = state && state.country && state.country.toUpperCase();
const data = {
fullName: profile.displayName,
email: [profile.email],
country,
auth: {
appleId: profile.id
}
};
return data;
};
export const buildStrategyHelper = (provider: string, Strategy: any, config: any, transformProfile: Function) => {
return new Strategy({ ...config, passReqToCallback: true },
// Gets called when user authorizes access to their profile
async (req: any, accessToken: string, refreshToken: string, profile: any, done: Function) => {
try {
const conditions = { provider, externalId: profile.id };
const credentials = { accessToken, refreshToken };
const userType = await PatientMethods.findDefaultType();
let identity = await UserIdentity.findOne(conditions).exec();
let currentUser = identity && await Patient.findOne({ _id: identity.userId, enabled: true }).select("+password +hash +salt +auth")
.populate("userType")
.exec();
if (provider == "apple") {
console.log({ profile, accessToken, refreshToken, done });
return done(new Error("Unknow Error"), false, { message: "err.unknowError" });
}
const state = req.query.state && JSON.parse(Buffer.from(req.query.state, "base64").toString());
const profileData = await transformProfile(profile, state);
if (identity && !currentUser) {
currentUser = new Patient(Object.assign(profileData, { userType: userType }));
identity.userId = currentUser._id;
await identity.save();
await currentUser.save();
}
if (!identity) {
const newUser = new Patient(Object.assign(profileData, { userType: userType }));
identity = new UserIdentity({
userId: newUser._id,
provider: provider,
externalId: profile.id,
credentials,
profile: profile,
});
const err: any = await newUser.validate();
if (err) throw err;
await identity.save();
await newUser.save();
} else {
identity.credentials = credentials;
identity.profile = profile;
await identity.save();
}
if (identity && identity.enabled) {
const user = await Patient.findOne({ _id: identity.userId }).select("+password +hash +salt +auth")
.populate("userType")
.exec();
if (!user) {
return done(new Error("User not found."), false, { message: "err.notExists" });
}
if (!user.active) {
return done(new Error("User not found."), false, { message: "error.inactive" });
}
req.state = Object.assign({}, state || {});
return done(undefined, user);
} else {
return done(new Error("User not found."), false, { message: "err.notExists" });
}
} catch (_err) {
console.error(_err);
return done(new Error("Unknow Error"), false, { message: "err.unknowError" });
}
}
);
};
export const SocialSignInStrategies: any = {
"apple": buildStrategyHelper("apple", AppleSignInStrategy, apple, transformAppleProfile),
"facebook": buildStrategyHelper("facebook", FacebookStrategy, facebook, transformFacebookProfile),
"google": buildStrategyHelper("google", GoogleStrategy, google, transformGoogleProfile),
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment