Record local email for social users where possible (#14029)

* fix(auth): record local email for social users where possible

* fix(auth): Apple emails are junk, prefer Google

* fix(auth): correct placement of logic to save local email

* fix(auth): run full function in both workflows to avoid conflicts

Co-authored-by: SabreCat <sabe@habitica.com>
This commit is contained in:
Sabe Jones
2022-06-03 15:06:40 -05:00
committed by GitHub
parent afc7b1218a
commit 341517083e
3 changed files with 33 additions and 6 deletions

View File

@@ -19,6 +19,7 @@ import {
hasBackupAuth,
loginSocial,
registerLocal,
socialEmailToLocal,
} from '../../libs/auth';
import { verifyUsername } from '../../libs/user/validation';
@@ -478,6 +479,7 @@ api.resetPasswordSetNewOne = {
// set new password and make sure it's using bcrypt for hashing
await passwordUtils.convertToBcrypt(user, String(newPassword));
user.auth.local.passwordResetCode = undefined; // Reset saved password reset code
if (!user.auth.local.email) user.auth.local.email = await socialEmailToLocal(user);
await user.save();
return res.respond(200, {}, res.t('passwordChangeSuccess'));

View File

@@ -12,7 +12,10 @@ import common from '../../../common';
import logger from '../logger';
import { decrypt } from '../encryption';
import { model as Group } from '../../models/group';
import { loginSocial } from './social';
import {
loginSocial,
socialEmailToLocal,
} from './social';
import { loginRes } from './utils';
import { verifyUsername } from '../user/validation';
@@ -227,4 +230,5 @@ export {
hasLocalAuth,
loginSocial,
registerLocal,
socialEmailToLocal,
};

View File

@@ -23,6 +23,21 @@ function _passportProfile (network, accessToken) {
});
}
export async function socialEmailToLocal (user) {
const socialEmail = (user.auth.google && user.auth.google.emails
&& user.auth.google.emails[0].value)
|| (user.auth.facebook && user.auth.facebook.emails && user.auth.facebook.emails[0].value)
|| (user.auth.apple && user.auth.apple.emails && user.auth.apple.emails[0].value);
if (socialEmail) {
const conflictingUser = await User.findOne(
{ 'auth.local.email': socialEmail },
{ _id: 1 },
).exec();
if (!conflictingUser) return socialEmail;
}
return null;
}
export async function loginSocial (req, res) { // eslint-disable-line import/prefer-default-export
let existingUser = res.locals.user;
const { network } = req.body;
@@ -45,17 +60,23 @@ export async function loginSocial (req, res) { // eslint-disable-line import/pre
[`auth.${network}.id`]: profile.id,
}, { _id: 1, apiToken: 1, auth: 1 }).exec();
let email;
if (profile.emails && profile.emails[0] && profile.emails[0].value) {
email = profile.emails[0].value.toLowerCase();
}
// User already signed up
if (user) {
if (existingUser) {
throw new NotAuthorized(res.t('socialAlreadyExists'));
}
return loginRes(user, req, res);
if (!user.auth.local.email) {
user.auth.local.email = await socialEmailToLocal(user);
if (user.auth.local.email) {
await user.save();
}
let email;
if (profile.emails && profile.emails[0] && profile.emails[0].value) {
email = profile.emails[0].value.toLowerCase();
}
return loginRes(user, req, res);
}
if (!existingUser && email) {