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()
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 using flask_jwt_extended for jwt authentication in my flask web application. After the user enters email and password, I make a token using create_access_token and then redirect to another link which can only be accessed with #jwt_required.
app.py file. Notice if the way of importing jwt from user.py file like this is correct.
from flask import Flask
from flask_restful import Api
from resources.organization import OrganizationResourceList, OrganizationResource
from resources.user import LoginResource, user_list, User, jwt
app = Flask(__name__)
app.config['SECRET_KEY'] = '_rV;He_7Bz8TVvA'
app.config['JWT_TOKEN_LOCATION'] = ['headers']
jwt.init_app(app)
api = Api(app)
user_list.append(User(name="Admin User", email="admin#test.com", password="12345", photo="", user_type="host"))
# Authorization
api.add_resource(LoginResource, '/login')
# Organization
api.add_resource(OrganizationResourceList, '/organizations')
if __name__ == '__main__':
app.run(port=5000, debug=True)
user.py file containing LoginResource This is where I am creating token.
from flask import request, Response, render_template, redirect
from flask_restful import Resource
from models.user import User, user_list
from passlib.hash import sha256_crypt
from flask_jwt_extended import create_access_token, create_refresh_token, JWTManager
jwt = JWTManager()
class LoginResource(Resource):
def post(self):
req = request.form
email = req.get("email")
user = [x for x in user_list if x.email == email]
if user:
user = user[0]
password = sha256_crypt.verify(req.get("password"), user.password)
if user and password:
access_token = create_access_token(identity=user.id)
refresh_token = create_refresh_token(user.id)
redir = redirect('/organizations')
redir.headers['Authorization'] = "Bearer %s" % access_token
return redir
return redirect("/login")
Interestingly, when I debug the app, I see the headers of redirect as shown.
organization.py file containing OrganizationResourceList class
from flask import request, Response, render_template
from flask_restful import Resource
from models.organization import Organization, organization_list
from flask_jwt_extended import jwt_required, get_jwt_identity
class OrganizationResourceList(Resource):
#jwt_required()
def get(self):
current_user = get_jwt_identity()
sample_org = Organization(
name='Bin Yousef',
description='My main working company in Qatar',
photo=''
)
data = []
for organization in organization_list:
data.append(organization.data)
return Response(response=render_template('organization/index.html', organizations=data))
After hours of searching, I am still not able to get rid of the error :( Please help
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():
# ...
I have following code for flask restful basic authentication
from flask import Flask
from flask_restful import Resource, Api
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__)
api = Api(app, prefix="/api/v1")
auth = HTTPBasicAuth()
USER_DATA = {
"admin": "SuperSecretPwd"
}
#auth.verify_password
def verify(username, password):
if not (username and password):
return False
return USER_DATA.get(username) == password
class PrivateResource(Resource):
#auth.login_required
def get(self):
return {"meaning_of_life": 42}
api.add_resource(PrivateResource, '/private')
if __name__ == '__main__':
app.run(debug=True)
But if My resource class PrivateResource is in separate file, how can I use #auth.login_required. I don't want to import app.py in every resource file.
You can structure your project like this:
In app.py
from flask import Flask
from flask_restful import Api
from my_resource import PrivateResource
app = Flask(__name__)
api = Api(app, prefix="/api/v1")
# add all resources here
api.add_resource(PrivateResource, '/private')
if __name__ == '__main__':
app.run(debug=True)
Handle authentication in authentication.py
from flask_httpauth import HTTPBasicAuth
auth = HTTPBasicAuth()
USER_DATA = {
"admin": "SuperSecretPwd"
}
#auth.verify_password
def verify(username, password):
if not (username and password):
return False
return USER_DATA.get(username) == password
and your resources in separate files like my_resource.py
from flask_restful import Resource
from authentication import auth
class PrivateResource(Resource):
#auth.login_required
def get(self):
return {"meaning_of_life": 42}
If every resource requires authentication, you can add the decorator automatically to every resource, see the decorators parameters in the third line
from flask_httpauth import HTTPBasicAuth
from werkzeug.security import check_password_hash
from flask import Flask
from flask_restful import Api
application: Flask = Flask(__name__)
auth = HTTPBasicAuth()
api: Api = Api(app=application, serve_challenge_on_401=True, decorators=[auth.login_required])
PW_HASH: str = ..
#auth.verify_password
def verify_password(username: str, password: str):
if username == 'foo' and check_password_hash(PW_HASH, password):
return username
You always need to import the auth instance from app.py, because you are creating a new instance of Auth by calling auth = HTTPBasicAuth(). And you are registering your validation method on that specific instance, see #auth.verify_password. So only this instance is able to handle your authentication. So you need to import it every time you want to use it. But you don't need to import the whole module. Its enough to import only that instance: from app.py import auth
I come to you today with a problem that seems to be annoying and bother me for 1 week (maybe more).
I implemented Flask and Flask-JWT to create a Token and use it in different parts of my web-app.
The process is : The user puts is credentials on the form and when he's logged a token is created by making a POST request to the Flask-JWT /auth endpoint.
The problem is :
When the user has loged-in, the token creation part of the code (POST request to /auth endpoint) seems to be in an endless loop.
The code stop at : "r = requests.post(url, headers=headers, data=payload)"
The funny part is that if a user login and after go to the /auth endpoint, it has the token created.
Someone already had this problem ?
Thank you
project/config.py
JWT_EXPIRATION_DELTA = timedelta(seconds=900)
JWT_AUTH_URL_RULE = "/api/v1/auth"
SECURITY_PASSWORD_HASH = 'pbkdf2_sha512'
SECURITY_TRACKABLE = True
SECURITY_PASSWORD_SALT = "xxxx"
WTF_CSRF_ENABLED = True
WTF_CSRF_SECRET_KEY = "xxxx"
project/app/init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, login_required
from flask_bcrypt import Bcrypt
from flask_security import Security, SQLAlchemyUserDatastore, \
UserMixin, RoleMixin, login_required
from flask_jwt import JWT, jwt_required
import os
app = Flask(__name__)
app.config.from_object('config')
bcrypt = Bcrypt(app)
db = SQLAlchemy(app)
# To have random secret key
secret_key = os.urandom(24)
app.secret_key = secret_key
from textr import views, models
from search_number import views
from buy_number import views
from users import views, models
from sms_receiver import views, models
from sms_sender import views, models
from phone_number import models, views
from users.models import User, Role
# Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "login"
#login_manager.user_loader
def load_user(user_id):
return User.query.filter(User.id == int(user_id)).first()
from textr.tools import retreive_user, verify_password
def authenticate(username, password):
user = retreive_user(username)
if verify_password(username, password):
return user
def identity(payload):
return User.query.filter(User.id == payload['identity']).scalar()
jwt = JWT(app, authenticate, identity)
project/app/users/views.py
from flask_jwt import jwt_required, JWT
from models import User
from error_handling import error_400
from flask import Flask, request, render_template, jsonify, session, redirect, url_for, flash
import json
import requests
#app.route("/api/v1/login", methods=["GET", "POST"])
def login():
error = None
if request.method == 'POST':
json_data = request.json
if not json_data:
username = request.form['username']
password = request.form['password']
else:
username = json_data['username']
password = json_data['password']
if not check_user_present(username):
return error_400('Error', 'User not found', 404)
if verify_password(username, password):
flash('Logged in successfully.')
user = retreive_user(username)
login_user(user)
# Infos to create POST request for Token
payload = ({'username':username, 'password':password})
url = "http://127.0.0.1:8080/api/v1/auth"
headers = {'Content-Type': 'application/json', 'charset': 'utf-8'}
# Token creation
r = requests.post(url, headers=headers, data=payload)
response = r.json()
if (r.status_code is 200):
token = response['user']['authentication_token']
session['api_session_token'] = token)
else:
flash('Wrong credentials!')
return error_400('Error', 'Wrong credentials', 401)
return render_template('users/login.html')