Skip to content
Snippets Groups Projects
Commit ca863338 authored by James Calfee's avatar James Calfee Committed by Valentine Zavgorodnev
Browse files

498 phone number validation (#501)

* Remove auto 1 prefix on phone numbers (breaks Norway numbers) #498

* Add country code drop-down. #498

* Improve where clause in confirm phone. #498
parent 64f4995f
No related branches found
No related tags found
No related merge requests found
import React from 'react'
export default props => {
return <select {...props}>
<option value=""></option>
<option value="93">Afghanistan +(93)</option>
<option value="355">Albania +(355)</option>
<option value="213">Algeria +(213)</option>
<option value="1">American Samoa +(1)</option>
<option value="376">Andorra +(376)</option>
<option value="244">Angola +(244)</option>
<option value="1">Anguilla +(1)</option>
<option value="1">Antigua and Barbuda +(1)</option>
<option value="54">Argentina +(54)</option>
<option value="374">Armenia +(374)</option>
<option value="297">Aruba +(297)</option>
<option value="247">Ascension +(247)</option>
<option value="61">Australia +(61)</option>
<option value="672">Australian External Territories +(672)</option>
<option value="43">Austria +(43)</option>
<option value="994">Azerbaijan +(994)</option>
<option value="1">Bahamas +(1)</option>
<option value="973">Bahrain +(973)</option>
<option value="880">Bangladesh +(880)</option>
<option value="1">Barbados +(1)</option>
<option value="375">Belarus +(375)</option>
<option value="32">Belgium +(32)</option>
<option value="501">Belize +(501)</option>
<option value="229">Benin +(229)</option>
<option value="1">Bermuda +(1)</option>
<option value="975">Bhutan +(975)</option>
<option value="591">Bolivia +(591)</option>
<option value="599">Bonaire, Sabo &amp; St. Eustacius +(599)</option>
<option value="387">Bosnia and Herzegovina +(387)</option>
<option value="267">Botswana +(267)</option>
<option value="55">Brazil +(55)</option>
<option value="1">British Virgin Islands +(1)</option>
<option value="673">Brunei Darussalam +(673)</option>
<option value="359">Bulgaria +(359)</option>
<option value="226">Burkina Faso +(226)</option>
<option value="257">Burundi +(257)</option>
<option value="855">Cambodia +(855)</option>
<option value="237">Cameroon +(237)</option>
<option value="1">Canada +(1)</option>
<option value="238">Cape Verde +(238)</option>
<option value="1">Cayman Islands +(1)</option>
<option value="236">Central African Republic +(236)</option>
<option value="235">Chad +(235)</option>
<option value="56">Chile +(56)</option>
<option value="86">China +(86)</option>
<option value="57">Colombia +(57)</option>
<option value="269">Comoros +(269)</option>
<option value="242">Congo +(242)</option>
<option value="682">Cook Islands +(682)</option>
<option value="506">Costa Rica +(506)</option>
<option value="225">Cote d'Ivoire +(225)</option>
<option value="385">Croatia +(385)</option>
<option value="53">Cuba +(53)</option>
<option value="599">Curacao +(599)</option>
<option value="357">Cyprus +(357)</option>
<option value="420">Czech Republic +(420)</option>
<option value="243">Democratic Republic of the Congo +(243)</option>
<option value="45">Denmark +(45)</option>
<option value="246">Diego Garcia +(246)</option>
<option value="253">Djibouti +(253)</option>
<option value="1">Dominica +(1)</option>
<option value="1">Dominican Republic +(1)</option>
<option value="670">East Timor +(670)</option>
<option value="593">Ecuador +(593)</option>
<option value="20">Egypt +(20)</option>
<option value="503">El Salvador +(503)</option>
<option value="240">Equatorial Guinea +(240)</option>
<option value="291">Eritrea +(291)</option>
<option value="372">Estonia +(372)</option>
<option value="251">Ethiopia +(251)</option>
<option value="388">European Telephony Numbering Space +(388)</option>
<option value="500">Falkland Islands +(500)</option>
<option value="298">Faroe Islands +(298)</option>
<option value="679">Fiji +(679)</option>
<option value="358">Finland +(358)</option>
<option value="33">France +(33)</option>
<option value="262">French Dept/Terr in Indian Ocean +(262)</option>
<option value="594">French Guiana +(594)</option>
<option value="689">French Polynesia +(689)</option>
<option value="241">Gabon +(241)</option>
<option value="220">Gambia +(220)</option>
<option value="995">Georgia +(995)</option>
<option value="49">Germany +(49)</option>
<option value="233">Ghana +(233)</option>
<option value="350">Gibraltar +(350)</option>
<option value="881">Global Mobile Satellite System +(881)</option>
<option value="30">Greece +(30)</option>
<option value="299">Greenland +(299)</option>
<option value="1">Grenada +(1)</option>
<option value="590">Guadeloupe +(590)</option>
<option value="1">Guam +(1)</option>
<option value="502">Guatemala +(502)</option>
<option value="224">Guinea +(224)</option>
<option value="245">Guinea-Bissau +(245)</option>
<option value="592">Guyana +(592)</option>
<option value="509">Haiti +(509)</option>
<option value="504">Honduras +(504)</option>
<option value="852">Hong Kong +(852)</option>
<option value="36">Hungary +(36)</option>
<option value="354">Iceland +(354)</option>
<option value="91">India +(91)</option>
<option value="62">Indonesia +(62)</option>
<option value="870">Inmarsat SNAC +(870)</option>
<option value="882">International Networks +(882)</option>
<option value="883">International VoIP +(883)</option>
<option value="98">Iran +(98)</option>
<option value="964">Iraq +(964)</option>
<option value="353">Ireland +(353)</option>
<option value="972">Israel +(972)</option>
<option value="39">Italy +(39)</option>
<option value="1">Jamaica +(1)</option>
<option value="81">Japan +(81)</option>
<option value="962">Jordan +(962)</option>
<option value="7">Kazakhstan +(7)</option>
<option value="254">Kenya +(254)</option>
<option value="686">Kiribati +(686)</option>
<option value="965">Kuwait +(965)</option>
<option value="996">Kyrgyzstan +(996)</option>
<option value="856">Lao +(856)</option>
<option value="371">Latvia +(371)</option>
<option value="961">Lebanon +(961)</option>
<option value="266">Lesotho +(266)</option>
<option value="231">Liberia +(231)</option>
<option value="218">Libya +(218)</option>
<option value="423">Liechtenstein +(423)</option>
<option value="370">Lithuania +(370)</option>
<option value="352">Luxembourg +(352)</option>
<option value="853">Macao +(853)</option>
<option value="389">Macedonia +(389)</option>
<option value="261">Madagascar +(261)</option>
<option value="265">Malawi +(265)</option>
<option value="60">Malaysia +(60)</option>
<option value="960">Maldives +(960)</option>
<option value="223">Mali +(223)</option>
<option value="356">Malta +(356)</option>
<option value="692">Marshall Islands +(692)</option>
<option value="596">Martinique +(596)</option>
<option value="222">Mauritania +(222)</option>
<option value="230">Mauritius +(230)</option>
<option value="52">Mexico +(52)</option>
<option value="691">Micronesia +(691)</option>
<option value="373">Moldova +(373)</option>
<option value="377">Monaco +(377)</option>
<option value="976">Mongolia +(976)</option>
<option value="382">Montenegro +(382)</option>
<option value="1">Montserrat +(1)</option>
<option value="212">Morocco +(212)</option>
<option value="258">Mozambique +(258)</option>
<option value="95">Myanmar +(95)</option>
<option value="264">Namibia +(264)</option>
<option value="674">Nauru +(674)</option>
<option value="977">Nepal +(977)</option>
<option value="31">Netherlands +(31)</option>
<option value="687">New Caledonia +(687)</option>
<option value="64">New Zealand +(64)</option>
<option value="505">Nicaragua +(505)</option>
<option value="227">Niger +(227)</option>
<option value="234">Nigeria +(234)</option>
<option value="683">Niue +(683)</option>
<option value="850">North Korea +(850)</option>
<option value="1">Northern Mariana Islands +(1)</option>
<option value="47">Norway +(47)</option>
<option value="968">Oman +(968)</option>
<option value="92">Pakistan +(92)</option>
<option value="680">Palau +(680)</option>
<option value="970">Palestine +(970)</option>
<option value="507">Panama +(507)</option>
<option value="675">Papua New Guinea +(675)</option>
<option value="595">Paraguay +(595)</option>
<option value="51">Peru +(51)</option>
<option value="63">Philippines +(63)</option>
<option value="48">Poland +(48)</option>
<option value="351">Portugal +(351)</option>
<option value="1">Puerto Rico +(1)</option>
<option value="974">Qatar +(974)</option>
<option value="262">Reunion +(262)</option>
<option value="40">Romania +(40)</option>
<option value="7">Russia +(7)</option>
<option value="250">Rwanda +(250)</option>
<option value="290">Saint Helena +(290)</option>
<option value="1">Saint Kitts and Nevis +(1)</option>
<option value="1">Saint Lucia +(1)</option>
<option value="508">Saint Pierre and Miquelon +(508)</option>
<option value="1">Saint Vincent and the Grenadines +(1)</option>
<option value="685">Samoa +(685)</option>
<option value="378">San Marino +(378)</option>
<option value="239">Sao Tome and Principe +(239)</option>
<option value="966">Saudi Arabia +(966)</option>
<option value="221">Senegal +(221)</option>
<option value="381">Serbia +(381)</option>
<option value="248">Seychelles +(248)</option>
<option value="232">Sierra Leone +(232)</option>
<option value="65">Singapore +(65)</option>
<option value="1">Sint Maarten +(1)</option>
<option value="421">Slovakia +(421)</option>
<option value="386">Slovenia +(386)</option>
<option value="677">Solomon Islands +(677)</option>
<option value="252">Somalia +(252)</option>
<option value="27">South Africa +(27)</option>
<option value="82">South Korea +(82)</option>
<option value="211">South Sudan +(211)</option>
<option value="34">Spain +(34)</option>
<option value="94">Sri Lanka +(94)</option>
<option value="249">Sudan +(249)</option>
<option value="597">Suriname +(597)</option>
<option value="268">Swaziland +(268)</option>
<option value="46">Sweden +(46)</option>
<option value="41">Switzerland +(41)</option>
<option value="963">Syria +(963)</option>
<option value="886">Taiwan +(886)</option>
<option value="992">Tajikistan +(992)</option>
<option value="255">Tanzania +(255)</option>
<option value="66">Thailand +(66)</option>
<option value="228">Togo +(228)</option>
<option value="690">Tokelau +(690)</option>
<option value="676">Tonga +(676)</option>
<option value="1">Trinidad and Tobago +(1)</option>
<option value="216">Tunisia +(216)</option>
<option value="90">Turkey +(90)</option>
<option value="993">Turkmenistan +(993)</option>
<option value="1">Turks and Caicos Islands +(1)</option>
<option value="688">Tuvalu +(688)</option>
<option value="1">U.S. Virgin Islands +(1)</option>
<option value="256">Uganda +(256)</option>
<option value="380">Ukraine +(380)</option>
<option value="971">United Arab Emirates +(971)</option>
<option value="44">United Kingdom +(44)</option>
<option value="1">United States +(1)</option>
<option value="991">Univ Int'l Telecom. Correspondence Svc. +(991)</option>
<option value="888">Univ Telecom. For Disaster Relief +(888)</option>
<option value="800">Universal International Freephone Number +(800)</option>
<option value="979">Universal Int'l Premium Rate Service +(979)</option>
<option value="808">Universal Int'l Shared Cost Service +(808)</option>
<option value="878">Universal Personal Telecommunications +(878)</option>
<option value="598">Uruguay +(598)</option>
<option value="998">Uzbekistan +(998)</option>
<option value="678">Vanuatu +(678)</option>
<option value="379">Vatican City State +(379)</option>
<option value="58">Venezuela +(58)</option>
<option value="84">Vietnam +(84)</option>
<option value="681">Wallis and Futuna +(681)</option>
<option value="967">Yemen +(967)</option>
<option value="260">Zambia +(260)</option>
<option value="263">Zimbabwe +(263)</option>
</select>
}
\ No newline at end of file
...@@ -6,6 +6,7 @@ import models from 'db/models'; ...@@ -6,6 +6,7 @@ import models from 'db/models';
import ServerHTML from 'server/server-html'; import ServerHTML from 'server/server-html';
import {verify} from 'server/teleSign'; import {verify} from 'server/teleSign';
import SignupProgressBar from 'app/components/elements/SignupProgressBar'; import SignupProgressBar from 'app/components/elements/SignupProgressBar';
import CountryCode from 'app/components/elements/CountryCode';
import {getRemoteIp, checkCSRF} from 'server/utils'; import {getRemoteIp, checkCSRF} from 'server/utils';
import MiniHeader from 'app/components/modules/MiniHeader'; import MiniHeader from 'app/components/modules/MiniHeader';
import secureRandom from 'secure-random' import secureRandom from 'secure-random'
...@@ -17,7 +18,7 @@ function *confirmMobileHandler() { ...@@ -17,7 +18,7 @@ function *confirmMobileHandler() {
const confirmation_code = this.params && this.params.code ? this.params.code : this.request.body.code; 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); console.log('-- /confirm_mobile -->', this.session.uid, this.session.user, confirmation_code);
const mid = yield models.Identity.findOne( const mid = yield models.Identity.findOne(
{attributes: ['id', 'user_id', 'verified', 'updated_at'], where: {user_id: this.session.user, confirmation_code}, order: 'id DESC'} {attributes: ['id', 'user_id', 'verified', 'updated_at'], where: {user_id: this.session.user, confirmation_code, provider: 'phone'}, order: 'id DESC'}
); );
if (!mid) { if (!mid) {
this.flash = {error: 'Wrong confirmation code.'}; this.flash = {error: 'Wrong confirmation code.'};
...@@ -39,8 +40,8 @@ function *confirmMobileHandler() { ...@@ -39,8 +40,8 @@ function *confirmMobileHandler() {
yield mid.update({verified: true}); yield mid.update({verified: true});
this.redirect('/create_account'); this.redirect('/create_account');
} }
export default function useEnterAndConfirmMobilePages(app) { export default function useEnterAndConfirmMobilePages(app) {
const router = koa_router(); const router = koa_router();
app.use(router.routes()); app.use(router.routes());
const koaBody = koa_body(); const koaBody = koa_body();
...@@ -57,6 +58,9 @@ export default function useEnterAndConfirmMobilePages(app) { ...@@ -57,6 +58,9 @@ export default function useEnterAndConfirmMobilePages(app) {
this.redirect('/create_account'); this.redirect('/create_account');
return; return;
} }
const phone = this.query.phone
const country = this.query.country
const body = renderToString(<div className="App"> const body = renderToString(<div className="App">
<MiniHeader /> <MiniHeader />
<SignupProgressBar steps={['email', 'phone', 'steem account']} current={2} /> <SignupProgressBar steps={['email', 'phone', 'steem account']} current={2} />
...@@ -68,11 +72,15 @@ export default function useEnterAndConfirmMobilePages(app) { ...@@ -68,11 +72,15 @@ export default function useEnterAndConfirmMobilePages(app) {
Your phone number will not be used for any other purpose other than phone verification and account recovery.</div> Your phone number will not be used for any other purpose other than phone verification and account recovery.</div>
<br /> <br />
<input type="hidden" name="csrf" value={this.csrf} /> <input type="hidden" name="csrf" value={this.csrf} />
<label>
Country Code
<CountryCode name="country" value={country} />
</label>
<label> <label>
Phone number Phone number
<input type="tel" name="mobile" defaultValue={mid ? mid.phone : ''} /> <input type="tel" name="phone" value={phone} />
</label> </label>
<div className="secondary">Examples: 1-541-754-3010 | +1-541-754-3010 | +49-89-636-48018</div> <div className="secondary">Examples: 541-754-3010 | 89-636-48018</div>
<br /> <br />
<div className="secondary">* Land lines cannot receive SMS messages</div> <div className="secondary">* Land lines cannot receive SMS messages</div>
<div className="secondary">* Message and data rates may apply</div> <div className="secondary">* Message and data rates may apply</div>
...@@ -90,23 +98,24 @@ export default function useEnterAndConfirmMobilePages(app) { ...@@ -90,23 +98,24 @@ export default function useEnterAndConfirmMobilePages(app) {
if (!checkCSRF(this, this.request.body.csrf)) return; if (!checkCSRF(this, this.request.body.csrf)) return;
const user_id = 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; }
let mobile = this.request.body.mobile;
if (!mobile) { const country = this.request.body.country;
this.flash = {error: 'Please provide a phone number'}; const localPhone = this.request.body.phone;
this.redirect('/enter_mobile'); const enterMobileUrl = `/enter_mobile?phone=${localPhone}&country=${country}`
if (!country && country === '') {
this.flash = {error: 'Please select a country code'};
this.redirect(enterMobileUrl);
return; return;
} }
mobile = mobile.match(/\d+/g).join('') if (!localPhone || digits(localPhone).length === 0) {
if(mobile.length < "9998887777".length) { this.flash = {error: 'Please provide a phone number'};
this.flash = {error: 'Please provide an area code'}; this.redirect(enterMobileUrl);
this.redirect('/enter_mobile');
return; return;
} }
if(mobile.length === "9998887777".length) { const phone = digits(country + localPhone)
mobile = `1${mobile}`
}
const eid = yield models.Identity.findOne( const eid = yield models.Identity.findOne(
{attributes: ['id'], where: {user_id, provider: 'email', verified: true}, order: 'id DESC'} {attributes: ['id'], where: {user_id, provider: 'email', verified: true}, order: 'id DESC'}
...@@ -118,12 +127,12 @@ export default function useEnterAndConfirmMobilePages(app) { ...@@ -118,12 +127,12 @@ export default function useEnterAndConfirmMobilePages(app) {
} }
const existing_phone = yield models.Identity.findOne( const existing_phone = yield models.Identity.findOne(
{attributes: ['user_id'], where: {phone: mobile, provider: 'phone', verified: true}, order: 'id DESC'} {attributes: ['user_id'], where: {phone, provider: 'phone', verified: true}, order: 'id DESC'}
); );
if (existing_phone && existing_phone.user_id != user_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); console.log('-- /submit_email existing_phone -->', user_id, this.session.uid, phone, existing_phone.user_id);
this.flash = {error: 'This phone number has already been used'}; this.flash = {error: 'This phone number has already been used'};
this.redirect('/enter_mobile'); this.redirect(enterMobileUrl);
return; return;
} }
...@@ -133,35 +142,38 @@ export default function useEnterAndConfirmMobilePages(app) { ...@@ -133,35 +142,38 @@ export default function useEnterAndConfirmMobilePages(app) {
); );
if (mid) { if (mid) {
if (mid.verified) { if (mid.verified) {
this.flash = {success: 'Phone number has been verified'}; if(mid.phone === phone) {
this.redirect('/create_account'); return; this.flash = {success: 'Phone number has been verified'};
} else { this.redirect('/create_account');
const seconds_ago = (Date.now() - mid.updated_at) / 1000.0;
if (seconds_ago < 120) {
this.flash = {error: 'Confirmation was attempted a moment ago. You can try again only in 2 minutes.'};
this.redirect('/enter_mobile');
return; return;
} }
yield mid.update({confirmation_code, phone: mobile}); yield mid.update({verified: false, phone});
}
const seconds_ago = (Date.now() - mid.updated_at) / 1000.0;
if (seconds_ago < 120) {
this.flash = {error: 'Confirmation was attempted a moment ago. You can try again only in 2 minutes.'};
this.redirect(enterMobileUrl);
return;
} }
yield mid.update({confirmation_code, phone});
} else { } else {
mid = yield models.Identity.create({ mid = yield models.Identity.create({
provider: 'phone', provider: 'phone',
user_id, user_id,
uid: this.session.uid, uid: this.session.uid,
phone: mobile, phone,
verified: false, verified: false,
confirmation_code confirmation_code
}); });
} }
console.log('-- /submit_mobile -->', this.session.uid, this.session.user, mobile, mid.id); console.log('-- /submit_mobile -->', this.session.uid, this.session.user, phone, mid.id);
const ip = getRemoteIp(this.req) const ip = getRemoteIp(this.req)
const verifyResult = yield verify({mobile, confirmation_code, ip}); const verifyResult = yield verify({mobile: phone, confirmation_code, ip});
if (verifyResult && verifyResult.score) mid.update({score: verifyResult.score}); if (verifyResult && verifyResult.score) mid.update({score: verifyResult.score});
if (verifyResult && verifyResult.error) { if (verifyResult && verifyResult.error) {
this.flash = {error: verifyResult.error}; this.flash = {error: verifyResult.error};
this.redirect('/enter_mobile'); this.redirect(enterMobileUrl);
return; return;
} }
...@@ -171,7 +183,7 @@ export default function useEnterAndConfirmMobilePages(app) { ...@@ -171,7 +183,7 @@ export default function useEnterAndConfirmMobilePages(app) {
<br /> <br />
<div className="row" style={{maxWidth: '32rem'}}> <div className="row" style={{maxWidth: '32rem'}}>
<div className="column"> <div className="column">
Thank you for providing your mobile number ({mobile}).<br /> Thank you for providing your phone number ({phone}).<br />
To continue please enter the SMS code we've sent you. To continue please enter the SMS code we've sent you.
</div> </div>
</div> </div>
...@@ -183,16 +195,21 @@ export default function useEnterAndConfirmMobilePages(app) { ...@@ -183,16 +195,21 @@ export default function useEnterAndConfirmMobilePages(app) {
<input type="text" name="code" /> <input type="text" name="code" />
</label> </label>
<br /> <br />
<div className="secondary">Didn't receive the verification code? <a href="/enter_mobile">Re-send</a></div> <div className="secondary">Didn't receive the verification code? <a href={enterMobileUrl}>Re-send</a></div>
<br /> <br />
<input type="submit" className="button" value="CONTINUE" /> <input type="submit" className="button" value="CONTINUE" />
</form> </form>
</div> </div>
</div>); </div>);
const props = { body, title: 'Mobile Confirmation', assets, meta: [] }; const props = { body, title: 'Phone Confirmation', assets, meta: [] };
this.body = '<!DOCTYPE html>' + renderToString(<ServerHTML { ...props } />); this.body = '<!DOCTYPE html>' + renderToString(<ServerHTML { ...props } />);
}); });
router.get('/confirm_mobile/:code', confirmMobileHandler); router.get('/confirm_mobile/:code', confirmMobileHandler);
router.post('/confirm_mobile', koaBody, confirmMobileHandler); router.post('/confirm_mobile', koaBody, confirmMobileHandler);
} }
function digits(text) {
const digitArray = text.match(/\d+/g)
return digitArray ? digitArray.join('') : ''
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment