Commit cd099eac authored by Stephane Sobucki's avatar Stephane Sobucki Committed by Vincent A

[carrefourbanque] Handle dsp2 login status code

On the website, after login if the user uses strong authentication a
pop-in will show up. This pop-in's content is generated on the fly using
js and depends on a 'popin_dsp2' parameter code.

We use this parameter code to raise exceptions with the appropriate
corresponding messages.
parent f4937c32
......@@ -27,7 +27,7 @@
from woob.browser import LoginBrowser, URL, need_login, StatesMixin
from woob.exceptions import (
BrowserIncorrectPassword, RecaptchaV2Question, BrowserUnavailable,
ActionNeeded, AuthMethodNotImplemented,
from import Account
from import basestring
......@@ -128,9 +128,7 @@ def do_login(self):
if not self.home.is_here():
raise AuthMethodNotImplemented("L'authentification forte Clé Secure n'est pas prise en charge.")
if self.login.is_here():
error =
# Sometimes some connections aren't able to login because of a
# maintenance randomly occuring.
......@@ -140,11 +138,45 @@ def do_login(self):
elif 'saisies ne correspondent pas à l\'identifiant' in error:
raise BrowserIncorrectPassword(error)
raise AssertionError('Unexpected error at login: "%s"' % error)
raise AssertionError('Unexpected error at login')
if self.login.is_here():
# Check if the website asks for strong authentication with OTP
dsp2_auth_code =
# The dsp2 authentication code gives informations on which strong authentication
# method is used by the user (Clé Secure: in-app validation or otp by SMS)
# on blocked access to the account and unavailability of the service.
if dsp2_auth_code:
if dsp2_auth_code == 'authent_cc':
raise ActionNeeded(
"Authentifiez-vous depuis l'appli Carrefour Banque avec Clé Secure."
elif dsp2_auth_code == 'enrolement_cc':
# On the website 'enrolement_cc' code corresponds to a pop-in in which the Clé Secure
# authentication method is advertised. The user is presented with instructions on how
# to install Clé Secure and he is also given the option to log in using otp by SMS.
# Unfortunately, every time the user logs in the pop-in will show up, so we have no way
# of knowing whether we need to perform otp by SMS or if it's just the advertisement for Clé Secure.
raise AuthMethodNotImplemented(
"L'authentification forte par SMS n'est pas prise en charge."
elif dsp2_auth_code == 'cle_secure_locked':
raise ActionNeeded(
"A la suite de 3 tentatives d'authentification erronées, votre Clé Secure a été bloquée."
+ ' Par mesure de sécurité, créez un nouveau code Clé Secure depuis votre appli Carrefour Banque.'
elif dsp2_auth_code == 'service_indisponible':
raise BrowserUnavailable(
'Le service est momentanément indisponible. Excusez-nous pour la gêne occasionnée.'
+ ' Veuillez ré-essayer ultérieurement.'
elif 'acces_bloque' in dsp2_auth_code:
raise ActionNeeded(
"L'accès à votre Espace Client a été bloqué pour des raisons de sécurité."
+ ' Pour le débloquer, contactez le service client de Carrefour Banque.'
raise AssertionError('Unhandled dsp2 authentication code at login %s' % dsp2_auth_code)
raise AssertionError('Unexpected error at login')
def get_account_list(self):
......@@ -28,7 +28,6 @@
from PIL import Image
from import json
from woob.browser.pages import HTMLPage, LoggedPage, pagination, JsonPage
from woob.browser.elements import ListElement, TableElement, ItemElement, method, DictElement
from woob.browser.filters.standard import (
......@@ -40,7 +39,6 @@
from woob.capabilities.wealth import Investment
from woob.capabilities.base import NotAvailable, empty
from import FrenchTransaction
from woob.exceptions import ActionNeeded
class CarrefourBanqueKeyboard(object):
......@@ -150,28 +148,15 @@ def enter_password(self, password):
def check_action_needed(self):
# The JavaScript variable 'tc_vars' is supposed to contain the 'user_login' value
# and 'user_login'='logged'. If there is no user_login and 'user_login'='unlogged',
# the customer has to validate an OTP by SMS.
raw_text = Regexp(
CleanText('//script[contains(text(), "tc_vars")]'),
r'var tc_vars = (\{[^]]+\})'
json_text = json.loads(raw_text)
if not json_text['user_ID'] and json_text['user_login'] == 'unlogged':
# The real message contains the user's phone number, so we send a generic message.
raise ActionNeeded(
"Veuillez vous connecter sur le site de Carrefour Banque pour "
+ "recevoir un code par SMS afin d'accéder à votre Espace Client."
raise AssertionError('Unhandled error: password submission failed and we are still on Login Page.')
def get_error_message(self):
return CleanText('//div[@class="messages error"]', default=None)(self.doc)
def has_2fa(self):
return bool(self.doc.xpath('//div[@id="region_content_dsp2"]'))
def get_dsp2_auth_code(self):
return Regexp(
CleanText('//script[contains(text(), "popin_dsp2")]', replace=[('-', '_')]),
class MaintenancePage(HTMLPage):
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment