diff --git a/client/coral-framework/graphql/fragments.js b/client/coral-framework/graphql/fragments.js
index bf75fa1a8..778214b8b 100644
--- a/client/coral-framework/graphql/fragments.js
+++ b/client/coral-framework/graphql/fragments.js
@@ -27,6 +27,7 @@ export default {
'UpdateAssetStatusResponse',
'UpdateSettingsResponse',
'ChangePasswordResponse',
- 'UpdateEmailAddressResponse'
+ 'UpdateEmailAddressResponse',
+ 'AttachLocalAuthResponse'
),
};
diff --git a/plugin-api/beta/client/hocs/index.js b/plugin-api/beta/client/hocs/index.js
index d33e7acc9..d7ca5fce9 100644
--- a/plugin-api/beta/client/hocs/index.js
+++ b/plugin-api/beta/client/hocs/index.js
@@ -27,6 +27,5 @@ export {
withSetCommentStatus,
withChangePassword,
withChangeUsername,
- withUpdateEmailAddress,
} from 'coral-framework/graphql/mutations';
export { compose } from 'recompose';
diff --git a/plugins/talk-plugin-facebook-auth/client/components/ErrorMessage.css b/plugins/talk-plugin-facebook-auth/client/components/ErrorMessage.css
new file mode 100644
index 000000000..039ffae30
--- /dev/null
+++ b/plugins/talk-plugin-facebook-auth/client/components/ErrorMessage.css
@@ -0,0 +1,8 @@
+.errorMsg {
+ color: #FA4643;
+ font-size: 0.9em;
+}
+
+.warningIcon {
+ color: #FA4643;
+}
diff --git a/plugins/talk-plugin-facebook-auth/client/components/ErrorMessage.js b/plugins/talk-plugin-facebook-auth/client/components/ErrorMessage.js
new file mode 100644
index 000000000..f39a8fc08
--- /dev/null
+++ b/plugins/talk-plugin-facebook-auth/client/components/ErrorMessage.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styles from './ErrorMessage.css';
+import { Icon } from 'plugin-api/beta/client/components/ui';
+
+const ErrorMessage = ({ children }) => (
+
diff --git a/plugins/talk-plugin-local-auth/client/components/VerifyEmailAddress.js b/plugins/talk-plugin-local-auth/client/components/VerifyEmailAddress.js
new file mode 100644
index 000000000..cc1ff3626
--- /dev/null
+++ b/plugins/talk-plugin-local-auth/client/components/VerifyEmailAddress.js
@@ -0,0 +1,28 @@
+import React from 'react';
+import cn from 'classnames';
+import PropTypes from 'prop-types';
+import styles from './AddEmailAddressDialog.css';
+import { t } from 'plugin-api/beta/client/services';
+
+const VerifyEmailAddress = ({ emailAddress, done }) => (
+
+
+ {t('talk-plugin-local-auth.add_email.verify.title')}
+
+
+ {t('talk-plugin-local-auth.add_email.verify.description', emailAddress)}
+
+
+
+);
+
+VerifyEmailAddress.propTypes = {
+ emailAddress: PropTypes.string.isRequired,
+ done: PropTypes.func.isRequired,
+};
+
+export default VerifyEmailAddress;
diff --git a/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js b/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js
new file mode 100644
index 000000000..841dc8420
--- /dev/null
+++ b/plugins/talk-plugin-local-auth/client/containers/AddEmailAddressDialog.js
@@ -0,0 +1,30 @@
+import { compose, gql } from 'react-apollo';
+import { bindActionCreators } from 'redux';
+import { connect, withFragments, excludeIf } from 'plugin-api/beta/client/hocs';
+import AddEmailAddressDialog from '../components/AddEmailAddressDialog';
+import { notify } from 'coral-framework/actions/notification';
+
+import { withAttachLocalAuth } from '../hocs';
+
+const mapDispatchToProps = dispatch => bindActionCreators({ notify }, dispatch);
+
+const withData = withFragments({
+ root: gql`
+ fragment TalkPluginLocalAuth_AddEmailAddressDialog_root on RootQuery {
+ me {
+ id
+ email
+ }
+ settings {
+ requireEmailConfirmation
+ }
+ }
+ `,
+});
+
+export default compose(
+ connect(null, mapDispatchToProps),
+ withAttachLocalAuth,
+ withData,
+ excludeIf(({ root: { me } }) => !me || me.email)
+)(AddEmailAddressDialog);
diff --git a/plugins/talk-plugin-local-auth/client/hocs/index.js b/plugins/talk-plugin-local-auth/client/hocs/index.js
index bcd439af2..573fa6b10 100644
--- a/plugins/talk-plugin-local-auth/client/hocs/index.js
+++ b/plugins/talk-plugin-local-auth/client/hocs/index.js
@@ -1,6 +1,50 @@
+import { withMutation } from 'plugin-api/beta/client/hocs';
import { gql } from 'react-apollo';
import update from 'immutability-helper';
-import withMutation from 'coral-framework/hocs/withMutation';
+
+export const withAttachLocalAuth = withMutation(
+ gql`
+ mutation AttachLocalAuth($input: AttachLocalAuthInput!) {
+ attachLocalAuth(input: $input) {
+ ...AttachLocalAuthResponse
+ }
+ }
+ `,
+ {
+ props: ({ mutate }) => ({
+ attachLocalAuth: input => {
+ return mutate({
+ variables: {
+ input,
+ },
+ update: proxy => {
+ const AttachLocalAuthQuery = gql`
+ query Talk_AttachLocalAuth {
+ me {
+ id
+ email
+ }
+ }
+ `;
+
+ const prev = proxy.readQuery({ query: AttachLocalAuthQuery });
+
+ const data = update(prev, {
+ me: {
+ email: { $set: input.email },
+ },
+ });
+
+ proxy.writeQuery({
+ query: AttachLocalAuthQuery,
+ data,
+ });
+ },
+ });
+ },
+ }),
+ }
+);
export const withUpdateEmailAddress = withMutation(
gql`
diff --git a/plugins/talk-plugin-local-auth/client/index.js b/plugins/talk-plugin-local-auth/client/index.js
index d5f9f8636..9c89e0490 100644
--- a/plugins/talk-plugin-local-auth/client/index.js
+++ b/plugins/talk-plugin-local-auth/client/index.js
@@ -1,4 +1,5 @@
import ChangePassword from './containers/ChangePassword';
+import AddEmailAddressDialog from './containers/AddEmailAddressDialog';
import Profile from './containers/Profile';
import translations from './translations.yml';
import graphql from './graphql';
@@ -8,6 +9,7 @@ export default {
slots: {
profileHeader: [Profile],
profileSettings: [ChangePassword],
+ stream: [AddEmailAddressDialog],
},
...graphql,
};
diff --git a/plugins/talk-plugin-local-auth/client/translations.yml b/plugins/talk-plugin-local-auth/client/translations.yml
index d3b323f54..2b46a0464 100644
--- a/plugins/talk-plugin-local-auth/client/translations.yml
+++ b/plugins/talk-plugin-local-auth/client/translations.yml
@@ -35,6 +35,29 @@ en:
change_email_msg: "Email Address Changed - Your email address has been successfully changed. This email address will now be used for signing in and email notifications."
changed_username_success_msg: "Username Changed - Your username has been successfully changed. You will not be able to change your user name for 14 days."
change_username_attempt: "Username can't be updated. Usernames can only be changed every 14 days."
+ add_email:
+ add_email_address: "Add Email Address"
+ enter_email_address: "Enter Email Address:"
+ invalid_email_address: "Invalid Email address"
+ confirm_email_address: "Confirm Email Address:"
+ email_does_not_match: "Email Address does not match"
+ insert_password: "Insert Password:"
+ done: "done"
+ content:
+ title: "Add an Email Address"
+ description: "For your added security, we require users to add an email address to their accounts. Your email address will be used to:"
+ item_1: "Receive updates regarding any changes to your account (email address, username, password, etc.)"
+ item_2: "Allow you to download your comments."
+ item_3: "Send comment notifications that you have chosen to receive."
+ verify:
+ title: "Verify Your Email Address"
+ description: "We’ve sent an email to {0} to verify your account. You must verify your email address so that it can be used for account change confirmations and notifications."
+ added:
+ title: "Email Address Added"
+ description: "Your email address has been added to your account."
+ subtitle: "Need to change your email address?"
+ description_2: "You can change your account settings by visiting"
+ path: "My Profile > Settings"
es:
talk-plugin-local-auth:
change_password:
diff --git a/plugins/talk-plugin-local-auth/server/mutators.js b/plugins/talk-plugin-local-auth/server/mutators.js
index 6798584f5..389574d2d 100644
--- a/plugins/talk-plugin-local-auth/server/mutators.js
+++ b/plugins/talk-plugin-local-auth/server/mutators.js
@@ -5,7 +5,6 @@ const {
ErrIncorrectPassword,
} = require('./errors');
const { get } = require('lodash');
-const bcrypt = require('bcryptjs');
// hasLocalProfile checks a user's profiles to see if they already have a local
// profile associated with their account.
@@ -89,7 +88,7 @@ async function attachUserLocalAuth(ctx, email, password) {
await Users.isValidPassword(password);
// Hash the new password.
- const hashedPassword = await bcrypt.hash(password, 10);
+ const hashedPassword = await Users.hashPassword(password);
try {
// Associate the account with the user.
diff --git a/services/users.js b/services/users.js
index d5486b3f8..061fae6fb 100644
--- a/services/users.js
+++ b/services/users.js
@@ -560,7 +560,7 @@ class Users {
throw new ErrPasswordTooShort();
}
- const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
+ const hashedPassword = await Users.hashPassword(password);
return User.update(
{ id },
@@ -637,7 +637,7 @@ class Users {
Users.isValidPassword(password),
]);
- const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
+ const hashedPassword = await Users.hashPassword(password);
let user = new User({
username,
@@ -814,6 +814,10 @@ class Users {
return { user, redirect, version };
}
+ static async hashPassword(password) {
+ return bcrypt.hash(password, SALT_ROUNDS);
+ }
+
// TODO: update doc
static async resetPassword(token, password) {
const { user, redirect, version } = await this.verifyPasswordResetToken(
@@ -824,7 +828,7 @@ class Users {
throw new ErrPasswordTooShort();
}
- const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
+ const hashedPassword = await Users.hashPassword(password);
// Update the user's password.
await User.update(