diff --git a/app/assets/stylesheets/app.scss b/app/assets/stylesheets/app.scss
index ad7923dba18f3a8b4e36d1959dcf6db558360595..830f271d19327b9eda078303e2807b6cbce797a5 100644
--- a/app/assets/stylesheets/app.scss
+++ b/app/assets/stylesheets/app.scss
@@ -37,6 +37,10 @@ a, path, circle {
   clear: right;
 }
 
+.clear-left {
+  clear: left;
+}
+
 .clear-both {
   clear: both;
 }
@@ -104,6 +108,7 @@ label {
 
 @media print {
   .noPrint {
-    display:none;
+    display: none;
   }
 }
+
diff --git a/app/components/App.jsx b/app/components/App.jsx
index 547b96342bae2bb5af581c0fd7c4571504ab2d39..516905b314195e88ca7dc48c98d8d54be19ebea0 100644
--- a/app/components/App.jsx
+++ b/app/components/App.jsx
@@ -5,7 +5,6 @@ import Header from 'app/components/modules/Header';
 import LpFooter from 'app/components/modules/lp/LpFooter';
 import user from 'app/redux/User';
 import g from 'app/redux/GlobalReducer';
-import { Link } from 'react-router';
 import TopRightMenu from 'app/components/modules/TopRightMenu';
 import { browserHistory } from 'react-router';
 import classNames from 'classnames';
@@ -14,7 +13,8 @@ import CloseButton from 'react-foundation-components/lib/global/close-button';
 import Dialogs from 'app/components/modules/Dialogs';
 import Modals from 'app/components/modules/Modals';
 import Icon from 'app/components/elements/Icon';
-import {key_utils} from 'shared/ecc'
+import {key_utils} from 'shared/ecc';
+import MiniHeader from 'app/components/modules/MiniHeader';
 
 class App extends React.Component {
     constructor(props) {
@@ -45,7 +45,6 @@ class App extends React.Component {
         const p = this.props;
         const n = nextProps;
         return p.location !== n.location || 
-                  p.loading !== n.loading ||
                   p.visitor !== n.visitor ||
                   p.flash !== n.flash || this.state !== nextState;
     }
@@ -74,9 +73,10 @@ class App extends React.Component {
     }
 
     render() {
-        const {location, params, children, loading, flash, showSignUp, new_visitor,
+        const {location, params, children, flash, showSignUp, new_visitor,
             depositSteem, signup_bonus} = this.props;
         const lp = false; //location.pathname === '/';
+        const miniHeader = location.pathname === '/create_account';
         const params_keys = Object.keys(params);
         const ip = location.pathname === '/' || (params_keys.length === 2 && params_keys[0] === 'order' && params_keys[1] === 'category');
         const alert = this.props.error || flash.get('alert');
@@ -149,7 +149,8 @@ class App extends React.Component {
             );
         }
 
-        return <div className={'App' + (lp ? ' LP' : '') + (ip ? ' index-page' : '')} onMouseMove={this.onEntropyEvent}>
+        return <div className={'App' + (lp ? ' LP' : '') + (ip ? ' index-page' : '') + (miniHeader ? ' mini-header' : '')}
+                    onMouseMove={this.onEntropyEvent}>
             <SidePanel ref="side_panel" alignment="right">
                 <TopRightMenu vertical navigate={this.navigate} />
                 <ul className="vertical menu">
@@ -171,7 +172,7 @@ class App extends React.Component {
                     <li><a href="/tos.html" onClick={this.navigate} rel="nofollow">Terms of Service</a></li>
                 </ul>
             </SidePanel>
-            <Header toggleOffCanvasMenu={this.toggleOffCanvasMenu} menuOpen={this.state.open} />
+            {miniHeader ? <MiniHeader /> : <Header toggleOffCanvasMenu={this.toggleOffCanvasMenu} menuOpen={this.state.open} />}
             <div className="App__content">
                 {welcome_screen}
                 {callout}
@@ -189,7 +190,6 @@ App.propTypes = {
     children: AppPropTypes.Children,
     location: React.PropTypes.object,
     signup_bonus: React.PropTypes.string,
-    loading: React.PropTypes.bool,
     loginUser: React.PropTypes.func.isRequired,
     depositSteem: React.PropTypes.func.isRequired,
 };
@@ -200,7 +200,6 @@ export default connect(
             error: state.app.get('error'),
             flash: state.offchain.get('flash'),
             signup_bonus: state.offchain.get('signup_bonus'),
-            loading: state.app.get('loading'),
             new_visitor: !state.user.get('current') &&
                 !state.offchain.get('user') &&
                 !state.offchain.get('account') &&
diff --git a/app/components/App.scss b/app/components/App.scss
index ff49db97deebcf0f55a019c1939a2d077ac2b053..0c2225da92aad57d766398bf1808840d742a3c06 100644
--- a/app/components/App.scss
+++ b/app/components/App.scss
@@ -17,7 +17,10 @@
     @media screen and (min-width: 64.063em) {
         margin-top: 3.5rem;
     }
+}
 
+.mini-header .App__content {
+  margin-top: 0;
 }
 
 .welcomeWrapper {
diff --git a/app/components/all.scss b/app/components/all.scss
index 7881cea67b1fe856187ecda9e33b66fe8a177e43..51773df2095d01598e019a208da91d919c6d1e4c 100644
--- a/app/components/all.scss
+++ b/app/components/all.scss
@@ -26,6 +26,7 @@
 @import "./elements/Reputation";
 @import "./elements/Reblog";
 @import "./elements/YoutubePreview";
+@import "./elements/SignupProgressBar";
 
 // modules
 @import "./modules/Header";
diff --git a/app/components/elements/SignupProgressBar.jsx b/app/components/elements/SignupProgressBar.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..0927728aee1b9242710577032abffe343c2e73d1
--- /dev/null
+++ b/app/components/elements/SignupProgressBar.jsx
@@ -0,0 +1,17 @@
+import React from 'react';
+
+export default function SignupProgressBar({steps, current}) {
+    const lis = steps.map((s, i) => {
+        const cn = i + 1 < current ? 'done' : (i + 1 == current ? 'current' : '');
+        return <li className={cn} key={i + 1}>{s}</li>
+    });
+    return <div className="SignupProgressBar__container expanded row">
+        <div className="column">
+            <div className="SignupProgressBar">
+                <ul>
+                    {lis}
+                </ul>
+            </div>
+        </div>
+    </div>;
+}
diff --git a/app/components/elements/SignupProgressBar.scss b/app/components/elements/SignupProgressBar.scss
new file mode 100644
index 0000000000000000000000000000000000000000..a744e8e192c88aa575657882ebf2e527f8aa3785
--- /dev/null
+++ b/app/components/elements/SignupProgressBar.scss
@@ -0,0 +1,71 @@
+.SignupProgressBar__container {
+  background-color: transparent;
+  padding-bottom: 0.5rem;
+  border-bottom: 1px solid $light-gray;
+}
+
+.SignupProgressBar {
+  width: 100%;
+  margin: 0 auto 50px 0;
+
+  > ul {
+    counter-reset: step;
+  }
+  > ul > li {
+    list-style-type: none;
+    width: 25%;
+    float: left;
+    font-size: 12px;
+    position: relative;
+    text-align: center;
+    text-transform: uppercase;
+    color: #7d7d7d;
+  }
+  > ul > li:before {
+    width: 30px;
+    height: 30px;
+    content: counter(step);
+    counter-increment: step;
+    line-height: 26px;
+    border: 2px solid #7d7d7d;
+    display: block;
+    text-align: center;
+    margin: 0 auto 10px auto;
+    border-radius: 50%;
+    background-color: white;
+  }
+  > ul > li:after {
+    width: 100%;
+    height: 2px;
+    content: '';
+    position: absolute;
+    background-color: #7d7d7d;
+    top: 15px;
+    left: -50%;
+    z-index: -1;
+  }
+  > ul > li:first-child:after {
+    content: none;
+  }
+  > ul > li.done {
+    color: #1A5099;
+  }
+  > ul > li.done:before {
+    content: "\2713";
+    color: #fff;
+    border-color: #4ba2f2;
+    background-color: #4ba2f2;
+  }
+  > ul > li.done:after {
+    background-color: #4ba2f2;
+  }
+  > ul > li.current {
+    color: #1A5099;
+  }
+  > ul > li.current:before {
+    border-color: #4ba2f2;
+  }
+  > ul > li.current:after {
+    background-color: #4ba2f2;
+  }
+}
diff --git a/app/components/modules/MiniHeader.jsx b/app/components/modules/MiniHeader.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..3cee99e46b94986b517a553b884b54ca5c9e7dfc
--- /dev/null
+++ b/app/components/modules/MiniHeader.jsx
@@ -0,0 +1,19 @@
+import React from 'react';
+import Icon from 'app/components/elements/Icon.jsx';
+
+export default function MiniHeader() {
+    return <header className="Header">
+        <div className="Header__top header">
+            <div className="expanded row">
+                <div className="columns">
+                    <ul className="menu">
+                        <li className="Header__top-logo">
+                            <a href="/"><Icon name="steem" size="2x" /></a>
+                        </li>
+                        <li className="Header__top-steemit show-for-medium"><a href="/">steemit<span className="beta">beta</span></a></li>
+                    </ul>
+                </div>
+            </div>
+        </div>
+    </header>;
+}
diff --git a/app/components/pages/CreateAccount.jsx b/app/components/pages/CreateAccount.jsx
index ab2de37ddcbdc37a03b65225f27018ab33f368bd..38638bf7724488642366e57e3987ce79a113d9a1 100644
--- a/app/components/pages/CreateAccount.jsx
+++ b/app/components/pages/CreateAccount.jsx
@@ -1,6 +1,5 @@
 /* eslint react/prop-types: 0 */
 import React from 'react';
-import { browserHistory } from 'react-router';
 import { connect } from 'react-redux';
 import LoadingIndicator from 'app/components/elements/LoadingIndicator';
 import Apis from 'shared/api_client/ApiInstances';
@@ -9,10 +8,8 @@ import user from 'app/redux/User';
 import {validate_account_name} from 'app/utils/ChainValidation';
 import SignUp from 'app/components/modules/SignUp';
 import runTests from 'shared/ecc/test/BrowserTests';
-import g from 'app/redux/GlobalReducer';
 import GeneratedPasswordInput from 'app/components/elements/GeneratedPasswordInput';
-
-const PASSWORD_MIN_LENGTH = 32;
+import SignupProgressBar from 'app/components/elements/SignupProgressBar';
 
 class CreateAccount extends React.Component {
 
@@ -31,7 +28,8 @@ class CreateAccount extends React.Component {
             name_error: '',
             server_error: '',
             loading: false,
-            cryptographyFailure: false
+            cryptographyFailure: false,
+            showRules: false
         };
         this.onSubmit = this.onSubmit.bind(this);
         this.onNameChange = this.onNameChange.bind(this);
@@ -42,7 +40,7 @@ class CreateAccount extends React.Component {
         const cryptoTestResult = runTests();
         if (cryptoTestResult !== undefined) {
             console.error('CreateAccount - cryptoTestResult: ', cryptoTestResult);
-            this.setState({cryptographyFailure: true});
+            this.setState({cryptographyFailure: true}); // TODO: do not use setState in componentDidMount
         }
     }
 
@@ -63,6 +61,7 @@ class CreateAccount extends React.Component {
             });
         }
 
+        // createAccount
         fetch('/api/v1/accounts', {
             method: 'post',
             mode: 'no-cors',
@@ -142,8 +141,8 @@ class CreateAccount extends React.Component {
         }
 
         const {
-            name, password_valid, showPasswordString,
-            name_error, server_error, loading, cryptographyFailure
+            name, password_valid, //showPasswordString,
+            name_error, server_error, loading, cryptographyFailure, showRules
         } = this.state;
 
         const {loggedIn, logout, offchainUser, serverBusy} = this.props;
@@ -203,45 +202,59 @@ class CreateAccount extends React.Component {
             </div>;
         }
 
+        const next_step = !server_error ? null :
+            server_error === 'Mobile is not confirmed' ? <div>
+                <a href="/enter_mobile">Verify a Mobile</a>
+            </div> : <div className="callout alert">
+                <h5>Couldn't create account. Server returned the following error:</h5>
+                <p>{server_error}</p>
+                {server_error === 'Email address is not confirmed' && <a href="/enter_email">Confirm Email</a>}
+            </div>
+
         return (
-            <div className="CreateAccount row">
-                <div className="column large-7 small-10">
-                    <h2>Sign Up</h2>
-                    <div className="CreateAccount__rules">
-                        <hr />
-                        <p>
-                            The first rule of Steemit is: Do not lose your password.<br />
-                            The second rule of Steemit is: Do <strong>not</strong> lose your password.<br />
-                            The third rule of Steemit is: We cannot recover your password.<br />
-                            The fourth rule: If you can remember the password, it&apos;s not secure.<br />
-                            The fifth rule: Use only randomly-generated passwords.<br />
-                            The sixth rule: Do not tell anyone your password.<br />
-                            The seventh rule: Always back up your password.
-                        </p>
-                        <hr />
-                    </div>
-                    <form onSubmit={this.onSubmit} autoComplete="off" noValidate method="post">
-                        <div className={name_error ? 'error' : ''}>
-                            <label>USERNAME
-                                <input type="text" name="name" autoComplete="off" onChange={this.onNameChange} value={name} />
-                            </label>
-                            <p>{name_error}</p>
-                        </div>
-                        <GeneratedPasswordInput onChange={this.onPasswordChange} disabled={loading} showPasswordString={name.length > 0 && !name_error} />
+            <div>
+                <SignupProgressBar steps={[offchainUser.get('prv') || 'identity', 'email', 'phone', 'steem account']} current={4} />
+                <div className="CreateAccount row">
+                    <div className="column" style={{maxWidth: '36rem', margin: '0 auto'}}>
                         <br />
-                        {server_error && <div className="callout alert">
-                            <h5>Couldn't create account. Server returned the following error:</h5>
-                            <p>{server_error}</p>
-                            {server_error === 'Email address is not confirmed' && <a href="/enter_email">Confirm Email</a>}
+                        {showRules ? <div className="CreateAccount__rules">
+                            <p>
+                                The first rule of Steemit is: Do not lose your password.<br />
+                                The second rule of Steemit is: Do <strong>not</strong> lose your password.<br />
+                                The third rule of Steemit is: We cannot recover your password.<br />
+                                The fourth rule: If you can remember the password, it&apos;s not secure.<br />
+                                The fifth rule: Use only randomly-generated passwords.<br />
+                                The sixth rule: Do not tell anyone your password.<br />
+                                The seventh rule: Always back up your password.
+                            </p>
+                            <div className="text-center">
+                                <a className="CreateAccount__rules-button" href="#" onClick={() => this.setState({showRules: false})}>
+                                    <span style={{display: 'inline-block', transform: 'rotate(-90deg)'}}>&raquo;</span>
+                                </a>
+                            </div>
+                            <hr />
+                        </div> : <div className="text-center">
+                            <a className="CreateAccount__rules-button" href="#" onClick={() => this.setState({showRules: true})}>Steemit Rules &nbsp; &raquo;</a>
                         </div>}
-                        <noscript>
-                            <div className="callout alert">
-                                <p>This form requires javascript to be enabled in your browser</p>
+                        <form onSubmit={this.onSubmit} autoComplete="off" noValidate method="post">
+                            <div className={name_error ? 'error' : ''}>
+                                <label>ACCOUNT NAME
+                                    <input type="text" name="name" autoComplete="off" onChange={this.onNameChange} value={name} />
+                                </label>
+                                <p>{name_error}</p>
                             </div>
-                        </noscript>
-                        {loading && <LoadingIndicator type="circle" />}
-                        <input disabled={submit_btn_disabled} type="submit" className={submit_btn_class} value="SIGN UP" />
-                    </form>
+                            <GeneratedPasswordInput onChange={this.onPasswordChange} disabled={loading} showPasswordString={name.length > 0 && !name_error} />
+                            <br />
+                            {next_step && <div>{next_step}<br /></div>}
+                            <noscript>
+                                <div className="callout alert">
+                                    <p>This form requires javascript to be enabled in your browser</p>
+                                </div>
+                            </noscript>
+                            {loading && <LoadingIndicator type="circle" />}
+                            <input disabled={submit_btn_disabled} type="submit" className={submit_btn_class} value="Create Account" />
+                        </form>
+                    </div>
                 </div>
             </div>
         );
diff --git a/app/components/pages/CreateAccount.scss b/app/components/pages/CreateAccount.scss
index c87e6cf7bc4f1cd61c19c6079444f480f88f0359..e71164a9b6d0ca2f1ebefd918d0f6b29158b99ae 100644
--- a/app/components/pages/CreateAccount.scss
+++ b/app/components/pages/CreateAccount.scss
@@ -4,3 +4,12 @@
     text-align: center;
   }
 }
+
+.CreateAccount__rules-button {
+  background-color: #f2f2f2;
+  padding: 0.2rem 1rem;
+  border-radius: 10px;
+  margin: 1rem 0;
+  font-size: 0.8rem;
+  color: $dark-gray;
+}
diff --git a/config/steem-example.json b/config/steem-example.json
index b1b9c8f98aad1f876d3d1aa1492559d1bc11a414..8ba3b057c1eb3917fd98b03affba9fde31352b3d 100644
--- a/config/steem-example.json
+++ b/config/steem-example.json
@@ -52,6 +52,10 @@
       "confirm_email": "some_template_id"
     }
   },
+  "telesign": {
+      "customer_id": "",
+      "rest_api_key": ""
+  },
   "recaptcha": {
     "site_key": "",
     "secret_key": ""
diff --git a/scripts/send_waiting_list_invites.js b/scripts/send_waiting_list_invites.js
index 8e2e5ce74fe829572a8df26bf6488972148cdc9d..651cb17096677bf12c2597351176a515ed9965f5 100644
--- a/scripts/send_waiting_list_invites.js
+++ b/scripts/send_waiting_list_invites.js
@@ -18,7 +18,7 @@ function inviteUser(u, email, number) {
 
 models.User.findAll({
     attributes: ['id', 'email'],
-    where: {waiting_list: true,  email: {$ne: null}, id: {$gt: 0}},
+    where: {waiting_list: true, email: {$ne: null}, id: {$gt: 0}},
     order: 'id',
     limit: 1000
 }).then(users => {
diff --git a/server/api/general.js b/server/api/general.js
index 8f7b7c4fb5fede5a0c9c5631e5a2e66eae66bac7..f50dfad567e6939f9aa6b95ba6310b56db4e6628 100644
--- a/server/api/general.js
+++ b/server/api/general.js
@@ -44,6 +44,18 @@ export default function useGeneralApi(app) {
                 return;
             }
 
+            // check if user's ip is associated with any bot
+            const same_ip_bot = yield models.User.findOne({
+                attributes: ['id', 'created_at'],
+                where: {remote_ip, bot: true}
+            });
+            if (same_ip_bot) {
+                console.log('-- /accounts same_ip_bot -->', user_id, this.session.uid, remote_ip, user.email);
+                this.body = JSON.stringify({error: 'We are sorry, we cannot sign you up at this time because your IP address is associated with bots activity. Please contact support@steemit.com for more information.'});
+                this.status = 401;
+                return;
+            }
+
             const existing_account = yield models.Account.findOne({
                 attributes: ['id', 'created_at'],
                 where: {user_id, ignored: false},
@@ -67,6 +79,8 @@ export default function useGeneralApi(app) {
                 console.log(`api /accounts: waiting_list user ${this.session.uid} #${user_id}`);
                 throw new Error('You are on the waiting list. We will get back to you at the earliest possible opportunity.');
             }
+
+            // check email
             const eid = yield models.Identity.findOne(
                 {attributes: ['id'], where: {user_id, provider: 'email', verified: true}, order: 'id DESC'}
             );
@@ -75,6 +89,15 @@ export default function useGeneralApi(app) {
                 throw new Error('Email address is not confirmed');
             }
 
+            // check phone
+            const mid = yield models.Identity.findOne(
+                {attributes: ['id'], where: {user_id, provider: 'phone', verified: true}, order: 'id DESC'}
+            );
+            if (!mid) {
+                console.log(`api /accounts: not confirmed sms for user ${this.session.uid} #${user_id}`);
+                throw new Error('Phone number is not confirmed');
+            }
+
             yield createAccount({
                 signingKey: config.registrar.signing_key,
                 fee: config.registrar.fee,
diff --git a/server/api/oauth.js b/server/api/oauth.js
index e86db2868c9b38f540d8f7d8b14294f96bfe4073..df1a2dbbf615d717868442c0d25269a0a8296a94 100644
--- a/server/api/oauth.js
+++ b/server/api/oauth.js
@@ -42,13 +42,13 @@ function retrieveFacebookUserData(access_token) {
 
 function* handleFacebookCallback() {
     console.log('-- /handle_facebook_callback -->', this.session.uid, this.query);
-    let verified_email = false;
+    let email = null;
     try {
         if (this.query['error[error][message]']) {
             return logErrorAndRedirect(this, 'facebook:1', this.query['error[error][message]']);
         }
         const u = yield retrieveFacebookUserData(this.query.access_token);
-        verified_email = false; // verified_email = !!(u.verified && u.email);
+        email = u.email;
         const attrs = {
             uid: this.session.uid,
             name: u.name,
@@ -75,17 +75,17 @@ function* handleFacebookCallback() {
             verified: u.verified,
             provider_user_id: u.id
         };
-        const i_attrs_email = {
-            provider: 'email',
-            email: u.email,
-            verified: verified_email
-        };
+        // const i_attrs_email = {
+        //     provider: 'email',
+        //     email: u.email,
+        //     verified: false
+        // };
 
         let user = yield findUser({email: u.email, provider_user_id: u.id});
         console.log('-- /handle_facebook_callback user id -->', this.session.uid, user ? user.id : 'not found');
 
         let account_recovery_record = null;
-        const provider = 'facebook';
+        const provider = this.session.prv = 'facebook';
         if (this.session.arec) {
             const arec = yield models.AccountRecoveryRequest.findOne({
                 attributes: ['id', 'created_at', 'account_name', 'owner_key'],
@@ -120,77 +120,40 @@ function* handleFacebookCallback() {
             }
             return null;
         }
-        if (!u.email) {
-            console.log('-- /handle_facebook_callback no email -->', this.session.uid, u);
-            this.flash = {alert: 'Facebook login didn\'t provide any email addresses. Please make sure your Facebook account has a primary email address and try again.'};
-            this.redirect('/');
-            return;
-        }
-
-        if (!u.verified) {
-            throw new Error('Not verified Facebook account. Please verify your Facebook account and try again to sign up to Steemit.');
-        }
-
-        const same_ip_bot = yield models.User.findOne({
-            attributes: ['id', 'created_at'],
-            where: {remote_ip: attrs.remote_ip, bot: true}
-        });
-        if (same_ip_bot) {
-            console.log('-- /handle_facebook_callback same_ip_bot -->', this.session.uid, attrs.remote_ip, attrs.email);
-            this.flash = {alert: 'We are sorry, we cannot sign you up at this time because your IP address is associated with bots activity. Please contact support@steemit.com for more information.'};
-            this.redirect('/');
-            return;
-        }
-
-        const email_provider = u.email.match(/([\w\d-]+\.\w+)$/)[1];
-        if (!email_provider) throw new Error('Incorrect email format');
-        const blocked_email = yield models.List.findOne({
-            attributes: ['id'],
-            where: {kk: 'block-email-provider', value: email_provider}
-        });
-        if (blocked_email) {
-            console.log('-- /handle_facebook_callback blocked_email -->', this.session.uid, u.email);
-            this.flash = {alert: 'Not supported email address: ' + u.email + '. Please make sure your you don\'t use any temporary email providers, contact support@steemit.com for more information.'};
-            this.redirect('/');
-            return;
-        }
+        // no longer necessary since there is phone verification now
+        // if (!u.email) {
+        //     console.log('-- /handle_facebook_callback no email -->', this.session.uid, u);
+        //     this.flash = {alert: 'Facebook login didn\'t provide any email addresses. Please make sure your Facebook account has a primary email address and try again.'};
+        //     this.redirect('/');
+        //     return;
+        // }
+        // if (!u.verified) {
+        //     throw new Error('Not verified Facebook account. Please verify your Facebook account and try again to sign up to Steemit.');
+        // }
 
         if (user) {
-            i_attrs_email.user_id = attrs.id = user.id;
+            attrs.id = user.id;
             yield models.User.update(attrs, {where: {id: user.id}});
             yield models.Identity.update(i_attrs, {where: {user_id: user.id, provider: 'facebook'}});
-            if (verified_email) {
-                const eid = yield models.Identity.findOne(
-                    {attributes: ['id', 'verified'], where: {user_id: user.id, provider: 'email'}, order: 'id DESC'}
-                );
-                if (eid) {
-                    if (!eid.verified) yield eid.update({email: u.email, verified: true});
-                } else {
-                    yield models.Identity.create(i_attrs_email);
-                }
-            }
             console.log('-- fb updated user -->', this.session.uid, user.id, u.name, u.email);
         } else {
             user = yield models.User.create(attrs);
-            i_attrs_email.user_id = i_attrs.user_id = user.id;
+            i_attrs.user_id = user.id;
             console.log('-- fb created user -->', user.id, u.name, u.email);
             const identity = yield models.Identity.create(i_attrs);
             console.log('-- fb created identity -->', this.session.uid, identity.id);
-            if (i_attrs_email.email) {
-                const email_identity = yield models.Identity.create(i_attrs_email);
-                console.log('-- fb created email identity -->', this.session.uid, email_identity.id);
-            }
+            // if (i_attrs_email.email) {
+            //     i_attrs_email.user_id = user.id
+            //     const email_identity = yield models.Identity.create(i_attrs_email);
+            //     console.log('-- fb created email identity -->', this.session.uid, email_identity.id);
+            // }
         }
         this.session.user = user.id;
     } catch (error) {
         return logErrorAndRedirect(this, 'facebook:2', error);
     }
     this.flash = {success: 'Successfully authenticated with Facebook'};
-    if (verified_email) {
-        this.redirect('/create_account');
-    } else {
-        this.redirect('/enter_email');
-    }
+    this.redirect('/enter_email' + (email ? `?email=${email}` : ''));
     return null;
 }
 
@@ -223,7 +186,7 @@ function* handleRedditCallback() {
         console.log('-- /handle_reddit_callback user id -->', this.session.uid, user ? user.id : 'not found');
 
         let account_recovery_record = null;
-        const provider = 'reddit';
+        const provider = this.session.prv = 'reddit';
         if (this.session.arec) {
             const arec = yield models.AccountRecoveryRequest.findOne({
                 attributes: ['id', 'created_at', 'account_name', 'owner_key'],
@@ -275,7 +238,7 @@ function* handleRedditCallback() {
         if (user) {
             if (!waiting_list) attrs.waiting_list = false;
             yield models.User.update(attrs, {where: {id: user.id}});
-            yield models.Identity.update(i_attrs, {where: {user_id: user.id}});
+            yield models.Identity.update(i_attrs, {where: {user_id: user.id, provider: 'reddit'}});
             console.log('-- reddit updated user -->', this.session.uid, user.id, u.name);
         } else {
             attrs.waiting_list = waiting_list;
diff --git a/server/app_render.jsx b/server/app_render.jsx
index 3d4ff65939a4387372680cc883cc9ff04a4867e4..82e382bfb6deb0da9f9aac9acc58b7c88329926f 100644
--- a/server/app_render.jsx
+++ b/server/app_render.jsx
@@ -49,6 +49,7 @@ async function appRender(ctx) {
                     name: user.name,
                     email: user.email,
                     picture: user.picture_small,
+                    prv: ctx.session.prv,
                     account
                 }
             }
diff --git a/server/sendEmail.js b/server/sendEmail.js
index d8e1dada538af85967829a535852db0938d45948..349196c8f628e52d0805e532e2597d82243a6bdf 100644
--- a/server/sendEmail.js
+++ b/server/sendEmail.js
@@ -4,6 +4,10 @@ import config from '../config';
 const sg = sendgrid(config.sendgrid.key);
 
 export default function sendEmail(template, to, params, from = null) {
+    if (process.env.NODE_ENV !== 'production') {
+        console.log(`mail: to <${to}>, from <${from}>, template ${template} (not sent due to not production env)`);
+        return;
+    }
     const tmpl_id = config.sendgrid.templates[template];
     if (!tmpl_id) throw new Error(`can't find template ${template}`);
 
diff --git a/server/server.js b/server/server.js
index fc0a5c94757a23cd4d2bc1dafcb5e56cc8193604..ccff0b7ae1790b05f8c47b0d3f63784d2e1550be 100644
--- a/server/server.js
+++ b/server/server.js
@@ -13,6 +13,7 @@ import useOauthLogin from './api/oauth';
 import useGeneralApi from './api/general';
 import useAccountRecoveryApi from './api/account_recovery';
 import useEnterAndConfirmEmailPages from './server_pages/enter_confirm_email';
+import useEnterAndConfirmMobilePages from './server_pages/enter_confirm_mobile';
 import isBot from 'koa-isbot';
 import session from 'koa-session';
 import csrf from 'koa-csrf';
@@ -79,8 +80,22 @@ app.use(mount('/robots.txt', function* () {
     this.body = "User-agent: *\nAllow: /";
 }));
 
+// set user's uid - used to identify users in logs and some other places
+app.use(function* (next) {
+    const last_visit = this.session.last_visit;
+    this.session.last_visit = (new Date()).getTime() / 1000 | 0;
+    if (!this.session.uid) {
+        this.session.uid = Math.random().toString(36).slice(2);
+        this.session.new_visit = true;
+    } else {
+        this.session.new_visit = this.session.last_visit - last_visit > 1800;
+    }
+    yield next;
+});
+
 useRedirects(app);
 useEnterAndConfirmEmailPages(app);
+useEnterAndConfirmMobilePages(app);
 
 if (env === 'production') {
     app.use(helmet.contentSecurityPolicy(config.helmet));
@@ -109,16 +124,6 @@ if (env === 'development') {
 if (env !== 'test') {
     const appRender = require('./app_render');
     app.use(function* () {
-        this.first_visit = false;
-        this.last_visit = this.session.last_visit;
-        this.session.last_visit = (new Date()).getTime() / 1000 | 0;
-        if (!this.session.uid) {
-            this.session.uid = Math.random().toString(36).slice(2);
-            this.first_visit = true;
-            this.session.new_visit = true;
-        } else {
-            this.session.new_visit = this.session.last_visit - this.last_visit > 1800;
-        }
         yield appRender(this);
         // if (app_router.dbStatus.ok) recordWebEvent(this, 'page_load');
         const bot = this.state.isBot;
diff --git a/server/server_pages/enter_confirm_email.jsx b/server/server_pages/enter_confirm_email.jsx
index 61a39bc67056dba999cdd2c1b27f05cd567ecb95..c1ab1e73dd8a474fd7a1e1ef6e6be17c81c60085 100644
--- a/server/server_pages/enter_confirm_email.jsx
+++ b/server/server_pages/enter_confirm_email.jsx
@@ -2,37 +2,19 @@ import koa_router from 'koa-router';
 import koa_body from 'koa-body';
 import request from 'co-request';
 import React from 'react';
-import { renderToString } from 'react-dom/server';
+import {renderToString} from 'react-dom/server';
 import models from 'db/models';
 import {esc, escAttrs} from 'db/models';
 import ServerHTML from '../server-html';
-import Icon from 'app/components/elements/Icon.jsx';
 import sendEmail from '../sendEmail';
 import {checkCSRF} from '../utils';
 import config from '../../config';
+import SignupProgressBar from 'app/components/elements/SignupProgressBar';
+import MiniHeader from 'app/components/modules/MiniHeader';
 
-let assets;
-if (process.env.NODE_ENV === 'production') {
-    assets = Object.assign({}, require('tmp/webpack-stats-prod.json'), {script: ['https://www.google.com/recaptcha/api.js']});
-} else {
-    assets = Object.assign({}, require('tmp/webpack-stats-dev.json'));
-    assets.script.push('https://www.google.com/recaptcha/api.js');
-}
-
-const header = <header className="Header">
-    <div className="Header__top header">
-        <div className="expanded row">
-            <div className="columns">
-                <ul className="menu">
-                    <li className="Header__top-logo">
-                        <a href="/"><Icon name="steem" size="2x" /></a>
-                    </li>
-                    <li className="Header__top-steemit show-for-medium"><a href="/">steemit<span className="beta">beta</span></a></li>
-                </ul>
-            </div>
-        </div>
-    </div>
-</header>;
+const assets_file = process.env.NODE_ENV === 'production' ? 'tmp/webpack-stats-prod.json' : 'tmp/webpack-stats-dev.json';
+const assets = Object.assign({}, require(assets_file), {script: []});
+assets.script.push('https://www.google.com/recaptcha/api.js');
 
 function *confirmEmailHandler() {
     const confirmation_code = this.params && this.params.code ? this.params.code : this.request.body.code;
@@ -45,18 +27,21 @@ function *confirmEmailHandler() {
         this.body = 'confirmation code not found';
         return;
     }
-    this.session.user = eid.user_id;
+    if (eid.verified) {
+        this.flash = {success: 'Email has already been verified'};
+        this.redirect('/enter_mobile');
+        return;
+    }
     const hours_ago = (Date.now() - eid.updated_at) / 1000.0 / 3600.0;
-    if (hours_ago > 24.0 * 30) {
+    if (hours_ago > 24.0 * 10) {
         this.status = 401;
         this.body = 'confirmation code not found or expired';
         return;
     }
-    if (!eid.verified) {
-        yield eid.update({verified: true});
-        yield models.User.update({email: eid.email, waiting_list: false}, {where: {id: eid.user_id}});
-    }
-    this.redirect('/create_account');
+    this.session.user = eid.user_id;
+    yield eid.update({verified: true});
+    yield models.User.update({email: eid.email, waiting_list: false}, {where: {id: eid.user_id}});
+    this.redirect('/enter_mobile');
 }
 
 export default function useEnterAndConfirmEmailPages(app) {
@@ -67,41 +52,59 @@ export default function useEnterAndConfirmEmailPages(app) {
     router.get('/enter_email', function *() {
         console.log('-- /enter_email -->', this.session.uid, this.session.user);
         const user_id = this.session.user;
-        if (!user_id) { this.body = 'user not found'; return; }
+        if (!user_id) {
+            this.body = 'user not found';
+            return;
+        }
         const eid = yield models.Identity.findOne(
-            {attributes: ['email'], where: {user_id, provider: 'email'}, order: 'id DESC'}
+            {attributes: ['email', 'verified'], where: {user_id, provider: 'email'}, order: 'id DESC'}
         );
+        if (eid && eid.verified) {
+            this.flash = {success: 'Email has already been verified'};
+            this.redirect('/enter_mobile');
+            return;
+        }
+        console.log('-- this.request.query -->', this.request.query);
+        let default_email = '';
+        if (this.request.query && this.request.query.email) default_email = this.request.query.email;
         const body = renderToString(<div className="App">
-            {header}
+            <MiniHeader />
+            <SignupProgressBar steps={[this.session.prv || 'identity', 'email', 'phone', 'steem account']} current={2} />
             <br />
-            <div className="row">
-                <form className="column small-4" action="/submit_email" method="POST">
-                    <p>
-                        Please provide your email address to continue the registration process.<br />
-                        <span className="secondary">This information allows Steemit to assist with Account Recovery in case your account is ever compromised.</span>
-                    </p>
-                    <input type="hidden" name="csrf" value={this.csrf} />
-                    <label>
-                        Email
-                        <input type="email" name="email" defaultValue={eid ? eid.email : ''} readOnly={eid && eid.email} />
-                    </label>
-                    {eid && eid.email && <div className="secondary"><i>Email address cannot be changed at this moment, sorry for the inconvenience.</i></div>}
-                    <br />
-                    <div className="g-recaptcha" data-sitekey={config.recaptcha.site_key}></div>
-                    <br />
-                    <div className="error">{this.flash.error}</div>
-                    <input type="submit" className="button" value="CONTINUE" />
-                </form>
+            <div className="row" style={{maxWidth: '32rem'}}>
+                <div className="column">
+                    <form action="/submit_email" method="POST">
+                        <h4>Please provide your email address to continue the registration process</h4>
+                        <p className="secondary">
+                            Email verification helps with preventing spam and allows Steemit to assist with Account Recovery in case your account is ever compromised.
+                        </p>
+                        <input type="hidden" name="csrf" value={this.csrf} />
+                        <label>
+                            Email
+                            <input type="email" name="email" defaultValue={default_email} />
+                        </label>
+                        {/*eid && eid.email &&
+                        <div className="secondary"><i>Email address cannot be changed at this moment, sorry for the inconvenience.</i></div>*/}
+                        <br />
+                        <div className="g-recaptcha" data-sitekey={config.recaptcha.site_key}></div>
+                        <br />
+                        <div className="error">{this.flash.error}</div>
+                        <input type="submit" className="button" value="CONTINUE" />
+                    </form>
+                </div>
             </div>
         </div>);
-        const props = { body, title: 'Email Address', assets, meta: [] };
+        const props = {body, title: 'Email Address', assets, meta: []};
         this.body = '<!DOCTYPE html>' + renderToString(<ServerHTML { ...props } />);
     });
 
     router.post('/submit_email', koaBody, function *() {
         if (!checkCSRF(this, this.request.body.csrf)) return;
         const user_id = this.session.user;
-        if (!user_id) { this.body = 'user not found'; return; }
+        if (!user_id) {
+            this.body = 'user not found';
+            return;
+        }
         const email = this.request.body.email;
         if (!email) {
             this.flash = {error: 'Please provide an email address'};
@@ -109,21 +112,52 @@ export default function useEnterAndConfirmEmailPages(app) {
             return;
         }
 
-        const recaptcha = this.request.body['g-recaptcha-response'];
-        const verificationUrl = 'https://www.google.com/recaptcha/api/siteverify?secret=' + config.recaptcha.secret_key + '&response=' + recaptcha + '&remoteip=' + this.req.connection.remoteAddress;
-        let captcha_failed;
-        try {
-            const recaptcha_res = yield request(verificationUrl);
-            const body = JSON.parse(recaptcha_res.body);
-            captcha_failed = !body.success;
-        } catch (e) {
-            captcha_failed = true;
-            console.error('-- /submit_email recaptcha request failed -->', verificationUrl, e);
+        if (process.env.NODE_ENV === 'production') {
+            const recaptcha = this.request.body['g-recaptcha-response'];
+            const verificationUrl = 'https://www.google.com/recaptcha/api/siteverify?secret=' + config.recaptcha.secret_key + '&response=' + recaptcha + '&remoteip=' + this.req.connection.remoteAddress;
+            let captcha_failed;
+            try {
+                const recaptcha_res = yield request(verificationUrl);
+                const body = JSON.parse(recaptcha_res.body);
+                captcha_failed = !body.success;
+            } catch (e) {
+                captcha_failed = true;
+                console.error('-- /submit_email recaptcha request failed -->', verificationUrl, e);
+            }
+            if (captcha_failed) {
+                console.log('-- /submit_email captcha verification failed -->', user_id, this.session.uid, email, this.req.connection.remoteAddress);
+                this.flash = {error: 'Failed captcha verification, please try again'};
+                this.redirect('/enter_email?email=' + email);
+                return;
+            }
         }
-        if (captcha_failed) {
-            console.log('-- /submit_email captcha verification failed -->', user_id, this.session.uid, email, this.req.connection.remoteAddress);
-            this.flash = {error: 'Failed captcha verification, please try again.'};
-            this.redirect('/enter_email');
+
+        const parsed_email = email.match(/^.+\@.*?([\w\d-]+\.\w+)$/);
+        if (!parsed_email || parsed_email.length < 2) {
+            console.log('-- /submit_email not valid email -->', user_id, this.session.uid, email);
+            this.flash = {error: 'Not valid email address'};
+            this.redirect('/enter_email?email=' + email);
+            return;
+        }
+        const email_provider = parsed_email[1];
+        const blocked_email = yield models.List.findOne({
+            attributes: ['id'],
+            where: {kk: 'block-email-provider', value: email_provider}
+        });
+        if (blocked_email) {
+            console.log('-- /submit_email blocked_email -->', this.session.uid, email);
+            this.flash = {error: 'Not supported email address: ' + email + '. Please make sure your you don\'t use any temporary email providers, contact support@steemit.com for more information.'};
+            this.redirect('/enter_email?email=' + email);
+            return;
+        }
+
+        const existing_email = yield models.Identity.findOne(
+            {attributes: ['user_id'], where: {email, provider: 'email', verified: true}, order: 'id'}
+        );
+        if (existing_email && existing_email.user_id != user_id) {
+            console.log('-- /submit_email existing_email -->', user_id, this.session.uid, email, existing_email.user_id);
+            this.flash = {error: 'This email has already been taken'};
+            this.redirect('/enter_email?email=' + email);
             return;
         }
 
@@ -132,7 +166,7 @@ export default function useEnterAndConfirmEmailPages(app) {
             {attributes: ['id', 'email'], where: {user_id, provider: 'email'}, order: 'id'}
         );
         if (eid) {
-            yield eid.update({confirmation_code});
+            yield eid.update({confirmation_code, email});
         } else {
             eid = yield models.Identity.create({
                 provider: 'email',
@@ -147,32 +181,18 @@ export default function useEnterAndConfirmEmailPages(app) {
         sendEmail('confirm_email', email, {confirmation_code});
 
         const body = renderToString(<div className="App">
-            {header}
+            <MiniHeader />
+            <SignupProgressBar steps={[this.session.prv || 'identity', 'email', 'phone', 'steem account']} current={2} />
             <br />
-            <div className="row">
+            <div className="row" style={{maxWidth: '32rem'}}>
                 <div className="column">
                     Thank you for providing your email address ({email}).<br />
-                    To continue please click on the link in the email we've sent you.
-                </div>
-            </div>
-            <br />
-            <div className="row">
-                <div className="column">
-                    <a href="/enter_email">Re-send email</a>
+                    To continue please click on the link in the email we've sent you.<br />
+                    <span className="secondary">Didn't recieve email? <a href={`/enter_email?email=${email}`}>Re-send</a></span>
                 </div>
             </div>
-            {/*<div className="row">
-                <form className="column small-4" action="/confirm_email" method="POST">
-                    <label>
-                        Confirmation code
-                        <input type="text" name="code" />
-                    </label>
-                    <br />
-                    <input type="submit" className="button" value="CONTINUE" />
-                </form>
-            </div>*/}
         </div>);
-        const props = { body, title: 'Email Confirmation', assets, meta: [] };
+        const props = {body, title: 'Email Confirmation', assets, meta: []};
         this.body = '<!DOCTYPE html>' + renderToString(<ServerHTML { ...props } />);
     });
 
diff --git a/server/server_pages/enter_confirm_mobile.jsx b/server/server_pages/enter_confirm_mobile.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..fd23b30b83f0444b621fe7e51b9be4c5c7ca5b20
--- /dev/null
+++ b/server/server_pages/enter_confirm_mobile.jsx
@@ -0,0 +1,219 @@
+import koa_router from 'koa-router';
+import koa_body from 'koa-body';
+import request from 'co-request';
+import React from 'react';
+import {renderToString} from 'react-dom/server';
+import models from 'db/models';
+import ServerHTML from 'server/server-html';
+import Icon from 'app/components/elements/Icon.jsx';
+import {verify} from 'server/teleSign';
+import SignupProgressBar from 'app/components/elements/SignupProgressBar';
+import {getRemoteIp, checkCSRF} from 'server/utils';
+import MiniHeader from 'app/components/modules/MiniHeader';
+
+const assets_file = process.env.NODE_ENV === 'production' ? 'tmp/webpack-stats-prod.json' : 'tmp/webpack-stats-dev.json';
+const assets = Object.assign({}, require(assets_file), {script: []});
+// assets.script.push('https://www.google.com/recaptcha/api.js');
+
+function *confirmMobileHandler() {
+    const confirmation_code = this.params && this.params.code ? this.params.code : this.request.body.code;
+    console.log('-- /confirm_mobile -->', this.session.uid, this.session.user, confirmation_code);
+    const mid = yield models.Identity.findOne(
+        {attributes: ['id', 'user_id', 'verified', 'updated_at'], where: {user_id: this.session.user, confirmation_code}, order: 'id DESC'}
+    );
+    if (!mid) {
+        this.status = 401;
+        this.body = 'Wrong confirmation code';
+        return;
+    }
+    if (mid.verified) {
+        this.flash = {success: 'Phone number has already been verified'};
+        this.redirect('/create_account');
+        return;
+    }
+    this.session.user = mid.user_id;
+    const hours_ago = (Date.now() - mid.updated_at) / 1000.0 / 3600.0;
+    if (hours_ago > 24.0) {
+        this.status = 401;
+        this.body = 'Confirmation code has been expired';
+        return;
+    }
+    yield mid.update({verified: true});
+    this.redirect('/create_account');
+}
+
+export default function useEnterAndConfirmMobilePages(app) {
+    const router = koa_router();
+    app.use(router.routes());
+    const koaBody = koa_body();
+
+    router.get('/enter_mobile', function *() {
+        console.log('-- /enter_mobile -->', this.session.uid, this.session.user);
+        const user_id = this.session.user;
+        if (!user_id) { this.body = 'user not found'; return; }
+        const mid = yield models.Identity.findOne(
+            {attributes: ['phone'], where: {user_id, provider: 'phone'}, order: 'id DESC'}
+        );
+        if (mid && mid.verified) {
+            this.flash = {success: 'Phone number has already been verified'};
+            this.redirect('/create_account');
+            return;
+        }
+        const body = renderToString(<div className="App">
+            <MiniHeader />
+            <SignupProgressBar steps={[this.session.prv || 'identity', 'email', 'phone', 'steem account']} current={3} />
+            <br />
+            <div className="row" style={{maxWidth: '32rem'}}>
+                <form className="column" action="/submit_mobile" method="POST">
+                    <h4>Please provide your phone number to continue the registration process</h4>
+                    <div className="secondary">Phone verification helps with preventing spam and allows Steemit to assist with Account Recovery in case your account is ever compromised.
+                        Your phone number will not be used for any other purpose other than phone verification and account recovery.</div>
+                    <br />
+                    <input type="hidden" name="csrf" value={this.csrf} />
+                    <label>
+                        Phone number
+                        <input type="tel" name="mobile" defaultValue={mid ? mid.phone : ''} />
+                    </label>
+                    <div className="secondary">Examples: 1-541-754-3010 | +1-541-754-3010 | +49-89-636-48018</div>
+                    <br />
+                    <div className="secondary">* fixed line phones cannot receive SMS messages</div>
+                    <div className="secondary">* message and data rates may apply</div>
+                    <br />
+                    {/*<div className="g-recaptcha" data-sitekey={config.recaptcha.site_key}></div>*/}
+                    <div className="error">{this.flash.error}</div>
+                    <input type="submit" className="button" value="CONTINUE" />
+                </form>
+            </div>
+        </div>);
+        const props = { body, title: 'Phone Number', assets, meta: [] };
+        this.body = '<!DOCTYPE html>' + renderToString(<ServerHTML { ...props } />);
+    });
+
+    router.post('/submit_mobile', koaBody, function *() {
+        if (!checkCSRF(this, this.request.body.csrf)) return;
+        const user_id = this.session.user;
+        if (!user_id) { this.body = 'user not found'; return; }
+        let mobile = this.request.body.mobile;
+        if (!mobile) {
+            this.flash = {error: 'Please provide a mobile number'};
+            this.redirect('/enter_mobile');
+            return;
+        }
+
+        mobile = mobile.match(/\d+/g).join('')
+        if(mobile.length < "9998887777".length) {
+            this.flash = {error: 'Please provide an area code'};
+            this.redirect('/enter_mobile');
+            return;
+        }
+
+        if(mobile.length === "9998887777".length) {
+            mobile = `1${mobile}`
+        }
+
+        const eid = yield models.Identity.findOne(
+            {attributes: ['id'], where: {user_id, provider: 'email', verified: true}, order: 'id DESC'}
+        );
+        if (!eid) {
+            this.flash = {error: 'Please confirm your email address first'};
+            this.redirect('/enter_mobile');
+            return;
+        }
+
+        // const recaptcha = this.request.body['g-recaptcha-response'];
+        // const verificationUrl = 'https://www.google.com/recaptcha/api/siteverify?secret=' + config.recaptcha.secret_key + '&response=' + recaptcha + '&remoteip=' + this.req.connection.remoteAddress;
+        // let captcha_failed;
+        // try {
+        //     const recaptcha_res = yield request(verificationUrl);
+        //     const body = JSON.parse(recaptcha_res.body);
+        //     captcha_failed = !body.success;
+        // } catch (e) {
+        //     captcha_failed = true;
+        //     console.error('-- /submit_mobile recaptcha request failed -->', verificationUrl, e);
+        // }
+        // if (captcha_failed) {
+        //     console.log('-- /submit_mobile captcha verification failed -->', user_id, this.session.uid, mobile, this.req.connection.remoteAddress);
+        //     this.flash = {error: 'Failed captcha verification, please try again.'};
+        //     this.redirect('/enter_mobile');
+        //     return;
+        // }
+
+        const existing_phone = yield models.Identity.findOne(
+            {attributes: ['user_id'], where: {phone: mobile, provider: 'phone', verified: true}, order: 'id'}
+        );
+        if (existing_phone && existing_phone.user_id != user_id) {
+            console.log('-- /submit_email existing_phone -->', user_id, this.session.uid, mobile, existing_phone.user_id);
+            this.flash = {error: 'This phone number has already been used'};
+            this.redirect('/enter_mobile');
+            return;
+        }
+
+        const confirmation_code = Math.random().toString().substring(2, 6);
+        let mid = yield models.Identity.findOne(
+            {attributes: ['id', 'phone', 'verified', 'updated_at'], where: {user_id, provider: 'phone'}, order: 'id'}
+        );
+        if (mid) {
+            if (mid.verified) {
+                this.flash = {success: 'Phone number has been verified'};
+                this.redirect('/create_account'); return;
+            } else {
+                const seconds_ago = (Date.now() - mid.updated_at) / 1000.0;
+                if (seconds_ago < 120) {
+                    this.flash = {error: 'Confirmation was sent a moment ago. You can try again only in 2 minutes.'};
+                    this.redirect('/enter_mobile');
+                    return;
+                }
+                yield mid.update({confirmation_code, phone: mobile});
+            }
+        } else {
+            mid = yield models.Identity.create({
+                provider: 'phone',
+                user_id,
+                uid: this.session.uid,
+                phone: mobile,
+                verified: false,
+                confirmation_code
+            });
+        }
+        console.log('-- /submit_mobile -->', this.session.uid, this.session.user, mobile, mid.id);
+        const ip = getRemoteIp(this.req)
+
+        const verifyResult = yield verify({mobile, confirmation_code, ip});
+        if (verifyResult && verifyResult.score) eid.update({score: verifyResult.score});
+        if(verifyResult && verifyResult.error) {
+            this.flash = {error: verifyResult.error};
+            this.redirect('/enter_mobile');
+            return;
+        }
+
+        const body = renderToString(<div className="App">
+            <MiniHeader />
+            <SignupProgressBar steps={[this.session.prv || 'identity', 'email', 'phone', 'steem account']} current={3} />
+            <br />
+            <div className="row" style={{maxWidth: '32rem'}}>
+                <div className="column">
+                    Thank you for providing your mobile number ({mobile}).<br />
+                    To continue please enter the SMS code we've sent you.
+                </div>
+            </div>
+            <br />
+            <div className="row" style={{maxWidth: '32rem'}}>
+                <form className="column" action="/confirm_mobile" method="POST">
+                    <label>
+                        Confirmation code
+                        <input type="text" name="code" />
+                    </label>
+                    <br />
+                    <div className="secondary">Didn't receive the verification code? <a href="/enter_mobile">Re-send</a></div>
+                    <br />
+                    <input type="submit" className="button" value="CONTINUE" />
+                </form>
+            </div>
+        </div>);
+        const props = { body, title: 'Mobile Confirmation', assets, meta: [] };
+        this.body = '<!DOCTYPE html>' + renderToString(<ServerHTML { ...props } />);
+    });
+
+    router.get('/confirm_mobile/:code', confirmMobileHandler);
+    router.post('/confirm_mobile', koaBody, confirmMobileHandler);
+}
diff --git a/server/teleSign.js b/server/teleSign.js
new file mode 100644
index 0000000000000000000000000000000000000000..5b97102975a13b400e0a7bb6fcae8009e76115c7
--- /dev/null
+++ b/server/teleSign.js
@@ -0,0 +1,138 @@
+import fetch from 'node-fetch';
+import config from '../config';
+import crypto from 'crypto'
+
+const {customer_id} = config.telesign
+const api_key = new Buffer(config.telesign.rest_api_key, 'base64')
+const use_case_code = 'BACS' // Use Case: avoid bulk attack and spammers
+
+// Testing, always blocked: 1-310-555-0100
+
+/** @return {object} - {reference_id} or {error} */
+export function* verify({mobile, confirmation_code, ip}) {
+    try {
+        const result = yield getScore(mobile)
+        const {recommendation, score} = result.risk
+        if(recommendation !== 'allow') {
+            console.log(`TeleSign did not allow phone ${mobile} ip ${ip}. TeleSign responded: ${recommendation}`);
+            return {error: 'Unable to verify your phone number. Please try a different phone number.', score}
+        }
+        const {reference_id} = yield verifySms({mobile, confirmation_code, ip})
+        return {reference_id, score}
+    } catch(error) {
+        console.log('-- verify score error -->', error);
+        return {error: 'Unable to verify phone, please try again later.'}
+    }
+}
+
+function getScore(mobile) {
+    const fields = urlencode({
+        ucid: use_case_code,
+    })
+    const resource = '/v1/phoneid/score/' + mobile.match(/\d+/g).join('')
+    const method = 'GET'
+    return fetch(
+        `https://rest.telesign.com${resource}?${fields}`, {
+            method,
+            headers: authHeaders({resource, method})
+        }
+    )
+    .then(r => r.json())
+    .catch(error => {
+        console.error(`ERROR: Phone ${mobile} score exception`, JSON.stringify(error, null, 0));
+        return Promise.reject(error)
+    })
+    .then(response => {
+        const {status} = response
+        if(status.code === 300) {
+            // Transaction successfully completed
+            console.log(`Phone ${mobile} score`, JSON.stringify(response, null, 0))
+            return Promise.resolve(response)
+        }
+        console.error(`ERROR: Phone ${mobile} score`, JSON.stringify(response, null, 0))
+        return Promise.reject(response)
+    })
+}
+
+
+function verifySms({mobile, confirmation_code, ip}) {
+    // https://developer.telesign.com/v2.0/docs/rest_api-verify-sms
+    const f = {
+        phone_number: mobile,
+        language: 'en-US',
+        ucid: use_case_code,
+        verify_code: confirmation_code,
+        template: '$$CODE$$ is your Steemit confirmation code',
+    }
+    if(ip) f.originating_ip = ip
+    const fields = urlencode(f)
+    // console.log('fields', fields) // logspam
+
+    const resource = '/v1/verify/sms'
+    const method = 'POST'
+    return fetch(
+        'https://rest.telesign.com' + resource, {
+            method,
+            body: fields,
+            headers: authHeaders({resource, method, fields})
+        }
+    )
+    .then(r => r.json())
+    .catch(error => {
+        console.error(`ERROR: SMS failed to ${mobile} code ${confirmation_code} req ip ${ip} exception`, JSON.stringify(error, null, 0));
+        return Promise.reject(error)
+    })
+    .then(response => {
+        const {status} = response
+        if(status.code === 290) {
+            // Message in progress
+            console.log(`Sent SMS to ${mobile} code ${confirmation_code}`, JSON.stringify(response, null, 0))
+            return Promise.resolve(response)
+        }
+        console.error(`ERROR: SMS failed to ${mobile} code ${confirmation_code}:`, JSON.stringify(response, null, 0))
+        return Promise.reject(response)
+    })
+}
+
+/**
+    @arg {string} resource `/v1/verify/AEBC93B5898342F790E4E19FED41A7DA`
+    @arg {string} method [GET|POST|PUT]
+    @arg {string} fields url query string
+*/
+function authHeaders({
+    resource,
+    fields,
+    method = 'GET',
+}) {
+    const auth_method = 'HMAC-SHA256'
+    const currDate = new Date().toUTCString()
+    const nonce = Math.random().toString(36).slice(15)
+
+    let content_type = ''
+    if(/POST|PUT/.test(method))
+        content_type = 'application/x-www-form-urlencoded'
+
+    let strToSign = `${method}\n${content_type}\n\nx-ts-auth-method:${auth_method}\nx-ts-date:${currDate}\nx-ts-nonce:${nonce}`
+
+    if(fields) {
+        strToSign += '\n' + fields
+    }
+    strToSign += '\n' + resource
+
+    // console.log('strToSign', strToSign) // logspam
+    const sig = crypto.createHmac('sha256', api_key).update(strToSign, 'utf8').digest('base64')
+
+    const headers = {
+        Authorization: `TSA ${customer_id}:${sig}`,
+        'Content-Type': content_type,
+        'x-ts-date': currDate,
+        'x-ts-auth-method': auth_method,
+        'x-ts-nonce': nonce
+    }
+    return headers
+}
+
+const urlencode = json =>
+    Object.keys(json).map(
+        key => encodeURI(key) + '=' + encodeURI(json[key])
+    ).join('&')
diff --git a/webpack/utils/start-koa.js b/webpack/utils/start-koa.js
index 410ac29a0683f40f5bd64dd298f197aba8d5f4b9..4d9ed203a84df8a590cc00b904845ecf6b43b6c4 100644
--- a/webpack/utils/start-koa.js
+++ b/webpack/utils/start-koa.js
@@ -30,14 +30,6 @@ const startServer = () => {
             if (!started) {
                 started = true;
 
-                // Start browserSync
-                //browserSync({
-                //    port: parseInt(process.env.PORT, 10) + 2 || 3002,
-                //    proxy: `0.0.0.0:${parseInt(process.env.PORT, 10) || 3000}`,
-                //    open: false,
-                //    ui: false
-                //});
-
                 // Listen for `rs` in stdin to restart server
                 console.log('type `rs` in console for restarting koa application');
                 process.stdin.setEncoding('utf8');
@@ -47,7 +39,9 @@ const startServer = () => {
                 });
 
                 // Start watcher on server files and restart server on change
-                watch(path.join(__dirname, '../../server'), () => restartServer());
+                const server_path = path.join(__dirname, '../../server');
+                // const app_path = path.join(__dirname, '../../app');
+                watch([server_path], () => restartServer());
             }
         }
     });