Flash security login via username, not email
I wanted to have a field in the model User
through which the user registers as username
instead ofemail
I have defined: app.config['SECURITY_USER_IDENTITY_ATTRIBUTES'] = 'username'
But I still get:
user_datastore.add_role_to_user(name, 'mgmt')
File "/Users/boazin/sentinal/sentinel-cloud/.env/lib/python2.7/site-packages/flask_security/datastore.py", line 105, in add_role_to_user
user, role = self._prepare_role_modify_args(user, role)
File "/Users/boazin/sentinal/sentinel-cloud/.env/lib/python2.7/site-packages/flask_security/datastore.py", line 72, in _prepare_role_modify_args
user = self.find_user(email=user)
File "/Users/boazin/sentinal/sentinel-cloud/.env/lib/python2.7/site-packages/flask_security/datastore.py", line 203, in find_user
return self.user_model.query.filter_by(**kwargs).first()
File "/Users/boazin/sentinal/sentinel-cloud/.env/lib/python2.7/site-packages/sqlalchemy/orm/query.py", line 1333, in filter_by
for key, value in kwargs.items()]
File "/Users/boazin/sentinal/sentinel-cloud/.env/lib/python2.7/site-packages/sqlalchemy/orm/base.py", line 383, in _entity_descriptor
(description, key)
InvalidRequestError: Entity '<class 'flask_app.models.User'>' has no property 'email'
The email seems to be hardcoded in flash security ...
Can I change it?
edit : User model (as mentioned in comment):
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(255), unique=True, index=True)
password = db.Column(db.String(255))
token = db.Column(db.String(255), unique=True, index=True)
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='dynamic'))
source to share
From https://pythonhosted.org/Flask-Security/models.html
Fields are id, email, password, active
required. Add
email = db.Column(db.String(255), unique=True)
Just add your custom field username
.
source to share
In enter username instead of email (using Flask-Security 1.7.0 or newer) you can replace field email
with field username
in User
model
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(255), unique=True, index=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='dynamic'))
and update the config app
.
app.config['SECURITY_USER_IDENTITY_ATTRIBUTES'] = 'username'
Then, so that users can log in using their username instead of email, we will use the fact that the LoginForm validation method assumes that the user has an identity attribute on the form field email
.
from flask_security.forms import LoginForm
from wtforms import StringField
from wtforms.validators import InputRequired
class ExtendedLoginForm(LoginForm):
email = StringField('Username', [InputRequired()])
# Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore,
login_form=ExtendedLoginForm)
This way we can log in using the username without overwriting the validation method or login pattern. Of course, this is a hack, and a more correct approach would be to add a custom method validate
that validates the form field username
to the class ExtendedLoginForm
and update the login template accordingly.
However, the above approach makes it easy to log in with a username or email address . To do this, define a user model with username and email fields.
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
username = db.Column(db.String(255), unique=True, index=True)
password = db.Column(db.String(255))
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship('Role', secondary=roles_users,
backref=db.backref('users', lazy='dynamic'))
and update the config app
.
app.config['SECURITY_USER_IDENTITY_ATTRIBUTES'] = ('username','email')
Finally, create a custom login form.
from flask_security.forms import LoginForm
from wtforms import StringField
from wtforms.validators import InputRequired
class ExtendedLoginForm(LoginForm):
email = StringField('Username or Email Address', [InputRequired()])
# Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore,
login_form=ExtendedLoginForm)
Now, when logged in, Flask-Security will accept an email or username in the email form field.
source to share