mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-05 14:11:41 -04:00
Changed registration flow to first send email verification link that
when visited sends sms code for second step of account verification. At that second step user enters just sms code sent to users mobile number. Also moved dao calls that simply proxied calls to client to calling client directly. There is still a place where a user will be a sent a code for verification to their email namely if they update email address.
This commit is contained in:
@@ -43,20 +43,11 @@ def increment_failed_login_count(id):
|
||||
|
||||
|
||||
def activate_user(user):
|
||||
user.state = 'active'
|
||||
return user_api_client.update_user(user)
|
||||
return user_api_client.activate_user(user)
|
||||
|
||||
|
||||
def is_email_unique(email_address):
|
||||
try:
|
||||
if user_api_client.get_user_by_email(email_address):
|
||||
return False
|
||||
return True
|
||||
except HTTPError as ex:
|
||||
if ex.status_code == 404:
|
||||
return True
|
||||
else:
|
||||
raise ex
|
||||
return user_api_client.is_email_unique(email_address)
|
||||
|
||||
|
||||
def send_verify_code(user_id, code_type, to):
|
||||
|
||||
@@ -150,49 +150,6 @@ class TwoFactorForm(Form):
|
||||
raise ValidationError(reason)
|
||||
|
||||
|
||||
class VerifySmsForm(Form):
|
||||
def __init__(self, validate_code_func, *args, **kwargs):
|
||||
'''
|
||||
Keyword arguments:
|
||||
validate_code_func -- Validates the code with the API.
|
||||
'''
|
||||
self.validate_code_func = validate_code_func
|
||||
super(VerifySmsForm, self).__init__(*args, **kwargs)
|
||||
|
||||
sms_code = sms_code()
|
||||
|
||||
def validate_sms_code(self, field):
|
||||
is_valid, reason = self.validate_code_func(field.data, 'sms')
|
||||
if not is_valid:
|
||||
raise ValidationError(reason)
|
||||
|
||||
|
||||
class VerifyForm(Form):
|
||||
def __init__(self, validate_code_func, *args, **kwargs):
|
||||
'''
|
||||
Keyword arguments:
|
||||
validate_code_func -- Validates the code with the API.
|
||||
'''
|
||||
self.validate_code_func = validate_code_func
|
||||
super(VerifyForm, self).__init__(*args, **kwargs)
|
||||
|
||||
sms_code = sms_code()
|
||||
email_code = email_code()
|
||||
|
||||
def _validate_code(self, cde, code_type):
|
||||
is_valid, reason = self.validate_code_func(cde, code_type)
|
||||
if not is_valid:
|
||||
raise ValidationError(reason)
|
||||
|
||||
def validate_email_code(self, field):
|
||||
if self.sms_code.data:
|
||||
self._validate_code(field.data, 'email')
|
||||
|
||||
def validate_sms_code(self, field):
|
||||
if self.email_code.data:
|
||||
self._validate_code(field.data, 'sms')
|
||||
|
||||
|
||||
class EmailNotReceivedForm(Form):
|
||||
email_address = email_address()
|
||||
|
||||
|
||||
@@ -8,25 +8,25 @@ from flask import (
|
||||
from flask_login import login_required
|
||||
|
||||
from app.main import main
|
||||
from app.main.dao import services_dao, users_dao
|
||||
from app.main.dao import services_dao
|
||||
from app.main.forms import AddServiceForm
|
||||
from app.notify_client.models import InvitedUser
|
||||
|
||||
from app import (
|
||||
invite_api_client,
|
||||
user_api_client,
|
||||
notifications_api_client)
|
||||
notifications_api_client
|
||||
)
|
||||
|
||||
|
||||
@main.route("/add-service", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def add_service():
|
||||
|
||||
invited_user = session.get('invited_user')
|
||||
if invited_user:
|
||||
invitation = InvitedUser(**invited_user)
|
||||
# if invited user add to service and redirect to dashboard
|
||||
user = users_dao.get_user_by_id(session['user_id'])
|
||||
user = user_api_client.get_user(session['user_id'])
|
||||
service_id = invited_user['service']
|
||||
user_api_client.add_user_to_service(service_id, user.id, invitation.permissions)
|
||||
invite_api_client.accept_invite(service_id, invitation.id)
|
||||
|
||||
@@ -1,35 +1,32 @@
|
||||
from flask import (
|
||||
render_template, redirect, session, url_for)
|
||||
|
||||
from flask_login import current_user
|
||||
render_template,
|
||||
redirect,
|
||||
session,
|
||||
url_for
|
||||
)
|
||||
|
||||
from app import user_api_client
|
||||
from app.main import main
|
||||
from app.main.dao import users_dao
|
||||
from app.main.forms import EmailNotReceivedForm, TextNotReceivedForm
|
||||
from app.main.forms import TextNotReceivedForm
|
||||
|
||||
|
||||
@main.route('/email-not-received', methods=['GET', 'POST'])
|
||||
def check_and_resend_email_code():
|
||||
@main.route('/resend-email-verification')
|
||||
def resend_email_verification():
|
||||
# TODO there needs to be a way to regenerate a session id
|
||||
user = users_dao.get_user_by_email(session['user_details']['email'])
|
||||
form = EmailNotReceivedForm(email_address=user.email_address)
|
||||
if form.validate_on_submit():
|
||||
users_dao.send_verify_code(user.id, 'email', to=form.email_address.data)
|
||||
user.email_address = form.email_address.data
|
||||
users_dao.update_user(user)
|
||||
return redirect(url_for('.verify'))
|
||||
return render_template('views/email-not-received.html', form=form)
|
||||
user = user_api_client.get_user_by_email(session['user_details']['email'])
|
||||
user_api_client.send_verify_email(user.id, user.email_address)
|
||||
return render_template('views/resend-email-verification.html', email=user.email_address)
|
||||
|
||||
|
||||
@main.route('/text-not-received', methods=['GET', 'POST'])
|
||||
def check_and_resend_text_code():
|
||||
# TODO there needs to be a way to regenerate a session id
|
||||
user = users_dao.get_user_by_email(session['user_details']['email'])
|
||||
user = user_api_client.get_user_by_email(session['user_details']['email'])
|
||||
form = TextNotReceivedForm(mobile_number=user.mobile_number)
|
||||
if form.validate_on_submit():
|
||||
users_dao.send_verify_code(user.id, 'sms', to=form.mobile_number.data)
|
||||
user_api_client.send_verify_code(user.id, 'sms', to=form.mobile_number.data)
|
||||
user.mobile_number = form.mobile_number.data
|
||||
users_dao.update_user(user)
|
||||
user_api_client.update_user(user)
|
||||
return redirect(url_for('.verify'))
|
||||
return render_template('views/text-not-received.html', form=form)
|
||||
|
||||
@@ -42,6 +39,6 @@ def verification_code_not_received():
|
||||
@main.route('/send-new-code', methods=['GET'])
|
||||
def check_and_resend_verification_code():
|
||||
# TODO there needs to be a way to generate a new session id
|
||||
user = users_dao.get_user_by_email(session['user_details']['email'])
|
||||
users_dao.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
user = user_api_client.get_user_by_email(session['user_details']['email'])
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
return redirect(url_for('main.two_factor'))
|
||||
|
||||
@@ -3,11 +3,15 @@ from flask import (
|
||||
url_for,
|
||||
session,
|
||||
flash,
|
||||
render_template)
|
||||
render_template
|
||||
)
|
||||
|
||||
|
||||
from notifications_python_client.errors import HTTPError
|
||||
|
||||
from app.main import main
|
||||
from app.main.dao.services_dao import get_service_by_id_or_404
|
||||
|
||||
from app import (
|
||||
invite_api_client,
|
||||
user_api_client
|
||||
@@ -32,7 +36,11 @@ def accept_invite(token):
|
||||
|
||||
session['invited_user'] = invited_user.serialize()
|
||||
|
||||
existing_user = user_api_client.get_user_by_email(invited_user.email_address)
|
||||
try:
|
||||
existing_user = user_api_client.get_user_by_email(invited_user.email_address)
|
||||
except HTTPError as ex:
|
||||
if ex.status_code == 404:
|
||||
existing_user = False
|
||||
|
||||
service_users = user_api_client.get_users_for_service(invited_user.service)
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ from flask import (
|
||||
request,
|
||||
render_template,
|
||||
redirect,
|
||||
abort,
|
||||
url_for,
|
||||
flash)
|
||||
flash
|
||||
)
|
||||
|
||||
from flask_login import (
|
||||
login_required,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta
|
||||
)
|
||||
|
||||
from flask import (
|
||||
render_template,
|
||||
@@ -12,7 +15,7 @@ from flask import (
|
||||
from flask.ext.login import current_user
|
||||
|
||||
from app.main import main
|
||||
from app.main.dao import users_dao
|
||||
|
||||
from app.main.forms import (
|
||||
RegisterUserForm,
|
||||
RegisterUserFromInviteForm
|
||||
@@ -28,9 +31,9 @@ def register():
|
||||
|
||||
form = RegisterUserForm()
|
||||
if form.validate_on_submit():
|
||||
registered = _do_registration(form)
|
||||
registered = _do_registration(form, send_sms=False)
|
||||
if registered:
|
||||
return redirect(url_for('main.verify'))
|
||||
return redirect(url_for('main.registration_continue'))
|
||||
else:
|
||||
flash('There was an error registering your account')
|
||||
return render_template('views/register.html', form=form), 400
|
||||
@@ -40,7 +43,6 @@ def register():
|
||||
|
||||
@main.route('/register-from-invite', methods=['GET', 'POST'])
|
||||
def register_from_invite():
|
||||
|
||||
form = RegisterUserFromInviteForm()
|
||||
invited_user = session.get('invited_user')
|
||||
if not invited_user:
|
||||
@@ -61,8 +63,8 @@ def register_from_invite():
|
||||
return render_template('views/register-from-invite.html', email_address=invited_user['email_address'], form=form)
|
||||
|
||||
|
||||
def _do_registration(form, service=None, send_email=True):
|
||||
if users_dao.is_email_unique(form.email_address.data):
|
||||
def _do_registration(form, service=None, send_sms=True, send_email=True):
|
||||
if user_api_client.is_email_unique(form.email_address.data):
|
||||
user = user_api_client.register_user(form.name.data,
|
||||
form.email_address.data,
|
||||
form.mobile_number.data,
|
||||
@@ -73,11 +75,19 @@ def _do_registration(form, service=None, send_email=True):
|
||||
# How do we report to the user there is a problem with
|
||||
# sending codes apart from service unavailable?
|
||||
# at the moment i believe http 500 is fine.
|
||||
users_dao.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
|
||||
if send_email:
|
||||
users_dao.send_verify_code(user.id, 'email', user.email_address)
|
||||
user_api_client.send_verify_email(user.id, user.email_address)
|
||||
|
||||
if send_sms:
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
session['expiry_date'] = str(datetime.now() + timedelta(hours=1))
|
||||
session['user_details'] = {"email": user.email_address, "id": user.id}
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@main.route('/registration-continue')
|
||||
def registration_continue():
|
||||
return render_template('views/registration-continue.html')
|
||||
|
||||
@@ -3,15 +3,22 @@ from flask import (
|
||||
redirect,
|
||||
url_for,
|
||||
session,
|
||||
abort,
|
||||
flash,
|
||||
request
|
||||
)
|
||||
|
||||
from flask.ext.login import (current_user, login_fresh, confirm_login)
|
||||
from flask.ext.login import (
|
||||
current_user,
|
||||
login_fresh,
|
||||
confirm_login
|
||||
)
|
||||
|
||||
from app.main import main
|
||||
from app.main.dao import (users_dao, services_dao)
|
||||
from app.main.dao import services_dao
|
||||
|
||||
from app import user_api_client
|
||||
|
||||
|
||||
from app.main.forms import LoginForm
|
||||
|
||||
|
||||
@@ -22,7 +29,7 @@ def sign_in():
|
||||
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
user = users_dao.get_user_by_email(form.email_address.data)
|
||||
user = user_api_client.get_user_by_email(form.email_address.data)
|
||||
user = _get_and_verify_user(user, form.password.data)
|
||||
if user:
|
||||
# Remember me login
|
||||
@@ -41,7 +48,7 @@ def sign_in():
|
||||
if user.state == 'pending':
|
||||
return redirect(url_for('.verify'))
|
||||
elif user.is_active():
|
||||
users_dao.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
if request.args.get('next'):
|
||||
return redirect(url_for('.two_factor', next=request.args.get('next')))
|
||||
else:
|
||||
@@ -62,7 +69,7 @@ def _get_and_verify_user(user, password):
|
||||
return None
|
||||
elif user.is_locked():
|
||||
return None
|
||||
elif not users_dao.verify_password(user.id, password):
|
||||
elif not user_api_client.verify_password(user.id, password):
|
||||
return None
|
||||
else:
|
||||
return user
|
||||
|
||||
@@ -1,16 +1,35 @@
|
||||
from flask import (
|
||||
request, render_template, redirect, url_for, session)
|
||||
request,
|
||||
render_template,
|
||||
redirect,
|
||||
url_for,
|
||||
session
|
||||
)
|
||||
|
||||
from flask.ext.login import current_user
|
||||
from flask_login import login_required
|
||||
from app.main import main
|
||||
|
||||
from app.main.dao.users_dao import (
|
||||
verify_password, update_user, check_verify_code, is_email_unique,
|
||||
send_verify_code)
|
||||
from app.main.forms import (
|
||||
ChangePasswordForm, ChangeNameForm, ChangeEmailForm, ConfirmEmailForm,
|
||||
ChangeMobileNumberForm, ConfirmMobileNumberForm, ConfirmPasswordForm
|
||||
verify_password,
|
||||
update_user,
|
||||
check_verify_code,
|
||||
is_email_unique,
|
||||
send_verify_code
|
||||
)
|
||||
|
||||
from app.main.forms import (
|
||||
ChangePasswordForm,
|
||||
ChangeNameForm,
|
||||
ChangeEmailForm,
|
||||
ConfirmEmailForm,
|
||||
ChangeMobileNumberForm,
|
||||
ConfirmMobileNumberForm,
|
||||
ConfirmPasswordForm
|
||||
)
|
||||
|
||||
from app import user_api_client
|
||||
|
||||
NEW_EMAIL = 'new-email'
|
||||
NEW_MOBILE = 'new-mob'
|
||||
NEW_EMAIL_PASSWORD_CONFIRMED = 'new-email-password-confirmed'
|
||||
@@ -63,7 +82,6 @@ def user_profile_email():
|
||||
@main.route("/user-profile/email/authenticate", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def user_profile_email_authenticate():
|
||||
|
||||
# Validate password for form
|
||||
def _check_password(pwd):
|
||||
return verify_password(current_user.id, pwd)
|
||||
|
||||
@@ -1,42 +1,72 @@
|
||||
import json
|
||||
|
||||
from flask import (
|
||||
render_template,
|
||||
redirect,
|
||||
session,
|
||||
url_for
|
||||
url_for,
|
||||
current_app,
|
||||
flash
|
||||
)
|
||||
|
||||
from itsdangerous import SignatureExpired
|
||||
|
||||
from flask_login import login_user
|
||||
|
||||
from notifications_python_client.errors import HTTPError
|
||||
|
||||
from app.main import main
|
||||
from app.main.dao import users_dao
|
||||
from app.main.forms import (
|
||||
VerifyForm,
|
||||
VerifySmsForm
|
||||
)
|
||||
from app.main.forms import TwoFactorForm
|
||||
|
||||
from app import user_api_client
|
||||
|
||||
|
||||
@main.route('/verify', methods=['GET', 'POST'])
|
||||
def verify():
|
||||
|
||||
# TODO there needs to be a way to regenerate a session id
|
||||
# or handle gracefully.
|
||||
user_id = session['user_details']['id']
|
||||
|
||||
def _check_code(code, code_type):
|
||||
return users_dao.check_verify_code(user_id, code, code_type)
|
||||
def _check_code(code):
|
||||
return user_api_client.check_verify_code(user_id, code, 'sms')
|
||||
|
||||
if session.get('invited_user'):
|
||||
form = VerifySmsForm(_check_code)
|
||||
else:
|
||||
form = VerifyForm(_check_code)
|
||||
form = TwoFactorForm(_check_code)
|
||||
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
user = users_dao.get_user_by_id(user_id)
|
||||
activated_user = users_dao.activate_user(user)
|
||||
user = user_api_client.get_user(user_id)
|
||||
activated_user = user_api_client.activate_user(user)
|
||||
login_user(activated_user)
|
||||
return redirect(url_for('main.add_service', first='first'))
|
||||
finally:
|
||||
session.pop('user_details', None)
|
||||
|
||||
return render_template('views/verify.html', form=form)
|
||||
return render_template('views/two-factor.html', form=form)
|
||||
|
||||
|
||||
@main.route('/verify-email/<token>')
|
||||
def verify_email(token):
|
||||
from utils.url_safe_token import check_token
|
||||
try:
|
||||
token_data = check_token(token,
|
||||
current_app.config['SECRET_KEY'],
|
||||
current_app.config['DANGEROUS_SALT'],
|
||||
current_app.config['EMAIL_EXPIRY_SECONDS'])
|
||||
|
||||
token_data = json.loads(token_data)
|
||||
verified = user_api_client.check_verify_code(token_data['user_id'], token_data['secret_code'], 'email')
|
||||
|
||||
if verified[0]:
|
||||
user = user_api_client.get_user(token_data['user_id'])
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
session['user_details'] = {"email": user.email_address, "id": user.id}
|
||||
return redirect('verify')
|
||||
else:
|
||||
message = "There was a problem verifying your account. Error message: '{}'".format(verified[1])
|
||||
flash(message)
|
||||
# TODO could this ask for a resend instead?
|
||||
return redirect(url_for('main.index'))
|
||||
|
||||
except SignatureExpired:
|
||||
flash('The link in the email we sent you has expired')
|
||||
return redirect(url_for('main.resend_email_verification'))
|
||||
|
||||
@@ -32,14 +32,7 @@ class UserApiClient(BaseAPIClient):
|
||||
return User(user_data['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
|
||||
def get_user_by_email(self, email_address):
|
||||
try:
|
||||
params = {'email': email_address}
|
||||
user_data = self.get('/user/email', params=params)
|
||||
except HTTPError as e:
|
||||
if e.status_code == 404:
|
||||
return None
|
||||
else:
|
||||
raise e
|
||||
user_data = self.get('/user/email', params={'email': email_address})
|
||||
return User(user_data['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
|
||||
def get_users(self):
|
||||
@@ -68,7 +61,12 @@ class UserApiClient(BaseAPIClient):
|
||||
def send_verify_code(self, user_id, code_type, to):
|
||||
data = {'to': to}
|
||||
endpoint = '/user/{0}/{1}-code'.format(user_id, code_type)
|
||||
resp = self.post(endpoint, data=data)
|
||||
self.post(endpoint, data=data)
|
||||
|
||||
def send_verify_email(self, user_id, to):
|
||||
data = {'to': to}
|
||||
endpoint = '/user/{0}/email-verification'.format(user_id)
|
||||
self.post(endpoint, data=data)
|
||||
|
||||
def check_verify_code(self, user_id, code, code_type):
|
||||
data = {'code_type': code_type, 'code': code}
|
||||
@@ -106,3 +104,18 @@ class UserApiClient(BaseAPIClient):
|
||||
endpoint = '/user/reset-password'
|
||||
data = {'email': email_address}
|
||||
self.post(endpoint, data=data)
|
||||
|
||||
def is_email_unique(self, email_address):
|
||||
try:
|
||||
if self.get_user_by_email(email_address):
|
||||
return False
|
||||
return True
|
||||
except HTTPError as ex:
|
||||
if ex.status_code == 404:
|
||||
return True
|
||||
else:
|
||||
raise ex
|
||||
|
||||
def activate_user(self, user):
|
||||
user.state = 'active'
|
||||
return self.update_user(user)
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
{% from "components/textbox.html" import textbox %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
|
||||
{% block page_title %}
|
||||
Check your email address – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-large">Check your email address</h1>
|
||||
|
||||
<p>Check your email address is correct and then resend the confirmation code.</p>
|
||||
|
||||
<form autocomplete="off" method="post">
|
||||
{{ textbox(form.email_address, hint='Your email address must end in .gov.uk') }}
|
||||
{{ page_footer('Resend confirmation code') }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
16
app/templates/views/registration-continue.html
Normal file
16
app/templates/views/registration-continue.html
Normal file
@@ -0,0 +1,16 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
Check your email – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-large">Now check your email</h1>
|
||||
<p>Click the link in the email we’ve sent you to continue your registration</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
18
app/templates/views/resend-email-verification.html
Normal file
18
app/templates/views/resend-email-verification.html
Normal file
@@ -0,0 +1,18 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
{% from "components/textbox.html" import textbox %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
|
||||
{% block page_title %}
|
||||
Check your email – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-large">Check your email</h1>
|
||||
<p>In order to verify your email address we've sent a new confirmation link to {{email}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,53 +0,0 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
{% from "components/textbox.html" import textbox %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
|
||||
{% block page_title %}
|
||||
{% if form.email_code %}
|
||||
Enter the codes we’ve sent you – GOV.UK Notify
|
||||
{% else %}
|
||||
Enter the code we’ve sent you – GOV.UK Notify
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
{% if form.email_code %}
|
||||
<h1 class="heading-large">Enter the codes we’ve sent you</h1>
|
||||
{% else %}
|
||||
<h1 class="heading-large">Enter the code we’ve sent you</h1>
|
||||
{% endif %}
|
||||
{% if form.email_code %}
|
||||
<p>
|
||||
We’ve sent you confirmation codes by email and text message.
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
We’ve sent you a confirmation code by text message.
|
||||
</p>
|
||||
{% endif %}
|
||||
<form autocomplete="off" method="post">
|
||||
{% if form.sms_code %}
|
||||
{{ textbox(
|
||||
form.sms_code,
|
||||
width='5em',
|
||||
help_link=url_for('.check_and_resend_text_code'),
|
||||
help_link_text='I haven’t received a text message'
|
||||
) }}
|
||||
{% endif %}
|
||||
{% if form.email_code %}
|
||||
{{ textbox(
|
||||
form.email_code,
|
||||
width='5em',
|
||||
help_link=url_for('.check_and_resend_email_code'),
|
||||
help_link_text='I haven’t received an email'
|
||||
) }}
|
||||
{% endif %}
|
||||
{{ page_footer("Continue") }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user