401 UNAUTHORIZED From Flask RESTAPI - python

I am attempting to log a user in and retrieve an auth token for the user; however, I am getting a 401 UNAUTHORIZED. Researching this issue I keep coming across the "Authorization Header" is incorrect or invalid; however, I am logging the user in and I do not have a valid auth code yet. I cannot understand why CORS will not allow this request through:
Using AXIOS:
export const sendServer = (url, token, method, data, callback) => {
let newUrl = `http://localhost:5000/${url}`
axios({
method: method.toUpperCase(),
url: newUrl,
data: data,
headers: { 'Authorization': token }
})
.then(response => callback({ success: true, data: response.data }))
.catch(error => callback({ success: false, error: error.toJSON() }))
}
On The Flask side:
from flask import Flask
from flask_restful import Api
from flask_cors import CORS
from config import ProdConfig
from auth import Authenticate, check_token
app = Flask(__name__)
app.config.from_object(ProdConfig)
CORS(app)
api = Api(app)
api.add_resource(Authenticate, '/login', endpoint="login")
The '/login' endpoint does not require authentication.
class Authenticate(Resource):
def __init__(self):
self.pwd_context = CryptContext(schemes=["pbkdf2_sha256"],default="pbkdf2_sha256",pbkdf2_sha256__default_rounds=30000)
def post(self):
if request.endpoint == 'login':
.......
Any help would greatly be appreciated.

Related

CSRF verification failed. Request aborted in Django rest framework sending the request from flutter

I've followed everything mentioned in both documentation of Django rest-framework and Flutter http but still getting the error ..here is my code :
Django
Settings
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
]
}
View
#csrf_exempt
#permission_classes(["isAuthenticated"])
#api_view(['POST'])
def chanage_image(request):
data = {}
if request.method == "POST":
token = request.META['HTTP_AUTHORIZATION'][6:]
lang = request.META['HTTP_LANG']
image = request.data['image']
main_user = Token.objects.get(key=token).user
app_user = AppUser.objects.get(main_user=main_user)
format, imgstr = image.split(';base64,')
ext = format.split('/')[-1]
data = ContentFile(base64.b64decode(imgstr), name='temp.' + ext) # You can save this as file instance.
app_user.image = data
app_user.save()
data = {"success": True, "details": AppUserSerializer(
app_user).data, "message": "Image changed" if lang == "en" else "تم تغيير الصورة"}
return Response(data, headers=get_headers())
URLS
path('chanage_image/', chanage_image,name="chanage_image"),
Flutter
Request
Map<String, dynamic> body = {
"image": base64Image,
};
Future<UserModel> changePlayerImage(Map<String, dynamic> body) async {
return await httpClient.post('api/user/change-image',
body: body,
headers: {'referer': 'https://www.l-dawri.com/'}).then((response) {
print(response.body);
return UserModel.fromJson(response.body);
});
}
but still in the end am always getting this error :
CSRF verification failed. Request aborted.
You are seeing this message because this site requires a CSRF cookie when submitting forms.
First you don't sent authorization token into header request while use from drf TokenAuthentication
Also into drf is better you use from class view api(like inheritance from APIView) replace def view's

Axios POST Method: 405 Method Not Allowed

I'm building a webapp with React and Flask and I have an issue with POST request.
This is my app.py file:
import sys
import os
from flask import Flask, jsonify, request, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS, cross_origin
from flask_mail import Mail, Message
from models import User, Project, Image, db
from api import blueprints
from tools import format_email
app = Flask(__name__,
static_folder='../build/static',
template_folder="../build"
)
app.config.from_object(os.environ['APP_SETTINGS'])
db.init_app(app)
cors = CORS(app)
mail = Mail(app)
# Register the blueprints
for b in blueprints:
app.register_blueprint(b)
#cross_origin
#app.route('/', defaults={'u_path': ''})
#app.route('/<path:u_path>')
def index(u_path=None):
return render_template("index.html")
#app.route('/api/process_email', methods=['POST'])
def process_email():
print('plop')
data = request.get_json()
formated_email = format_email(
data['firstname'],
data['lastname'],
data['email'],
data['message']
)
msg = Message(formated_email['title'], sender='plop#gmail.com', recipients=['plop#gmail.com'])
msg.body = formated_email['textbody']
msg.html = formated_email['htmlbody']
mail.send(msg)
return 'done'
if __name__ == "__main__":
app.run()
In the config.py I have this set CORS_HEADERS = 'Content-Type'
When I test this with Postman, my email is sent without any issue. But from the app, I get a 405 METHOD NOT ALLOWED response.
This is how I send the request:
axios.post(API_URL + 'process_email/', {
"firstname": values.firstname,
"lastname": values.lastname,
"email": values.email,
"message": values.message
}, {
headers: {
'Content-Type': 'text/plain;charset=utf-8',
},
withCredentials: 'same-origin'
})
.then(({ data }) => {
console.log(data)
}, (error) => {
toast.error("gneuuuu");
})
.catch(() => {toast.error("Oups! Something went wrong!")});
This is what I have:
Since I put a proxy in place, I don't see preflights request anymore.
I tried with a simple fetch or use superagent but issue is still here, and I clearly don't understand how CORS works. Any help would be appreciated. Thanks!
If anyone needs the answer: issue was in the way I was sending the request with axios. Instead of:
axios.post(API_URL + 'process_email/', {...
I have to remove the trailing / ! This works fine:
axios.post(API_URL + 'process_email', {...

POST requests not working with token validation checks

With flask_jwt_extended, whenever I'm trying to send a POST request with the following decorators:
#jwt_refresh_token_required
#jwt_required
I am having this 401 error:
{
"msg": "Missing CSRF token"
}
When I use a GET instead, it's working fine.
I have read the documentation that talk about double submit protection, but that does not solve my problem. Any ideas how I could fix my issue?
The code to reproduce the problem is below.
Below is the structure of my code:
- src/__init.py__ # where I put all configs
- src/auth.py # where are the endpoints
init.py
login_serializer = URLSafeTimedSerializer(SERIALIZER_SECRET_KEY)
jwt = JWTManager()
def create_app():
app = Flask(__name__)
app.config["SECRET_KEY"] = SERIALIZER_SECRET_KEY
app.config['JWT_SECRET_KEY'] = JWT_SECRET_KEY
app.config['JWT_TOKEN_LOCATION'] = ['cookies']
app.config['JWT_COOKIE_CSRF_PROTECT'] = True
db.init_app(app)
jwt.init_app(app)
# blueprint for auth routes in our app
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint)
# blueprint for non-auth parts of app
from .routes import main as main_blueprint
app.register_blueprint(main_blueprint)
return app
auth.py
import logging
from flask import Blueprint, request, current_app as app, jsonify
from werkzeug.security import generate_password_hash, check_password_hash
from . import login_serializer, jwt
from flask_jwt_extended import (jwt_required, jwt_refresh_token_required,
get_jwt_identity, get_raw_jwt, unset_jwt_cookies,
current_user, create_access_token, create_refresh_token, set_access_cookies, set_refresh_cookies)
auth = Blueprint('auth', __name__)
def set_response_cookies(token_identity, resp=None, token_types=["access", "refresh"]):
"""
Helper function to set cookies in response
"""
logging.warning("Setting cookies")
resp = jsonify(resp)
token_types.sort()
if token_types == ["access", "refresh"]:
access_token = create_access_token(identity = token_identity)
refresh_token = create_refresh_token(identity = token_identity)
if not resp:
resp = jsonify({"access_token": access_token, "refresh_token": refresh_token})
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp
elif token_types == ["access"]:
access_token = create_access_token(identity = token_identity)
if not resp:
resp = jsonify({"access_token": access_token})
set_access_cookies(resp, access_token)
return resp
elif token_types == ["refresh"]:
refresh_token = create_refresh_token(identity = token_identity)
if not resp:
resp = jsonify({"refresh_token": refresh_token})
set_refresh_cookies(resp, refresh_token)
return resp
else:
raise ValueError("Wrong Call to this function")
#jwt.user_claims_loader
def add_claims_to_access_token(identity):
"""
"""
return {
'email': identity
}
#jwt.user_loader_callback_loader
def user_loader_callback(identity):
"""
Ignore Here, but I use it to get a User object (not mentionned here) from a Token.
"""
return User.objects(
email=identity,
).first()
#auth.route('/token', methods=['POST'])
def token_post():
""" obj =
{"email": "email", "password": "password"} => Tokens
"""
obj = request.get_json()
resp = set_response_cookies(obj["email"], {"token": True}, ["access", "refresh"])
return resp, 200
#auth.route('/token/access', methods=['POST'])
#jwt_refresh_token_required
def refresh_access_cookies():
if current_user:
resp = set_response_cookies(current_user.email, {"token_refreshed": True}, ["access"])
return resp, 200
So, here, all I have to do to reproduce the error is:
Make a POST request to /token => In postman, my response will get all cookies and headers.
Make a POST request to /token/access => Give the error mentioned above.
On your configuration, you enabled JWT_COOKIE_CSRF_PROTECT to true.
For devep purpose, the error will be gone if you can set it to False which may not safe.
On production, You need to pass csrf_token on your request header.
I think this links can help you.
https://flask-jwt-extended.readthedocs.io/en/stable/tokens_in_cookies/ (see the last section)
https://flask-wtf.readthedocs.io/en/stable/csrf.html

Flask-WTF - Unable to test a form submission

I would like to test a wtf-form with a crsf token but I don't know how to send the token.
Here is my form :
class myLoginForm(FlaskForm):
username = StringField()
password = StringField()
mySubmit = SubmitField('Save')
Here is my route :
#app.route('/login', methods=['GET', 'POST'])
def login():
loginForm = myLoginForm()
if loginForm.validate_on_submit():
result = request.form
username = result.get("username")
password = result.get("password")
Here is my test :
import unittest
from flask_testing import TestCase
from flask import Flask
import json
class TestLogin(TestCase):
def create_app(self):
app = Flask(__name__)
return app
def test_submission(self):
headers = {
'ContentType': 'application/json',
'dataType': 'json'
}
data = {
'username': 'foo',
'password': 'bar'
}
response = app.test_client().post(
'/login',
data=json.dumps(data),
content_type='application/json',
follow_redirects=True
)
assert self.get_context_variable("loginForm").validate_on_submit() == True
The assertion fails, because the validate_on_submit() returns False. I think it is due to the crsf token.
How can I send the crsf token to the POST request ?
Have a nice day
Maybe this could help you.
Make it easier to access a CSRF token in automated tests
Unless you want to test actual CSRF protection in Flask-WTF it's much simpler to turn off CSRF completely in app config when running unit tests. The conditions that trigger CSRF protection may be easier tested with integration/e2e tests.

How to use TokenAuthentication for API in django-rest-framework

I have a django project, using django-rest-framework to create api.
Want to use token base authentication system so api call for (put, post, delete) will only execute for authorized user.
I installed 'rest_framework.authtoken' and created token for each users.
So, now from django.contrib.auth.backends authenticate, it returns user, with auth_token as attribute. (when loged in successfully).
Now my question is how can I send the token with post request to my api and
at api side how can I verify if token is valid and belongs to the correct user?
Are there any methods in app rest_framework.authtoken to validate given user and its token?
not found this very useful!
Update (changes I made):
Added this in my settings.py:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)
}
Also sending Token in my header but its still not working:
if new_form.is_valid:
payload= {"createNewUser":
{ "users": request.POST["newusers"],
"email": request.POST["newemail"]
}
}
headers = {'content-type' : 'application/json',
'Authorization': 'Token 6b929e47f278068fe6ac8235cda09707a3aa7ba1'}
r = requests.post('http://localhost:8000/api/v1.0/user_list',
data=json.dumps(payload),
headers=headers, verify=False)
"how can I send the token with post request to my api"
From the docs...
For clients to authenticate, the token key should be included in the Authorization HTTP header. The key should be prefixed by the string literal "Token", with whitespace separating the two strings. For example:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
"at api side how can I verify if token is valid and belongs to the correct user?"
You don't need to do anything, just access request.user to return the authenticated user - REST framework will deal with returning a '401 Unauthorized' response to any incorrect authentication.
To answer the first half of your question:
how can I send the token with post request to my api
You can use the Python requests library. For the django-rest-framework TokenAuthentication, the token needs to be passed in the header and prefixed by the string Token (see here):
import requests
mytoken = "4652400bd6c3df8eaa360d26560ab59c81e0a164"
myurl = "http://localhost:8000/api/user_list"
# A get request (json example):
response = requests.get(myurl, headers={'Authorization': 'Token {}'.format(mytoken)})
data = response.json()
# A post request:
data = { < your post data >}
requests.post(myurl, data=data, headers={'Authorization': 'Token {}'.format(mytoken)})
I finally have the django "rest-auth" package working for token authentication.
If this helps, here is the client-side jQuery code that worked for me, after you successfully log in and receive the "auth_token":
var user_url = {API URL}/rest-auth/login
var auth_headers = {
Authorization: 'Token ' + auth_token
}
var user_ajax_obj = {
url : user_url,
dataType : 'json',
headers: auth_headers,
success : function(data) {
console.log('authorized user returned');
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log('error returned from ' + user_url);
}
};
$.ajax(
user_ajax_obj
);
If you are using coreapi. To add the Authorisation you do
import coreapi
auth = coreapi.auth.TokenAuthentication(scheme='Token', token=token_key)
Then you can do
client = coreapi.Client(auth=auth)
response = client.get(my_url)

Categories

Resources