Again fighting trying to make my first flask application, this time, (after I created every I need and all works smoothly) I'm trying to protect some endpoints with flask_jwt_extended, but I can't find how to work with them in my pages, the documentation is mostly about displaying JSON messages and some tutorials use postman while in my case I'm using HTML templates.
For example, a user sends his credentials from the login page to this endpoint :
#app.route('/login', methods=['POST'])
def UserLogin():
data = parser.parse_args()
current_user = UserModel.find_by_username(data['username'])
if not current_user:
return {'message': 'User {} doesn\'t exist'.format(data['username'])}
if UserModel.verify_hash(data['password'], current_user.password):
access_token = create_access_token(identity = data['username'])
refresh_token = create_refresh_token(identity = data['username'])
resp = jsonify({'login': True}) #I just added this line from the documentation
set_access_cookies(resp, access_token) # and this one
set_refresh_cookies(resp, refresh_token) # and this one
return redirect(url_for('results'))
else:
return {'message': 'Wrong credentials'}
and of course, I added the #jwt_required decorator the results endpoint:
#app.route('/result',methods = ['POST','GET'])
#jwt_required
def results():
temp={}
if request.method == 'POST':
# some code to fill temp with values
return render_template('result.html',data=temp)
So I'm getting a {
"msg": "Missing cookie \"access_token_cookie\""
}Obviously because I'm not sending the jwt back but if send it in the return statement how can I redirect the user the page I want ?? And indeed I used app.config['JWT_TOKEN_LOCATION'] = ['cookies']
You may want to:
resp = make_response(redirect(url_for('results')))
set_access_cookies(resp, access_token)
set_refresh_cookies(resp, refresh_token)
return resp
I don't think you need this line! --> resp = jsonify({'login': True})
Took me a while to figure it out, not sure why this part is not clear in the docs, most of the examples there just returns JSON directly
Also, you get same error if JWT_ACCESS_COOKIE_PATH is routed wrongly.
Related
I want my page to reload and show the django messages sent to the page when a POST call is made (by another user).
From api I am calling the method like that:
def create(self, request, pk=None):
json_data = json.loads(request.body)
sample_path = json_data['sample_path']
try:
sample = BloodSample.objects.get(sample_path = sample_path)
if json_data['status'] == 201:
BloodSampleAdmin(sample, BloodSample).display_messages(request, sample)
return Response(json_data['body'], status = status.HTTP_201_CREATED)
and the method in BloodSampleAdmin is:
def display_messages(self, request, sample):
messages.success(request, ("hurray"))
return HttpResponseRedirect(reverse(
"admin:backend_bloodsample_change",
args=[sample.id]
))
I am 100% sure that the method is called (debug). But message wont pop anyway.
I am using Postman to send the POST request.
Any ideas on what is going wrong?
i'm doing my own project.
communicate python program - django server.
first is when program send information about signup(like name, password, id etc.) server return success signal.
next step is when program send login information about sign(like name, password), server return jwt token and program receive jwt token.
I'm try everything what i know... but i don't know how to return jwt token to python program.
any idea?
Assuming you already have a proper way to generate the token correctly:
create an endpoint to login with credentials (note the csrf_exempt to allow POST calls from your program)
path('/login', csrf_exempt(login)) )
create a view to process the request - to protect the credentials, expect them as the payload of a POST request:
#require_POST
def login(request):
username = request.data.get('username', None)
password = request.data.get('password', None)
if username is None:
return HttpResponseBadRequest('username is missing')
if password is None:
return HttpResponseBadRequest('password is missing')
# validate the user/credentials
your_function_to_validate(username, password)
jwt = your_function_to_generate_and_save_the_JWT(username, password)
return HttpResponse(jwt)
call the endpoint using the Python program:
url = base_url + '/login'
credentials = {
'username': 'admin',
'password': '12345'
}
res = post(url, data=credentials)
if res.status_code != 200:
# deal with bad credentials
pass
jwt = res.data
This question already has answers here:
#csrf_exempt does not work on generic view based class
(4 answers)
Closed 3 years ago.
I wrote a test file to check if the URL works or not and it keeps printing Forbidden (CSRF cookie not set.) could please check what's the problem
#post handler
#csrf_exempt
def post(self, request, *args, **kwargs):
valid_json = is_json(request.body)
if not valid_json:
error_data = json.dumps({'message': 'Invalid data sent, please send using JSON format'})
return self.render_to_response(error_data, status=400)
data = json.loads(request.body)
form = SupervisorForm(data)
if form.is_valid():
obj = form.save(commit=True)
obj_data = obj.serialize()
return self.render_to_response(obj_data, status=201)
if form.errors:
data_error = json.dumps(form.errors)
return self.render_to_response(data_error, status=400)
json_data = json.dumps({'message': 'Not Allowed'})
status_code = HTTP_400_BAD_REQUEST
return self.render_to_response(json_data, status_code)
def post():
data = {
'supervisor_name':'name',
'supervisor_phone': '76786875',
'supervisor_email': 'sdsds#sdsd.com',
'supervisor_image': 'path to local image',
}
json_data = json.dumps(data)
json_loads = json.loads(json_data)
print(type(json_data))
print(type(json_loads))
print(help(requests.put))
r = requests.put('http://127.0.0.1:8000/api', json = json.dumps(data))
return r.json()
You have probably configured your Django to use a CSRF token but have not set it up for your API. Are you able to disable CSRF in your configuration? Otherwise, you'd have to set it up in accordance with the documentation
CSRF is important for websites that are at a high risk of getting hacked through scripts/iframes. CSRF is what prevents your bank account from sending money to a hacker via email/popup scripts. Unless you're building a website that has confidential data scoped to the user (e.g. Facebook, Venmo, PayPal) CSRF is not necessary.
I'm trying to allow users to login to my Flask app using their accounts from a separate web service. I can contact the api of this web service and receive a security token. How do I use this token to authenticate users so that they have access to restricted views?
I don't need to save users into my own database. I only want to authenticate them for a session. I believe this can be done using Flask-Security and the #auth_token_required decorator but the documentation is not very detailed and I'm not sure how to implement this.
EDIT:
Here's a code example:
#main.route("/login", methods=["GET", "POST"])
def login():
payload = {"User": "john", "Password": "password123"}
url = "http://webserviceexample/api/login"
headers = {'content-type': 'application/json'})
#login to web service
r = requests.post(url, headers=headers, json=payload)
response = r.json()
if (r.status_code is 200):
token = response['user']['authentication_token']
# allow user into protected view
return render_template("login.html", form=form)
#main.route('/protected')
#auth_token_required
def protected():
return render_template('protected.html')
Hey there Amedrikaner!
It looks like your use-case is simple enough that we can implement this ourselves. In the code below, I'll be storing your token in the users session and checking in a new wrapper. Let's get started by making our own wrapper, I usually just put these in a wrappers.py file but can you can place it where you like.
def require_api_token(func):
#wraps(func)
def check_token(*args, **kwargs):
# Check to see if it's in their session
if 'api_session_token' not in session:
# If it isn't return our access denied message (you can also return a redirect or render_template)
return Response("Access denied")
# Otherwise just send them where they wanted to go
return func(*args, **kwargs)
return check_token
Cool!
Now we've got our wrapper implemented we can just save their token to the session. Super simple. Let's modify your function...
#main.route("/login", methods=["GET", "POST"])
def login():
payload = {"User": "john", "Password": "password123"}
url = "http://webserviceexample/api/login"
headers = {'content-type': 'application/json'})
#login to web service
r = requests.post(url, headers=headers, json=payload)
response = r.json()
if (r.status_code is 200):
token = response['user']['authentication_token']
# Move the import to the top of your file!
from flask import session
# Put it in the session
session['api_session_token'] = token
# allow user into protected view
return render_template("login.html", form=form)
Now you can check the protected views using the #require_api_token wrapper, like this...
#main.route('/super_secret')
#require_api_token
def super_secret():
return "Sssshhh, this is a secret"
EDIT
Woah! I forgot to mention you need to set your SECRET_KEY in your apps config.
Just a config.py file with SECRET_KEY="SOME_RANDOM_STRING" will do. Then load it with...
main.config.from_object(config)
I have an endpoint that does some logic and a flash()and then redirects to another endpoint, which adds another flash and displays both flashed messages. When I test the endpoint in my browser, I get both messages. However, when I do my unittest, the first flash doesn't show up.
#bp.route('/signup/', methods=['POST'])
def signup():
form = SignupForm(prefix='signup')
next_url = url_for('.home')
if form.validate_on_submit():
# do stuff like add to waiting list
person = persons.new()
form.populate_obj(person)
person = persons.save(person)
flash(Markup(u'Thanks for signing up!'), 'success')
return redirect(next_url)
#bp.route('/')
def home():
flash('This is home', 'info')
return render_template('home.html')
class PageTests(MTestCase):
def test_signup(self):
r = self.post('/signup/',
data={
'signup-email': 'test1#test.com',
})
person = persons.find(email='test1#test.com').first()
self.assertIsNotNone(person)
self.assertIn('Thanks for signing up', r.data)
I'm guessing that during the redirect the flash queue is lost, but I'm not entirely sure how or why.
Add follow_redirects=True argument because login page redirects.
r = self.post(
'/signup/',
data={
'signup-email': 'test1#test.com',
},
follow_redirects=True # <----
)
See Logging in and out - Testing Flask Application.