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
Related
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
As I'm testing the URL endpoint for a resource, I'm getting a 404 Not Found error. I don't understand why it cannot be found as the resource is added to the api instance and the blueprint is added to the flask app.
tests.py
def test_todo_collection_resource(self):
with app.test_client() as client:
http_response = client.get("/todos/")
json_data = http_response.get_json()
self.assertEqual(http_response.status_code, 200) <<<---FAILS
self.assertTrue(http_response.is_json)
self.assertTrue(all(
(instance['name'] in self.todo_resources.values()
for instance in json_data)
))
todos.py
from flask import Blueprint, jsonify
from flask_restful import Api, Resource, fields, marshal
from models import Todo
todo_api = Blueprint("resources.todos", __name__)
api = Api(todo_api)
todos_fields = {
'name': fields.String
}
class TodoList(Resource):
pass
api.add_resource(
TodoList,
''
'todos'
)
app.py
from flask import Flask, g, jsonify, render_template
from config import HOST, PORT, DEBUG
from peewee import *
import models
from resources.todos import todo_api
app = Flask(__name__)
app.register_blueprint(todo_api, url_prefix="/todos/")
models.DATABASE.init('todo_api.db')
models.initialize(models.User, models.Todo)
#app.route('/')
def my_todos():
return render_template('index.html')
if __name__ == '__main__':
app.run(host=HOST, port=PORT, debug=DEBUG)
You have not defined any methods in your TodoList class that is why there is a 404 error as it can't find any HTTP methods defined on that endpoint. The Flask-RESTful documentation specifies defining HTTP methods in your Resource class e.g.
class TodoList(Resource):
def get(self):
return TODOS
I have an API with 12 endpoints which was working fine, then the endpoints started failing and now all return 404 status code. Here are some of my files
My run.py
import os
from app import create_app
config = os.getenv('APP_SETTINGS')
app = create_app(config)
if __name__ == "__main__":
app.run(debug=True)
I register my endpoints in app.py like so
from flask import Flask
from .api.v2.views.userview import auth
def create_app(config):
'''Creates all Flask configurations and returns app.
Expects config name'''
app = Flask(__name__, instance_relative_config=True)
app.config['JSON_SORT_KEYS'] = False
app.config.from_object(app_config[config])
app.config.from_pyfile('config.py', silent=True)
app.url_map.strict_slashes = False
app.register_blueprint(auth)
return app
And finally my endpoint user.py
from flask import request, jsonify
from flask import Blueprint
from ..models.usermodel import UserModel
usr_obj = UserModel()
auth = Blueprint('auth', __name__, '/api/v2')
#auth.route('/auth/signup', methods=['POST'])
def user_signup():
fullname = request.json['fullname']
username = request.json['username']
email = request.json['email']
password = request.json['password']
data = usr_obj.inituser(fullname, username, email, password)
return jsonify(data), 201
When I try to run this endpoint or any other in Version 1 (/api/v1) I get a Not Found error. I have also tried THIS SOLUTION with no success.
I made a silly mistake the Blueprint declaration ought to be
auth = Blueprint('auth', __name__, url_prefix='/api/v2')
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'm trying to have my backend server sign and send the username at the end of an OAuth request. The example I'm trying to follow uses this package (https://www.npmjs.com/package/express-jwt) but my backend is in Google App Engine. So, I was wondering how I can achieve the same (signing the username with some secret) inside the App Engine/webapp2 framework?
First download python jwt to folder lib of projects
Code:
import cgi
import datetime
import webapp2
from base_handler import BaseRequestHandler, login_required
from webapp2 import Route
from models import Customer
from lib import jwt
import logging
def login_required(fn):
def check(self, *args, **kargs):
auth = self.request.headers.get('Authorization')
login_info = jwt.decode(auth, 'secret', algorithms=['HS256'])
# your verify login token here
logging.error(login_info)
# verify success
fn(self, *args, **kargs)
return check
class LoginHandler(BaseRequestHandler):
def login(self):
username = self.request.get('username')
password = self.request.get('password')
if Customer.login(username, password) is True:
encoded = jwt.encode({'username': username}, 'secret', algorithm='HS256')
self.response.write({'token': encoded})
return
self.response.write('login unsuccess', status=404)
class APIHandler(BaseRequestHandler):
#login_required
def get(self):
self.response.write('product found')
app = webapp2.WSGIApplication([
Route('/login', handler=LoginHandler, handler_method='login', name='login', methods=['POST']),
('/', APIHandler)
], debug=True)
Note: for test client you can usage postman and add header (token are get from login request)