Unable to get Authorization Header in Flask JWT Extended - python

I am trying to using to use Flask-JWT extended at the basic level at the moment.
I get the user and password authenticated from the user form. I create an accesss token, include it in the response and route to another protected route. Please find the shorter version of code as below...
from flask import Flask, jsonify, request
from flask_jwt_extended import,JWTManager, jwt_required, create_access_token,get_jwt_identity)
app.config['JWT_SECRET_KEY'] = 'super-secret'
jwt = JWTManager(app)
app.config['JWT_TOKEN_LOCATION'] = ['headers']
app.config['JWT_BLACKLIST_ENABLED'] = True
jwt = JWTManager(app)
app.config['PROPAGATE_EXCEPTIONS'] = True
#log_blueprint.route('/', methods=['GET', 'POST'])
def login():
form = LoginForm()
if request.method == 'POST':
if error is None and username = form.request['user'] and pwd = form.request['pwd'] :
access_token = create_access_token(identity=user)
resp = redirect(url_for('log_blueprint.protected'),access_token)
resp.headers = {'Authorization': 'Bearer {}'.format(access_token)}
return resp
#log_blueprint.route('/protected', methods=["POST","GET"])
#jwt_required
def protected():
current_user = get_jwt_identity()
return jsonify(logged_in_as=current_user), 200
It gives me the error as below...
{"msg":"Missing Authorization Header"}
I tried the answers on this page...https://stackoverflow.com/questions/52087743/flask-restful-noauthorizationerror-missing-authorization-header
But couldnt get better.
Please let me know any solution for this issue.
Sorry if any typo mistake.
Thanks and regards,
Abhinay J K

Depending on the version you are using, accordind to change log of latest stable version, you should be using notation like:
#log_blueprint.route('/protected', methods=["POST","GET"])
#jwt_required()
def protected():
current_user = get_jwt_identity()
return jsonify(logged_in_as=current_user), 200

If you are using postman to send request, make sure you check the "key".

Related

How to use #token_required decorator for PyJWT in Flask App to access route after user gets logged in?

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()

How can the following issue be resolved in flask? "Method Not Allowed The method is not allowed for the requested URL"

Here is the code
import os
import redis
import flask
import json
import urllib.parse
from flask import Flask, Response, request, render_template, abort
from flask_cors import CORS, cross_origin
#from flask.ext.cors import CORS, cross_origin
app = Flask(__name__)
app.config['CORS_HEADERS'] = 'Content-Type'
redis_handle = redis.Redis('localhost')
requiredFields = ("id", "title", "name") # fields required for user object
#app.route('/')
#cross_origin()
def hello():
return 'Hello World!'
#app.route('/users/<user_id>', methods=['GET'])
#cross_origin()
def get_user(user_id):
response = {}
# user_id = request.args.get("id")
user = redis_handle.get(user_id)
if not user:
response["msg"] = "no user found"
return Response(json.dumps(response), status=404, mimetype="application/json")
return user
#app.route('/users', methods=['POST'])
#cross_origin()
def save_user():
data = request.get_json(force=True)
response = {}
if all(field in data for field in requiredFields):
redis_handle.set(data["id"], json.dumps(data))
return Response(status=201)
else:
missing_key = str([val for val in requiredFields if val not in dict(data).keys()])
response["msg"] = "required key " + missing_key + " not found"
return Response(json.dumps(response), status=400)
#app.route('/users/<user_id>', methods=['DELETE'])
#cross_origin()
def delete_user(user_id):
response = {}
resp = redis_handle.delete(user_id)
if resp == 0:
response["msg"] = "no such entity found"
status = 404
else:
response["msg"] = "Delete op is successful"
status = 200
return Response(json.dumps(response), status=status)
#app.route('/clear', methods=['GET'])
#cross_origin()
def clear_data():
redis_handle.flushall()
return "ok!"
if __name__ == "__main__":
app.run(debug=True)
As of my knowledge, I have even included the method = "POST" as well but still don't know what is going wrong.
I tried to create a small crud application using redis, python, flask but couldn't encountering this issue. Can someone tell me where and what am I doing wrong?
Browsers don't run POST methods outside of a <form> entry or AJAX function. Therefore, you're running a GET, which "isn't allowed".
Unclear what you expected, but to see all users, you'll need to edit your route to first add the GET method, then if so, return a response that returns/renders all users rather than checking the request json body, which won't exist for GET requests
If you only wanted to get one user, edit the url to include the user ID
The browser will use the GET method for URLs that you input in URL/search bar but you don't have any function decorated with #app.route('/users', methods=['GET']).
If you want to create a user with POST /users then it would be easier to use some HTTP client like https://www.postman.com, https://insomnia.rest, etc. or even fetch in the browser's console.

flask_jwt_extended giving "jwt.exceptions.InvalidSignatureError: Signature verification failed" error

I am using AWS Coginto to sign in a user and retrieve the authorization and refresh token response. I am able to successfully authenticate, retrieve the tokens, and decode the tokens. I verify the tokens are decoded on https://jwt.io/.
However, when I use the flask_jwt_extended.set_access_cookies() with the access_token returned from Cognito I get an error saying
jwt.exceptions.InvalidSignatureError: Signature verification failed
The login and code setting the access token is below.
import os
import boto3
from flask import Flask, request, make_response, redirect, render_template
from flask_jwt_extended import set_access_cookies
app = Flask(__name__)
#app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
auth_response = boto3.client('cognito-idp').admin_initiate_auth(
UserPoolId=os.environ['AWS_COGNITO_USER_POOL_ID'],
ClientId=os.environ['APP_CLIENT_ID'],
AuthFlow='ADMIN_NO_SRP_AUTH',
AuthParameters={
'USERNAME': username,
'PASSWORD': password
}
)
response = make_response(redirect('login_success', 302))
set_access_cookies(response, auth_response['AccessToken'], max_age=15)
return response
return render_template('login.html')
The issue was the public key being set was from a previously deleted cognito pool and needed to be updated to the current one.

Pytest Flask Request Referrer

I'm currently testing my Flask application using Pytest and ran into a problem with a POST request and a redirect. Let me explain a bit more.
A user wants to register for our new site, but must confirm they have an account with a different site. Once they confirm the credentials of the other account, they are taken to the register page. They can only hit the register page if coming from the confirmation page else they are redirected back to the home page.
I want to test this functionality and can successfully make a POST request to the confirmation page. If I don't specify follow_redirects=True and print the response data, I get the following HTML:
Redirecting...
Redirecting...
You should be redirected automatically to target URL: /register?emp_id=1. If not click the link.
Great! Exactly what I'm looking for! I want to be redirected to the registration page.
Now when I do specify follow_redirects=True and print out the response data, I expected the register page HTML to return. The response data instead returns the home page HTML.
I further investigated where the problem was. As I mentioned before, the only way you can hit the registration page is from the confirmation page. I took a look at the request.referrer attribute in the view during the test and it will return None. I attempted setting the Referrer header content in the test's POST request, but to no luck.
Here is the code I'm working with:
views.py
#app.route('/confirm', methods=['GET', 'POST'])
def confirm_bpm():
if current_user.is_authenticated:
return redirect(url_for('index'))
form = BPMLoginForm()
if form.validate_on_submit():
bpm_user = BPMUser.query\
.filter(and_(BPMUser.group_name == form.group_name.data,
BPMUser.user_name == form.username.data,
BPMUser.password == encrypt(form.password.data)))\
.first()
if not bpm_user:
flash('BPM user account does not exist!')
url = url_for('confirm_bpm')
return redirect(url)
if bpm_user.user_level != 3:
flash('Only L3 Users can register through this portal.')
url = url_for('confirm_bpm')
return redirect(url)
url = url_for('register', emp_id=bpm_user.emp_id)
return redirect(url)
return render_template('login/confirm_bpm.html', form=form)
#app.route('/register', methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('index'))
if request.method == 'GET' and\
request.referrer != request.url_root + 'confirm':
return redirect(url_for('index'))
emp_id = request.args.get('emp_id')
emp_id_exists = User.query.filter_by(emp_id=emp_id).first()
if emp_id_exists:
flash('User is already registered!')
return redirect(url_for('login'))
form = RegistrationForm()
if form.validate_on_submit():
new_user = User(login_type=form.login_type.data, login=form.login.data,
emp_id=emp_id)
new_user.set_password(form.password.data)
db.session.add(new_user)
db.session.commit()
flash('Registration successful!')
return redirect(url_for('login'))
return render_template('login/register.html', form=form)
TESTS
base.py
from config import TestConfig
from app import app, db
#pytest.fixture
def client():
"""
Initializes test requests for each individual test. The test request
keeps track of cookies.
"""
app.config.from_object(TestConfig)
client = app.test_client()
ctx = app.app_context()
ctx.push()
yield client
ctx.pop()
def confirm_bpm_login(client, group_name, username, password):
"""
POST to /confirm
"""
return client.post('/confirm', data=dict(
group_name=group_name,
username=username,
password=password,
submit=True
), follow_redirects=True)
test_auth.py
from app import db
from app.models import BPMCompany, BPMEmployee, User, BPMUser
from tests.base import client, db_data, login, confirm_bpm_login
def test_registration_page_from_confirm(client, db_data):
"""
Test registration page by HTTP GET request from "/confirm" url.
Should cause redirect to registration page.
!!! FAILING !!!
Reason: The POST to /confirm will redirect us to /register?emp_id=1,
but it will return the index.html because in the register view,
request.referrer does not recognize the POST is coming from /confirm_bpm
"""
bpm_user = BPMUser.query.filter_by(id=1).first()
rv = confirm_bpm_login(client, bpm_user.group_name,
bpm_user.user_name, 'jsmith01')
assert b'Register' in rv.data
The db_data parameter is a fixture in base.py that just populates the DB with the necessary data for the registration flow to work.
My goal is to test a complete registration flow without having to separate the confirmation and registration into two tests.
The Flask test client does not appear to add the Referer header when it follows redirects.
What you can do is implement your own version of following redirects. Maybe something like this:
def confirm_bpm_login(client, group_name, username, password):
"""
POST to /confirm
"""
response = client.post('/confirm', data=dict(
group_name=group_name,
username=username,
password=password,
submit=True
), follow_redirects=False)
if 300 <= response.status_code < 400:
response = client.get(response.headers['Location'], headers={
"Referer": 'http://localhost/confirm'
})
return response
Please test this, I wrote it from memory and may need some minor adjustments.

In Flask, set a cookie and then re-direct user

It seems like in Flask, cookies are set by modifying the response object directly.
How can I return a response object, but also redirect a user to a different page upon successful login? I'd like to specifically redirect the user instead of rendering a different page, in case the user hits REFRESH.
Here's my current code, which simply displays the same page, login.html:
#app.route('/login', methods=['POST', 'GET'])
def login():
errors = []
if request.method == 'POST':
email = request.form['email']
password = request.form['password']
#Check the user's e-mail
try:
u = User(email)
except UserError, e:
errors.append(e)
else:
#Check the user's password
if not u.authenticatePassword(password):
errors.append(('password','Invalid password'))
return render_template('login.html',error=errors)
#Set the session
s = Session()
s.user_id = u.user_id
s.ip = request.remote_addr
#Try to set the cookie
if s.setSession():
response = make_response( render_template('login.html',error=errors))
response.set_cookie('session_id', s.session_id)
return response
return render_template('login.html',error=errors)
You should change your code to something like:
from flask import make_response
if s.setSession():
response = make_response(redirect('/home'))
response.set_cookie('session_id', s.session_id)
return response

Categories