To set some context I'm creating an API through Flask. To authenticate users, I'm using
flask-HTTPAuth. As a part of accessing login protected resources, I've defined my verify_password callback in auth.py. If the user credentials provided evaluate to True, the user is attached to the g object.
In app.py, there is the route /api/v1/users/token, that when requested, a token is issued to a user that is logged in. However when I try to access g.user in app.py, I get the error: AttributeError: '_AppCtxGlobals' object has no attribute 'user'.
Why isn't there any existing 'user' attribute not while accessing the g object in app.py?
auth.py
from flask import g
from flask_http import HTTPBasicAuth
from models import User
basic_auth = HTTPBasicAuth()
#basic_auth.verify_password
def verify_password(username, password):
try:
api_user = User.get(User.username == username)
except User.DoesNotExist:
return False
user_verified = api_user.check_password(password)
if user_verified:
g.user = api_user
return True
return False
app.py
from flask import Flask, g, jsonify
from auth import basic_auth as auth
app = Flask(__name__)
#auth.login_required
#app.route("/api/v1/users/token")
def issue_api_token():
token = g.user.request_token()
return jsonify({'token': token})
The order of your decorators is wrong, #app.route should always be first.
#app.route("/api/v1/users/token")
#auth.login_required
def issue_api_token():
# ...
Related
I've used PyJWT for authenticating the user now my main concern is how to use authentication decorator in API endpoints as I've addes SQL query to to fetch user detail using uid in my route but in token_required definition for current user do I've to add that query again?
Ex. After login I want to access API to display user profile.
#app.route('/users/<uid>', methods=['GET'])
**#token_required** ??
I've used SqlAlchemy core to execute and get data from database in my route.
In token_required definition can we add SqlAlchmey core query for current user & how to implement because I've already used that in my route /users/.
def token_required(f):
#wraps(f)
def decorator(*args, **kwargs):
token = None
if 'x-access-tokens' in request.headers:
token = request.headers['x-access-tokens']
if not token:
return jsonify({'message': 'a valid token is missing'})
try:
data = jwt.decode(token, app.config['SECRET_KEY'])
current_user = User.query.filter_by(uid=data['uid']).first()
except:
return jsonify({'message': 'token is invalid'})
return f(current_user, *args, **kwargs)
return decorator
#app.route('/users/<uid>', methods=['GET'])
def profile_view(uid):
print("user_details")
conn = engine.connect()
str_sql = text(**"""SELECT * FROM user WHERE uid = uid""",{"uid": uid}**)
results = conn.execute(str_sql).fetchall()
print(results)
return users_scehma.dump(results)
First of all import flask and JWTManager
from flask import Flask, jsonify
from flask_jwt_extended import (create_access_token,get_jwt_identity,jwt_required,JWTManager)
Define the Flask app and set a JWT_SECRET_KEY
app = Flask(__name__)
app.config["JWT_SECRET_KEY"] = "supersecret" # Change this!
Initialize the jwt with the app
jwt = JWTManager(app)
Make a #app.route to define the method and path.
def login() is to get the Authentifikation key
#app.route("/token/login", methods=["GET"])
def login():
additional_claims = {"aud": "some_audience", "foo": "bar"}
access_token = create_access_token(identity="username", additional_claims=additional_claims) #you can add additional parameters
return jsonify(access_token=access_token),200#, encoded=False), 200
Our second route is protected to with #jwt_required()
#app.route("/protected", methods=["GET"])
#jwt_required()
def protected():
# Access the identity of the current user with get_jwt_identity
current_user = get_jwt_identity()
return jsonify(logged_in_as=current_user), 200
if __name__ == '__main__':
app.run()
the code looks like this at the end:
from flask import Flask, jsonify
from flask_jwt_extended import (create_access_token,get_jwt_identity,jwt_required,JWTManager)
app = Flask(__name__)
app.config["JWT_SECRET_KEY"] = "supersecret" # Change this!
jwt = JWTManager(app)
#app.route("/token/login", methods=["Get"])
def login():
additional_claims = {"aud": "some_audience", "foo": "bar"}
access_token = create_access_token(identity="username", additional_claims=additional_claims) #you can add additional parameters
return jsonify(access_token=access_token),200#, encoded=False), 200
#app.route("/protected", methods=["GET"])
#jwt_required()
def protected():
# Access the identity of the current user with get_jwt_identity
current_user = get_jwt_identity()
return jsonify(logged_in_as=current_user), 200
if __name__ == '__main__':
app.run()
Output:
{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2Njk2MTA3MCwianRpIjoiNGViY2MwYzAtMjAxYy00ODAwLThjMTUtNmQzNDQ1MmVhYmQxIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6InVzZXJuYW1lIiwibmJmIjoxNjY2OTYxMDcwLCJleHAiOjE2NjY5NjE5NzAsImF1ZCI6InNvbWVfYXVkaWVuY2UiLCJmb28iOiJiYXIifQ.qAn8rhsxyF_00Ayu9L7ddd6USkbYIHKvsUneDMzzjHs"}
To use the access_token we need an module that can call the Webserver with header.
I will use requests
import requests
key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY2Njk2MTA3MCwianRpIjoiNGViY2MwYzAtMjAxYy00ODAwLThjMTUtNmQzNDQ1MmVhYmQxIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6InVzZXJuYW1lIiwibmJmIjoxNjY2OTYxMDcwLCJleHAiOjE2NjY5NjE5NzAsImF1ZCI6InNvbWVfYXVkaWVuY2UiLCJmb28iOiJiYXIifQ.qAn8rhsxyF_00Ayu9L7ddd6USkbYIHKvsUneDMzzjHs"
requests.get("http://127.0.0.1:5000/protected", headers={'Authorization': 'Bearer ' + key}
If you want to set an expire time set this:
app.config["JWT_SECRET_KEY"] = "supersecret" # Change this!
from datetime import timedelta
pp.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(minutes=1000)
jwt = JWTManager(app)
if you need to know more you can follow the link:
https://flask-jwt-extended.readthedocs.io/en/stable/token_locations/
On the right side is an Navigation to go throw the sides.
This will also show you how to use Cookies with the #jwt_required()
I am able to successfully login and authenticate the user and I can access their info with current_user from the login method. But then when I make another API call and try to access current_user, I get an error and current_user is of type AnonymousUserMixin.
I thought the issue may be with the load_user function, so I added a print statement but it seems as though it never gets called.
Is there anything wrong with the below setup that would prevent load_user from getting executed when an API call is made or cause the user's session to not persist?
# app.py
login_manager = LoginManager()
login_manager.init_app(app)
#login_manager.user_loader
def load_user(user_id):
print("[LoadUser]")
return Users.query.get(user_id)
#app.route("/login")
def login():
user = Users(
id = unique_id, name=users_name, email=users_email
)
login_user(user, remember=True)
print(current_user.username) # Works with no issue
return
#app.route("/test")
def test():
print(current_user.username) # AttributeError: 'AnonymousUserMixin' object has no attribute 'username'
I noticed your test route does not protect against anonymous access. according to flask-login docs 0.7..current_user would then be anonymous... Which means if you lock the test route to logged in users only current_user will then be populated!
#app.route("/test")
#login_required
def test():
print(current_user.username)
https://flask-login.readthedocs.io/en/latest/
I'm trying to import a Flask blueprint into my main file. However, when I do from flask_first.login_user.login import login, I get cannot import name 'login' from partially initialized module 'flask_first.login_user.login' (most likely due to a circular import) (C:\Users\Max\PycharmProjects\python1\flask_first\login_user\login.py). How do I fix this?
flask_first/login_user/login.py:
from flask_first.main import *
from flask import Blueprint
from flask_first.main import users, db
from flask_sqlalchemy import SQLAlchemy
from datetime import timedelta
#login.route('/')
def login():
error = None
if request.method == 'POST': # checking if the method is POST, it means we got a query from button
if request.form['nm'] == 'admin' or request.form['ps'] == 'secret':
flash("You were successfully logged in into the admin's user page")
session.permanent = True
session['user'] = request.form['nm']
password = request.form['ps']
password = session['password']
return redirect(url_for('administrating'))
else: # if we are not admins, continue with this code
flash(f"You were successfully logged in", category='success')
session.permanent = True # setting the bool of session to permanent
session['user'] = request.form[
'nm'] # we are just saying that the session['user']= the name, which we typed into the field
user1 = session['user'] # user1 not a function
session['password'] = request.form['ps'] # session['password']= field, in which we typed our password
password1 = request.form['ps']
found_user = users.query.filter_by(name=user1,
password=password1).first() # we are filtering all the users in the database by the name and password, we typed while logging in
if found_user: # if we have found this user, we say that the email he typed previously is now in the field of email
session[
'email'] = found_user.email # we are saying that the email user typed previously, is now the session['email']
else:
usr = users(user1, '',
password1) # if we haven't found that user by name and password, we create a new one
db.session.add(usr)
db.session.commit()
return redirect(
url_for('user')) # redirecting to the user's page after logging in(using user's name)
else: # below is a standard script, which checks whether we are logged or not
if 'user' in session: # if user is already logged, it will download the user page.
flash('You are already logged in, to log out, type logout')
return redirect(url_for('user'))
else:
flash("You have not logged yet", category='success')
return render_template('login_user.html', error=error) # if it didn't go properly, we force the comeback
# to the login_user page again
flask_first/main.py:
from datetime import timedelta
from flask import Flask, redirect, url_for, render_template, request, session, flash
from flask_sqlalchemy import SQLAlchemy
from flask_first.admin.second import second
from flask_first.login_user.login import login
app = Flask(__name__)
app.secret_key = 'hello world'
app.register_blueprint(second, url_prefix='/test')
app.register_blueprint(login, url_prefix='login_user')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.sqlite3.html' # access to the SQL
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.permanent_session_lifetime = timedelta(minutes=5) # setting the time for long-lasting session
db = SQLAlchemy(app)
As the error suggests, you have a circular dependency issue. You are importing login from main and main from login. Modules cannot do that.
Check out the "problems" section In this wiki https://en.m.wikipedia.org/wiki/Circular_dependency
I've been working with some of Miguel Grinberg's auth tutorials and have come across an issue using flask_httpauth's HTTPBasicAuth decorators. Whenever I use one of them on a function I get an error stating that the decorator is missing required positional argument f. It was my understanding that the function beneath the decorator was implicitly passed to the decorator function as an argument. Am I wrong? I'm using Python 3.5.
My views file looks like this:
from mymodule import app, api, auth
from flask import abort, request, jsonify, g, url_for
from mymodule.users.models import User
#auth.verify_password
def verify_password(username_or_token, password):
# first try to authenticate by token
user = User.verify_auth_token(username_or_token)
if not user:
# try to authenticate with username/password
user = User.query.filter_by(username=username_or_token).first()
if not user or not user.verify_password(password):
return False
g.user = user
return True
#app.route('/api/users', methods=['POST'])
def new_user():
username = request.json.get('username')
password = request.json.get('password')
if username is None or password is None:
abort(400) # missing arguments
if User.query.filter_by(username=username).first() is not None:
abort(400) # existing user
user = User(username=username)
user.hash_password(password)
db.session.add(user)
db.session.commit()
return (jsonify({'username': user.username}), 201,
{'Location': url_for('get_user', id=user.id, _external=True)})
#app.route('/api/users/<int:id>')
def get_user(id):
user = User.query.get(id)
if not user:
abort(400)
return jsonify({'username': user.username})
#app.route('/api/token')
#auth.login_required
def get_auth_token():
token = g.user.generate_auth_token(600)
return jsonify({'token': token.decode('ascii'), 'duration': 600})
#app.route('/')
#auth.login_required
def index():
return "Hello, %s!" % g.current_user
#app.route('/api/resource')
#auth.login_required
def get_resource():
return jsonify({'data': 'Hello, %s!' % g.user.username})
and my init file (where auth, app, api, etc are imported from) looks like this:
import logging
from logging.handlers import RotatingFileHandler
from flask_sqlalchemy import SQLAlchemy
from flask_httpauth import HTTPTokenAuth, HTTPBasicAuth
from flask_restful import Api
from itsdangerous import TimedJSONWebSignatureSerializer as JWT
from flask import Flask
app = Flask(__name__)
"""
Database Config
"""
app.config['SQLALCHEMY_DATABASE_URL'] = 'sqlite:////db.sqlite'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
app.config['SECRET_KEY'] = 'top secret!'
handler = RotatingFileHandler('logfile.log', maxBytes=10000, backupCount=1)
handler.setLevel(logging.INFO)
app.logger.addHandler(handler)
api = Api(app)
db = SQLAlchemy(app)
auth = HTTPBasicAuth
jwt = JWT(app.config['SECRET_KEY'], expires_in=3600)
import mymodule.users.views
Any idea why this isn't working for me? The exact error runs like this:
File "/.../users/views.py", line 14, in <module>
#auth.verify_password
TypeError: verify_password() missing 1 required positional argument: 'f'
Change this:
auth = HTTPBasicAuth
to this:
auth = HTTPBasicAuth()
I am new to Flask and have been working on an existing app for its login module. It has to be delivered asap. The login credentials validates by Active Directory authentication, which is working as expected. I am referring this flask link which says
url_for('main', page=2): return the internal URL /?page=2. All optional keyword arguments are treated as GET parameters.
My index page is loaded from here (working perfectly):
#app.route("/", methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
userName = request.form['username']
pwd = request.form['password']
try:
ldapobject = auth.bind(userName,pwd)
return redirect(url_for('scheduler')) ----> NEED TO PASS USER AND PWD TO scheduler function using post here
except ldap.LDAPError, error_message:
error = 'Invalid Credentials. Please try again'
return render_template('login.html', error=error)
My scheduler function:
#app.route("/home", methods=['POST'])
def scheduler():
# some code here
# WANT TO HAVE USER CREDENTIALS IN THIS FUNCTION FROM login page
return render_template("scheduler.html", scheduler=form)
Problem: How to pass user's credentials to scheduler method as a POST call
If you want to persist user data between your application pages - you will need to store some session data on user side, e.g. cookies.
flask-login module (https://flask-login.readthedocs.org/en/) can do all the dirty work for you.
In short you need to create User class with several required fields and methods (https://flask-login.readthedocs.org/en/latest/#your-user-class) and specify several methods with decorators to load and check users that are being authenticated:
from flask_login import LoginManager
login_manager = LoginManager()
login_manager.init_app(app=app)
#login_manager.user_loader
def load_user(username):
return User(username)
#login_manager.request_loader
def load_from_request(request):
return User.auth_is_valid(request.authorization)
After that you can mark any routes that need user credentials with #login_requied decorator and access logged in user via current_user variable:
from flask_login import login_required, current_user
#app.route("/user_needed")
#login_required
def some_function():
print(current_user)