AttributeError with django-rest-auth and django-rest-knox - Token Serializer - django

I am currently setting up a django backend for a React frontend app.
As part of of, I am using django-rest-auth and django-allauth for registration and authentication. I wanted to implement better token management through django-rest-knox.
I used the following code to start from and modify my existing app with rest-auth and allauth (which works):
https://gist.github.com/AndrewPix/cdd9276b1d5683459b965d5cc4517b26
My problem is the following:
When I POST my credentials to the url /rest-auth/login, in the backend a token gets created for the right user, with all the attributes (e.g. expiry date), but I don't get the token and user returned as part of the API call. Instead I receive the below error:
AttributeError at /rest-auth/login/
Got AttributeError when attempting to get a value for field token on serializer KnoxSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the str instance.
Original exception text was: 'str' object has no attribute 'token'.
Any help would be greatly appreciated. I think something is wrong with the KnoxSerializer but I can't figure out what?
Thanks!
AM

The fix came from the urls! Very simple late night mistakes. The URL path to KnoxLoginView was put after the inclusion of djang-rest-auth urls, instead of before.
By registering the URL url(r'^rest-auth/login/', views.KnoxLoginView.as_view()) , and including that before the line url(r'^rest-auth/', include('rest_auth.urls')) , it works.

Related

#JWT_Required decorator Exception Handling

I am using an auth API using JWT and it works great.
This API is being used to authorize users for my web app. For this to work, I store JWT access_tokens as cookie manually with Flask.
I secure my resource with #JWT_required decorator and if I try to access a secure resource with a valid token everything works fine.
However, if the access token is missing or invalid/expired I get a JSON saying:
{
"message": "Missing cookie \"access_token_cookie\""
}
This is obvious the right message but rather then showing a JSON I want to redirect to the appropriate statuscode error page that is provided by Flask - in this case 401.
I have tried adding error handling for Flask and JWT Manager
Custom decorator, although I have played only poorly with this as I believe there has to be solution within FLASK-JWT-extended
#app.route('/dashbord')
#jwt_required
def dashbord():
return render_template('dashbord.html', title='Home')
My goal is to redirect to appropriate error page 404, 403, 401 if anything is wrong with the access token.
THE SOLUTION:
#jwt.unauthorized_loader
def my_invalid_token_callback(expired_token):
return render_template('401.html', title='Home')
Here's the solution Benjo posted at the bottom of his question:
#jwt.unauthorized_loader
def my_invalid_token_callback(expired_token):
return render_template('401.html', title='Home')
Here is the documentation for changing the results for invalid tokens: https://flask-jwt-extended.readthedocs.io/en/stable/changing_default_behavior.html#changing-callback-functions

Manually validate flask-extended-jwt's access token

I have a SPA app that contains an form with an upload file field. I have a rest API whose endpoints are protected via flask-extended-jwt JWT. To authenticate the REST endpoints I use #jwt_required. I want to authenticate the upload request as well.
Because of the client side I can't add an Authorization Bearer header so I thought to add the access token as a hidden field when submitting the form.
What is the best way to manually validate the JWT access token after I read it from the form?
class Upload(Resource):
def post(self):
#TODO: check for access token
access_token = None
if 'access_token' in request.form and request.form['access_token']:
access_token = request.form['access_token']
else:
message = json.dumps({'message': 'Invalid or missing token', 'success': False})
return Response(response=message, status=401, mimetype='text/plain')
if access_token:
#TODO: validate_token(access_token)
Thank you
Author of flask-jwt-extended here. That's a great question. There is currently no supported way to do that in the extension, the grabbing the token from the request and decoding it are tightly coupled together. This would be hard to de-couple because there is a lot of conditional things that are going on when the full decode chain runs. For example, checking the CSRF value only if the request is sent in via a cookie, or differentiating between an access and refresh token for the sake of the blacklisting feature.
A generalized function could be created, it's signature would look something like decode_and_verify_jwt(encoded_token, is_access_token=True, check_csrf=False). However, this would complicate the rest of the code in flask_jwt_extended and be a rather confusing function to use for the general case.
I think in this case it would be easier just to add a fourth lookup in the extension, so you could use something like:
app.config['JWT_TOKEN_LOCATION'] = ['headers', 'forms']
app.config['JWT_FORM_KEY'] = 'access_token'
# Use the rest of the application normally
If you want to make a ticket on the github page so I can track this, I would be happy to work on it.

Flask helper method to find user from header

I am playing around with Flask. I like that it is fairly thin and works for most of my requirements.
I would like to know what is your recommended way of retrieving the current logged in user. I would like every HTTP request, which is made, to pass/carry a token in the header, which is first retrieved by the login api
/user/login (params: username, password)
# returns {success: True, token: "<some-unique-string>"
Now is the subsequent APIs I would like to get the user object from the from passed token, like so
#app.route("/user/info", methods = ["GET"])
#apify
def user_get_info():
return {"name": current_user().name}
How could I have current_user read from the header without having to pass the request object every time ?
Any thoughts?
Every token is stored against a user. So you can make a query to get a user against a given token. Something like:
token = Token.query.get(token='token_value')
return jsonify({'name: User.query.get(id=token.user_id).name})
This can be done in one line as well but it totally depends upon your models and relationships.
Hope that helps. If I haven't understood your question correctly, do elaborate a little.

django rest framework nested routes

I try to implement a REST API with DRF, and want to do the following:
/profile/ returns serialized representation of logged in user
/profile/keys/ returns some other info, also about logged in user
API root should look like this:
/profile/
/some-other-stuff/
where profile expands to
/
/keys/
Is there a way to expand /profile/ to the picture mentioned above without including /keys/ into response for /profile/ or at least to the following:
/profile/
/profile/keys/

Django-social-auth google oauth token usage

I'm using Django-socila-auth plugin. It uses google API for Oauth 1.0 Authentication. Question is have anybody used it with google python API (gdata). I mean how to apply auth session_token, stored in django-social-auth model to my api call.
Can you help me with code to get this token from model and apply to gdata.PhotoService() instance. For now it is like this:
#getting model instance from django-social-auth model
association = Association.objects.get(user=request.user)
google_session_token=association.handle
google_secret=association.secret
#token string from django-social-auth
#model Association field "handle" looks like:
#google_session_token = '.......XG84PjwytqJkvr8WQhDxm1w-JplWK5zPndSHB13f.........'
gd_client = gdata.photos.service.PhotosService()
gd_client.debug = 'true'
gd_client.auth_token = google_session_token
#image.image is a file field, but problem not in this.
#it tries to send file in debug text.
#It just recieves 403 unauthorised callback.
photo = gd_client.InsertPhotoSimple(
'/data/feed/api/user/default/albumid/default', 'New Photo',
'Uploaded using the API', image.image, content_type='image/jpeg')
I'm recieving error
403 Invalid token string.
I understand that it needs secret too but how to apply it to API for auth?(To receive authorization to post photos.). BTW I added Picassa feed URL, as an option string for social-auth to ask permissions, so token I have asks for Picassa feed permissions when authorizing with google.
BTW. Google tutorial I've used is: here
I understand it's Oauth 1.0 rather than AusSub, but question is:
how to authenticate with token and secret I have and post a photo with this permission?
Just to answer my own problem. I used wrong way to do it, because problem in 'gd_client' and AuthSub.
It must check token on server. And it can not do it on localhost. You need to look ahead to Oauth/Oauth2 for better debugging and so on... No matter that it is much complex than AuthSub