Ejemplo n.º 1
0
class Authentication(object):
    def __init__(self, portal_cookie, token_name, workflow):
        self.token_name = token_name
        self.redis_base = REDISBase()
        self.redis_portal_session = REDISPortalSession(self.redis_base,
                                                       portal_cookie)
        self.workflow = workflow

        self.backend_id = self.authenticated_on_backend()
        self.redis_oauth2_session = REDISOauth2Session(
            self.redis_base,
            self.redis_portal_session.get_oauth2_token(self.backend_id))

        if not self.workflow.authentication:
            raise RedirectionNeededError(
                "Workflow '{}' does not need authentication".format(
                    self.workflow.name), self.get_redirect_url())
        self.credentials = ["", ""]

    def is_authenticated(self):
        if self.redis_portal_session.exists(
        ) and self.redis_portal_session.authenticated_app(self.workflow.id):
            # If user authenticated, retrieve its login
            self.credentials[0] = self.redis_portal_session.get_login(
                str(self.backend_id))
            return True
        return False

    def double_authentication_required(self):
        return self.workflow.authentication.otp_repository and \
            not self.redis_portal_session.is_double_authenticated(self.workflow.authentication.otp_repository.id)

    def authenticated_on_backend(self):
        backend_list = list(
            self.workflow.authentication.repositories_fallback.all())
        backend_list.append(self.workflow.authentication.repository)
        for backend in backend_list:
            if self.redis_portal_session.authenticated_backend(
                    backend.id) == '1':
                return str(backend.id)
        return ""

    def authenticate_sso_acls(self):
        backend_list = list(
            self.workflow.authentication.repositories_fallback.all())
        backend_list.append(self.workflow.authentication.repository)
        e, login = None, ""
        for backend in backend_list:
            if self.redis_portal_session.authenticated_backend(
                    backend.id) == '1':
                # The user is authenticated on backend, but he's not necessarily authorized on the app
                # The ACL is only supported by LDAP
                if backend.subtype == "LDAP":
                    # Retrieve needed infos in redis
                    login = self.redis_portal_session.keys['login_' +
                                                           str(backend.id)]
                    app_id = self.redis_portal_session.keys['app_id_' +
                                                            str(backend.id)]
                    password = self.redis_portal_session.getAutologonPassword(
                        app_id, str(backend.id), login)
                    # And try to re-authenticate user to verify credentials and ACLs
                    try:
                        backend.authenticate(
                            login,
                            password,  # acls=self.workflow.access_control_list,  # FIXME : Implement LDAP ACL in AccessControl model
                            logger=logger)
                        logger.info(
                            "User '{}' successfully re-authenticated on {} for SSO needs"
                            .format(login, backend.repo_name))
                    except Exception as e:
                        logger.error(
                            "Error while trying to re-authenticate user '{}' on '{}' for SSO needs : {}"
                            .format(login, backend.repo_name, e))
                        continue
                return str(backend.id)
        if login and e:
            raise e
        return ""

    def set_authentication_params(self, repo, authentication_results):
        if authentication_results:
            result = {}
            self.backend_id = str(repo.id)

            if isinstance(authentication_results, User):
                result = {
                    'data': {
                        'password_expired': False,
                        'account_locked':
                        (not authentication_results.is_active),
                        'user_email': authentication_results.email
                    },
                    'backend': repo
                }
                """ OAuth2 enabled in any case """
                result['data']['oauth2'] = {
                    'scope': '{}',
                    'token_return_type': 'both',
                    'token_ttl': self.workflow.authentication.auth_timeout
                }
            logger.debug(
                "AUTH::set_authentication_params: Authentication results : {}".
                format(authentication_results))
            return result
        else:
            raise AuthenticationError(
                "AUTH::set_authentication_params: Authentication results is empty : '{}' "
                "for username '{}'".format(authentication_results,
                                           self.credentials[0]))

    def authenticate_on_backend(self, backend):
        if backend.subtype == "internal":
            return self.set_authentication_params(
                backend,
                backend.authenticate(self.credentials[0], self.credentials[1]))
        else:
            return backend.authenticate(
                self.credentials[0],
                self.credentials[1],
                # FIXME : ACLs
                # acls=self.workflow.access_control_list,
                logger=logger)

    def authenticate(self, request):
        e = None
        backend = self.workflow.authentication.repository
        try:
            authentication_results = self.authenticate_on_backend(backend)
            logger.info(
                "AUTH::authenticate: User '{}' successfully authenticated on backend '{}'"
                .format(self.credentials[0], backend))
            self.backend_id = str(backend.id)
            return authentication_results

        except (AuthenticationError, ACLError, DBAPIError, PyMongoError,
                LDAPError) as e:
            logger.error(
                "AUTH::authenticate: Authentication failure for username '{}' on primary backend '{}' : '{}'"
                .format(self.credentials[0], str(backend), str(e)))
            logger.exception(e)
            for fallback_backend in self.workflow.authentication.repositories_fallback.all(
            ):
                try:
                    authentication_results = self.authenticate_on_backend(
                        backend)
                    self.backend_id = str(fallback_backend.id)
                    logger.info(
                        "AUTH::authenticate: User '{}' successfully authenticated on fallback backend '{}'"
                        .format(self.credentials[0], fallback_backend))
                    return authentication_results

                except (AuthenticationError, ACLError, DBAPIError,
                        PyMongoError, LDAPError) as e:
                    logger.error(
                        "AUTH::authenticate: Authentication failure for username '{}' on fallback backend '{}'"
                        " : '{}'".format(self.credentials[0],
                                         str(fallback_backend), str(e)))
                    logger.exception(e)
                    continue
            raise e or AuthenticationError

    def register_user(self, authentication_results):
        oauth2_token = None
        # No OAuth2 disabled
        # if self.application.enable_oauth2:
        timeout = self.workflow.authentication.auth_timeout
        oauth2_token = Uuid4().generate()
        self.redis_oauth2_session = REDISOauth2Session(
            self.redis_base, "oauth2_" + oauth2_token)
        if authentication_results['data'].get('oauth2', None):
            self.redis_oauth2_session.register_authentication(
                authentication_results['data']['oauth2'],
                authentication_results['data']['oauth2']['token_ttl'])
        else:
            self.redis_oauth2_session.register_authentication(
                {
                    'scope': '{}',
                    'token_return_type': 'both',
                    'token_ttl': timeout
                }, timeout)
        logger.debug(
            "AUTH::register_user: Redis oauth2 session successfully written in Redis"
        )

        portal_cookie = self.redis_portal_session.register_authentication(
            str(self.workflow.id), str(self.workflow.name),
            str(self.backend_id), self.workflow.get_redirect_uri(),
            self.workflow.authentication.otp_repository, self.credentials[0],
            self.credentials[1], oauth2_token, authentication_results['data'],
            timeout)
        logger.debug(
            "AUTH::register_user: Authentication results successfully written in Redis portal session"
        )

        return portal_cookie, oauth2_token

    def register_sso(self, backend_id):
        username = self.redis_portal_session.keys['login_' + backend_id]
        oauth2_token = self.redis_portal_session.keys['oauth2_' + backend_id]
        self.redis_oauth2_session = REDISOauth2Session(
            self.redis_portal_session.handler, "oauth2_" + oauth2_token)
        logger.debug(
            "AUTH::register_sso: Redis oauth2 session successfully retrieven")
        password = self.redis_portal_session.getAutologonPassword(
            self.workflow.id, backend_id, username)
        logger.debug(
            "AUTH::register_sso: Password successfully retrieven from Redis portal session"
        )
        portal_cookie = self.redis_portal_session.register_sso(
            self.workflow.authentication.auth_timeout, backend_id,
            str(self.workflow.id), str(self.workflow.get_redirect_uri()),
            username, oauth2_token)
        logger.debug(
            "AUTH::register_sso: SSO informations successfully written in Redis for user {}"
            .format(username))
        self.credentials = [username, password]
        if self.double_authentication_required():
            self.redis_portal_session.register_doubleauthentication(
                self.workflow.id, self.workflow.authentication.otp_repository)
            logger.debug(
                "AUTH::register_sso: DoubleAuthentication required : "
                "successfully written in Redis for user '{}'".format(username))
        return portal_cookie, oauth2_token

    def get_redirect_url(self):
        try:
            return self.workflow.get_redirect_uri() or \
                self.redis_portal_session.keys.get('url_{}'.format(self.workflow.id), None)
        except:
            return self.redis_portal_session.keys.get('url_{}'.format(self.workflow.id), None) or \
                self.workflow.get_redirect_uri()

    def get_url_portal(self):
        try:
            # FIXME : auth_portal attribute ?
            return self.workflow.auth_portal or self.workflow.get_redirect_uri(
            )
        except:
            return self.workflow.get_redirect_uri()

    def get_redirect_url_domain(self):
        return split_domain(self.get_redirect_url())

    def get_credentials(self, request):
        if not self.credentials[0]:
            try:
                self.retrieve_credentials(request)
            except:
                self.credentials[0] = self.redis_portal_session.get_login(
                    self.backend_id)
        logger.debug(
            "AUTH::get_credentials: User's login successfully retrieven from Redis session : '{}'"
            .format(self.credentials[0]))
        if not self.credentials[1]:
            try:
                self.retrieve_credentials(request)
            except:
                if not self.backend_id:
                    self.backend_id = self.authenticated_on_backend()
                self.credentials[
                    1] = self.redis_portal_session.getAutologonPassword(
                        str(self.workflow.id), self.backend_id,
                        self.credentials[0])
        logger.debug(
            "AUTH::get_credentials: User's password successfully retrieven/decrypted from Redis session"
        )

    def ask_learning_credentials(self, **kwargs):
        # FIXME : workflow.auth_portal ?
        response = learning_authentication_response(
            kwargs.get('request'),
            self.workflow.template.id,
            # FIXME : auth_portal ?
            # self.workflow.auth_portal or
            self.workflow.get_redirect_uri(),
            "None",
            kwargs.get('fields'),
            error=kwargs.get('error', None))

        portal_cookie_name = kwargs.get('portal_cookie_name', None)
        if portal_cookie_name:
            response.set_cookie(
                portal_cookie_name,
                self.redis_portal_session.key,
                domain=self.get_redirect_url_domain(),
                httponly=True,
                secure=self.get_redirect_url().startswith('https'))

        return response
Ejemplo n.º 2
0
class SELFServiceChange(SELFService):
    def __init__(self, app_id, token_name):
        super(SELFServiceChange, self).__init__(app_id, token_name)
        self.backend = None

    def authenticated_on_backend(self):
        backend_list = self.application.getAuthBackendFallback()
        backend_list.append(self.application.getAuthBackend())
        for backend in backend_list:
            if self.redis_portal_session.authenticated_backend(
                    backend.id) == '1':
                return str(backend.id)
        return ""

    def retrieve_credentials(self, request):
        """ We may have a password reset token in URI """
        rdm = request.GET.get("rdm", None) or request.POST.get('rdm', None)

        if not rdm:
            super(SELFServiceChange, self).retrieve_credentials(request)

        old_password = None  # None if rdm
        new_passwd = request.POST['password_1']
        new_passwd_cfrm = request.POST['password_2']
        if new_passwd != new_passwd_cfrm:
            raise PasswordMatchError("Password and confirmation mismatches")

        auth_backend = self.application.getAuthBackend()
        auth_backend_fallbacks = self.application.getAuthBackendFallback()

        if rdm:
            assert re_match("^[0-9a-f-]+$",
                            rdm), "PORTAL::self: Injection attempt on 'rdm'"

            email = self.redis_base.hget('password_reset_' + rdm, 'email')
            assert email, "SELF::Change: Invalid Random Key provided: '{}'".format(
                rdm)

            user_infos = self.get_username_by_email(auth_backend,
                                                    auth_backend_fallbacks,
                                                    email)
            self.username = user_infos['user']
            self.backend = user_infos['backend']

        else:
            # Get old_password
            old_password = request.POST[
                'password_old']  # If raise -> ask credentials

            # Get redis_portal_session
            portal_cookie = request.COOKIES.get(self.cluster.getPortalCookie())
            self.redis_portal_session = REDISPortalSession(
                self.redis_base, portal_cookie)
            # If not present -> 403
            assert self.redis_portal_session.exists(
            ), "PORTAL::self: portal session is not valid !"

            # And get username & backend from redis_portal_session
            user_infos = dict()
            auth_backend = self.application.getAuthBackend()
            if self.redis_portal_session.keys.get(
                    'login_' + str(self.application.getAuthBackend().id)):
                try:
                    user_infos = self.authenticate_on_backend(
                        auth_backend, self.username, old_password)
                    self.username = self.redis_portal_session.keys.get(
                        'login_' + str(auth_backend.id))
                    self.backend = auth_backend
                except Exception as e:
                    logger.error(
                        "Seems to have wrong old password on backend : '{}', exception details : "
                        + self.application.getAuthBackend().repo_name)
                    logger.exception(e)
            if not user_infos:
                for backend_fallback in self.application.getAuthBackendFallback(
                ):
                    if self.redis_portal_session.keys.get(
                            'login_' + str(backend_fallback.id)):
                        try:
                            user_infos = self.authenticate_on_backend(
                                backend_fallback, self.username, old_password)
                            self.username = self.redis_portal_session.keys.get(
                                'login_' + str(backend_fallback.id))
                            self.backend = backend_fallback
                            break
                        except:
                            logger.error(
                                "Seems to have wrong old password on backend : "
                                + backend_fallback.repo_name)

            if not self.backend:
                raise AuthenticationError("Wrong old password")
            logger.debug(
                "PORTAL::self: Found username from portal session: {}".format(
                    self.username))

        return old_password

    # Change password
    def perform_action(self, request, old_password):
        new_passwd = request.POST['password_1']
        new_passwd_cfrm = request.POST['password_2']

        rdm = (request.GET.get("rdm", None) or request.POST.get('rdm', None))

        # If not rdm : Verify password
        if not rdm:
            saved_app_id = self.redis_portal_session.keys['app_id_' +
                                                          str(self.backend.id)]
            saved_app = Application.objects(id=ObjectId(saved_app_id)).only(
                'id', 'name', 'pw_min_len', 'pw_min_upper', 'pw_min_lower',
                'pw_min_number', 'pw_min_symbol').first()
            if not self.redis_portal_session.getAutologonPassword(
                    str(saved_app.id), str(self.backend.id), self.username):
                raise AuthenticationError("Wrong old password")
        else:
            saved_app = self.application

        #Check if password meets required complexity
        upper_case = 0
        lower_case = 0
        number = 0
        symbol = 0

        min_len = int(saved_app.pw_min_len)
        min_upper = int(saved_app.pw_min_upper)
        min_lower = int(saved_app.pw_min_lower)
        min_number = int(saved_app.pw_min_number)
        min_symbol = int(saved_app.pw_min_symbol)

        for i in new_passwd:
            if i.isupper():
                upper_case += 1
            elif i.islower():
                lower_case += 1
            elif i.isdigit():
                number += 1
            else:
                symbol += 1

        if not (len(new_passwd) >= min_len and upper_case >= min_upper
                and lower_case >= min_lower and number >= min_number
                and symbol >= min_symbol):
            logger.info("SELF::change_password: Password is too weak")
            raise AuthenticationError(
                "Password do not meet complexity requirements")

        if issubclass(self.backend.__class__, Backend):
            self.backend.change_password(
                self.username,
                old_password,
                new_passwd,
                krb5_service=self.application.app_krb_service)
        elif isinstance(self.backend.get_backend(), MongoEngineBackend):
            user = User.objects.get(username=str(self.username))
            new_password_hash = make_password(new_passwd)
            user.password = new_password_hash
            user.save()
        else:
            self.backend.get_backend().change_password(
                self.username,
                old_password,
                new_passwd,
                krb5_service=self.application.app_krb_service)
        logger.info(
            "SELF::change_password: Password successfully changed in backend")

        # If not rdm : set new password in Redis portal session
        if not rdm:
            if self.redis_portal_session.setAutologonPassword(
                    str(saved_app.id), str(saved_app.name), str(
                        self.backend.id), self.username, old_password,
                    new_passwd) is None:
                # If setAutologonPasswd return None : the old_password was incorrect
                raise AuthenticationError("Wrong old password")
            logger.info(
                "SELF::change_password: Password successfully changed in Redis"
            )

        return "Password successfully changed"
Ejemplo n.º 3
0
class Authentication(object):
    def __init__(self, token_name, redis_token, app_cookie, portal_cookie):
        self.token_name = token_name
        self.anonymous_token = redis_token
        self.redis_base = REDISBase()
        self.redis_session = REDISAppSession(self.redis_base,
                                             token=redis_token,
                                             cookie=app_cookie)
        self.redis_portal_session = REDISPortalSession(self.redis_base,
                                                       portal_cookie)
        self.application = Application.objects.with_id(
            ObjectId(self.redis_session.keys['application_id']))
        self.backend_id = self.authenticated_on_backend()
        self.redis_oauth2_session = REDISOauth2Session(
            self.redis_base,
            self.redis_portal_session.get_oauth2_token(self.backend_id))

        if not self.application.need_auth:
            raise RedirectionNeededError(
                "Application '{}' does not need authentication".format(
                    self.application.name), self.get_redirect_url())
        self.credentials = ["", ""]

    def is_authenticated(self):
        if self.redis_session.is_authenticated(
        ) and self.redis_portal_session.exists():
            # If user authenticated, retrieve its login
            self.credentials[0] = self.redis_session.get_login()
            return True
        return False

    def double_authentication_required(self):
        return (self.application.otp_repository
                and not self.redis_session.is_double_authenticated())

    def authenticated_on_backend(self):
        backend_list = self.application.getAuthBackendFallback()
        backend_list.append(self.application.getAuthBackend())
        for backend in backend_list:
            if self.redis_portal_session.authenticated_backend(
                    backend.id) == '1':
                return str(backend.id)
        return ""

    def authenticate_on_backend(self):
        backend_list = self.application.getAuthBackendFallback()
        backend_list.append(self.application.getAuthBackend())
        e, login = None, ""
        for backend in backend_list:
            if self.redis_portal_session.authenticated_backend(
                    backend.id) == '1':
                # The user is authenticated on backend, but he's not necessarily authorized on the app
                # The ACL is only supported by LDAP
                if isinstance(backend, LDAPRepository):
                    # Retrieve needed infos in redis
                    login = self.redis_portal_session.keys['login_' +
                                                           str(backend.id)]
                    app_id = self.redis_portal_session.keys['app_id_' +
                                                            str(backend.id)]
                    password = self.redis_portal_session.getAutologonPassword(
                        app_id, str(backend.id), login)
                    # And try to re-authenticate user to verify credentials and ACLs
                    try:
                        backend.get_backend().authenticate(
                            login,
                            password,
                            acls=self.application.access_mode,
                            logger=logger)
                        logger.info(
                            "User '{}' successfully re-authenticated on {} for SSO needs"
                            .format(login, backend.repo_name))
                    except Exception as e:
                        logger.error(
                            "Error while trying to re-authenticate user '{}' on '{}' for SSO needs : {}"
                            .format(login, backend.repo_name, e))
                        continue
                return str(backend.id)
        if login and e:
            raise e
        return ""

    def set_authentication_params(self, repo, authentication_results):
        if authentication_results:
            self.backend_id = str(repo.id)

            if isinstance(authentication_results, User):
                result = {
                    'data': {
                        'password_expired': False,
                        'account_locked':
                        (not authentication_results.is_active),
                        'user_email': authentication_results.email
                    },
                    'backend': repo
                }
                if self.application.enable_oauth2:
                    result['data']['oauth2'] = {
                        'scope': '{}',
                        'token_return_type': 'both',
                        'token_ttl': self.application.auth_timeout
                    }
            logger.debug(
                "AUTH::set_authentication_params: Authentication results : {}".
                format(authentication_results))
            return result
        else:
            raise AuthenticationError(
                "AUTH::set_authentication_params: Authentication results is empty : '{}' for username '{}'"
                .format(authentication_results, self.credentials[0]))

    def authenticate(self, request):
        error = None

        login_user = self.credentials[0]
        login_password = self.credentials[1]

        login_pattern = '^[A-Za-z0-9@\.\-_!\?\$]+$'
        if not re.match(login_pattern, login_user):
            logger.info(
                "AUTH::authenticate: USERNAME INJECTION : '{}' !!!".format(
                    login_user))
            login_user = ""

        try:
            backend = self.application.getAuthBackend()
            if isinstance(backend.get_backend(), MongoEngineBackend):
                authentication_results = self.set_authentication_params(
                    backend,
                    backend.get_backend().authenticate(login_user,
                                                       login_password))
            else:
                authentication_results = backend.get_backend().authenticate(
                    login_user,
                    login_password,
                    acls=self.application.access_mode,
                    logger=logger)
            logger.info(
                "AUTH::authenticate: User '{}' successfully authenticated on backend '{}'"
                .format(login_user, backend))
            self.backend_id = str(backend.id)
            return authentication_results

        except (AuthenticationError, ACLError, DBAPIError, PyMongoError,
                LDAPError) as e:
            error = e
            logger.error(
                "AUTH::authenticate: Authentication failure for username '{}' on primary backend '{}' : '{}'"
                .format(login_user, str(backend), str(e)))
            for fallback_backend in self.application.getAuthBackendFallback():
                try:
                    if isinstance(fallback_backend.get_backend(),
                                  MongoEngineBackend):
                        authentication_results = self.set_authentication_params(
                            fallback_backend,
                            fallback_backend.get_backend().authenticate(
                                login_user, login_password))
                    else:
                        authentication_results = fallback_backend.get_backend(
                        ).authenticate(login_user,
                                       login_password,
                                       acls=self.application.access_mode,
                                       logger=logger)
                    self.backend_id = str(fallback_backend.id)
                    logger.info(
                        "AUTH::authenticate: User '{}' successfully authenticated on fallback backend '{}'"
                        .format(login_user, fallback_backend))
                    return authentication_results

                except (AuthenticationError, ACLError, DBAPIError,
                        PyMongoError, LDAPError) as e:
                    error = e
                    logger.error(
                        "AUTH::authenticate: Authentication failure for username '{}' on fallback backend '{}' : '{}'"
                        .format(login_user, str(fallback_backend), str(e)))
                    continue

        raise error or AuthenticationError

    def register_user(self, authentication_results):
        app_cookie = self.redis_session.register_authentication(
            str(self.application.id), self.credentials[0],
            self.application.auth_timeout)
        logger.debug(
            "AUTH::register_user: Authentication results successfully written in Redis session"
        )
        oauth2_token = None
        if self.application.enable_oauth2:
            oauth2_token = Uuid4().generate()
            self.redis_oauth2_session = REDISOauth2Session(
                self.redis_base, "oauth2_" + oauth2_token)
            if authentication_results['data'].get('oauth2', None):
                self.redis_oauth2_session.register_authentication(
                    authentication_results['data']['oauth2'],
                    authentication_results['data']['oauth2']['token_ttl'])
            else:
                self.redis_oauth2_session.register_authentication(
                    {
                        'scope': '{}',
                        'token_return_type': 'both',
                        'token_ttl': self.application.auth_timeout
                    }, self.application.auth_timeout)
            logger.debug(
                "AUTH::register_user: Redis oauth2 session successfully written in Redis"
            )
        portal_cookie = self.redis_portal_session.register_authentication(
            str(self.application.id), str(self.application.name),
            str(self.backend_id), self.application.get_redirect_uri(),
            self.application.otp_repository, self.credentials[0],
            self.credentials[1], oauth2_token, app_cookie,
            authentication_results['data'], self.application.auth_timeout)
        logger.debug(
            "AUTH::register_user: Authentication results successfully written in Redis portal session"
        )
        return app_cookie, portal_cookie, oauth2_token

    def register_sso(self, backend_id):
        username = self.redis_portal_session.keys['login_' + backend_id]
        saved_app_id = self.redis_portal_session.keys['app_id_' + backend_id]
        oauth2_token = None
        try:
            # TODO: Create Oauth2 if does not exists and oauth2 enabled
            oauth2_token = self.redis_portal_session.keys['oauth2_' +
                                                          backend_id]
            self.redis_oauth2_session = REDISOauth2Session(
                self.redis_session.handler, "oauth2_" + oauth2_token)
            logger.debug(
                "AUTH::register_sso: Redis oauth2 session successfully retrieven"
            )
        except Exception as e:
            logger.error(
                "Unable to retrieve Oauth2 token for user '{}', it may be possible if Oauth2 is not activated on repository"
                .format(username))
        password = self.redis_portal_session.getAutologonPassword(
            saved_app_id, backend_id, username)
        logger.debug(
            "AUTH::register_sso: Password successfully retrieven from Redis portal session"
        )
        app_cookie = self.redis_session.register_authentication(
            str(self.application.id), username, self.application.auth_timeout)
        portal_cookie = self.redis_portal_session.register_sso(
            self.redis_session.key, self.application.auth_timeout, backend_id,
            str(self.application.id), str(self.application.get_redirect_uri()),
            username, oauth2_token)
        logger.debug(
            "AUTH::register_sso: SSO informations successfully written in Redis for user '{}'"
            .format(username))
        self.credentials = [username, password]
        if self.double_authentication_required():
            self.redis_session.register_doubleauthentication()
            logger.debug(
                "AUTH::register_sso: DoubleAuthentication required : successfully written in Redis for user '{}'"
                .format(username))
        return app_cookie, portal_cookie, oauth2_token

    def get_redirect_url(self):
        try:
            return self.application.redirect_uri or self.redis_session.keys.get(
                'url', None)
        except:
            return self.redis_session.keys.get(
                'url', None) or self.application.get_redirect_uri()

    def get_url_portal(self):
        try:
            return self.application.auth_portal or self.application.get_redirect_uri(
            )
        except:
            return self.application.get_redirect_uri()

    def get_redirect_url_domain(self):
        return split_domain(self.get_redirect_url())

    def get_credentials(self, request):
        if not self.credentials[0]:
            try:
                self.retrieve_credentials(request)
            except:
                self.credentials[0] = self.redis_session.get_login()
        logger.debug(
            "AUTH::get_credentials: User's login successfully retrieven from Redis session : '{}'"
            .format(self.credentials[0]))
        if not self.credentials[1]:
            try:
                self.retrieve_credentials(request)
            except:
                if not self.backend_id:
                    self.backend_id = self.authenticated_on_backend()
                self.credentials[
                    1] = self.redis_portal_session.getAutologonPassword(
                        str(self.application.id), self.backend_id,
                        self.credentials[0])
        logger.debug(
            "AUTH::get_credentials: User's password successfully retrieven/decrypted from Redis session"
        )

    def ask_learning_credentials(self, **kwargs):
        response = learning_authentication_response(
            kwargs.get('request'),
            self.application.template,
            self.application.auth_portal
            or self.application.get_redirect_uri(),
            self.token_name,
            "None",
            kwargs.get('fields'),
            error=kwargs.get('error', None))

        portal_cookie_name = kwargs.get('portal_cookie_name', None)
        if portal_cookie_name:
            response.set_cookie(
                portal_cookie_name,
                self.redis_portal_session.key,
                domain=self.get_redirect_url_domain(),
                httponly=True,
                secure=self.get_redirect_url().startswith('https'))

        return response