Skip to content
Snippets Groups Projects
Commit 790fe873 authored by Julian's avatar Julian
Browse files

added login_required to auth_webauthn_* and implemented pre-mfa-session timeout

parent f32460b6
No related branches found
No related tags found
No related merge requests found
...@@ -50,8 +50,16 @@ function begin_webauthn() { ...@@ -50,8 +50,16 @@ function begin_webauthn() {
fetch({{ url_for('mfa.auth_webauthn_begin')|tojson }}, { fetch({{ url_for('mfa.auth_webauthn_begin')|tojson }}, {
method: 'POST', method: 'POST',
}).then(function(response) { }).then(function(response) {
if(response.ok) return response.arrayBuffer(); if (response.ok) {
return response.arrayBuffer();
} else if (response.status == 403) {
window.location = {{ request.url|tojson }}; /* reload */
throw new Error('Session timed out');
} else if (response.status == 404) {
throw new Error('You have not registered any U2F/FIDO2 devices for your account'); throw new Error('You have not registered any U2F/FIDO2 devices for your account');
} else {
throw new Error('Server error');
}
}).then(CBOR.decode).then(function(options) { }).then(CBOR.decode).then(function(options) {
$('#webauthn-btn-text').text('Waiting for response from your device'); $('#webauthn-btn-text').text('Waiting for response from your device');
return navigator.credentials.get(options); return navigator.credentials.get(options);
...@@ -72,10 +80,13 @@ function begin_webauthn() { ...@@ -72,10 +80,13 @@ function begin_webauthn() {
$('#webauthn-spinner').addClass('d-none'); $('#webauthn-spinner').addClass('d-none');
$('#webauthn-btn-text').text('Success, redirecting'); $('#webauthn-btn-text').text('Success, redirecting');
window.location = {{ (ref or url_for('index'))|tojson }}; window.location = {{ (ref or url_for('index'))|tojson }};
} else if (response.status == 403) {
window.location = {{ request.url|tojson }}; /* reload */
throw new Error('Session timed out');
} else { } else {
throw new Error('Response from authenticator rejected'); throw new Error('Response from authenticator rejected');
} }
}, function(err) { }).catch(function(err) {
console.log(err); console.log(err);
/* various webauthn errors */ /* various webauthn errors */
if (err.name == 'NotAllowedError') if (err.name == 'NotAllowedError')
......
...@@ -5,7 +5,7 @@ from flask import Blueprint, render_template, session, request, redirect, url_fo ...@@ -5,7 +5,7 @@ from flask import Blueprint, render_template, session, request, redirect, url_fo
from uffd.database import db from uffd.database import db
from uffd.mfa.models import MFAMethod, TOTPMethod, WebauthnMethod, RecoveryCodeMethod from uffd.mfa.models import MFAMethod, TOTPMethod, WebauthnMethod, RecoveryCodeMethod
from uffd.session.views import get_current_user, login_required from uffd.session.views import get_current_user, login_required, pre_mfa_login_required
from uffd.ldap import uid_to_dn from uffd.ldap import uid_to_dn
from uffd.user.models import User from uffd.user.models import User
from uffd.csrf import csrf_protect from uffd.csrf import csrf_protect
...@@ -156,6 +156,7 @@ if WEBAUTHN_SUPPORTED: ...@@ -156,6 +156,7 @@ if WEBAUTHN_SUPPORTED:
return cbor.dumps({"status": "OK"}) return cbor.dumps({"status": "OK"})
@bp.route("/auth/webauthn/begin", methods=["POST"]) @bp.route("/auth/webauthn/begin", methods=["POST"])
@pre_mfa_login_required(no_redirect=True)
def auth_webauthn_begin(): def auth_webauthn_begin():
user = get_current_user() user = get_current_user()
server = get_webauthn_server() server = get_webauthn_server()
...@@ -168,6 +169,7 @@ if WEBAUTHN_SUPPORTED: ...@@ -168,6 +169,7 @@ if WEBAUTHN_SUPPORTED:
return cbor.dumps(auth_data) return cbor.dumps(auth_data)
@bp.route("/auth/webauthn/complete", methods=["POST"]) @bp.route("/auth/webauthn/complete", methods=["POST"])
@pre_mfa_login_required(no_redirect=True)
def auth_webauthn_complete(): def auth_webauthn_complete():
user = get_current_user() user = get_current_user()
server = get_webauthn_server() server = get_webauthn_server()
...@@ -204,7 +206,7 @@ def delete_webauthn(id): #pylint: disable=redefined-builtin ...@@ -204,7 +206,7 @@ def delete_webauthn(id): #pylint: disable=redefined-builtin
return redirect(url_for('mfa.setup')) return redirect(url_for('mfa.setup'))
@bp.route('/auth', methods=['GET']) @bp.route('/auth', methods=['GET'])
@login_required(skip_mfa=True) @pre_mfa_login_required()
def auth(): def auth():
user = get_current_user() user = get_current_user()
recovery_methods = RecoveryCodeMethod.query.filter_by(dn=user.dn).all() recovery_methods = RecoveryCodeMethod.query.filter_by(dn=user.dn).all()
...@@ -218,7 +220,7 @@ def auth(): ...@@ -218,7 +220,7 @@ def auth():
webauthn_methods=webauthn_methods, recovery_methods=recovery_methods) webauthn_methods=webauthn_methods, recovery_methods=recovery_methods)
@bp.route('/auth', methods=['POST']) @bp.route('/auth', methods=['POST'])
@login_required(skip_mfa=True) @pre_mfa_login_required()
def auth_finish(): def auth_finish():
user = get_current_user() user = get_current_user()
recovery_methods = RecoveryCodeMethod.query.filter_by(dn=user.dn).all() recovery_methods = RecoveryCodeMethod.query.filter_by(dn=user.dn).all()
......
...@@ -2,7 +2,7 @@ import datetime ...@@ -2,7 +2,7 @@ import datetime
import secrets import secrets
import functools import functools
from flask import Blueprint, render_template, request, url_for, redirect, flash, current_app, session from flask import Blueprint, render_template, request, url_for, redirect, flash, current_app, session, abort
from uffd.user.models import User from uffd.user.models import User
from uffd.ldap import user_conn, uid_to_dn from uffd.ldap import user_conn, uid_to_dn
...@@ -59,15 +59,28 @@ def is_valid_session(): ...@@ -59,15 +59,28 @@ def is_valid_session():
return True return True
bp.add_app_template_global(is_valid_session) bp.add_app_template_global(is_valid_session)
def login_required(group=None, skip_mfa=False): def pre_mfa_login_required(no_redirect=False):
def wrapper(func):
@functools.wraps(func)
def decorator(*args, **kwargs):
if not login_valid() or datetime.datetime.now().timestamp() > session['logintime'] + 10*60:
session.clear()
if no_redirect:
abort(403)
flash('You need to login first')
return redirect(url_for('session.login', ref=request.url))
return func(*args, **kwargs)
return decorator
return wrapper
def login_required(group=None):
def wrapper(func): def wrapper(func):
@functools.wraps(func) @functools.wraps(func)
def decorator(*args, **kwargs): def decorator(*args, **kwargs):
if not login_valid(): if not login_valid():
flash('You need to login first') flash('You need to login first')
return redirect(url_for('session.login', ref=request.url)) return redirect(url_for('session.login', ref=request.url))
if not skip_mfa and not session.get('user_mfa'): if not session.get('user_mfa'):
print('redirecting login_required', skip_mfa, session.get('user_mfa'))
return redirect(url_for('mfa.auth', ref=request.url)) return redirect(url_for('mfa.auth', ref=request.url))
if not get_current_user().is_in_group(group): if not get_current_user().is_in_group(group):
flash('Access denied') flash('Access denied')
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment