Can we use JWT with sqlalchemy in flask api as below? - python

Following are the code files with relevant code snippets:
init.py:
app = Flask(__name__)
cors = CORS(app, resources={r"/api/*": {"origins": "*"}})
app.config['SECRET_KEY'] = 'super-secret'
In auth.py:
def authenticate_user(login, password):
'''
Return dict or None after checking against db for valid user
'''
s = select([users]).where(users.c.email==login)
result_set = conn.execute(s)
if result_set.rowcount == 1:
for r in result_set:
print r[users.c.password], 'result_set[users.c.password]'
if pwd_context.verify(password, r[users.c.password]):
# There is only one unique email/password pair
print 'matched'
return dict(r)
else:
return None
return
How to get the access_token value for the user on login? I have installed Flassk-JWT in the virtualenv and followed this doc: https://pythonhosted.org/Flask-JWT/ But please note I am not using OOPs ie. User class etc. I am using sqlalchemy core with Flask and python. To further use this token, I need to call it as a decorator for the API call is what I understand as:
#app.route('/rt/api/v1.0/list', methods=['GET'])
#jwt_required()
In views.py:
from myapp.auth import authenticate_user
#app.route('/auth', methods=['POST','GET'])
def login():
email = request.form["email"]
password = request.form["password"]
if request.method == 'POST':
result_set = authenticate_user(email, password)
if result_set:
session['email'] = result_set['email']
user_dict = result_set
if user_dict:
session['email'] = user_dict['email']
jwt = JWT(app, user_dict['email'], user_dict["id"])
How to exactly connect the various code files to get the access token value is what I am stuck up with.Please guide. Also Wish to exclude the login API request from the before_request callback(). All other APIs can have the before and after_request callbacks() executed.

Finally found a way better implementation with the basic usage on readthedocs

Related

RuntimeError: working outside of request context Flask Session

I am getting RuntimeError: working outside of request context error while running a test in Flask. I've tried multiple suggestions from other threads, but none has worked for me.
Part of my views.py:
#user_app.route('/login', methods =('GET', 'POST'))
def login():
form = LoginForm()
error = None
if form.validate_on_submit():
user = User.objects.filter(username=form.username.data).first()
if user:
if bc.hashpw(form.password.data, user.password) == user.password:
session['username']=form.username.data
return 'User Logged In'
else:
user = None
if not user:
error = 'Incorrect credentials'
return render_template('user/login.html',form=form,error=error)
Relevant part of my tests.py:
from application import create_app as create_app_base
def create_app(self):
self.db_name = 'flaskbook_test'
return create_app_base(
MONGODB_SETTINGS={'DB':self.db_name},
TESTING=True,
WTF_CSRF_ENABLED=False,
SECRET_KEY='SecretKey'
)
def setUp(self):
self.app_factory = self.create_app()
self.app = self.app_factory.test_client()
#self.app.application.app_context().push() <-- this did not help
def tearDown(self):
db = _get_db()
db.client.drop_database(db)
def test_login_user(self):
#create user
self.app.post('/register', data=self.user_dict())
#login user
rv = self.app.post('/login',data=dict(
username='username',
password='password'
))
#check session is set
with self.app as c:
rv = c.get('/')
assert session.get('username') == self.user_dict()['username']
I have already tried adding app_context and self.app.application.app_context().push() as mentioned above:
with self.app.application.app_context():
assert session.get('username') == self.user_dict()['username']
But it didn't work. Whenever I call session['username'] I get RuntimeError: working outside of request context.
My requirements.txt: Flask0.10.1 Flask-Script 2.0.5 flask-mongoengine 0.7.4
Please help.
What you want is the request context, not the app context.
Flask includes some handy functions to push a request context for you - check out the Flask testing docs and you'll see a lot of relevant info, including the test_request_context method on the app object.
Combine that with app.test_client to push a request context and then simulate client behaviour such as POSTing to your endpoint. Try this:
with self.app.test_request_context('/'), self.app.test_client() as c:
rv = c.post('/')
assert session.get('username') == self.user_dict()['username']

How to merge Flask login with a Dash application?

I have to design a web-app that provides Flask services and Dash services. For example I would like to create a login in Flask, combined with a Dash application. The problem is that I can't bind the flask login with dash. I would need a method like '#require_login' that filters access to even Dash services.
The code is as follows:
app_flask = Flask(__name__)
app_flask.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////login.db'
app_flask.config['SECRET_KEY'] = 'thisissecret'
db = SQLAlchemy(app_flask)
login_manager = LoginManager()
login_manager.init_app(app_flask)
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(30), unique=True)
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
#app_flask.route('/')
def index():
user = User.query.filter_by(username='admin').first()
login_user(user)
return 'You are now logged in!'
#app_flask.route('/logout')
#login_required
def logout():
logout_user()
return 'You are now logged out!'
#app_flask.route('/home')
#login_required
def home():
return 'The current FLASK user is ' + current_user.username
# TODO how to add login_required for dash?
app_dash = Dash(server=app_flask, url_base_pathname='/dash/')
app_dash.layout = html.H1('MY DASH APP')
if __name__ == '__main__':
app_dash.run_server(debug=True)
This line, app_dash = Dash(server=app_flask, url_base_pathname='/dash/'), creates new view_functions in app_flask identified by its url_base_pathname.
You can debug and inspect the value of app_flask.view_functions before and after the creation of app_dash.
Now that we know which view_functions are created by app_dash, we can apply login_required to them manually.
for view_func in app_flask.view_functions:
if view_func.startswith(app_dash.url_base_pathname):
app_flask.view_functions[view_func] = login_required(app_flask.view_functions[view_func])
The `app_dash` endpoints will now be protected.
It would be better if you block all requests by using #app.before_request, and only allow the request if logged in or if the endpoint is marked as public.
def check_route_access():
if request.endpoint is None:
return redirect("/login")
func = app.view_functions[request.endpoint]
if (getattr(func, "is_public", False)):
return # Access granted
# check if user is logged in (using session variable)
user = session.get("user", None)
if not user:
redirect("/login")
else:
return # Access granted```
Now all endpoints will be checked, even dash app endpoints.
Add this decorator named public_route:
def public_route(function):
function.is_public = True
return function
And add the decorator to the public methods, like login, error pages etc.
#public_route
#app.route("/login")
def login():
# show/handle login page
# call did_login(username) when somebody successfully logged in
def did_login(username):
session["user"] = username
This way you never need the #login_required anymore because all endpoints require login unless stated otherwise by #public_route.
A solution : session from flask (work with cookie)
from flask import session
it's an exemple :
#login_manager.user_loader
def load_user(user_id):
# I think here it's good
session["uid"] = user_id
return User.query.get(int(user_id))
# TODO how to add login_required for dash?
if "uid" in session :
app_dash = Dash(server=app_flask, url_base_pathname='/dash/')
app_dash.layout = html.H1('MY DASH APP')

Is it more secure to use itsdangerous with Flask-Login without the remember me option? Why or why not?

I am learning how to create a Flask app using a MongoDB database by putting together a user authentication system. When I went through the code with my superior at work, he said using werkzeug.security for hashing passwords may not be a good idea and asked me to make use of itsdangerous library in my code (below).
What has been driving me insane is I couldn't find anywhere online why using werkzeug.security on its own is a bad idea. As far as I can gather itsdangerous package lets you use a login serialiser to encrypt and decrypt the cookie token when you use the remember me option in Flask-Login (I am not using this option). I found this article explaining Flask-Login tokens.
Is it still important to use itsdangerous even if I don't use the remember me option?
from flask import Flask, url_for, redirect, render_template, request
from flask_mongoengine import MongoEngine
from wtforms import form, fields, validators
from werkzeug.security import generate_password_hash, check_password_hash
import flask_login
# Create application
app = Flask(__name__)
# Create a secret key so we can use sessions
app.config['SECRET_KEY'] = 'super-secret'
# MongoDB settings
app.config['MONGODB_SETTINGS'] = {'DB': 'mymongodb'}
db = MongoEngine()
db.init_app(app)
# Create user document (MongoDB documents are like rows in a relational table).
class User(db.Document):
login = db.StringField(max_length=80, unique=True)
password = db.StringField(max_length=80)
email = db.StringField(max_length=80)
name = db.StringField(max_length=80)
# Flask-Login integration
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return str(self.id)
# Define login and registration forms (for flask-login)
class LoginForm(form.Form):
login = fields.StringField(validators=[validators.required()])
password = fields.PasswordField(validators=[validators.required()])
def validate_login(self, field):
user = self.get_user()
if user is None:
raise validators.ValidationError('Invalid username.')
if not check_password_hash(user.password, self.password.data):
raise validators.ValidationError('Invalid password.')
def get_user(self):
return User.objects(login=self.login.data).first()
class RegistrationForm(form.Form):
login = fields.StringField(validators=[validators.required(),validators.length(min=3, max=80)])
password = fields.PasswordField(validators=[validators.required(), validators.length(min=6, max=80)])
email = fields.StringField(validators=[validators.required(),validators.email(), validators.length(min=6, max=80)])
name = fields.StringField(validators=[validators.required(), validators.length(min=3, max=80)])
def validate_login(self, field):
if User.objects(login=self.login.data):
raise validators.ValidationError('Duplicate username.')
# Initialise flask-login
def init_login():
login_manager = flask_login.LoginManager()
login_manager.init_app(app)
# Create user loader function
#login_manager.user_loader
def load_user(user_id):
return User.objects(id=user_id).first()
# Flask views
#app.route('/', methods=('GET', 'POST'))
#app.route('/login/', methods=('GET', 'POST'))
def login_view():
form = LoginForm(request.form)
if request.method == 'POST' and form.validate():
user = form.get_user()
flask_login.login_user(user)
return render_template('main.html', user=flask_login.current_user)
return render_template('form.html', form=form)
#app.route('/register/', methods=('GET', 'POST'))
def register_view():
form = RegistrationForm(request.form)
if request.method == 'POST' and form.validate():
hashpass = generate_password_hash(form.password.data, method='sha256', salt_length=8)
user = User(form.login.data, hashpass, form.email.data, form.name.data)
user.save()
flask_login.login_user(user)
return render_template('main.html', user=flask_login.current_user)
return render_template('form.html', form=form)
#app.route('/logout/')
def logout_view():
flask_login.logout_user()
return redirect(url_for('login_view'))
#app.route('/main/')
def main_view():
if flask_login.current_user.is_authenticated:
return render_template('main.html', user=flask_login.current_user)
return redirect(url_for('login_view'))
#app.route('/map/')
def map_view():
if flask_login.current_user.is_authenticated:
return render_template('map.html', user=flask_login.current_user)
return redirect(url_for('login_view'))
if __name__ == '__main__':
# Initialise flask-login
init_login()
# Start the Flask app
app.run(debug=True)
Thanks,
Aina.
generate_password_hash is a function for securely hashing passwords. ItsDangerous is a library for securely signing (but not hashing or encrypting) arbitrary data. ItsDangerous would not be appropriate for hashing passwords.
Since you're using Flask-Login, it will handle cookies for you. You would not use ItsDangerous directly in that case. Flask uses ItsDangerous behind the scenes to sign the session cookie.

redis database for flask application

I am trying to modify an existing flask application to use a redis database instead of tokens. I searched for an example and through redis documentation but could not find anything. Here is what I have:
from flask import Flask, Response
from flask.ext.login import LoginManager, UserMixin, login_required
from redis import Redis
redis = Redis()
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
class User(UserMixin):
# proxy for a database of users
user_database = {"JohnDoe": ("JohnDoe", "John"),
"JaneDoe": ("JaneDoe", "Jane")}
def __init__(self, username, password):
self.id = username
self.password = password
#classmethod
def get(cls,id):
return cls.user_database.get(id)
#login_manager.request_loader
def load_user(request):
token = request.headers.get('Authorization')
if token is None:
token = request.args.get('token')
if token is not None:
username,password = token.split(":") # naive token
user_entry = User.get(username)
if (user_entry is not None):
user = User(user_entry[0],user_entry[1])
if (user.password == password):
return user
return None
#app.route("/",methods=["GET"])
def index():
return Response(response="Hello World!",status=200)
#app.route("/protected/",methods=["GET"])
#login_required
def protected():
return Response(response="Hello Protected World!", status=200)
if __name__ == '__main__':
app.config["SECRET_KEY"] = "ITSASECRET"
app.run(port=5000,debug=True)
Can anyone point me in the right direction in terms of using redis as a simple authorization database?
This isn't much of a specific suggestion, but there is pretty good documentation for Flask-Cache. It provides numerous ways to integrate Redis (or another cache) into a flask application. I am not sure if this will help you with your purpose, but in any case, it will eliminate some boiler plate.

flask-login user is set to anonymous after login

im new to flask and flask-login and ive been struggling with this for days.
Im trying to log a user in like this:
from creds import auth_username, auth_password, pgsql_dbuser, pgsql_dbpassword, pgsql_db1name
from flask import Flask, render_template, request, Response, redirect, url_for
from flask.ext.bcrypt import Bcrypt
from flask.ext.login import LoginManager, login_required, login_user, current_user, logout_user
import logging
import psycopg2
import uuid
import datetime
app = Flask(__name__)
app.secret_key = str(uuid.uuid4()) # <- required by login_manager.init_app(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'index'
#app.route('/', methods=['GET','POST'])
def index():
page_name = '/'
if request.method == 'POST':
email = request.form['email']
candidate_password = request.form['password']
user = finduserindbbyemail(email)
if user != None:
password_hash = checkuserpasswordindb(email)
if bcrypt.check_password_hash(password_hash, candidate_password):
user_object = User(user)
result = login_user(user_object) # <- here for successful login
return redirect(url_for('loggedin', user_object=type(user_object), user=user, result=result, current_user=current_user))
else:
user_object = User(user)
error_message = "The password you entered is incorrect"
return render_template('index.html', error_message=error_message)
else:
error_message = "The email address you entered does not match any we have in our records"
return render_template('index.html', error_message=error_message)
if request.method == 'GET':
return render_template('index.html')
I have a User class and a user callback:
class User():
def __init__(self, user):
self.user = user
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return unicode(self.user)
#login_manager.user_loader
def load_user(user):
con = psycopg2.connect(database=pgsql_db1name, user=pgsql_dbuser, password=pgsql_dbpassword, host='localhost')
uuid = "'"+user+"'"
cur = con.cursor()
cur.execute("SELECT uuid FROM users WHERE uuid = "+ uuid)
uuid = cur.fetchone()
con.close()
if uuid != None:
user = unicode(uuid[0])
return User.get_id(user)
else:
return None
After authentication is successful (apparently?), the user is redirected to a loggedin page which has a #login_required decorator. But instead of loading the loggedin page, the app redirects the user to the login page, telling me the user isnt being logged in?
If try to send values to the page and i remove the #login_required decorator so i can see the page, this is what i see in the browser after 'logging in':
current_user.is_authenticated() = False
current_user.is_active() = False
current_user.is_anonymous() = True
current_user.get_id() = None
user_object = <type 'instance'>
user = 2ca1296c-374d-43b4-bb7b-94b8c8fe7e44
login_user = True
current_user = <flask_login.AnonymousUserMixin object at 0x7f2aec80f190> Logout
It looks like my user hasn't been logged and is being treated as anonymous?
Can anyone see what I've done wrong? I'm having a lot of trouble understanding how this is supposed to work.
Another reason you might not be able to log a user in or current_user is Anonymous after going through your login form: The active=false flag is set on the user in the db. This behavior is confirmed in the docs:
flask_login.login_user(user, remember=False, duration=None, force=False, fresh=True)[source]
Logs a user in. You should pass the actual user object to this. If the user’s is_active property is False, they will not be logged in unless force is True.
This will return True if the log in attempt succeeds, and False if it fails (i.e. because the user is inactive).
So, when you call login_user, you can do this:
login_user(user, remember=form.remember_me.data, force=True), if you want to allow inactive users to log in.
So.. I managed to get it to work, but not using the user_loader callback. For whatever reason, my user loader exhibits the same behaviour as this:
Flask-login with static user always yielding 401- Unauthorized
Anyway, I used a request_loader callback instead based on this example:
http://gouthamanbalaraman.com/blog/minimal-flask-login-example.html
so for a user logging in, which starts here:
if bcrypt.check_password_hash(password_hash, candidate_password):
user_object = User(user, password_hash)
result = login_user(user_object) # <- here for successful login
token = user_object.get_auth_token(user, password_hash)
return redirect(url_for('loggedin', token=token))
I create a user object which has the user's id and their password hash.
then i log the user in. then i create a time-serialized token of the user id and password hash using itsdangerous. the get_auth_token function is part of the User class. it looks like this:
class User():
def __init__(self, user, password_hash):
self.user = user
self.password = password_hash
.
.
.
def get_auth_token(self, user, password):
data = [str(self.user), self.password]
return serializer.dumps(data, salt=serializer_secret)
you need to create a serializer at the beginning of your code somewhere:
serializer = URLSafeTimedSerializer(serializer_secret)
So after the token is created, pass it to the loggedin view as a URL query parameter.
When you try to load a login_required page, like my loggedin page, which is where login_user redirects me to after a successful login, the request_loader callback is executed. it looks like this:
#login_manager.request_loader
def load_user_from_request(request):
if request.args.get('token'):
token = request.args.get('token')
max_age = 1
try:
data = serializer.loads(token, salt=serializer_secret, max_age=max_age)
username = data[0]
password_hash = data[1]
found_user = finduserindbbyuuid(username)
found_password = checkuserpasswordindbbyuuid(username)
if found_user and found_password == password_hash:
user_object = User(found_user, password_hash)
if (user_object.password == password_hash):
return user_object
else:
return None
else:
return None
except BadSignature, e:
pass
else:
return None
This is the point where my user_loader was failing. I was logging in successfully, but the user_loader was always returning None and so my user would be deemed as anonymous.
So with the request loader, it checks that the request URL contains a 'token' argument in the query string. if so, it takes its value and using itsdangerous, deserializes the data.
you can make the token expire with timed serializers, but there are also non timed ones. after the token is deserialized, take the user and password hash and check in the database if they exist, in exactly the same way that the user_loader was supposed to work.. i imagine? my user_loader didnt work so i was most probably doing it wrong.
anyway if a user and password match in the db, then return the user object and bam, login works.
Im not sure if im doing it the right way, cos pretty much flying by the seat of my pants. i saw examples where people used the token_loader, rather than the request_loader callback function, to load the token, but i couldnt figure out how to set & get the auth token to & from the client. maybe ill figure it out... one day...
if you have the same problem, maybe this might help? or just let me know what you think
cheers
I found this page when searching for help with Flask-Login + Flask-Dance. I was seeing current_user as AnonymousUserMixin in a handler with the #login_required decorator. In my case making sure #app.route is on the line above #login_required fixed the problem. The correct order is in the docs: https://flask-login.readthedocs.io/en/latest/#flask_login.login_required.

Categories

Resources