Prerequisites to removing Facebook authentication (#13683)

* Don't sign in user when trying to connect a social account that was already created

* Log social users into matching local auth accounts

If the social account has an email that already exists as a local user, instead of creating a new account log them into their account and add the social auth to the account

* If possible set local authentication email for social users

* Allow password reset emails to be sent to social login users

* lint fixes

* Fix issues and tests

* fix tests

* Fix lint error.
This commit is contained in:
Phillip Thelen
2022-01-21 22:15:58 +01:00
committed by GitHub
parent d11810677c
commit 1177ad8b8c
12 changed files with 288 additions and 72 deletions

View File

@@ -1,6 +1,6 @@
import passport from 'passport';
import common from '../../../common';
import { BadRequest } from '../errors';
import { BadRequest, NotAuthorized } from '../errors';
import logger from '../logger';
import {
generateUsername,
@@ -24,7 +24,7 @@ function _passportProfile (network, accessToken) {
}
export async function loginSocial (req, res) { // eslint-disable-line import/prefer-default-export
const existingUser = res.locals.user;
let existingUser = res.locals.user;
const { network } = req.body;
const isSupportedNetwork = common.constants.SUPPORTED_SOCIAL_NETWORKS
@@ -47,37 +47,52 @@ export async function loginSocial (req, res) { // eslint-disable-line import/pre
// User already signed up
if (user) {
if (existingUser) {
throw new NotAuthorized(res.t('socialAlreadyExists'));
}
return loginRes(user, req, res);
}
const generatedUsername = generateUsername();
let email;
if (profile.emails && profile.emails[0] && profile.emails[0].value) {
email = profile.emails[0].value.toLowerCase();
}
user = {
auth: {
[network]: {
id: profile.id,
emails: profile.emails,
},
local: {
username: generatedUsername,
lowerCaseUsername: generatedUsername,
},
},
profile: {
name: profile.displayName || profile.name || profile.username,
},
preferences: {
language: req.language,
},
flags: {
verifiedUsername: true,
},
};
if (!existingUser && email) {
existingUser = await User.findOne({ 'auth.local.email': email }).exec();
}
if (existingUser) {
existingUser.auth[network] = user.auth[network];
existingUser.auth[network] = {
id: profile.id,
emails: profile.emails,
};
user = existingUser;
} else {
const generatedUsername = generateUsername();
user = {
auth: {
[network]: {
id: profile.id,
emails: profile.emails,
},
local: {
username: generatedUsername,
lowerCaseUsername: generatedUsername,
email,
},
},
profile: {
name: profile.displayName || profile.name || profile.username,
},
preferences: {
language: req.language,
},
flags: {
verifiedUsername: true,
},
};
user = new User(user);
user.registeredThrough = req.headers['x-client']; // Not saved, used to create the correct tasks based on the device used
}
@@ -85,19 +100,15 @@ export async function loginSocial (req, res) { // eslint-disable-line import/pre
const savedUser = await user.save();
if (!existingUser) {
user.newUser = true;
savedUser.newUser = true;
}
const response = loginRes(user, req, res);
const response = loginRes(savedUser, req, res);
// Clean previous email preferences
if (
savedUser.auth[network].emails
&& savedUser.auth[network].emails[0]
&& savedUser.auth[network].emails[0].value
) {
if (email) {
EmailUnsubscription
.remove({ email: savedUser.auth[network].emails[0].value.toLowerCase() })
.remove({ email })
.exec()
.then(() => {
if (!existingUser) {