Email confirmation system added

User confirmation based on email
This commit is contained in:
kelepirci 2016-07-31 14:22:14 +03:00
parent c8c5d627bd
commit 669eeaabad
8 changed files with 115 additions and 20 deletions

View File

@ -14,8 +14,8 @@ class Config:
class DevelopmentConfig(Config): class DevelopmentConfig(Config):
DEBUG = True DEBUG = True
MAIL_SERVER = 'smtp.googlemail.com' MAIL_SERVER = 'localhost'
MAIL_PORT = 587 MAIL_PORT = 25
MAIL_USE_TLS = True MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('MAIL_USERNAME') MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD') MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')

View File

@ -8,6 +8,19 @@ from ..models import User
from ..email import send_email from ..email import send_email
from .forms import LoginForm, RegistrationForm from .forms import LoginForm, RegistrationForm
@auth.before_app_request
def before_request():
if current_user.is_authenticated \
and not current_user.confirmed \
and request.endpoint[:5] != 'auth.' \
and request.endpoint != 'static':
return redirect(url_for('auth.unconfirmed'))
@auth.route('/unconfirmed')
def unconfirmed():
if current_user.is_anonymous or current_user.confirmed:
return redirect(url_for('main.index'))
return render_template('auth/unconfirmed.html')
@auth.route('/login', methods=['GET', 'POST']) @auth.route('/login', methods=['GET', 'POST'])
def login(): def login():
@ -26,7 +39,7 @@ def logout():
logout_user() logout_user()
flash('You have been logged out.') flash('You have been logged out.')
return redirect(url_for('main.index')) return redirect(url_for('main.index'))
@auth.route('/register', methods=['GET', 'POST']) @auth.route('/register', methods=['GET', 'POST'])
def register(): def register():
form = RegistrationForm() form = RegistrationForm()
@ -38,6 +51,32 @@ def register():
avatar="/static/img/user2-160x160.jpg", avatar="/static/img/user2-160x160.jpg",
created_at=datetime.datetime.now()) created_at=datetime.datetime.now())
db.session.add(user) db.session.add(user)
flash('You can now login.') db.session.commit()
return redirect(url_for('auth.login')) token = user.generate_confirmation_token()
return render_template('auth/register.html', form=form) send_email(user.email, 'Confirm Your Account',
'auth/email/confirm', user=user, token=token)
flash('A confirmation email has been sent to you by email.')
return redirect(url_for('main.index'))
return render_template('auth/register.html', form=form)
# user email confirmation function
@auth.route('/confirm/<token>')
@login_required
def confirm(token):
if current_user.confirmed:
return redirect(url_for('main.index'))
if current_user.confirm(token):
flash('You have confirmed your account. Thanks!')
else:
flash('The confirmation link is invalid or has expired.')
return redirect(url_for('main.index'))
# resend confirmation email
@auth.route('/confirm')
@login_required
def resend_confirmation():
token = current_user.generate_confirmation_token()
send_email(current_user.email, 'Confirm Your Account',
'auth/email/confirm', user=current_user, token=token)
flash('A new confirmation email has been sent to you by email.')
return redirect(url_for('main.index'))

View File

@ -4,17 +4,17 @@ from flask_mail import Message
from . import mail from . import mail
def send_async_email(app, msg): def send_async_email(dash, msg):
with app.app_context(): with dash.app_context():
mail.send(msg) mail.send(msg)
def send_email(to, subject, template, **kwargs): def send_email(to, subject, template, **kwargs):
app = current_app._get_current_object() dash = current_app._get_current_object()
msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject, msg = Message(dash.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to]) sender=dash.config['FLASKY_MAIL_SENDER'], recipients=[to])
msg.body = render_template(template + '.txt', **kwargs) msg.body = render_template(template + '.txt', **kwargs)
msg.html = render_template(template + '.html', **kwargs) msg.html = render_template(template + '.html', **kwargs)
thr = Thread(target=send_async_email, args=[app, msg]) thr = Thread(target=send_async_email, args=[dash, msg])
thr.start() thr.start()
return thr return thr

View File

@ -10,9 +10,6 @@ from flask_login import UserMixin
from . import db from . import db
from . import login_manager from . import login_manager
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class Role(db.Model): class Role(db.Model):
__tablename__ = 'roles' __tablename__ = 'roles'
@ -49,11 +46,12 @@ class User(UserMixin, db.Model):
def verify_password(self, password): def verify_password(self, password):
return check_password_hash(self.password_hash, password) return check_password_hash(self.password_hash, password)
# user email confirmation # generates confirmation token for user email confirmation
def generate_confirmation_token(self, expiration=3600): def generate_confirmation_token(self, expiration=3600):
s = Serializer(current_app.config['SECRET_KEY'], expiration) s = Serializer(current_app.config['SECRET_KEY'], expiration)
return s.dumps({'confirm': self.id}) return s.dumps({'confirm': self.id})
# confirms uer email by id
def confirm(self, token): def confirm(self, token):
s = Serializer(current_app.config['SECRET_KEY']) s = Serializer(current_app.config['SECRET_KEY'])
try: try:
@ -68,3 +66,7 @@ class User(UserMixin, db.Model):
def __repr__(self): def __repr__(self):
return '<User %r>' % self.username return '<User %r>' % self.username
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))

View File

@ -0,0 +1,8 @@
<p>Dear {{ user.username }},</p>
<p>Welcome to <b>- Stack</b>!</p>
<p>To confirm your account please <a href="{{ url_for('auth.confirm', token=token, _external=True) }}">click here</a>.</p>
<p>Alternatively, you can paste the following link in your browser's address bar:</p>
<p>{{ url_for('auth.confirm', token=token, _external=True) }}</p>
<p>Sincerely,</p>
<p>The - Stack Team</p>
<p><small>Note: replies to this email address are not monitored.</small></p>

View File

@ -0,0 +1,11 @@
Dear {{ user.username }},
Welcome to - Stack!
To confirm your account please click on the following link:
{{ url_for('auth.confirm', token=token, _external=True) }}
Sincerely,
The - Stack Team

View File

@ -27,21 +27,21 @@
<form action="{{ url_for('auth.register') }}" method="post" name="register"> <form action="{{ url_for('auth.register') }}" method="post" name="register">
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<input type="email" name="email" class="form-control" placeholder="Email"> <input type="email" name="email" class="form-control" placeholder="Email" value="{{ request.form['email'] }}">
<span class="glyphicon glyphicon-envelope form-control-feedback"></span> <span class="glyphicon glyphicon-envelope form-control-feedback"></span>
{% if form.email.errors %} {% if form.email.errors %}
<span class="text-red">{% for error in form.email.errors %} {{ error }} {% endfor %}</span> <span class="text-red">{% for error in form.email.errors %} {{ error }} {% endfor %}</span>
{% endif %} {% endif %}
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<input type="text" name="username" class="form-control" placeholder="User name"> <input type="text" name="username" class="form-control" placeholder="User name" value="{{ request.form['username'] }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span> <span class="glyphicon glyphicon-user form-control-feedback"></span>
{% if form.username.errors %} {% if form.username.errors %}
<span class="text-red">{% for error in form.username.errors %} {{ error }} {% endfor %}</span> <span class="text-red">{% for error in form.username.errors %} {{ error }} {% endfor %}</span>
{% endif %} {% endif %}
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">
<input type="text" name="full_name" class="form-control" placeholder="Full name"> <input type="text" name="full_name" class="form-control" placeholder="Full name" value="{{ request.form['full_name'] }}">
<span class="glyphicon glyphicon-user form-control-feedback"></span> <span class="glyphicon glyphicon-user form-control-feedback"></span>
</div> </div>
<div class="form-group has-feedback"> <div class="form-group has-feedback">

View File

@ -0,0 +1,35 @@
{% extends "adminlte/base_without_nav.html" %}
{% block title %}Confirm Account{% endblock %}
{% block description %}You have not confirmed your account!{% endblock %}
{% block bodytag %}login-page{% endblock %}
{% block body %}
<div class="login-box">
<div class="login-logo">
<strong>-</strong> stack
</div>
<div class="login-box-body">
{# Display errors (if there are any). #}
{% with messages = get_flashed_messages() %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
<h3>Hello {{ current_user.username }}</h3>
<h4>You have not confirmed your account yet!</h4>
<p>Before accessing this page, you need to confirm your account.
Please check your email inbox and click on confirmation link.</p>
<p>If you have not received confirmation email:</p>
<a href="{{ url_for('auth.resend_confirmation') }}">
<button type="button" class="btn btn-primary">Re-Send Confirmation</button>
</a>
</div>
</div>
{% endblock %}