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')
Related
I am working on a simple user authentication feature for a website using Angular front end and Flask back end. Currently, API requests made through Postman are successful, but when trying to log in through the browser, I receive the console error that login:1 Access to XMLHttpRequest at 'http://localhost:5000/api/auth/login' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I am able to see that the inject_csrf_token() works in generating a csrf token, but when I print the response, the response.cookies['csrf_token'] returns blank. I have tried adding response headers for Access-Control-Allow-Origin with *, but am still receiving the KeyError: 'csrf_token' error.
Any assistance would be greatly appreciated, this has been a major blocker for a few days now.
Here are what I believe are the relevant files:
app.init.py
from flask import Flask, render_template, request, session, redirect
from flask_cors import CORS
from flask_migrate import Migrate
from flask_wtf.csrf import CSRFProtect, generate_csrf
from flask_login import LoginManager
from .models import db, User
from .api.user_routes import user_routes
from .api.auth_routes import auth_routes
from .seeds import seed_commands
from .config import Config
app = Flask(__name__)
# Setup login manager
login = LoginManager(app)
login.login_view = 'auth.unauthorized'
# Tell flask about our seed commands
app.cli.add_command(seed_commands)
app.config.from_object(Config)
app.register_blueprint(user_routes, url_prefix='/api/users')
app.register_blueprint(auth_routes, url_prefix='/api/auth')
db.init_app(app)
Migrate(app, db)
# Application Security
CORS(app)
#app.before_request
def https_redirect():
if os.environ.get('FLASK_ENV') == 'production':
if request.headers.get('X-Forwarded-Proto') == 'http':
url = request.url.replace('http://', 'https://', 1)
code = 301
return redirect(url, code=code)
#app.after_request
def inject_csrf_token(response):
print("INJECT: ", generate_csrf())
response.set_cookie(
'csrf_token',
generate_csrf(),
secure=True if os.environ.get('FLASK_ENV') == 'production' else False,
samesite='Strict' if os.environ.get(
'FLASK_ENV') == 'production' else None,
httponly=True)
response.headers.add("Access-Control-Allow-Origin", "*")
return response
#app.route('/', defaults={'path': ''})
#app.route('/<path:path>')
def react_root(path):
if path == 'favicon.ico':
return app.send_static_file('favicon.ico')
return app.send_static_file('index.html')
auth_routes.py
from flask import Blueprint, jsonify, session, request
from app.models import User, db
from app.forms import LoginForm
from app.forms import SignUpForm
from flask_login import current_user, login_user, logout_user, login_required
auth_routes = Blueprint('auth', __name__)
def validation_errors_to_error_messages(validation_errors):
"""
Simple function that turns the WTForms validation errors into a simple list
"""
errorMessages = []
for field in validation_errors:
for error in validation_errors[field]:
errorMessages.append(f'{field} : {error}')
return errorMessages
#auth_routes.route('/')
def authenticate():
"""
Authenticates a user.
"""
if current_user.is_authenticated:
return current_user.to_dict()
return {'errors': ['Unauthorized']}
#auth_routes.route('/login', methods=['POST'])
def login():
"""
Logs a user in
"""
form = LoginForm()
# Get the csrf_token from the request cookie and put it into the
# form manually to validate_on_submit can be used
print("##########REQUEST: ", request.cookies.to_dict())
form['csrf_token'].data = request.cookies['csrf_token']
if form.validate_on_submit():
# Add the user to the session, we are logged in!
user = User.query.filter(User.email == form.data['email']).first()
login_user(user)
return user.to_dict()
return {'errors': validation_errors_to_error_messages(form.errors)}, 401
#auth_routes.route('/logout')
def logout():
"""
Logs a user out
"""
logout_user()
return {'message': 'User logged out'}
#auth_routes.route('/signup', methods=['POST'])
def sign_up():
"""
Creates a new user and logs them in
"""
form = SignUpForm()
form['csrf_token'].data = request.cookies['csrf_token']
if form.validate_on_submit():
user = User(
username=form.data['username'],
email=form.data['email'],
password=form.data['password']
)
db.session.add(user)
db.session.commit()
login_user(user)
return user.to_dict()
return {'errors': validation_errors_to_error_messages(form.errors)}, 401
#auth_routes.route('/unauthorized')
def unauthorized():
"""
Returns unauthorized JSON when flask-login authentication fails
"""
return {'errors': ['Unauthorized']}, 401
login_form.py
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired, Email, ValidationError
from app.models import User
def user_exists(form, field):
# Checking if user exists
email = field.data
user = User.query.filter(User.email == email).first()
if not user:
raise ValidationError('Email provided not found.')
def password_matches(form, field):
# Checking if password matches
password = field.data
email = form.data['email']
user = User.query.filter(User.email == email).first()
if not user:
raise ValidationError('No such user exists.')
if not user.check_password(password):
raise ValidationError('Password was incorrect.')
class LoginForm(FlaskForm):
email = StringField('email', validators=[DataRequired(), user_exists])
password = StringField('password', validators=[
DataRequired(), password_matches])
users.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { environment } from 'src/environments/environment';
#Injectable({
providedIn: 'root'
})
export class UsersService {
constructor(
private httpClient: HttpClient
) { }
loginUser(credentials: any) {
console.log("log in clicked")
return this.httpClient.post(`${environment.apiUrl}/auth/login`, {
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(credentials),
});
};
getUser(username: string) {
return this.httpClient.get(`${environment.apiUrl}/users/${username}`, { observe: 'response' })
};
}
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
I am building a website using Flask and when I try to log in and signup I get this error AttributeError: 'list' object has no attribute 'is_active'
an error like this appears when I have used login_user in the flask_login library
here I use firestore database
I have tried various solutions on StackOverflow but none of them worked, I'm very confused now
this is my code
__ini__.py
from flask import Flask
from flask_login import UserMixin
from flask_login import LoginManager
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = 'adjadlkahd'
db = connect_database()
from .views import views
from .auth import auth
from .models import User
#check user login
login_manager = LoginManager()
login_manager.login_view = 'auth.login'
login_manager.init_app(app)
#create user model
#login_manager.user_loader
def load_user(id):
return User.get_user_id(id)
#register blueprints
app.register_blueprint(views, url_prefix='/')
app.register_blueprint(auth, url_prefix='/')
return app
cred = credentials.Certificate("websites\serviceAccountKey.json")
firebase_admin.initialize_app(cred)
def connect_database():
db = firestore.client()
return db
auth.py
from flask import Blueprint, render_template, request, redirect, url_for
from .models import User
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import login_user, login_required, logout_user, current_user
auth = Blueprint('auth', __name__)
#auth.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
email = request.form.get('email')
password = request.form.get('password')
user = User.log_in(email)
if user:
if check_password_hash(user[0].to_dict()['password'], password):
login_user(user, remember=True)
redirect(url_for('views.home'))
else:
return "<h1>paass salah</h1>"
else:
return "<h1>user tidak ada</h1>"
return render_template('login.html')
#auth.route('/logout')
#login_required
def logout():
logout_user()
return redirect(url_for('auth.login'))
#auth.route('/signup', methods=['GET', 'POST'])
def signup():
if request.method == 'POST':
username = request.form.get('username')
email = request.form.get('email')
password = request.form.get('password')
if User.check_user(email):
return "<h1>email already exist</h1>"
new_user = User(username=username, email=email,
password=generate_password_hash(password)).create_user()
login_user(new_user, remember=True)
return redirect(url_for('views.home'))
return render_template('signup.html')
models.py
from flask_login import UserMixin
from . import connect_database
import uuid
db = connect_database()
class User(UserMixin):
def __init__(self, username, email, password):
self.username = username
self.email = email
self.password = password
def create_user(self):
db.collection('user').document(self.username).set({
'id' : str(uuid.uuid4()),
'username': self.username,
'email': self.email,
'password': self.password
})
def log_in(email):
user = db.collection('user').where('email', '==', email).get()
return user
def check_user(email):
user = db.collection('user').where('email', '==', email).get()
return bool(user)
def get_user_id(id):
return db.collection('user').document(id).get()
views.py
from flask import Blueprint
from flask_login import login_required, current_user
views = Blueprint('views', __name__)
#views.route('/')
#login_required
def home():
return "<h1>logged in</h1>"
For flask login to work you need to have a user class that implements some properties and methods. When you use login_user method the argument should be an instance of that user class.
make user that your method def log_in(email) returns a user object not a list.
def log_in(email):
user = db.collection('user').where('email', '==', email).get()
return user # <-- most probably is a list.
Read the items form the user collection
Create an instance of the user class
fill the properties of the user class from the collection
return the instance
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'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()