mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 15:17:25 +01:00
Purge Facebook (#13696)
* 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. * purge Facebook. Only keep it in some select places to allow for some compatablilty. * Fix error * fix error * Let settings handle it when you don't have a password set but an email * fix error * Fix boolean logic * fix json conversion * . * fix password reset for old social accounts * 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. * purge Facebook. Only keep it in some select places to allow for some compatablilty. * Fix error * fix error * Let settings handle it when you don't have a password set but an email * fix error * Fix boolean logic * fix json conversion * fix password reset for old social accounts * Revert "lint fixes" This reverts commitc244b1651c. # Conflicts: # website/client/src/components/auth/registerLoginReset.vue # website/client/src/components/static/contact.vue * Revert "fix password reset for old social accounts" This reverts commit7e0069a80f. * fix duplicate code * chore(misc): remove irrelevant changes * chore(privacy): update policy page with note about FB Co-authored-by: SabreCat <sabe@habitica.com>
This commit is contained in:
@@ -13,11 +13,6 @@ function getUser () {
|
|||||||
username: 'username',
|
username: 'username',
|
||||||
email: 'email@email',
|
email: 'email@email',
|
||||||
},
|
},
|
||||||
facebook: {
|
|
||||||
emails: [{
|
|
||||||
value: 'email@facebook',
|
|
||||||
}],
|
|
||||||
},
|
|
||||||
google: {
|
google: {
|
||||||
emails: [{
|
emails: [{
|
||||||
value: 'email@google',
|
value: 'email@google',
|
||||||
@@ -62,30 +57,12 @@ describe('emails', () => {
|
|||||||
expect(data).to.have.property('canSend', true);
|
expect(data).to.have.property('canSend', true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns correct user data [facebook users]', () => {
|
|
||||||
const attachEmail = requireAgain(pathToEmailLib);
|
|
||||||
const { getUserInfo } = attachEmail;
|
|
||||||
const user = getUser();
|
|
||||||
delete user.profile.name;
|
|
||||||
delete user.auth.local.email;
|
|
||||||
delete user.auth.google.emails;
|
|
||||||
delete user.auth.apple.emails;
|
|
||||||
|
|
||||||
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
|
|
||||||
|
|
||||||
expect(data).to.have.property('name', user.auth.local.username);
|
|
||||||
expect(data).to.have.property('email', user.auth.facebook.emails[0].value);
|
|
||||||
expect(data).to.have.property('_id', user._id);
|
|
||||||
expect(data).to.have.property('canSend', true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns correct user data [google users]', () => {
|
it('returns correct user data [google users]', () => {
|
||||||
const attachEmail = requireAgain(pathToEmailLib);
|
const attachEmail = requireAgain(pathToEmailLib);
|
||||||
const { getUserInfo } = attachEmail;
|
const { getUserInfo } = attachEmail;
|
||||||
const user = getUser();
|
const user = getUser();
|
||||||
delete user.profile.name;
|
delete user.profile.name;
|
||||||
delete user.auth.local.email;
|
delete user.auth.local.email;
|
||||||
delete user.auth.facebook.emails;
|
|
||||||
delete user.auth.apple.emails;
|
delete user.auth.apple.emails;
|
||||||
|
|
||||||
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
|
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
|
||||||
@@ -103,7 +80,6 @@ describe('emails', () => {
|
|||||||
delete user.profile.name;
|
delete user.profile.name;
|
||||||
delete user.auth.local.email;
|
delete user.auth.local.email;
|
||||||
delete user.auth.google.emails;
|
delete user.auth.google.emails;
|
||||||
delete user.auth.facebook.emails;
|
|
||||||
|
|
||||||
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
|
const data = getUserInfo(user, ['name', 'email', '_id', 'canSend']);
|
||||||
|
|
||||||
@@ -118,7 +94,6 @@ describe('emails', () => {
|
|||||||
const { getUserInfo } = attachEmail;
|
const { getUserInfo } = attachEmail;
|
||||||
const user = getUser();
|
const user = getUser();
|
||||||
delete user.auth.local.email;
|
delete user.auth.local.email;
|
||||||
delete user.auth.facebook;
|
|
||||||
delete user.auth.google;
|
delete user.auth.google;
|
||||||
delete user.auth.apple;
|
delete user.auth.apple;
|
||||||
|
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ describe('Password Utilities', () => {
|
|||||||
it('returns false if the user has no local auth', async () => {
|
it('returns false if the user has no local auth', async () => {
|
||||||
const user = await generateUser({
|
const user = await generateUser({
|
||||||
auth: {
|
auth: {
|
||||||
facebook: {},
|
google: {},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
|
const res = await validatePasswordResetCodeAndFindUser(encrypt(JSON.stringify({
|
||||||
|
|||||||
@@ -289,45 +289,6 @@ describe('DELETE /user', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('user with Facebook auth', async () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
user = await generateUser({
|
|
||||||
auth: {
|
|
||||||
facebook: {
|
|
||||||
id: 'facebook-id',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an error if confirmation phrase is wrong', async () => {
|
|
||||||
await expect(user.del('/user', {
|
|
||||||
password: 'just-do-it',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('incorrectDeletePhrase', { magicWord: 'DELETE' }),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an error if confirmation phrase is not supplied', async () => {
|
|
||||||
await expect(user.del('/user', {
|
|
||||||
password: '',
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 400,
|
|
||||||
error: 'BadRequest',
|
|
||||||
message: t('missingPassword'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('deletes a Facebook user', async () => {
|
|
||||||
await user.del('/user', {
|
|
||||||
password: DELETE_CONFIRMATION,
|
|
||||||
});
|
|
||||||
await expect(checkExistence('users', user._id)).to.eventually.eql(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('user with Google auth', async () => {
|
context('user with Google auth', async () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
user = await generateUser({
|
user = await generateUser({
|
||||||
|
|||||||
@@ -20,44 +20,6 @@ describe('DELETE social registration', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
context('Facebook', () => {
|
|
||||||
it('fails if user does not have an alternative registration method', async () => {
|
|
||||||
await user.update({
|
|
||||||
'auth.facebook.id': 'some-fb-id',
|
|
||||||
'auth.local': { ok: true },
|
|
||||||
});
|
|
||||||
await expect(user.del('/user/auth/social/facebook')).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('cantDetachSocial'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('succeeds if user has a local registration', async () => {
|
|
||||||
await user.update({
|
|
||||||
'auth.facebook.id': 'some-fb-id',
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await user.del('/user/auth/social/facebook');
|
|
||||||
expect(response).to.eql({});
|
|
||||||
await user.sync();
|
|
||||||
expect(user.auth.facebook).to.be.undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('succeeds if user has a google registration', async () => {
|
|
||||||
await user.update({
|
|
||||||
'auth.facebook.id': 'some-fb-id',
|
|
||||||
'auth.google.id': 'some-google-id',
|
|
||||||
'auth.local': { ok: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await user.del('/user/auth/social/facebook');
|
|
||||||
expect(response).to.eql({});
|
|
||||||
await user.sync();
|
|
||||||
expect(user.auth.facebook).to.be.undefined;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
context('Google', () => {
|
context('Google', () => {
|
||||||
it('fails if user does not have an alternative registration method', async () => {
|
it('fails if user does not have an alternative registration method', async () => {
|
||||||
await user.update({
|
await user.update({
|
||||||
@@ -81,19 +43,6 @@ describe('DELETE social registration', () => {
|
|||||||
await user.sync();
|
await user.sync();
|
||||||
expect(user.auth.google).to.be.undefined;
|
expect(user.auth.google).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('succeeds if user has a facebook registration', async () => {
|
|
||||||
await user.update({
|
|
||||||
'auth.google.id': 'some-google-id',
|
|
||||||
'auth.facebook.id': 'some-facebook-id',
|
|
||||||
'auth.local': { ok: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await user.del('/user/auth/social/google');
|
|
||||||
expect(response).to.eql({});
|
|
||||||
await user.sync();
|
|
||||||
expect(user.auth.goodl).to.be.undefined;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
context('Apple', () => {
|
context('Apple', () => {
|
||||||
@@ -119,18 +68,5 @@ describe('DELETE social registration', () => {
|
|||||||
await user.sync();
|
await user.sync();
|
||||||
expect(user.auth.apple).to.be.undefined;
|
expect(user.auth.apple).to.be.undefined;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('succeeds if user has a facebook registration', async () => {
|
|
||||||
await user.update({
|
|
||||||
'auth.apple.id': 'some-apple-id',
|
|
||||||
'auth.facebook.id': 'some-facebook-id',
|
|
||||||
'auth.local': { ok: true },
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await user.del('/user/auth/social/apple');
|
|
||||||
expect(response).to.eql({});
|
|
||||||
await user.sync();
|
|
||||||
expect(user.auth.goodl).to.be.undefined;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ describe('POST /user/auth/social', () => {
|
|||||||
let user;
|
let user;
|
||||||
const endpoint = '/user/auth/social';
|
const endpoint = '/user/auth/social';
|
||||||
let randomAccessToken = '123456';
|
let randomAccessToken = '123456';
|
||||||
let randomFacebookId = 'facebookId';
|
|
||||||
let randomGoogleId = 'googleId';
|
let randomGoogleId = 'googleId';
|
||||||
let network = 'NoNetwork';
|
let network = 'NoNetwork';
|
||||||
|
|
||||||
@@ -33,146 +32,6 @@ describe('POST /user/auth/social', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('facebook', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
randomFacebookId = generateUUID();
|
|
||||||
const expectedResult = {
|
|
||||||
id: randomFacebookId,
|
|
||||||
displayName: 'a facebook user',
|
|
||||||
emails: [
|
|
||||||
{ value: `${user.auth.local.username}+facebook@example.com` },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
sandbox.stub(passport._strategies.facebook, 'userProfile').yields(null, expectedResult);
|
|
||||||
network = 'facebook';
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
passport._strategies.facebook.userProfile.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('registers a new user', async () => {
|
|
||||||
const response = await api.post(endpoint, {
|
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(response.apiToken).to.exist;
|
|
||||||
expect(response.id).to.exist;
|
|
||||||
expect(response.newUser).to.be.true;
|
|
||||||
expect(response.username).to.exist;
|
|
||||||
|
|
||||||
await expect(getProperty('users', response.id, 'profile.name')).to.eventually.equal('a facebook user');
|
|
||||||
await expect(getProperty('users', response.id, 'auth.local.lowerCaseUsername')).to.exist;
|
|
||||||
await expect(getProperty('users', response.id, 'auth.local.email')).to.eventually.equal(`${user.auth.local.username}+facebook@example.com`);
|
|
||||||
await expect(getProperty('users', response.id, 'auth.facebook.id')).to.eventually.equal(randomFacebookId);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs an existing user in', async () => {
|
|
||||||
const registerResponse = await api.post(endpoint, {
|
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = await api.post(endpoint, {
|
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(response.apiToken).to.eql(registerResponse.apiToken);
|
|
||||||
expect(response.id).to.eql(registerResponse.id);
|
|
||||||
expect(response.newUser).to.be.false;
|
|
||||||
expect(registerResponse.newUser).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs an existing user in if they have local auth with matching email', async () => {
|
|
||||||
passport._strategies.facebook.userProfile.restore();
|
|
||||||
const expectedResult = {
|
|
||||||
id: randomFacebookId,
|
|
||||||
displayName: 'a facebook user',
|
|
||||||
emails: [
|
|
||||||
{ value: user.auth.local.email },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
sandbox.stub(passport._strategies.facebook, 'userProfile').yields(null, expectedResult);
|
|
||||||
|
|
||||||
const response = await api.post(endpoint, {
|
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(response.apiToken).to.eql(user.apiToken);
|
|
||||||
expect(response.id).to.eql(user._id);
|
|
||||||
expect(response.newUser).to.be.false;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs an existing user into their social account if they have local auth with matching email', async () => {
|
|
||||||
const registerResponse = await api.post(endpoint, {
|
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
expect(registerResponse.newUser).to.be.true;
|
|
||||||
// This is important for existing accounts before the new social handling
|
|
||||||
passport._strategies.facebook.userProfile.restore();
|
|
||||||
const expectedResult = {
|
|
||||||
id: randomFacebookId,
|
|
||||||
displayName: 'a facebook user',
|
|
||||||
emails: [
|
|
||||||
{ value: user.auth.local.email },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
sandbox.stub(passport._strategies.facebook, 'userProfile').yields(null, expectedResult);
|
|
||||||
|
|
||||||
const response = await api.post(endpoint, {
|
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(response.apiToken).to.eql(registerResponse.apiToken);
|
|
||||||
expect(response.id).to.eql(registerResponse.id);
|
|
||||||
expect(response.apiToken).not.to.eql(user.apiToken);
|
|
||||||
expect(response.id).not.to.eql(user._id);
|
|
||||||
expect(response.newUser).to.be.false;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('add social auth to an existing user', async () => {
|
|
||||||
const response = await user.post(endpoint, {
|
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(response.apiToken).to.eql(user.apiToken);
|
|
||||||
expect(response.id).to.eql(user._id);
|
|
||||||
expect(response.newUser).to.be.false;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not log into other account if social auth already exists', async () => {
|
|
||||||
const registerResponse = await api.post(endpoint, {
|
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
expect(registerResponse.newUser).to.be.true;
|
|
||||||
|
|
||||||
await expect(user.post(endpoint, {
|
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
|
||||||
network,
|
|
||||||
})).to.eventually.be.rejected.and.eql({
|
|
||||||
code: 401,
|
|
||||||
error: 'NotAuthorized',
|
|
||||||
message: t('socialAlreadyExists'),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
xit('enrolls a new user in an A/B test', async () => {
|
|
||||||
await api.post(endpoint, {
|
|
||||||
authResponse: { access_token: randomAccessToken }, // eslint-disable-line camelcase
|
|
||||||
network,
|
|
||||||
});
|
|
||||||
|
|
||||||
await expect(getProperty('users', user._id, '_ABtests')).to.eventually.be.a('object');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('google', () => {
|
describe('google', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
randomGoogleId = generateUUID();
|
randomGoogleId = generateUUID();
|
||||||
|
|||||||
@@ -25,6 +25,19 @@ describe('POST /user/reset-password', async () => {
|
|||||||
expect(user.auth.local.hashed_password).to.not.eql(previousPassword);
|
expect(user.auth.local.hashed_password).to.not.eql(previousPassword);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('resets password for social users', async () => {
|
||||||
|
const email = `${user.auth.local.username}+google@example.com`;
|
||||||
|
await user.update({ 'auth.google.emails': [{ value: email }] });
|
||||||
|
await user.sync();
|
||||||
|
const previousPassword = user.auth.local.passwordResetCode;
|
||||||
|
const response = await user.post(endpoint, {
|
||||||
|
email,
|
||||||
|
});
|
||||||
|
expect(response).to.eql({ data: {}, message: t('passwordReset') });
|
||||||
|
await user.sync();
|
||||||
|
expect(user.auth.local.passwordResetCode).to.not.eql(previousPassword);
|
||||||
|
});
|
||||||
|
|
||||||
it('same message on error as on success', async () => {
|
it('same message on error as on success', async () => {
|
||||||
const response = await user.post(endpoint, {
|
const response = await user.post(endpoint, {
|
||||||
email: 'nonExistent@email.com',
|
email: 'nonExistent@email.com',
|
||||||
|
|||||||
@@ -1,21 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="form">
|
<div class="form">
|
||||||
<div class="form-group row text-center">
|
|
||||||
<div class="col-12">
|
|
||||||
<div
|
|
||||||
class="btn btn-secondary social-button"
|
|
||||||
@click="socialAuth('facebook')"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="svg-icon social-icon"
|
|
||||||
v-html="icons.facebookIcon"
|
|
||||||
></div>
|
|
||||||
<span>{{ registering
|
|
||||||
? $t('signUpWithSocial', {social: 'Facebook'})
|
|
||||||
: $t('loginWithSocial', {social: 'Facebook'}) }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row text-center">
|
<div class="form-group row text-center">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div
|
<div
|
||||||
@@ -243,7 +227,6 @@ import debounce from 'lodash/debounce';
|
|||||||
import isEmail from 'validator/lib/isEmail';
|
import isEmail from 'validator/lib/isEmail';
|
||||||
import { setUpAxios, buildAppleAuthUrl } from '@/libs/auth';
|
import { setUpAxios, buildAppleAuthUrl } from '@/libs/auth';
|
||||||
import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
|
import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
|
||||||
import facebookSquareIcon from '@/assets/svg/facebook-square.svg';
|
|
||||||
import googleIcon from '@/assets/svg/google.svg';
|
import googleIcon from '@/assets/svg/google.svg';
|
||||||
import appleIcon from '@/assets/svg/apple_black.svg';
|
import appleIcon from '@/assets/svg/apple_black.svg';
|
||||||
|
|
||||||
@@ -260,7 +243,6 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
data.icons = Object.freeze({
|
data.icons = Object.freeze({
|
||||||
facebookIcon: facebookSquareIcon,
|
|
||||||
googleIcon,
|
googleIcon,
|
||||||
appleIcon,
|
appleIcon,
|
||||||
});
|
});
|
||||||
@@ -308,8 +290,6 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
hello.init({
|
hello.init({
|
||||||
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line
|
|
||||||
// windows: WINDOWS_CLIENT_ID,
|
|
||||||
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
|
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -621,7 +621,6 @@ import { MINIMUM_PASSWORD_LENGTH } from '@/../../common/script/constants';
|
|||||||
import exclamation from '@/assets/svg/exclamation.svg';
|
import exclamation from '@/assets/svg/exclamation.svg';
|
||||||
import gryphon from '@/assets/svg/gryphon.svg';
|
import gryphon from '@/assets/svg/gryphon.svg';
|
||||||
import habiticaIcon from '@/assets/svg/habitica-logo.svg';
|
import habiticaIcon from '@/assets/svg/habitica-logo.svg';
|
||||||
import facebookSquareIcon from '@/assets/svg/facebook-square.svg';
|
|
||||||
import googleIcon from '@/assets/svg/google.svg';
|
import googleIcon from '@/assets/svg/google.svg';
|
||||||
import appleIcon from '@/assets/svg/apple_black.svg';
|
import appleIcon from '@/assets/svg/apple_black.svg';
|
||||||
|
|
||||||
@@ -644,7 +643,6 @@ export default {
|
|||||||
exclamation,
|
exclamation,
|
||||||
gryphon,
|
gryphon,
|
||||||
habiticaIcon,
|
habiticaIcon,
|
||||||
facebookIcon: facebookSquareIcon,
|
|
||||||
googleIcon,
|
googleIcon,
|
||||||
appleIcon,
|
appleIcon,
|
||||||
});
|
});
|
||||||
@@ -734,8 +732,6 @@ export default {
|
|||||||
},
|
},
|
||||||
mounted () {
|
mounted () {
|
||||||
hello.init({
|
hello.init({
|
||||||
facebook: process.env.FACEBOOK_KEY, // eslint-disable-line
|
|
||||||
// windows: WINDOWS_CLIENT_ID,
|
|
||||||
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
|
google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -387,9 +387,7 @@
|
|||||||
{{ $t('saveAndConfirm') }}
|
{{ $t('saveAndConfirm') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<h5
|
<h5 v-if="user.auth.local.has_password">
|
||||||
v-if="user.auth.local.email"
|
|
||||||
>
|
|
||||||
{{ $t('changeEmail') }}
|
{{ $t('changeEmail') }}
|
||||||
</h5>
|
</h5>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<h1>Privacy Notice</h1>
|
<h1>Privacy Notice</h1>
|
||||||
<p class="strong pagemeta">
|
<p class="strong pagemeta">
|
||||||
Last Updated: December 10, 2021
|
Last Updated: September 15, 2022
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
HabitRPG, Inc. (“HabitRPG,” “we,” “us,” or “our”) welcomes you. This privacy notice (the “Privacy
|
HabitRPG, Inc. (“HabitRPG,” “we,” “us,” or “our”) welcomes you. This privacy notice (the “Privacy
|
||||||
@@ -39,8 +39,9 @@
|
|||||||
In connection with the creation of an account on our Platforms, we collect account credentials such as
|
In connection with the creation of an account on our Platforms, we collect account credentials such as
|
||||||
your email, username, and password. We use this account information to create your account, including to
|
your email, username, and password. We use this account information to create your account, including to
|
||||||
verify your identity. We also use this information to manage your account, including your transactions. If
|
verify your identity. We also use this information to manage your account, including your transactions. If
|
||||||
you choose to log into your account through Google, Apple or Facebook, we capture and store the User
|
you choose to log into your account through Google or Apple, we capture and store the User ID and email
|
||||||
ID and email address connected to the respective account, so we can verify your identity when you log in.
|
address connected to the respective account, so we can verify your identity when you log in.
|
||||||
|
(We no longer offer the ability to log in using Facebook's authentication procedure.)
|
||||||
</p>
|
</p>
|
||||||
<h3>User Content</h3>
|
<h3>User Content</h3>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ const envVars = [
|
|||||||
'BASE_URL',
|
'BASE_URL',
|
||||||
'GA_ID',
|
'GA_ID',
|
||||||
'STRIPE_PUB_KEY',
|
'STRIPE_PUB_KEY',
|
||||||
'FACEBOOK_KEY',
|
|
||||||
'GOOGLE_CLIENT_ID',
|
'GOOGLE_CLIENT_ID',
|
||||||
'APPLE_AUTH_CLIENT_ID',
|
'APPLE_AUTH_CLIENT_ID',
|
||||||
'AMPLITUDE_KEY',
|
'AMPLITUDE_KEY',
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ export const CHAT_FLAG_FROM_SHADOW_MUTE = 10;
|
|||||||
// @TODO use those constants to replace hard-coded numbers
|
// @TODO use those constants to replace hard-coded numbers
|
||||||
|
|
||||||
export const SUPPORTED_SOCIAL_NETWORKS = [
|
export const SUPPORTED_SOCIAL_NETWORKS = [
|
||||||
{ key: 'facebook', name: 'Facebook' },
|
|
||||||
{ key: 'google', name: 'Google' },
|
{ key: 'google', name: 'Google' },
|
||||||
{ key: 'apple', name: 'Apple' },
|
{ key: 'apple', name: 'Apple' },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -161,13 +161,16 @@ async function registerLocal (req, res, { isV3 = false }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
const hasSocialAuth = common.constants.SUPPORTED_SOCIAL_NETWORKS.find(network => {
|
const networks = common.constants.SUPPORTED_SOCIAL_NETWORKS;
|
||||||
|
// need to insert FB here to allow users who only have FB auth to connect local auth.
|
||||||
|
networks.push({ key: 'facebook', name: 'Facebook' });
|
||||||
|
const hasSocialAuth = networks.find(network => {
|
||||||
if (existingUser.auth.hasOwnProperty(network.key)) { // eslint-disable-line no-prototype-builtins, max-len
|
if (existingUser.auth.hasOwnProperty(network.key)) { // eslint-disable-line no-prototype-builtins, max-len
|
||||||
return existingUser.auth[network.key].id;
|
return existingUser.auth[network.key].id;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
if (!hasSocialAuth) throw new NotAuthorized(res.t('onlySocialAttachLocal'));
|
if (!hasSocialAuth && existingUser.auth.local.hashed_password) throw new NotAuthorized(res.t('onlySocialAttachLocal'));
|
||||||
existingUser.auth.local = newUser.auth.local;
|
existingUser.auth.local = newUser.auth.local;
|
||||||
newUser = existingUser;
|
newUser = existingUser;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -60,11 +60,6 @@ export async function loginSocial (req, res) { // eslint-disable-line import/pre
|
|||||||
[`auth.${network}.id`]: profile.id,
|
[`auth.${network}.id`]: profile.id,
|
||||||
}, { _id: 1, apiToken: 1, auth: 1 }).exec();
|
}, { _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
|
// User already signed up
|
||||||
if (user) {
|
if (user) {
|
||||||
if (existingUser) {
|
if (existingUser) {
|
||||||
@@ -79,6 +74,11 @@ export async function loginSocial (req, res) { // eslint-disable-line import/pre
|
|||||||
return loginRes(user, req, res);
|
return loginRes(user, req, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let email;
|
||||||
|
if (profile.emails && profile.emails[0] && profile.emails[0].value) {
|
||||||
|
email = profile.emails[0].value.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
if (!existingUser && email) {
|
if (!existingUser && email) {
|
||||||
existingUser = await User.findOne({ 'auth.local.email': email }).exec();
|
existingUser = await User.findOne({ 'auth.local.email': email }).exec();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import passport from 'passport';
|
import passport from 'passport';
|
||||||
import nconf from 'nconf';
|
import nconf from 'nconf';
|
||||||
import { Strategy as FacebookStrategy } from 'passport-facebook';
|
|
||||||
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
|
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
|
||||||
|
|
||||||
// Passport session setup.
|
// Passport session setup.
|
||||||
@@ -13,20 +12,6 @@ import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
|
|||||||
passport.serializeUser((user, done) => done(null, user));
|
passport.serializeUser((user, done) => done(null, user));
|
||||||
passport.deserializeUser((obj, done) => done(null, obj));
|
passport.deserializeUser((obj, done) => done(null, obj));
|
||||||
|
|
||||||
// TODO remove?
|
|
||||||
// This auth strategy is no longer used.
|
|
||||||
// It's just kept around for auth.js#loginFacebook() (passport._strategies.facebook.userProfile)
|
|
||||||
// The proper fix would be to move to a general OAuth module simply to verify accessTokens
|
|
||||||
passport.use(new FacebookStrategy({
|
|
||||||
clientID: nconf.get('FACEBOOK_KEY'),
|
|
||||||
clientSecret: nconf.get('FACEBOOK_SECRET'),
|
|
||||||
profileFields: ['id', 'email', 'displayName'],
|
|
||||||
profileURL: 'https://graph.facebook.com/v2.8/me',
|
|
||||||
authorizationURL: 'https://www.facebook.com/v2.8/dialog/oauth',
|
|
||||||
tokenURL: 'https://graph.facebook.com/v2.8/oauth/access_token',
|
|
||||||
// callbackURL: nconf.get("BASE_URL") + "/auth/facebook/callback"
|
|
||||||
}, (accessToken, refreshToken, profile, done) => done(null, profile)));
|
|
||||||
|
|
||||||
passport.use(new GoogleStrategy({
|
passport.use(new GoogleStrategy({
|
||||||
clientID: nconf.get('GOOGLE_CLIENT_ID'),
|
clientID: nconf.get('GOOGLE_CLIENT_ID'),
|
||||||
clientSecret: nconf.get('GOOGLE_CLIENT_SECRET'),
|
clientSecret: nconf.get('GOOGLE_CLIENT_SECRET'),
|
||||||
|
|||||||
Reference in New Issue
Block a user