how to use django REST JWT authorization and authentication in class based views - authentication

I am using JWT authentication I am using this type of authorization app wide.
I am trying to figure out how to ue it in a view.
Example. Say I only want to allow a user to create an approved venue if they have the correct permissions. What would I add to this view to get access to the user?
I know that django has request.user but how do I turn that on? Is it always on and request.user is null if there is no token passed into the header? Or is it middleware? The problem I am ultimately having is there is a lot of info getting to this point, but very little on actually using the JWT on the view.
please help.
# for creating an approved venue add ons later
class CreateApprovedVenue(CreateAPIView):
queryset = Venue.objects.all()
serializer_class = VenueSerializer
Django rest framework jwt docs
https://jpadilla.github.io/django-rest-framework-jwt/
Django rest framework permissions docs
http://www.django-rest-framework.org/api-guide/permissions/
so I discovered this resource and looking at it now.
https://code.tutsplus.com/tutorials/how-to-authenticate-with-jwt-in-django--cms-30460
This example is sheading light:
# users/views.py
class CreateUserAPIView(APIView):
# Allow any user (authenticated or not) to access this url
permission_classes = (AllowAny,)
def post(self, request):
user = request.data
serializer = UserSerializer(data=user)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

To use JWT authentication, you need to do the following installation steps: https://jpadilla.github.io/django-rest-framework-jwt/#installation
Once that is done, you can include the auth by simply adding the authentication_classes as follows
# for creating an approved venue add ons later
class CreateApprovedVenue(CreateAPIView):
authentication_classes = (JSONWebTokenAuthentication, )
queryset = Venue.objects.all()
serializer_class = VenueSerializer
And you have user available to you as request.user in all the request methods. In the case of the CreateAPIView you can do:
def post(self, request, *args, **kwargs):
user = request.user
...

Related

Redirect User to original URL after Google login in Flask

I have implemented flask-dance and authlib Flask client for Google sign-in, one answer was unclear in all implementations were how to redirect a user to original user once they login. For example, a flow I wanted => clicks on /results checks if not logged in redirect to login makes them login and then again redirects back to results with logged in session.
I saw some answers using state and kwargs addition but didn't see any clear answer or a pseudo code implementation.
If you have implemented such a scenario please answer the question it will help a lot or you can also reference to your Github projects if implemented
The simple solution i discovered to my own problem was in any implementation for any such library use a session variable to record orignal url and then redirect user after login using that variable so in here i have used next param variable which stores it temp then once authorized sends user to orignal url what they asked for
see the code below
#app.route('/login')
def login():
google = oauth.create_client('google')
redirect_uri = url_for('authorize', _external=True)
return google.authorize_redirect(redirect_uri)
#app.route("/dashboard")
def protect():
if not session.get('profile'):
session['next']='/dashboard'
return redirect('/login')
if session['profile']:
#load dashboard
else:
return "forbidden"
#app.route('/authorize')
def authorize():
google = oauth.create_client('google')
token = google.authorize_access_token()
resp = google.get('userinfo')
user_info = resp.json()
user = oauth.google.userinfo()
session['profile'] = user_info
session.permanent = True
redirecti=session.get("next",None)
return redirect(redirecti)

Flask JWT to SQLAlchemy User Object?

I have an app where the user details are passed as a JWT containing information about the current user and it's roles.
Everytime the user is logged in (via a KeyCloak instance), the information from the JWT is parsed on my end in a function that updates the user object via SQLAlchemy. However, since there is no user object being passed back and forth in the backend, I have to parse the JWT for roles for every action that requires it. I also have a need for auditing, and due to the structure of the app, this module does not necessarily have access to the request objects at the time of logging.
I'm looking for a neat way to make something like flask_users current_user() functionality work by mapping JWT -> ORM user object, to be able to transparently get the current user. Is there any way to go about this? The user registration and so on is completely separate from the app, and Flask only knows which user it is based on tokens in the requests that are being sent.
TLDR; Is there a way to load a user from the DB based on an already issued JWT (which contains information to map to a user), and is there perhaps already a lib or extension to flask that supports this?
I use a decorator to parse the JWT token using pyjwt.
Then from the parsed token you can get the user and do the proper authorization.
If you don't want to add the decorator to all your functions that require authorization you can use Flasks before_request.
from functools import wraps
from flask import Response, current_app, request
from jwt import decode
from jwt.exceptions import (DecodeError, ExpiredSignatureError,
InvalidSignatureError)
def authorize(func):
#wraps(func)
def check_authorization(*args, **kwargs):
try:
jwt_token = request.cookies.get('auth_token') # get token from request
if jwt_token is None:
return Response(status=401, headers={'WWW-Authenticate': 'Bearer'})
token = decode(
jwt_token,
key='pub_key', # public key to validate key
algorithms=['RS256'], # list of algs the key could be signed
verify=True
)
# you can call another function to do check user roles here
# e.g authorize(token['sub'])
return func(*args, **kwargs)
except (InvalidSignatureError, DecodeError, ExpiredSignatureError):
return Response(
response='{ "error": "token_invalid"}',
status=401,
headers={'WWW-Authenticate': 'Bearer'})
return check_authorization
This is supported with flask-jwt-extended: https://flask-jwt-extended.readthedocs.io/en/stable/complex_objects_from_token/

What api authentication method should be used for server to server communication?

I have a webapp in flask where users can login with email and password. Connected to the same database I have an api where those same users will use it programmatically. When they make requests I need to know who's making the request.
I read about athentication and authorization, but I'am confused about what's the best method for my use case. I focused on JWT tokens but the expiration of the tokens makes me think it's not the best in this scenario.
How should the server login programmatically when the token expires and so on? Is there a common way to do what I pretend?
Use jwt to auth api(i use Flask-JWT-Extended)
example:
def login_required(func):
#wraps(func)
def decorate(*args, **kwargs):
verify_refresh_token()
identify = get_jwt_identity()
expires_time = datetime.fromtimestamp(get_raw_jwt().get('exp'))
remaining = expires_time - datetime.now()
# auto refresh token if token expiring soon
refresh_space = current_app.config['JWT_MIN_REFRESH_SPACE']
# store the token in requests.g object
if refresh_space and remaining < refresh_space:
g.refresh_token = create_refresh_token(identity=identify)
g.id = identify
return func(*args, **kwargs)
return decorate
#login_required
def view_func():
pass
# return json
When token will expire, the func will auto refresh token, you can get new token in requests.g object and then return to user.
quote
quote

Flask - How to require login only for post requests

I'm working in Flask and I want to allow users to enter information into a form without logging in but be required to login if they submit the form. After logging in, it should be as though a user just submitted the form (they shouldn't have to re-enter any information).
To store their information, I've used sessions like this. It works well:
if request.method == "POST":
if "arg1" not in session.keys() and "arg2" not in session.keys():
session["arg1"] = request.form.get('arg1')
session["arg2"] = request.form.get('arg2')
However, I'm having trouble with the login required part. I know I can use #login_required on the whole route but I just want #login_required to apply if the request is a post method. I've tried simply adding #login_required after checking if the method is a post request but it doesn't work.
My login route looks like this:
#app.route("/login", methods = ["POST", "GET"])
def login():
#log user in
return redirect(request.args.get("next") or url_for('index'))
It seems as though I need two things.
1: To apply #login_required solely to a post request.
2: To have request.args.get("next") call a post request, not get request
How could I go about doing these two things and achieve my goal?
Thank you!
Break out your routes. 1 for GET and 1 for POST
#app.route("/login", methods = ["GET"])
def get_login():
return stuff
#app.route("/login", methods = ["POST"])
#login_required
def post_login():
return stuff
There are a couple patterns that could be used here but this one is the most straight forward.

Django-Rest-Framework: How to Document GET-less Endpoint?

My co-worker implemented an API that only allows GET requests with an ID parameter (so I can GET /foo/5 but can't GET /foo/). If I try to access the API's endpoint without providing an ID parameter, it (correctly) throws an unimplemented exception.
I want to fix this endpoint to show its documentation when viewed, without an ID, over the web. However, I still want it to throw an exception when that endpoint is accessed programatically.
As I remember it, django-rest-framework is capable of distinguishing those two cases (via request headers), but I'm not sure how to define the endpoint such that it returns either documentation HTML or an exception as appropriate.
Can anyone help provide the pattern for this?
Based on the description, I would guess that the endpoint is a function based view, which is registered on a route where it listens for get requests WITH parameters. I would suggest to register another route where you will listen for get requests without parameters...
from rest_framework.decorators import api_view
from rest_framework import status
#api_view(['GET'])
def existing_get_item_api(request, item_id, *args, **kwargs):
# query and return the item here ...
pass
#api_view(['GET'])
def get_help(request, *args, **kwargs):
# compose the help
return Response(data=help, status = status.HTTP_200_OK)
# somewhere in urls.py
urlpatterns = [
url(r'api/items/(?P<item_id>[0-9]+)/', existing_get_item_api),
url(r'api/items/', get_help),
]
Let me know how is this working out for you.
We can user modelviewsets and routers for this implementation
viewsets.py
class AccountViewSet(viewsets.ModelViewSet):
"""
A simple ViewSet for viewing and editing accounts.
"""
http_method_names = ['GET']
queryset = Account.objects.all()
serializer_class = AccountSerializer
routers.py
from rest_framework import routers
router = routers.SimpleRouter()
router.register(r'accounts', AccountViewSet)