mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-07-28 19:59:18 -04:00
when a user enters their 2FA code, the API will store a random UUID
against them in the database - this code is then stored on the cookie
on the front end.
At the beginning of each authenticated request, we do the following
steps:
* Retrieve the user's cookie, and get the user_id from it
* Request that user's details from the database
* populate current_user with the DB model
* run the login_required decorator, which calls
current_user.is_authenticated
is_authenticated now also checks that the database model matches the
cookie for session_id. The potential states and meanings are as follows:
database | cookie | meaning
----------+--------+---------
None | None | New user, or system just been deployed.
| | Redirect to start page.
----------+--------+---------
'abc' | None | New browser (or cleared cookies). Redirect to
| | start page.
----------+--------+---------
None | 'abc' | Invalid state (cookie is set from user obj, so
| | would only happen if DB is cleared)
----------+--------+---------
'abc' | 'abc' | Same browser. Business as usual
----------+--------+---------
'abc' | 'def' | Different browser in cookie - db has been changed
| | since then. Redirect to start
65 lines
2.3 KiB
Python
65 lines
2.3 KiB
Python
from flask import (
|
|
render_template,
|
|
redirect,
|
|
session,
|
|
url_for,
|
|
request
|
|
)
|
|
|
|
from flask_login import login_user, current_user
|
|
from app.main import main
|
|
from app.main.forms import TwoFactorForm
|
|
from app import service_api_client, user_api_client
|
|
from app.utils import redirect_to_sign_in
|
|
|
|
|
|
@main.route('/two-factor', methods=['GET', 'POST'])
|
|
@redirect_to_sign_in
|
|
def two_factor():
|
|
user_id = session['user_details']['id']
|
|
|
|
def _check_code(code):
|
|
return user_api_client.check_verify_code(user_id, code, "sms")
|
|
|
|
form = TwoFactorForm(_check_code)
|
|
|
|
if form.validate_on_submit():
|
|
try:
|
|
user = user_api_client.get_user(user_id)
|
|
# the user will have a new current_session_id set by the API - store it in the cookie so we can match it in
|
|
# future requests
|
|
session['current_session_id'] = user.current_session_id
|
|
services = service_api_client.get_active_services({'user_id': str(user_id)}).get('data', [])
|
|
# Check if coming from new password page
|
|
if 'password' in session['user_details']:
|
|
user_api_client.update_password(user.id, password=session['user_details']['password'])
|
|
if user.is_locked():
|
|
form.sms_code.errors.append('Code not found')
|
|
return render_template('views/two-factor.html', form=form)
|
|
activated_user = user_api_client.activate_user(user)
|
|
login_user(activated_user, remember=True)
|
|
finally:
|
|
del session['user_details']
|
|
|
|
next_url = request.args.get('next')
|
|
if next_url and _is_safe_redirect_url(next_url):
|
|
return redirect(next_url)
|
|
|
|
if current_user.platform_admin:
|
|
return redirect(url_for('main.platform_admin'))
|
|
if len(services) == 1:
|
|
return redirect(url_for('main.service_dashboard', service_id=services[0]['id']))
|
|
else:
|
|
return redirect(url_for('main.choose_service'))
|
|
|
|
return render_template('views/two-factor.html', form=form)
|
|
|
|
|
|
# see http://flask.pocoo.org/snippets/62/
|
|
def _is_safe_redirect_url(target):
|
|
from urllib.parse import urlparse, urljoin
|
|
host_url = urlparse(request.host_url)
|
|
redirect_url = urlparse(urljoin(request.host_url, target))
|
|
return redirect_url.scheme in ('http', 'https') and \
|
|
host_url.netloc == redirect_url.netloc
|