Example #1
0
    def createuser():
        """Create an user"""
        username = click.prompt("Username", type=str)
        email = click.prompt("Email", type=str)
        password = click.prompt("Password",
                                type=str,
                                hide_input=True,
                                confirmation_prompt=True)
        while True:
            role = click.prompt("Role [admin/user]", type=str)
            if role == "admin" or role == "user":
                break

        if click.confirm("Do you want to continue ?"):
            role = Role.query.filter(Role.name == role).first()
            if not role:
                raise click.UsageError("Roles not present in database")
            u = user_datastore.create_user(name=username,
                                           email=email,
                                           password=encrypt_password(password),
                                           roles=[role])

            db.session.commit()

            if FSConfirmable.requires_confirmation(u):
                FSConfirmable.send_confirmation_instructions(u)
                print("Look at your emails for validation instructions.")
Example #2
0
    def validate_on_submit(self):
        if not CustomForm.validate_on_submit(self):
            return self.validation_error()

        self.user = g.datastore.find_user(username=self.username.data)

        if self.user is None:
            self.user = g.datastore.find_user(email=self.username.data)

        if self.user is None:
            self.username.errors.append(
                current_app.config['SECURITY_MSG_USER_DOES_NOT_EXIST'][0])
            return self.validation_error()
        if not self.user.password:
            self.password.errors.append(
                current_app.config['SECURITY_MSG_PASSWORD_NOT_SET'][0])
            return self.validation_error()
        if not verify_and_update_password(self.password.data, self.user):
            self.password.errors.append(
                current_app.config['SECURITY_MSG_INVALID_PASSWORD'][0])
            return self.validation_error()
        if requires_confirmation(self.user):
            do_flash(*get_message('CONFIRMATION_REQUIRED'))
            return self.validation_error()
        if not self.user.is_active:
            do_flash(*current_app.config['SECURITY_MSG_DISABLED_ACCOUNT'])
            return self.validation_error()
        return self.validation_success()
Example #3
0
    def validate(self):
        if not super().validate():
            return False

        self.user = find_user(self.username.data)

        if self.user is None:
            self.username.errors.append(get_message("USER_DOES_NOT_EXIST")[0])
            # Reduce timing variation between existing and non-existing users
            hash_password(self.password.data)
            return False
        if not self.user.password:
            self.password.errors.append(get_message("PASSWORD_NOT_SET")[0])
            # Reduce timing variation between existing and non-existing users
            hash_password(self.password.data)
            return False
        self.password.data = _security._password_util.normalize(
            self.password.data)
        if not self.user.verify_and_update_password(self.password.data):
            self.password.errors.append(get_message("INVALID_PASSWORD")[0])
            return False
        self.requires_confirmation = requires_confirmation(self.user)
        if self.requires_confirmation:
            self.username.errors.append(
                get_message("CONFIRMATION_REQUIRED")[0])
            return False
        if not self.user.is_active:
            self.username.errors.append(get_message("DISABLED_ACCOUNT")[0])
            return False
        return True
Example #4
0
    def validate(self):
        """
        Copy-pasted from flask_security and use the altered version of
        `verify_and_update_password`.
        """
        if not super(LoginForm, self).validate():
            return False

        self.user = _datastore.get_user(self.email.data)

        if self.user is None:
            self.email.errors.append(get_message('USER_DOES_NOT_EXIST')[0])
            return False
        if not self.user.password:
            self.password.errors.append(get_message('PASSWORD_NOT_SET')[0])
            return False
        if not verify_and_update_password(self.password.data, self.user):
            self.password.errors.append(get_message('INVALID_PASSWORD')[0])
            return False
        if requires_confirmation(self.user):
            self.email.errors.append(get_message('CONFIRMATION_REQUIRED')[0])
            return False
        if not self.user.is_active:
            self.email.errors.append(get_message('DISABLED_ACCOUNT')[0])
            return False
        return True
Example #5
0
def account_authenticate(user):
    """Authenticate an ACS callback.

    :param user: A user instance.
    :returns: ``True`` if the user is successfully authenticated.
    """
    if not requires_confirmation(user):
        after_this_request(_commit)
        return login_user(user, remember=False)
    return False
Example #6
0
 def verify_login(self, user, password=None, **kwargs):
     """Verify the login via password."""
     if not user.password:
         _abort(get_message("PASSWORD_NOT_SET")[0], "password")
     if not verify_and_update_password(password, user):
         _abort(get_message("INVALID_PASSWORD")[0], "password")
     if requires_confirmation(user):
         _abort(get_message("CONFIRMATION_REQUIRED")[0])
     if not user.is_active:
         _abort(get_message("DISABLED_ACCOUNT")[0])
Example #7
0
 def validate(self):
     if not super(SignedMessageLoginForm, self).validate():
         return False
     self.user = _user.find_beet_account_name(self.message.signed_by_name)
     if self.user and requires_confirmation(self.user):
         self.message.errors.append(get_message("CONFIRMATION_REQUIRED")[0])
         return False
     if self.user and not self.user.is_active:
         self.message.errors.append(get_message("DISABLED_ACCOUNT")[0])
         return False
     return True
Example #8
0
    def validate(self):
        """Validate LDAP logins against AD."""
        if not super(LDAPLoginForm, self).validate():
            return False

        if self.email.data.strip() == '':
            self.email.errors.append(get_message('USERID_NOT_PROVIDED')[0])
            return False

        if self.password.data.strip() == '':
            self.password.errors.append(
                get_message('PASSWORD_NOT_PROVIDED')[0])
            return False

        try:
            user_dn, ldap_data = _datastore.query_ldap_user(self.email.data)

            if user_dn is None:
                self.email.errors.append(get_message('USER_DOES_NOT_EXIST')[0])
                return False

            if not _datastore.verify_password(user_dn, self.password.data):
                self.password.errors.append(get_message('INVALID_PASSWORD')[0])
                return False

            ldap_email = ldap_data[config_value('LDAP_EMAIL_FIELDNAME')].value
            password = encrypt_password(self.password.data)

            if _datastore.find_user(email=ldap_email):
                self.user = _datastore.get_user(ldap_email)
                # note that this is being stored per user login
                self.user.password = password
            else:
                self.user = _datastore.create_user(email=ldap_email,
                                                   password=password)
                _datastore.commit()
        except ldap3.LDAPExceptionError:
            self.password.errors.append(get_message('LDAP_SERVER_DOWN')[0])
            self.user = _datastore.get_user(self.email.data)
            if not self.user.password:
                self.password.errors.append(get_message('PASSWORD_NOT_SET')[0])
                return False
            if not verify_and_update_password(self.password.data, self.user):
                self.password.errors.append(get_message('INVALID_PASSWORD')[0])
                return False
            if requires_confirmation(self.user):
                self.email.errors.append(
                    get_message('CONFIRMATION_REQUIRED')[0])
                return False
            if not self.user.is_active:
                self.email.errors.append(get_message('DISABLED_ACCOUNT')[0])
                return False

        return True
Example #9
0
    def validate_on_submit(self):
        if not CustomForm.validate_on_submit(self):
            return self.validation_error()

        self.user = g.datastore.find_user(email=self.email.data)
        if not self.user:
            self.email.errors.append('Email Not Found')
            return self.validation_error()
        if requires_confirmation(self.user):
            do_flash(*get_message('CONFIRMATION_REQUIRED'))
            return self.validation_error()
        return self.update_success()
Example #10
0
def oauth_authenticate(client_id, user, require_existing_link=False,
                       remember=False):
    """Authenticate an oauth authorized callback."""
    # Authenticate via the access token (access token used to get user_id)
    if not requires_confirmation(user):
        after_this_request(_commit)
        if login_user(user):
            if require_existing_link:
                account = RemoteAccount.get(user.id, client_id)
                if account is None:
                    logout_user()
                    return False
            return True
    return False
Example #11
0
    def test_web_login(self):
        user_details = self.create_user("*****@*****.**", "password")
        self.assertEquals(user_details[0], '*****@*****.**')
        user_obj = self.app.datastore.get_user('*****@*****.**')
        self.assertNotEquals(user_obj, None)
        self.assertEquals('*****@*****.**', user_obj.email)

        self.assertNotEquals(None, user_obj.password)
        self.assertTrue(user_obj.verify_and_update_password("password"))
        self.assertFalse(requires_confirmation(user_obj))
        self.assertTrue(user_obj.is_active)

        login_response = self.login(*user_details)
        #self.assertTrue(current_user.is_authenticated)
        self.assertNotIn(b'USER = { }', login_response.data)
Example #12
0
def oauth_authenticate(client_id,
                       user,
                       require_existing_link=False,
                       remember=False):
    """Authenticate an oauth authorized callback."""
    # Authenticate via the access token (access token used to get user_id)
    if not requires_confirmation(user):
        after_this_request(_commit)
        if login_user(user):
            if require_existing_link:
                account = RemoteAccount.get(user.id, client_id)
                if account is None:
                    logout_user()
                    return False
            return True
    return False
Example #13
0
    def validate(self):
        if not super(LoginForm, self).validate():
            return False

        # Verify username field is not blank. We don't concern ourselves with email
        # because we don't use that to validate
        if self.username.data.strip() == '':
            self.username.errors.append('USERNAME NOT PROVIDED')
            return False

        # If the password field is left blank, fail.
        if self.password.data.strip() == '':
            self.password.errors.append('PASSWORD NOT PROVIDED')
            return False

        # set the user to be the user name in the field and look it up
        # in the database
        self.user = security.datastore.get_user(self.username.data)

        # Ensure the user exists in the database
        if self.user is None:
            self.username.errors.append('INCORRECT USERNAME/PASSWORD')
            return False

        # Ensure the password was set
        if not self.user.password:
            self.password.errors.append('PASSWORD WAS NOT SET')
            return False

        # Verify the password provided matches what is in the database for that user
        if not verify_and_update_password(self.password.data, self.user):
            self.password.errors.append('INCORRECT USERNAME/PASSWORD')
            return False

        # If user confirmation is enabled and the user has not confirmed, deny access
        if requires_confirmation(self.user):
            self.user.errors.append('CONFIRMATION REQUIRED')
            return False

        # Make sure that the user account is active and not disabled
        if not self.user.is_active:
            self.username.errors.append('DISABLED ACCOUNT')
            return False

        # If all other checks are passed, the user is valid
        return True
Example #14
0
    def validate(self):
        r = super(AdsClassicFallBackLoginForm, self).validate()
        if r is True:
            return r

        cu = None
        try:
            cu = ClassicUserInfo(self.email.data, self.password.data)
        except HTTPError:
            return False  # if we can't contact ADS Classic, make it non-fatal

        if cu.is_authenticated():  # Classic did let them in....

            if not hasattr(
                    self,
                    'user') or self.user is None:  # User does not exist yet
                user_manipulator.create(email=self.email.data,
                                        password=self.password.data,
                                        name=cu.get_name(),
                                        active=True)
            else:
                if not self.user.password:  # password not set
                    return False
                if not self.user.validate_password(
                        self.password.data):  # Invalid passwd
                    self.user.password = self.password.data
                    user_manipulator.save(self.user)
                if requires_confirmation(self.user):
                    return False
                if not self.user.is_active() and cu.is_real_user(
                ):  # Disabled account
                    self.user.active = True
                    user_manipulator.save(self.user)

            # revalidate
            return super(AdsClassicFallBackLoginForm, self).validate()

        elif cu.is_real_user(
        ):  # they didn't get it, but the account at least exists...
            if self.user is None:
                user_manipulator.create(email=self.email.data,
                                        password=gen_salt(12),
                                        name=cu.get_name(),
                                        active=False)
        return False
Example #15
0
def oauth_authenticate(client_id, user, require_existing_link=False):
    """Authenticate an oauth authorized callback.

    :param client_id: The client id.
    :param user: A user instance.
    :param require_existing_link: If ``True``, check if remote account exists.
        (Default: ``False``)
    :returns: ``True`` if the user is successfully authenticated.
    """
    # Authenticate via the access token (access token used to get user_id)
    if not requires_confirmation(user):
        after_this_request(_commit)
        if login_user(user, remember=False):
            if require_existing_link:
                account = RemoteAccount.get(user.id, client_id)
                if account is None:
                    logout_user()
                    return False
            return True
    return False
Example #16
0
    def _try_local_auth(self):
        self.user = _datastore.find_user(username=self.email.data)

        if not self.user:
            self.email.errors.append(get_message("USER_DOES_NOT_EXIST")[0])
            return False
        if not self.user.password:
            self.password.errors.append(get_message("PASSWORD_NOT_SET")[0])
            return False
        if not verify_and_update_password(self.password.data, self.user):
            self.password.errors.append(get_message("INVALID_PASSWORD")[0])
            return False
        if requires_confirmation(self.user):
            self.email.errors.append(get_message("CONFIRMATION_REQUIRED")[0])
            return False
        if not self.user.is_active:
            self.email.errors.append(get_message("DISABLED_ACCOUNT")[0])
            return False

        return True
Example #17
0
def oauth_authenticate(client_id, user, require_existing_link=False,
                       remember=False):
    """Authenticate an oauth authorized callback.

    :param client_id: The client id.
    :param user: A user instance.
    :param require_existing_link: If ``True``, check if remote account exists.
        (Default: ``False``)
    :returns: ``True`` if the user is successfully authenticated.
    """
    # Authenticate via the access token (access token used to get user_id)
    if not requires_confirmation(user):
        after_this_request(_commit)
        if login_user(user, remember=remember):
            if require_existing_link:
                account = RemoteAccount.get(user.id, client_id)
                if account is None:
                    logout_user()
                    return False
            return True
    return False
Example #18
0
    def validate(self):

        self.user = _datastore.get_user(self.email.data)

        if self.user is None:
            self.email.validate(self)
            self.email.errors.append("User or password invalid.")
            return False
        if not verify_and_update_password(self.password.data, self.user):
            self.password.errors.append(get_message('INVALID_PASSWORD')[0])
            return False
        if not self.user.password:
            self.password.errors.append(get_message('PASSWORD_NOT_SET')[0])
            return False
        if requires_confirmation(self.user):
            self.email.errors.append(get_message('CONFIRMATION_REQUIRED')[0])
            return False
        if not self.user.is_active:
            self.email.errors.append(get_message('DISABLED_ACCOUNT')[0])
            return False
        return True
Example #19
0
    def validate(self):
        r = super(AdsClassicFallBackLoginForm, self).validate()
        if r is True:
            return r
        
        cu = None
        try:
            cu = ClassicUserInfo(self.email.data, self.password.data)
        except HTTPError:
            return False  # if we can't contact ADS Classic, make it non-fatal

        if cu.is_authenticated(): # Classic did let them in....
            
            if not hasattr(self, 'user') or self.user is None:  # User does not exist yet
                user_manipulator.create(email=self.email.data, 
                                     password=self.password.data,
                                     name=cu.get_name(),
                                     active=True)
            else:
                if not self.user.password: # password not set
                    return False
                if not self.user.validate_password(self.password.data): # Invalid passwd 
                    self.user.password = self.password.data
                    user_manipulator.save(self.user)
                if requires_confirmation(self.user):
                    return False
                if not self.user.is_active() and cu.is_real_user(): # Disabled account
                    self.user.active = True
                    user_manipulator.save(self.user)
                
            # revalidate
            return super(AdsClassicFallBackLoginForm, self).validate()
        
        elif cu.is_real_user(): # they didn't get it, but the account at least exists...
            if self.user is None:
                user_manipulator.create(email=self.email.data, 
                                     password=gen_salt(12),
                                     name=cu.get_name(),
                                     active=False)
        return False
Example #20
0
    def validate(self):
        from flask_security.utils import (
            _datastore,
            get_message,
            hash_password,
        )
        from flask_security.confirmable import requires_confirmation

        if not super(LoginForm, self).validate():
            return False

        # try login using email
        self.user = _datastore.get_user(self.email.data)

        # if that didn't work try log in using the username
        if self.user is None:
            self.user = _datastore.get_user(self.username.data)

        if self.user is None:
            self.email.errors.append(get_message("USER_DOES_NOT_EXIST")[0])
            # Reduce timing variation between existing and non-existing users
            hash_password(self.password.data)
            return False
        if not self.user.password:
            self.password.errors.append(get_message("PASSWORD_NOT_SET")[0])
            # Reduce timing variation between existing and non-existing users
            hash_password(self.password.data)
            return False
        if not self.user.verify_and_update_password(self.password.data):
            self.password.errors.append(get_message("INVALID_PASSWORD")[0])
            return False
        if requires_confirmation(self.user):
            self.email.errors.append(get_message("CONFIRMATION_REQUIRED")[0])
            return False
        if not self.user.is_active:
            self.email.errors.append(get_message("DISABLED_ACCOUNT")[0])
            return False
        return True
Example #21
0
    def validate(self):
        if not super(LoginForm, self).validate():
            return False

        if not self.username.data.strip():
            self.username.errors.append("Username not provided")
            return False

        if not self.password.data.strip():
            self.password.errors.append(
                get_message("PASSWORD_NOT_PROVIDED")[0])
            return False

        username = self.username.data
        self.user = _security.datastore.find_user(username=username)
        if not self.user:
            logger.warning("not found {} using username field, "
                           "now using fallback with email".format(username))
            self.user = _security.datastore.find_user(email=username)

        if self.user is None:
            self.username.errors.append(get_message("USER_DOES_NOT_EXIST")[0])
            return False
        if not self.user.password:
            self.password.errors.append(get_message("PASSWORD_NOT_SET")[0])
            return False
        if not verify_and_update_password(self.password.data, self.user):
            self.password.errors.append(get_message("INVALID_PASSWORD")[0])
            return False
        if requires_confirmation(self.user):
            self.username.errors.append(
                get_message("CONFIRMATION_REQUIRED")[0])
            return False
        if not self.user.is_active:
            self.username.errors.append(get_message("DISABLED_ACCOUNT")[0])
            return False
        return True
Example #22
0
def create():
    """
    Create a user.
    """
    current_app.config["SERVER_NAME"] = current_app.config[
        "REEL2BITS_HOSTNAME"]
    username = click.prompt("Username", type=str)
    email = click.prompt("Email", type=str)
    password = click.prompt("Password",
                            type=str,
                            hide_input=True,
                            confirmation_prompt=True)
    while True:
        role = click.prompt("Role [admin/user]", type=str)
        if role == "admin" or role == "user":
            break

    if click.confirm("Do you want to continue ?"):
        role = Role.query.filter(Role.name == role).first()
        if not role:
            raise click.UsageError("Roles not present in database")
        u = user_datastore.create_user(name=username,
                                       email=email,
                                       password=hash_password(password),
                                       roles=[role])

        actor = create_actor(u)
        actor.user = u
        actor.user_id = u.id
        db.session.add(actor)

        db.session.commit()

        if FSConfirmable.requires_confirmation(u):
            with current_app.app_context():
                FSConfirmable.send_confirmation_instructions(u)
                print("Look at your emails for validation instructions.")
Example #23
0
    def validate(self):
        if not super(DeploymentLoginForm, self).validate():
            participant = models.Participant.query.filter(
                models.Participant.participant_id == self.email.data.strip(),
                models.Participant.password == self.password.data.strip(),
                models.Participant.participant_set ==
                g.event.participant_set).join(models.ParticipantRole).filter(
                    models.ParticipantRole.name == '$FC').first()
            if participant:
                # get the field co-ordinator user and create it if necessary
                _user = _datastore.find_user(email=current_app.config.get(
                    'APOLLO_FIELD_COORDINATOR_EMAIL'))
                if not _user:
                    _role = _datastore.find_or_create_role(
                        'field-coordinator', deployment=g.deployment)
                    _user = _datastore.create_user(
                        email=current_app.config.get(
                            'APOLLO_FIELD_COORDINATOR_EMAIL'),
                        username='',
                        password='',
                        deployment=g.deployment,
                        roles=[_role])
                self.user = _datastore.get_user(
                    current_app.config.get('APOLLO_FIELD_COORDINATOR_EMAIL'))
                session['participant'] = participant.id
                return True
            else:
                return False
        else:
            if self.email.data.strip() == '':
                self.email.errors.append(get_message('EMAIL_NOT_PROVIDED')[0])
                return False

            if self.password.data.strip() == '':
                self.password.errors.append(
                    get_message('PASSWORD_NOT_PROVIDED')[0])
                return False

            # check by the user identity attributes defined in
            # the settings
            for identity_attribute in current_app.config.get(
                    'SECURITY_USER_IDENTITY_ATTRIBUTES'):
                kwargs = {
                    'deployment': g.deployment,
                    identity_attribute: self.email.data
                }
                self.user = _datastore.find_user(**kwargs)

                if self.user:
                    break

            if self.user is None:
                self.email.errors.append(get_message('USER_DOES_NOT_EXIST')[0])
                return False
            if not self.user.password:
                self.password.errors.append(get_message('PASSWORD_NOT_SET')[0])
                return False
            if not verify_and_update_password(self.password.data, self.user):
                self.password.errors.append(get_message('INVALID_PASSWORD')[0])
                return False
            if requires_confirmation(self.user):
                self.email.errors.append(
                    get_message('CONFIRMATION_REQUIRED')[0])
                return False
            if not self.user.is_active:
                self.email.errors.append(get_message('DISABLED_ACCOUNT')[0])
                return False
            return True
Example #24
0
def idp_initiated():

    user_email = None
    user_nic = None
    first_name = None
    last_name = None

    auth_servers = current_app.config.get('SECURITY_SAML_IDP_METADATA').split(
        ',')

    for server in auth_servers:
        saml_client = saml_client_for(server)
        try:
            authn_response = saml_client.parse_authn_request_response(
                request.form['SAMLResponse'], entity.BINDING_HTTP_POST)
        except sigver.MissingKey:
            continue
        else:
            break

    root = ET.fromstring(str(authn_response))
    ns = {
        'assertion': 'urn:oasis:names:tc:SAML:2.0:assertion',
        'atributos': 'http://autenticacao.cartaodecidadao.pt/atributos'
    }

    for child in root.find('assertion:Assertion',
                           ns).find('assertion:AttributeStatement', ns):
        try:
            if child.attrib[
                    'Name'] == 'http://interop.gov.pt/MDC/Cidadao/CorreioElectronico':
                user_email = child.find('assertion:AttributeValue', ns).text
            elif child.attrib[
                    'Name'] == 'http://interop.gov.pt/MDC/Cidadao/NICCifrado':
                user_nic = child.find('assertion:AttributeValue', ns).text
            elif child.attrib[
                    'Name'] == 'http://interop.gov.pt/MDC/Cidadao/NomeProprio':
                first_name = child.find('assertion:AttributeValue', ns).text
            elif child.attrib[
                    'Name'] == 'http://interop.gov.pt/MDC/Cidadao/NomeApelido':
                last_name = child.find('assertion:AttributeValue', ns).text
            else:
                pass
        except AttributeError:
            pass

    data = {'email': user_email}
    extras = {'extras': {'auth_nic': user_nic}}
    userUdata = datastore.find_user(**extras) or datastore.find_user(**data)

    if not userUdata:
        # Redirects to new custom registration form
        session['user_email'] = user_email
        session['user_nic'] = user_nic
        session['first_name'] = first_name
        session['last_name'] = last_name
        return redirect(url_for('saml.register'))

    elif requires_confirmation(userUdata):
        do_flash(*get_message('CONFIRMATION_REQUIRED'))
        return redirect(url_for('security.login'))

    elif userUdata.deleted:
        do_flash(*get_message('DISABLED_ACCOUNT'))
        return redirect(url_for('site.home'))

    else:
        login_user(userUdata)
        session['saml_login'] = True
        # do_flash(*get_message('PASSWORDLESS_LOGIN_SUCCESSFUL'))
        return redirect(url_for('site.home'))
Example #25
0
def accounts():
    """
    Register an account
    The method is available to apps with a token obtained via the client credentials grant.
    It creates a user and account records, as well as an access token for the app that initiated the request.
    The method returns the access token, which the app should save for later.
    ---
    tags:
        - Accounts
    definitions:
      Token:
        type: object
        properties:
            access_token:
                type: string
            token_type:
                type: string
            scope:
                type: string
            created_at:
                type: integer
    responses:
      200:
        description: Returns Token
        schema:
            $ref: '#/definitions/Token'
    """

    if not current_app.config["REGISTRATION_ENABLED"]:
        abort(403)

    errors = {}

    # Get the bearer token
    bearer = None
    if "Authorization" in request.headers:
        b = request.headers.get("Authorization")
        b = b.strip().split(" ")
        if len(b) == 2:
            bearer = b[1]
        else:
            errors["bearer"] = ["API Bearer Authorization format issue"]
    else:
        current_app.logger.info(
            "/api/v1/accounts: no Authorization bearer given")

    if not request.json:
        abort(400)

    if "nickname" not in request.json:
        errors["nickname"] = ["nickname is missing"]
    if "email" not in request.json:
        errors["email"] = ["email is missing"]
    if "fullname" not in request.json:
        errors["fullname"] = ["fullname is missing"]
    if "password" not in request.json:
        errors["password"] = ["password is missing"]
    if "confirm" not in request.json:
        errors["confirm"] = ["password confirm is missing"]
    if "agreement" not in request.json:
        errors["agreement"] = ["agreement is missing"]

    if len(errors) > 0:
        return jsonify({"error": str(errors)}), 400

    if forbidden_username(request.json["nickname"]):
        return jsonify(
            {"error": str({"nickname":
                           ["this username cannot be used"]})}), 400

    if request.json["password"] != request.json["confirm"]:
        return jsonify(
            {"error": str({"confirm": ["passwords doesn't match"]})}), 400

    if "agreement" not in request.json:
        return jsonify({
            "error":
            str({"agreement": ["you need to accept the terms and conditions"]})
        }), 400

    # Check if user already exists by local user username
    user = User.query.filter(User.name == request.json["username"]).first()
    if user:
        return jsonify({"error": str({"ap_id":
                                      ["has already been taken"]})}), 400

    # Check if user already exists by old local user (Actors)
    user = Actor.query.filter(
        Actor.preferred_username == request.json["username"]).first()
    if user:
        return jsonify({"error": str({"ap_id":
                                      ["has already been taken"]})}), 400

    # Check if user already exists by email
    user = User.query.filter(User.email == request.json["email"]).first()
    if user:
        return jsonify({"error": str({"email":
                                      ["has already been taken"]})}), 400

    # Check username is valid
    # /^[a-zA-Z\d]+$/
    if not username_is_legal.match(request.json["username"]):
        return jsonify({
            "error":
            str({"ap_id": ["should contains only letters and numbers"]})
        }), 400

    # Proceed to register the user
    role = Role.query.filter(Role.name == "user").first()
    if not role:
        return jsonify({"error": "server error"}), 500

    u = user_datastore.create_user(
        name=request.json["username"],
        email=request.json["email"],
        display_name=request.json["fullname"],
        password=hash_password(request.json["password"]),
        roles=[role],
    )

    actor = create_actor(u)
    actor.user = u
    actor.user_id = u.id
    if "bio" in request.json:
        actor.summary = request.json["bio"]

    db.session.add(actor)
    db.session.commit()

    if FSConfirmable.requires_confirmation(u):
        FSConfirmable.send_confirmation_instructions(u)

    # get the matching item from the given bearer
    bearer_item = OAuth2Token.query.filter(
        OAuth2Token.access_token == bearer).first()
    if not bearer_item:
        abort(400)
    client_item = OAuth2Client.query.filter(
        OAuth2Client.client_id == bearer_item.client_id).first()
    if not client_item:
        abort(400)

    # https://github.com/lepture/authlib/blob/master/authlib/oauth2/rfc6749/grants/base.py#L51
    token = authorization.generate_token(client_item.client_id,
                                         "client_credentials",
                                         user=u,
                                         scope=client_item.scope,
                                         expires_in=None)

    tok = OAuth2Token()
    tok.user_id = u.id
    tok.client_id = client_item.client_id
    # the frontend should request an app every time it doesn't have one in local storage
    # and this app should allow delivering a somewhat non usuable Token
    # token which gets sent to this endpoint and gets used to get back the right client_id
    # to associate in the database...
    tok.token_type = token["token_type"]
    tok.access_token = token["access_token"]
    tok.refresh_token = None
    tok.scope = token["scope"]
    tok.revoked = False
    tok.expires_in = token["expires_in"]
    db.session.add(tok)
    db.session.commit()

    return jsonify({**token, "created_at": tok.issued_at}), 200