Django Oauth2 Toolkit read access token from request body not headers - django

I have setup an OAuth2 provider using Django Oauth Toolkit, and it works fine. The issue is that the Client which is making requests to my API does not pass the access token in the headers (No "AUTHORIZATION : BEARER XXXXXXX"). Instead, the access token is passed in JSON data.
How can I change the toolkit's behaviour to read the access token from the data ?

I found a way to satisfy OAuth toolkit's requirements by writing my own Django Middleware.
import json
class TokenMiddlewareFix(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
try:
body = json.loads(request.body)
token = body.get("accessToken", None)
if token is not None:
request.META["HTTP_AUTHORIZATION"] = "Bearer {}".format(token)
except ValueError:
pass
response = self.get_response(request)
return response

Related

How to add token in response after registering user - DRF Social OAuth2

I am using DRF Social OAuth2 for social authentication, it's giving me the token when I log in, but I want to return the token in response when I register a user with an email and password. How can I do that
We would need to see your endpoint in order to answer the question better. Here is a suggestion if you are using token auth.
from rest_framework.authtoken.models import Token
def get_token_response(user):
token, _ = Token.objects.get_or_create(user=user)
response = {"token": "Token " + str(token)}
return response
And then your endpoint would look something like this (if you are using a viewset):
class UserViewSet(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
response = super().create(request, *args, **kwargs)
user = User.objects.get(id=response.data["id"])
return Response(get_token_response(user), status=201)
My point is that you need to get the token from the database and adjust your create user endpoint (aka registration) to return it.
Hope this helps.

Django's Custom Authentication Middleware & Authentication Backend

I'm writing a custom Authentication middleware that check the incoming requests for the "Authorization" key in the header, which contains a token.
I'm using this token to check with a third-party (Microsoft Graph) for the validity of the user. MS Graph will respond with an object like below
# the response object
{
'#odata.context': 'https://graph.microsoft.com/v1.0/$metadata#users/$entity',
'businessPhones': ['xxx'],
'displayName': 'xxx',
'givenName': 'xxx',
'id': 'xxx',
'jobTitle': None,
'mail': 'xxx',
'mobilePhone': None,
'officeLocation': None,
'preferredLanguage': 'xxx',
'surname': 'xxx',
'userPrincipalName': 'xxx'
}
EDIT: Adding custom middleware code here:
class AuthenticationMiddleware(MiddlewareMixin):
if not request.user.is_authenticated:
if "Authorization" in request.headers:
# Make a request to MS Graph with the given token
# to get user details and append to request
token = request.headers["Authorization"]
elif "accessToken" in request.GET:
token = request.GET["accessToken"]
else:
token = None
if token:
url = "https://graph.microsoft.com/v1.0/me/"
payload = {}
headers = {"Authorization": "Bearer {}".format(token)}
response = requests.request("GET", url, headers=headers, data=payload)
if response.ok:
request.custom_user = response.json()
else:
request.custom_user = AnonymousUser
else:
request.custom_user = AnonymousUser
Now I want to design this to work just like Django's default authentication backend with proper group and permission. How can I work on a LazyObject to be able to check for user's group membership and permission?
UPDATE
It looks like there's also a custom backend authentication that works like this.
Is it doing the same thing as I'm doing with the middleware?
from django.contrib.auth.backends import BaseBackend
class MyBackend(BaseBackend):
def authenticate(self, request, token=None):
# Check the token and return a user.
...
you should custom an Middleware like the below, and add it to middlewares in settings
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# todo: do something you want in response
return response
see also:https://docs.djangoproject.com/en/3.1/topics/http/middleware/
EDIT:
Is it doing the same thing as I'm doing with the middleware?
no, it's not.
the most different is that
The backend is used for connecting with database, and the middleware is aim to process the request. You can find more examples code in django.middleware package.
and if you want to custome how to save the infomation to database eg: customer the authenticate method, you should customer a backend for the work. otherwise you may custome an middleware to process all of the requests. Because it is easy to customize middleware.

JWT authentication for django rest framework

I have a very interesting issue. We have built couple of apis using django rest framework. These apis are consumed by frontend designed using React. We use SAML withe sessions to manage the authentication process.This SAML authentication is handled by a django middleware.
Now I have a requirement to make some of these apis accessible to a different domain, where their backend would call our apis programmatically. So I need to give auth details to them for them to able to access it.
Since SAML is enabled here, how do I able to set the authentication using JWT and give them the tokens?
The saml middleware code looks like this,
class SamlMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if hasattr(request, 'user') and request.user.is_anonymous and 'samlUserdata' in request.session:
data = request.session['samlUserdata']['mail'][0]
if data is not None:
try:
email=data
user = User.objects.get(email=email)
except ObjectDoesNotExist:
user = User.objects.create_user(username=data.split("#")[0],
email=data)
login(request,user)
request.session.set_expiry(settings.SESSION_EXPIRY)
elif not settings.DEBUG and 'samlUserdata' not in request.session \
and not request.get_full_path().startswith('/admin'):
if not request.path == settings.REDIRECT_LOGIN_URL and not request.path == REDIRECT_LOGOUT_URL and \
not request.path == '/saml':
return redirect(settings.REDIRECT_LOGIN_URL)
response = self.get_response(request)
request.session.set_expiry(settings.SESSION_EXPIRY)
if request.user.is_authenticated:
tokens=self.__class__.get_tokens_for_user(self,request,request.user)
request.session.set_expiry(settings.SESSION_EXPIRY)
return response
#staticmethod
def get_tokens_for_user(self,request,user):
refresh = RefreshToken.for_user(user)
return {
'refresh': str(refresh),
'access': str(refresh.access_token),
}
return response
Now for this I am trying to use djangorestframework-simplejwt. But since SAML is used here, I am not sure how do I implement JWT here. Do I have to change something in the above middleware or any other ways?

Custom backend authenticate success without returning User object

I'm writing a backend to validate users based on tokens from AWS Cognito. Is it possible to write an authenticate() method for a backend that "succeeds" without returning a Django User object?
For example:
class CustomBackend(BaseBackend):
def authenticate(self, request, username=None, password=None):
if token_is_valid(request.COOKIES["auth_jwt"]):
# Success, let the user through
else:
# Failure, redirect to login page to get token from Cognito
My understanding of "authenticate()" is that "success" is denoted by returning a User object, and failure by returning None. Can this work or do we need to make User objects based on the tokens so we have a User to return?
Edit: Found something called RemoteUserBackend, which sounds like it might be what I want, but I think has the same issue. Will leave link here in case it is relevant: https://docs.djangoproject.com/en/3.0/ref/contrib/auth/#django.contrib.auth.backends.RemoteUserBackend
I don't know if a "userless backend" is possible, but I solved the problem with middleware, which seems more appropriate.
class ValidateTokenMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
response = self.get_response(request)
token = request.COOKIES.get("auth_jwt", None)
# Here you can validate your token, redirect, set new access token, etc.

Get username in a middleware from Django Rest Framework SIMPLE JWT token (3rd party)

I am using Django Rest Framework and I've included a 3rd party package called REST framework simple JWT Auth which is the new framework referenced,
and this one, REST framework JWT Auth, which is the old one (I Imagine), since there was no update on github since a long time and maybe not supported for newer versions.
And I'm looking for a way, like this link on stackoverflow-3rd answer, via middlewares, to get the user information for each request in order to apply/save it, in needed, the user object in my models by using django signals.
I checked in documentation and on internet, but I didn't find anything. So, if you already had that case, I will appreciate your help.
Thank you
To get username from the user model you should use request.user. This will give you the authenticated user's info with the request. However if you use simple_jwt, it's not possible to directly use request.user in a middleware because authentication mechanism works in view function.
So you should manually authenticate inside the middleware and then you can use request.user to get whatever data from the user model.
from rest_framework_simplejwt import authentication
class MyMiddleware():
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
return response
def process_view(self, request, view_func, view_args, view_kwargs):
request.user = authentication.JWTAuthentication().authenticate(request)[0] # Manually authenticate the token
print(request.user) # Use the request.user as you want
With simple_jwt the user information will not be set on the request initially but after the request is processed it will be. So if you're just logging the information and you can do it after the request is processed, do it there.
So for example in a simple middleware:
def simple_middleware(get_response):
def middleware(request):
# request.user is an anonymous user at this point
response = get_response(request)
# request.uesr is the logged-in user here
return response
return middleware