A Django Rest Framework Jwt middleware to support request.user

I am using following Django (2.0.1), djangorestframework (3.7.7), djangorestframework-jwt (1.11.0) on top of python 3.6.3. By default djangorestframework-jwt does not include users in django’s usual requst.user. If you are using code>djangorestframework, chances are you have a huge code base at API which is leveraging this, not to mention at your permission_classes at viewsets. Since you are convinced about the fact that jwts are the best tools for your project, no wonder that you would love to migrate from your old token to new jwt tokens. To make the migration steps easier, we will write a middleware that will set request.user for us.

from django.utils.functional import SimpleLazyObject
from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
from rest_framework.exceptions import ValidationError

#from rest_framework.request from Request
class AuthenticationMiddlewareJWT(object):
    def __init__(self, get_response):
        self.get_response = get_response


    def __call__(self, request):
        request.user = SimpleLazyObject(lambda: self.__class__.get_jwt_user(request))
        if not request.user.is_authenticated:
            token = request.META.get('HTTP_AUTHORIZATION', " ").split(' ')[1]
            print(token)
            data = {'token': token}
            try:
                valid_data = VerifyJSONWebTokenSerializer().validate(data)
                user = valid_data['user']
                request.user = user
            except ValidationError as v:
                print("validation error", v)
            

        return self.get_response(request)

And you need to register your middleware in settings:



MIDDLEWARE = [
    #...
    'path.to.AuthenticationMiddlewareJWT',
]

In app user to user chat using auth0 and firebase

So we are using auth0 for our user authentication, we have a server of our own which runs using relational database which seems to be perfect for our use case. Now we need to install a chat application for our user to user communication. How do we do that? Chat is a big data issue, and we need faster communication that happens in real time. How do we do that? We are using firebase for this. This is actually first time I am using firebase, to be more precise its my second day with firebase so I am quite excited that I am writing about this cool technology. The goal of this post is not only to help other but also to get help from experienced. Please point out my mistake and tell me how can I improve this.

Firebase supports actually social logins pretty well, but since we already has relied on auth0 for authentication, we won’t bother firebase to handle that. Firebase supports custom authentication, we will be using that. To issue a custom authentication token we actually need to use our firebase secret, we can’t let any user to see that so ideally which means our API server need to issue that then other client will use. Auth0 actually made our life a lot easier, we can add a firebase plugin to auth0 and auth0 will generate that custom authentication token for us for now on, so we don’t need to do that using api anymore.

I appreciate their initiative for firebase but there was some limitation at their service as well, they actually do not provide an authentication uid straight away for firebase rule auth.uid which is a must have before we start writing our firebase rules. But don’t panic we actually have a solution for that. We solved this problem by adding a simple auth0 rule:

function (user, context, callback) {
  if (context.protocol === 'delegation') {
    return  callback(null, user, context);
  }

  user.firebase_data = {
    uid: ""+user.user_id,
    user_id: ""+user.user_id
  };
  
  callback(null, user, context);
}

Now we are good to write our rules for firebase. We understand firebase is completely schema less but we will chalk down pseudo schema, a model for our database.

I think the key idea behind a beautiful designing is to see what is coming next, we want to design our model in a way that it becomes easier for it to its evolution. Later at some point we want to use our app for a conference chat, we want to let user edit and delete their messages. We want to avoid any sort of data duplication anywhere possible.

So we will have a conversations that each of the participants can access upon permission.

Lets do the relatively hard stuff first, we will design our conversation model. Roughly a conversation will have a limited array of members and lots and lots of message entries. So it should look like following:

conversations:{
 conversation0:{
  members:{
    0: user0,
    1: user1,
    ...
  }
  messages:{
    messages0:{
      message: "lets chat!",
      fromUserId: user0,
      createdAt:  timestamp
    }
  }
}

But how are our users going to access their rooms? Yet we don’t have any way to figure out which conversations a user has subscribed. We also need to figure out which conversation has unread messages, this is not symmetric, remember? So we would like to keep them inside our user data.

users:{
 users0:{
  conversations:{
   converation0:{
    last_read: timestamp,
    subject: weneedspace,
    added_by: timestamp,
    added_at: timestamp
   }
  }
 }
} 

Now we have everything, but are we secure enough from the bad guys out there? NOPE. We need rules based on which firebase will be protecting us.

Lets go for our users rules. Firebase apps are actually easily will go fragile if we don’t take enough care of it, so we know that user will have 4 properties at least last_read, subject,added_by, added_at.

newData.hasChildren(['last_read','subject','added_by','added_at'])

So we will be validate that they exists in the first place. Now we have the valid structure, but we actually don’t know that added_by is valid or not.

newData.child('added_by').val() = auth.uid

Let it be always the authenticated user uid. The user is authenticated but does he allowed to make someone else subscribe to that conversation? We will look into conversation membership list to validate that.

root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)

Now who has write permissions? As long as you are authenticated you are fine.

auth != null

Who can see my subscription? Privacy issue here. I won’t let ‘A’ authenticated user read my conversations. He must be ‘THE’ authenticated user.

auth != null && auth.uid == $user_id

For now we will only users to see through /users/conversations/ and write through /users/conversations/conv0/

so user rules go like this:

    "users":{
        "$user_id":{
          "conversations":{
              ".read": "auth != null && auth.uid == $user_id",
              "$conversation_name":{
                ".write": "auth != null",
                ".validate": "newData.hasChildren(['last_read','subject','added_by','added_at']) 
                              && newData.child('added_by').val() = auth.uid
                              && root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)
                              "
              },
              
            }
          }
      }

You can see that our validation relies on the existance of /conversations/conv0/members/0. So we need to ensure that we do first. While creating a conversation you must include yourself in a conversation thats how we validate.

newData.hasChild(auth.uid)

Any very simple to have write access you need to be authenticated.

auth != null

Now what would be our basic message look like?

newData.hasChildren(['fromUserId', 'message', 'sent_at'])

We also need to check if fromUserId is valid for that user or not.

auth.uid == newData.child('fromUserId').val()

Who can write messages? only if you are a member.

root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)

We have messages, and members both of them has at least someone who can write them. But we don’t have anyone who can read them. You can only read messages only if you are a member of it. We will add this to the parent of both of them.

data.child('members').hasChild(''+auth.uid)

Altogether conversations rule:

       "conversations":{
          "$conversation_name":{

                ".read": "data.child('members').hasChild(''+auth.uid)",
                "messages":{
                    "$hash": { 
                      ".write": "root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)",
                      //".validate": "auth.uid == newData.child('fromUserId').val()"
                      ".validate": "newData.hasChildren(['fromUserId', 'message', 'sent_at'])
                                   && auth.uid == newData.child('fromUserId').val()"
                    }
                  },
                "members":{
                   ".write": "auth != null", //"!data.exists()",
                   ".validate": "newData.hasChild(auth.uid)"
                  }
                
              
            }
        }

Altogather the rules are:

{
  "rules": {
 
    
    "users":{
      
        "$user_id":{
          "conversations":{
            
              
              ".read": "auth != null && auth.uid == $user_id",
              "$hash":{
                ".write": "auth != null"
              }
              
            }
          }
      },
      
      "conversations":{
          "$conversation_name":{

                ".read": "root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)",  
                ".write": "root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)",
                "messages":{
                    "$hash": { 
                      ".write": "root.child('conversations').child($conversation_name).child('members').hasChild(''+auth.uid)"
                      //".validate": "auth.uid == newData.child('fromUserId').val()"
                    }
                  },
                "members":{
                   ".write": "true", //"!data.exists()",
                   ".validate": "newData.hasChild(auth.uid)"
                  }
                
              
            }
        }
  }
}

Now we will add a quick proof of concept demo chat app using js and html.

Lets do auth0 sign in first:

signin.html

<script src="http://cdn.auth0.com/w2/auth0-6.7.js"></script>
<script
 src="https://code.jquery.com/jquery-1.12.3.min.js"
 integrity="sha256-aaODHAgvwQW1bFOGXMeX+pC4PZIPsvn2h1sArYOhgXQ="
 crossorigin="anonymous"></script>

<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js'></script>


<script type="text/javascript">
 var auth0 = new Auth0({
 domain: '<your auth0 >.auth0.com',
 clientID: '<your client id>',
 callbackURL: '/signin.html',
 callbackOnLocationHash: true
 });


 auth0.login({ popup: true }, function(err, profile, id_token, access_token, state, refresh_token) {
 if (err) {
 // Handle the error!
 alert(err)
 return;
 }
 $.cookie("auth0token", id_token);

var options = {
 id_token: id_token, // The id_token you have now
 api: 'firebase', // The type of app (Auth0 can generate multiple token formats)
 scope: "openid name email" // default: openid. Add any other scopes you need, i.e. "openid {scope1} {scope2}"
 };

auth0.getDelegationToken(options, function (err, delegationResult) {
 // Call your API using delegationResult.id_token
 console.log(err)
 $.cookie("firebasetoken", delegationResult.id_token);

$(location).attr('href', 'minchat.html')


 });


 // refresh_token is sent only if offline_access is set as a scope
 });

</script>
</pre>

Now we will write our client:

minichat.html
<pre>
<html><head>
 <meta http-equiv="content-type" content="text/html; charset=UTF-8">
 <meta name="robots" content="noindex, nofollow">
 <meta name="googlebot" content="noindex, nofollow">

<script type="text/javascript" src="//code.jquery.com/jquery-1.11.0.js"></script>
 <link rel="stylesheet" type="text/css" href="/css/result-light.css">
 <link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css">
 
 
 
 <script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script>
 <script type="text/javascript" src="https://cdn.firebase.com/js/client/2.2.1/firebase.js"></script>

<script src='https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js'></script>
 


<style type="text/css">
 .tabs-left {
 border-bottom: none;
 padding-top: 2px;
 }

.tabs-left {
 border-right: 1px solid #ddd;
 }

.tabs-left>li {
 float: none;
 margin-bottom: 2px;
 }

.tabs-left>li {
 margin-right: -1px;
 }

.tabs-left>li.active>a,
 .tabs-left>li.active>a:hover,
 .tabs-left>li.active>a:focus {
 border-bottom-color: #ddd;
 border-right-color: transparent;
 }

.tabs-left>li>a {
 border-radius: 4px 0 0 4px;
 margin-right: 0;
 display: block;
 }
 .bubble{
 width: 74%;
 padding: 4px;
 border-radius: 4px;
 }
 .you{
 background-color: skyblue;
 float: left;
 text-align:left;
 }
 .me{
 background-color: orange;
 float: right;
 text-align: right;
 }

</style>

<title>In app Chat</title>


<script type="text/javascript">//<![CDATA[
$(window).load(function(){

var FirebaseChat = (function(){
 function FirebaseChat() {
 this.firebase = new Firebase('https://jobcue.firebaseio.com/');
 }
 var instance;
 return {
 get: function(){
 if (instance == null) {
 instance = new FirebaseChat()
 instance.uid = instance.firebase.getAuth().uid

var token = String($.cookie("firebasetoken") );

instance.firebase.authWithCustomToken(token, function(error, result) {
 if (error) {
 console.log("Authentication Failed!", error);
 } else {
 console.log("Authenticated successfully with payload:", result.auth);

console.log("Auth expires at:", new Date(result.expires * 1000));

// alert( JSON.stringify (firebase.getAuth()) )
 }
 });


 // Hide the constructor so the returned objected can't be new'd...
 instance.constructor = null;
 }
 return instance;
 },
 uid: function(){
 return get().getAuth().uid;
 }
 };
})();

function User(id){
 this.id = id;

this.firebaseRef = FirebaseChat.get().firebase.child('users').child(this.id) 
}

User.prototype.subscribe = function(conversation_name){
 
 c={}
 c.subject = conversation_name
 c.last_read = Firebase.ServerValue.TIMESTAMP
 c.added_by = FirebaseChat.get().uid
 c.added_at = Firebase.ServerValue.TIMESTAMP
 alert(conversation_name)
 this.firebaseRef.child('conversations').child(conversation_name).set(c)

};

User.prototype.getId = function(){return this.id };

User.prototype.read = function(conversation_name){
 c={}
 //c.subject = conversation_name


this.firebaseRef.child('conversations').child(conversation_name).child('last_read').set(Firebase.ServerValue.TIMESTAMP) 
};
User.prototype.subscriptions = function(){
 this.firebaseRef.child('conversations') 
};


User.prototype.onNewSubscription = function(callback){

this.firebaseRef.child('conversations').on('child_added', 
 function(conversation_snap){

callback( conversation_snap.key() ) 
 })

};

function Conversation(name){
 this.name = name
 this.firebaseRef = FirebaseChat.get().firebase.child('conversations').child(this.name)
}

Conversation.prototype.create = function(participants){
 firebaseRef = this.firebaseRef.child('members')

participants.forEach(function(user){

var u={}
 u.added_at = Firebase.ServerValue.TIMESTAMP
 u.added_by = FirebaseChat.get().uid

firebaseRef.child( user.getId() ).set(u) 
 })

};


Conversation.prototype.sendMessage = function(message){
 var m = {}
 m.message = message
 m.sent_at = Firebase.ServerValue.TIMESTAMP
 m.fromUserId = FirebaseChat.get().uid

this.firebaseRef.child('messages').push(m)
}


Conversation.prototype.onNewMessage = function(callback){
 this.firebaseRef.child('messages').on('child_added', 
 function(new_message_snap){ 
 callback(new_message_snap.val() ) 
 } )
}


var me = new User( FirebaseChat.get().uid );

me.onNewSubscription(function(new_conversation){
 addPeople(new_conversation)

var conversation = new Conversation(new_conversation)
 conversation.onNewMessage(function(new_message){
 addMessage(new_conversation, new_message)

})
})


$('#messageInput').keypress(function(e) {
 if (e.keyCode == 13) {


 var conversation = new Conversation($('.active').find('.name').text())

conversation.sendMessage($('#messageInput').val())

}
});

var n = 7;

function addPeople(conversation_list) {
 $("#conversations").append(
 '<li ><a href="#' + conversation_list + '" data-toggle="tab"><span class="name">' + conversation_list + '</span></a></li>'
 );

$("#individual_conversations").append(
 '<div class="tab-pane" id="'+conversation_list+'"><div style="width:100%">'+conversation_list+'</div></div>'
/* 
 '<div class="chat" data-chat="person' + (n++) + '" id="' + conversation_list.key() +
 '"><div class="conversation-start"></div>'
*/
 );
 bind();
}

function addMessage(conversation_list, msg) {
 $("#" + conversation_list).append(

'<div class="bubble ' + (msg.fromUserId == FirebaseChat.get().uid ? 'you' : 'me') + '"><span><b>'+
 (msg.fromUserId == FirebaseChat.get().uid ? 'you: ' : '')
 +'</b>' +
 msg.message+
 '</span><b>'+
 (msg.fromUserId == FirebaseChat.get().uid ? '' : ' :'+(msg.fromUserId))+
 '</b></div>'


 );


}

function bind(){
$("#conversations li a").on("click", function(){
 var conversation =$(this).find(".name").text(); 
 if(me !=null){
 me.read(conversation)
 } 
})
}


$("#newConv").on("click", function(){
 var c = $("#newConversationInput").val();
 var u = $("#toUserInput").val();
 var m =$("#newMessageInput").val();

if(me !=null){
 
 var auser = new User(u);

var con = new Conversation(c)
 con.create( [me, auser] )
 con.sendMessage(m);

me.subscribe(c);
 
 auser.subscribe(c);


 } 
})

});//]]>


</script>


</head>

<body>

<div class="col-sm-6">
 <div class="row">
 <h3>Messages
 <!-- Button trigger modal -->
 <button class="btn btn-primary btn-sm pull-right" data-toggle="modal" data-target="#myModalNorm">
 New Message
 </button>

</h3>

<hr>
 <div class="col-xs-4">
 <!-- required for floating -->
 <!-- Nav tabs -->
 <ul class="nav nav-tabs tabs-left sideways" id="conversations">
 <li class="center">Discussion </li>
 <!--li class="active"><a href="#home-v" data-toggle="tab">Home</a></li-->
 </ul>

</div>

<div class="col-xs-8">
 <!-- Tab panes -->
 <div class="tab-content" id="individual_conversations">

<!--div class="tab-pane active" id="home-v">Home Tab.</div!-->
 </div>


 </div>
 </div>


<div class="row">
 <div class="col-xs-4">SEND MESSAGE:</div>
 <input class="col-xs-8" id="messageInput" placeholder="write message and press ENTER">
</div>



</div></body></html>

<!-- Modal -->
<div class="modal fade" id="myModalNorm" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
 <div class="modal-dialog">
 <div class="modal-content">
 <!-- Modal Header -->
 <div class="modal-header">

<h4 class="modal-title" id="myModalLabel">
 New Message
 </h4>
 </div>

<!-- Modal Body -->
 <div class="modal-body">


 <div class="form-group">
 <input class="form-control" type='text' id='newConversationInput' placeholder='Conversation'>

<input class="form-control" type='text' id='toUserInput' placeholder='to'>
 <textarea class="form-control" type='text' id='newMessageInput' placeholder='Message'></textarea>

<button id="newConv" type="submit" class="btn btn-primary">Submit</button>

<button type="button" class="btn btn-primary" data-dismiss="modal">
 Close
 </button>
 
 </div>


 </div>
 </div>
 </div>

So we are ready to go. go to and it will redirect to after signing in.