views: keep a nonce during a forceAuthn request (#55953)

Nonce value and forceAuthn is linked to the request id which is randomly
generated by lasso and returned by IdPs as part of a SAML SSO.
This commit is contained in:
Benjamin Dauvergne 2021-08-03 16:38:02 +02:00
parent dbdd6fd70b
commit 2704f4feaa
2 changed files with 29 additions and 2 deletions

View File

@ -251,6 +251,11 @@ class LoginView(ProfileMixin, LogMixin, View):
if content is not None:
values.append(content)
attributes['issuer'] = login.remoteProviderId
in_response_to = login.response.inResponseTo
if in_response_to:
attributes['nonce'] = request.session.get('mellon-nonce-%s' % in_response_to)
attributes['force_authn'] = request.session.get('mellon-force-authn-%s' % in_response_to, False)
if login.nameIdentifier:
name_id = login.nameIdentifier
name_id_format = force_text(name_id.format or lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED)
@ -490,8 +495,12 @@ class LoginView(ProfileMixin, LogMixin, View):
policy = authn_request.nameIdPolicy
policy.allowCreate = utils.get_setting(idp, 'NAME_ID_POLICY_ALLOW_CREATE')
policy.format = utils.get_setting(idp, 'NAME_ID_POLICY_FORMAT')
force_authn = utils.get_setting(idp, 'FORCE_AUTHN')
force_authn = utils.get_setting(idp, 'FORCE_AUTHN') or 'force-authn' in request.GET
# link the nonce and forceAuthn state to the request-id
if 'nonce' in request.GET:
request.session['mellon-nonce-%s' % authn_request.id] = request.GET['nonce']
if force_authn:
request.session['mellon-force-authn-%s' % authn_request.id] = True
authn_request.forceAuthn = True
if request.GET.get('passive') == '1':
authn_request.isPassive = True

View File

@ -94,7 +94,7 @@ class MockIdp(object):
self.session_dump = None
def process_authn_request_redirect(self, url, auth_result=True, consent=True, msg=None):
login = lasso.Login(self.server)
login = self.login = lasso.Login(self.server)
if self.identity_dump:
login.setIdentityFromDump(self.identity_dump)
if self.session_dump:
@ -421,6 +421,8 @@ def test_sso(db, app, idp, caplog, sp_settings):
assert app.session['mellon_session']['first_name'] == ['<i>Fr\xe9d\xe9ric</i>']
assert app.session['mellon_session']['email'] == ['john.doe@gmail.com']
assert app.session['mellon_session']['wtf'] == []
assert not app.session['mellon_session']['force_authn']
assert not app.session['mellon_session']['nonce']
def test_sso_request_denied(db, app, idp, caplog, sp_settings):
@ -732,3 +734,19 @@ def test_debug_sso(db, app, idp, caplog, sp_settings, settings):
assert '<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"' in response_text
assert '<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"' in response_text
assert 'mellon: created new user _' in response_text
def test_nonce(db, app, idp, caplog, sp_settings):
response = app.get(reverse('mellon_login') + '?nonce=1234')
url, body, relay_state = idp.process_authn_request_redirect(response['Location'])
response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state})
assert app.session['mellon_session']['nonce'] == '1234'
def test_force_authn(db, app, idp, caplog, sp_settings):
response = app.get(reverse('mellon_login') + '?force-authn=1')
url, body, relay_state = idp.process_authn_request_redirect(response['Location'])
assert idp.login.request.forceAuthn
response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state})
assert app.session['mellon_session']['force_authn']