JWT and custom authentication in Django Rest Framework - python

I have written a very basic custom authentication class in order to implement the simple JWT library for my custom authentication needs.
I generate tokens manually and then send the access token to my API. By default, this would be enough but since I do not use the default Django user Model, I get "User not found". This is because I need to implement a custom authentication backend.
I need to read that token in order to query the database with the given user id and check if that token is valid as well. In my example, I have fixed number 2.
class ExampleAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
try:
user = vAuthUser.objects.get(pk=2) #this should receive the user_id from the token
except:
raise AuthenticationFailed('No such user')
return (user, None)
My API looks like:
class MyAPI(APIView):
authentication_classes = (ExampleAuthentication,)
permission_classes = (IsAuthenticated ,)
def get()...

Try this:
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth.models import User
user = User.objects.first()
refresh = RefreshToken.for_user(user)
raw_token = str(refresh.access_token)
jwt_a = JWTAuthentication()
validated_token = jwt_a.get_validated_token(raw_token)
repr(validated_token)
print(validated_token["user_id"])
Here I feed a raw access token to get_validated_token() method of JWTAuthentication class. it returns a dictionary with these keys: token_type, jti, user_id, exp, and their associated values.
I've used the default Django User Model for my sample, but it should work with your custom Model.
There are more useful methods like get_header(). check this document and this one too.

Related

Accessing to Odoo data with Django REST API

I'm trying to build a Django REST API e-commerce by retrieving data from Odoo, for that I need first of all to connect to odoo database.
Any idea of how to do that !?
PS: with XML-RPC it was easy but I want to to work with REST
Thanks a lot.
Odoo is usually extended internally via modules, but many of its features and all of its data are also available from the outside for external analysis or integration with various tools. Part of the Models API is easily available over XML-RPC and accessible from a variety of languages.
Read more : https://www.odoo.com/documentation/14.0/webservices/odoo.html
Example of XML-RPC by using Django rest framework:
import xmlrpclib
from rest_framework.decorators import api_view
from rest_framework.response import Response
db = '<DB-NAME>'
url = '<your-host-name:8069>'
models = xmlrpclib.ServerProxy('{}/xmlrpc/2/object'.format(url))
common = xmlrpclib.ServerProxy('{}/xmlrpc/2/common'.format(url))
def get_uid():
username = '<your-odoo-username>'
password = '<your-odoo-password>'
uid = common.authenticate(db, username, password, {})
return uid, password
# create default execute method (Odoo Xmlrpc)
def execute(*args):
uid = get_uid()[0]
password = get_uid()[1]
return models.execute_kw(db, uid, password, *args)
# Rest Api for get contact list of odoo with name only
#api_view(['GET'])
def get_contacts_list(request):
contacts = execute('res.partner', 'search_read', [[]], {'fields':['name']})
return Response({'result': contacts, 'status_code': 200})

Store more than default information in django-rest-framework-jwt

I am using Django version 1.8 and authentication with django-rest-framework-jwt.
After authentication, our application will return to front-end with information:
from rest_framework_jwt.settings import api_settings
from rest_framework.response import Response
from django.contrib.auth.models import User
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
user.profile_picture = "test_profile_picture.jpg" #user is django.contrib.auth.models.User object
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
jwt_login_response = {
"token": token
}
return Response(jwt_login_response,status=status.HTTP_200_OK)
In front-end, we will decode by
import jwtDecode from 'jwt-decode';
var decodedToken = jwtDecode(token);
But at the moment decodedToken only contains value which seem default by django-rest-framework-jwt
Object {username: "test", user_id: 9, email: "test#gmail.com", exp: 1454754137}
I want to store more information in jwt like profile_picture but I don't know how. I really want to find a solution about this problem.
You can override the payload that is attached to the token with the JWT_PAYLOAD_HANDLER setting.
The default implementation includes the username, user_id, email, and exp (expiration time), as you've noticed. You can find the default implementation on GitHub.
I think what you need is a custom user model. You can create this by creating a new Model with the fields you need and reference it to the Default User model via a One-One relationship.
This link might help - http://blog.mathandpencil.com/replacing-django-custom-user-models-in-an-existing-application/

How to do Django JSON Web Token Authentication without forcing the user to re-type their password?

My Django application uses the Rest Framework JWT for authentication. It works great and very elegant.
However, I have a use-case which I am struggling to build. I have already coded up a working solution for the "Forgot Password" workflow. I allow an un-authenticated user to reset their password if-and-only-if they click on a secret link that I send to their email address. However, I would like to modify this solution such that after the password-reset workflow is successfully completed, the user is automatically logged in without having to retype their username and (new) password. I would like to do this to make the user's experience as frictionless as possible.
The problem is I do not know how to make this work without having the user re-type their password (or storing it in clear-text in the DB which is obviously very bad). Below is the current way I get the JWT token. You can see that in line #12, I need the user's clear password. I don't have it. I only have the encrypted password stored in my_user.password.
How can I use the encrypted password in my_user.password instead of the clear password to obtain the JWT? If I cannot use it, then how is this workflow achieved using the Rest Framework JWT?
from rest_framework_jwt.views import ObtainJSONWebToken
from rest_framework status
from django.contrib.auth.models import User
my_user = User.objects.get(pk=1)
ojwt = ObtainJSONWebToken()
if "_mutable" in dir(request.DATA):
mutable = request.DATA._mutable
request.DATA._mutable = True
request.DATA['username'] = my_user.username
request.DATA['password'] = "<my_user's clear password>"
if "_mutable" in dir(request.DATA):
request.DATA._mutable = mutable
token_response = ojwt.post(request)
if status.is_success(token_response.status_code):
# Tell the user login succeeded!!
else:
# Tell the user login failed.
# But hopefully, this shouldn't happen
When working with Django REST Framework JWT, it is typically expected that the user is generating the token on their own. Because you are generating the token on behalf of the user, you can't use any of the standard views to make it work.
You are going to need to generate the token on your own, similar to how DRF JWT does it in the views. This means using something like the following for your view code
from rest_framework_jwt.settings import api_settings
from datetime import datetime
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
my_user = User.objects.get(pk=1) # replace with your existing logic
payload = jwt_payload_handler(my_user)
# Include original issued at time for a brand new token,
# to allow token refresh
if api_settings.JWT_ALLOW_REFRESH:
payload['orig_iat'] = timegm(
datetime.utcnow().utctimetuple()
)
return {
'token': jwt_encode_handler(payload)
}
This should allow you to manually generate the token within the view, without having to know the user's password.

tastypie - where to restrict fields that may be updated by PATCH?

I have a working GET / tastypie (read-only) solution.
I've allowed PUT/PATCH requests and been successful in PATCHING a record.
However I want to limit PATCH capability to only certain fields, on appropriate modelresources, for (already) authenticated and authorised users. I still want users to be able to GET (see) all fields.
Where is the best place (method?) to achieve this sort of restriction?
Docs:
https://django-tastypie.readthedocs.org/en/latest/interacting.html?highlight=patch#partially-updating-an-existing-resource-patch
A bit late but maybe this will help somebody.
My solution was to override update_in_place and check for the data passed.
from tastypie.resources import ModelResource
from tastypie.exceptions import BadRequest
class MyResource(ModelResource):
class Meta:
...
allowed_update_fields = ['field1', 'field2']
def update_in_place(self, request, original_bundle, new_data):
if set(new_data.keys()) - set(self._meta.allowed_update_fields):
raise BadRequest(
'Only update on %s allowed' % ', '.join(
self._meta.allowed_update_fields
)
)
return super(MyResource, self).update_in_place(
request, original_bundle, new_data
)
Since you seem to have authorization for users in place already, you should be able to implement this by adding to the Meta class in your ModelResource. For example, using the DjangoAuthorization (from tastypie docs):
from tastypie.authentication import BasicAuthentication
from tastypie.authorization import DjangoAuthorization
...
class SomeResource(ModelResource):
...
class Meta:
...
authentication = BasicAuthentication()
authorization = DjangoAuthorization()
This example would give you user authorization for actions as defined in django.contrib.auth.models.Permission.
I also had this from the tastypie Google Group. It uses the dehydrate method. Here is the example provided in the Google Groups link:
def dehydrate(self, bundle):
bundle = super(self, MyResource).dehydrate(bundle)
# exclude the restricted field for users w/o the permission foo
if not bundle.request.user.has_perm('app.foo'):
del bundle.data['restricted']
return bundle

Google App Engine (Python) : use UserProperty with Webapp2 User Model

i have an application where we allow users to use Oauth2 for authentication and even Custom User Registrations. All the Users are saved into the default User entity in the datastore. If the user is logging in using Oauth2 for the first time a new record in the default User entity is created like this:
"""Check if user is already logged in"""
if self.logged_in:
logging.info('User Already Logged In. Updating User Login Information')
u = self.current_user
u.auth_ids.append(auth_id)
u.populate(**self._to_user_model_attrs(data, self.USER_ATTRS[provider]))
u.put()
else:
"""Create a New User"""
logging.info('Creating a New User')
ok, user = self.auth.store.user_model.create_user(auth_id, **self._to_user_model_attrs(data, self.USER_ATTRS[provider]))
if ok:
self.auth.set_session(
self.auth.store.user_to_dict(user)
)
self.redirect(continue_url)
for custom registrations records are inserted through the following handler.
class RegistrationHandler(TemplateHandler, SimpleAuthHandler):
def get(self):
self.render('register.html')
def post(self):
"""Process registration form."""
user = 'appname:%s' % self.request.get('email')
name = '%s %s' % (self.request.get('first_name'), self.request.get('last_name'))
password = self.request.get('password')
avatar = self.request.get('avatar')
act_url = user_activation.Activate(self.request.get('first_name'), self.request.get('email'))
ok, user = User.create_user(auth_id=user, name=name, password_raw=password, email=self.request.get('email'))
if ok:
self.auth.set_session(self.auth.store.user_to_dict(user))
acc = models.Account(display_name=self.request.get('first_name'), act_url=act_url, act_key=act_url.split('activate/')[1], user=users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email))
acc.put()
if avatar:
avt = models.Picture(is_avatar=True, is_approved=True, image=avatar, user=users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email))
avt.put()
self.redirect('/')
Now we are using webapp2_extras.sessions for session handling. We have different models like, Comments, Images, Reviews etc in which we want to use db.UserProperty() as the author field. However, the author field shows blank or None whenever we enter a record into any of these models using 'users.get_current_user()'. I think this is because we are handling the sessions through webapp2 sessions.
What we want to achieve is to be able to use the db.UserProperty field in various models and link appropriately to the current user using webapp2 sessions ?
the UserProperty() has to be passed with a User Object in order for it to properly insert the records. Even though we are able to enter the records using the following code :
user = users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email)
or
user = users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).name)
but then we are not able to get the whole user object by referencing to model.author
Any ideas how we should achieve this ?
OAuth 2.0 is not currently supported by Users service. Supported options are
Google Accounts
OpenId
OAuth 1.0
I don't frankly understand what you're trying to accomplish with introducing db.User in to the codebase. Given there's self.current_user, I assume you're already handling authentication process.
When you do self.auth.store.user_model.create_user - that already gives you a webapp2's user object/entity (it has nothing to do with db.User though). I believe that's what you'll have to use as your author field given OAuth 2.0 constraint.
users.get_current_user() relies on a special cookie (App Engine internal). In fact, it has nothing to do with webapp2's session (or any other "custom" session for that matter). You could hack it by setting the cookie to a value that App Engine internals can understand and be tricked as if a user were logged in with one of the methods I mentioned, but I wouldn't recommend this approach. It is not documented (cookie name, format, etc.) and might be changed at any time.
Instead of using UserProperty to store references to the webapp2 user objects, you should instead store the auth_id as a StringProperty and add a convenience method for fetching the corresponding webapp2 user entity.
Something like this
from webapp2_extras.appengine.auth.models import User
class Comment(db.model):
text = db.StringProperty()
author = db.StringProperty()
def get_author(self):
return User.get_by_auth_id(self.author)

Categories

Resources