otpform and otp generation

-issue: how to save generated OTP to user, to check if user inputs the correct OTP
This commit is contained in:
Allyssa Poulin 2023-03-12 17:35:40 -04:00
parent 833ded80b2
commit c0a13afe13
11 changed files with 171 additions and 7 deletions

View File

@ -0,0 +1,59 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyInterpreterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="false" level="WARNING" enabled_by_default="false">
<option name="ignoredPackages">
<value>
<list size="45">
<item index="0" class="java.lang.String" itemvalue="PyJWT" />
<item index="1" class="java.lang.String" itemvalue="alembic" />
<item index="2" class="java.lang.String" itemvalue="greenlet" />
<item index="3" class="java.lang.String" itemvalue="Babel" />
<item index="4" class="java.lang.String" itemvalue="python-dateutil" />
<item index="5" class="java.lang.String" itemvalue="SQLAlchemy" />
<item index="6" class="java.lang.String" itemvalue="python-dotenv" />
<item index="7" class="java.lang.String" itemvalue="MarkupSafe" />
<item index="8" class="java.lang.String" itemvalue="requests" />
<item index="9" class="java.lang.String" itemvalue="python-editor" />
<item index="10" class="java.lang.String" itemvalue="Jinja2" />
<item index="11" class="java.lang.String" itemvalue="Flask-Bootstrap" />
<item index="12" class="java.lang.String" itemvalue="Flask-Login" />
<item index="13" class="java.lang.String" itemvalue="redis" />
<item index="14" class="java.lang.String" itemvalue="dominate" />
<item index="15" class="java.lang.String" itemvalue="elasticsearch" />
<item index="16" class="java.lang.String" itemvalue="Pygments" />
<item index="17" class="java.lang.String" itemvalue="certifi" />
<item index="18" class="java.lang.String" itemvalue="urllib3" />
<item index="19" class="java.lang.String" itemvalue="itsdangerous" />
<item index="20" class="java.lang.String" itemvalue="Flask" />
<item index="21" class="java.lang.String" itemvalue="blinker" />
<item index="22" class="java.lang.String" itemvalue="email-validator" />
<item index="23" class="java.lang.String" itemvalue="dnspython" />
<item index="24" class="java.lang.String" itemvalue="six" />
<item index="25" class="java.lang.String" itemvalue="Werkzeug" />
<item index="26" class="java.lang.String" itemvalue="Flask-HTTPAuth" />
<item index="27" class="java.lang.String" itemvalue="langdetect" />
<item index="28" class="java.lang.String" itemvalue="Flask-WTF" />
<item index="29" class="java.lang.String" itemvalue="click" />
<item index="30" class="java.lang.String" itemvalue="Flask-SQLAlchemy" />
<item index="31" class="java.lang.String" itemvalue="chardet" />
<item index="32" class="java.lang.String" itemvalue="Flask-Moment" />
<item index="33" class="java.lang.String" itemvalue="WTForms" />
<item index="34" class="java.lang.String" itemvalue="PySocks" />
<item index="35" class="java.lang.String" itemvalue="httpie" />
<item index="36" class="java.lang.String" itemvalue="Flask-Mail" />
<item index="37" class="java.lang.String" itemvalue="Flask-Migrate" />
<item index="38" class="java.lang.String" itemvalue="pytz" />
<item index="39" class="java.lang.String" itemvalue="Mako" />
<item index="40" class="java.lang.String" itemvalue="visitor" />
<item index="41" class="java.lang.String" itemvalue="Flask-Babel" />
<item index="42" class="java.lang.String" itemvalue="idna" />
<item index="43" class="java.lang.String" itemvalue="requests-toolbelt" />
<item index="44" class="java.lang.String" itemvalue="rq" />
</list>
</value>
</option>
</inspection_tool>
</profile>
</component>

35
.idea/workspace.xml Normal file
View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="285438c8-50b9-448a-a339-55b76b90c8e5" name="Changes" comment="" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="HTML File" />
</list>
</option>
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectLevelVcsManager">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"DefaultHtmlFileTemplate": "HTML File",
"node.js.selected.package.tslint": "(autodetect)"
}
}]]></component>
<component name="TaskManager">
<servers />
</component>
</project>

View File

@ -12,7 +12,6 @@ def verify_password(username, password):
if user and user.check_password(password):
return user
@basic_auth.error_handler
def basic_auth_error(status):
return error_response(status)

View File

@ -1,8 +1,31 @@
from flask import render_template, current_app
from flask_babel import _
from app.email import send_email
import math, random
def generate_otp(user):
# Declare a digits variable
# which stores all digits
digits = "0123456789"
otp = ""
# length of password can be changed
# by changing value in range
for i in range(4):
otp += digits[math.floor(random.random() * 10)]
return otp
def send_otp_email(user):
otp = generate_otp(user)
send_email(_('[Microblog] Your one time passcode'),
sender=current_app.config['ADMINS'][0],
recipients=[user.email],
text_body=render_template('email/otp.txt',
user=user, otp=otp),
html_body=render_template('email/otp.html',
user=user, otp=otp))
def send_password_reset_email(user):
token = user.get_reset_password_token()
send_email(_('[Microblog] Reset Your Password'),

View File

@ -4,13 +4,15 @@ from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
from flask_babel import _, lazy_gettext as _l
from app.models import User
class LoginForm(FlaskForm):
username = StringField(_l('Username'), validators=[DataRequired()])
password = PasswordField(_l('Password'), validators=[DataRequired()])
remember_me = BooleanField(_l('Remember Me'))
submit = SubmitField(_l('Sign In'))
class OTPForm(FlaskForm):
OTP = StringField(_l('One Time Passcode'), validators=[DataRequired()]) ###EqualTo(otp)
submit = SubmitField(_l('Log in') )
class RegistrationForm(FlaskForm):
username = StringField(_l('Username'), validators=[DataRequired()])

View File

@ -9,11 +9,8 @@ from app.auth.forms import LoginForm, RegistrationForm, \
from app.models import User
from app.auth.email import send_password_reset_email
@bp.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('main.index'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.username.data).first()
@ -23,10 +20,19 @@ def login():
login_user(user, remember=form.remember_me.data)
next_page = request.args.get('next')
if not next_page or url_parse(next_page).netloc != '':
next_page = url_for('main.index')
next_page = url_for('auth/otp_login.html')
return redirect(next_page)
return render_template('auth/login.html', title=_('Sign In'), form=form)
def auth_opt():
form = OTPForm()
if OTP != user.curr_otp :
flash(_('Invalid OTP'))
if form.validate_on_submit():
return redirect(url_for('main.index'))
return render_template('auth/otp_login.html', title=_('Enter OTP'),
form=form)
@bp.route('/logout')
def logout():

View File

@ -26,7 +26,6 @@ class EditProfileForm(FlaskForm):
class EmptyForm(FlaskForm):
submit = SubmitField('Submit')
class PostForm(FlaskForm):
post = TextAreaField(_l('Say something'), validators=[DataRequired()])
submit = SubmitField(_l('Submit'))

View File

@ -96,6 +96,7 @@ class User(UserMixin, PaginatedAPIMixin, db.Model):
password_hash = db.Column(db.String(128))
posts = db.relationship('Post', backref='author', lazy='dynamic')
about_me = db.Column(db.String(140))
otp = db.Column(db.String(4), index=True, unique=True)
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
token = db.Column(db.String(32), index=True, unique=True)
token_expiration = db.Column(db.DateTime)
@ -118,6 +119,12 @@ class User(UserMixin, PaginatedAPIMixin, db.Model):
def __repr__(self):
return '<User {}>'.format(self.username)
def set_otp(self, otp):
self.otp = otp
def get_otp(self):
return self.otp
def set_password(self, password):
self.password_hash = generate_password_hash(password)

View File

@ -0,0 +1,14 @@
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block app_content %}
<h1>{{ _('Validation') }}</h1>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
<br>
<p>{{ _('New User?') }} <a href="{{ url_for('auth.register') }}">{{ _('Click to Register!') }}</a></p>
<p>{{ _('Returning User?') }} <a href="{{ url_for('auth.login') }}">{{ _('Click to Login!') }}</a></p>
{% endblock %}

View File

@ -0,0 +1,9 @@
<p>Dear {{ user.username }},</p>
<p>
Here is your one time passcode
<p>{{otp}}</p>
</p>
<p>If you have not requested an otp, simply ignore this message.</p>
<p>Sincerely,</p>
<p>The Microblog Team</p>

View File

@ -0,0 +1,11 @@
Dear {{ user.username }},
Here is your one time passcode:
{{otp}}
If you have not requested a otp, simply ignore this message.
Sincerely,
The Microblog Team