DRF Token Authentication Tutorial - angularjs

I am pretty new to Django, but I want to learn how to implement a DRF Token Authentication with Angularjs. Some of the tutorials I have found haven't been too helpful in showing how to set it up, along with their source code etc...
Also, for production purposes, is it more practical to use a third party package? Or set up my own (it's for a personal project, so time contribution is not an issue).
My Buggy Code for Token Auth: Github

In settings.py
INSTALLED_APPS = (
...
'rest_framework.authtoken'
)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
}
In signals.py
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
In views.py
class ExampleAuthToken(APIView):
def post(self, request, format=None):
username = request.data.get("username")
password = request.data.get("password")
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
user = User.objects.create_user(username=username)
user.set_password(password)
user.save()
content = {
'user': unicode(user.username),
'token': unicode(user.auth_token),
}
return Response(content)
In urls.py
urlpatterns = [
url(r'^authtoken/', ExampleAuthToken.as_view(), name='authtoken'),
]
To call using the angularjs;
var credentials = {
username: "root",
password: "root123"
};
$.post("http://localhost:8000/authtoken/", credentials {
success: function(data){
console.log(data);
}
}

I would definitely use a library. For token authentication there is the nifty django-rest-framework-jwt - it's straightforward to install and setup. To help with Angular JS looks like there is drf-angular-jwt (which uses DRF-JWT but I have not used it).

Related

HTTP method’s error: 401 (Unauthorized) on React & Django(DRF)

I completed manipulating authentication with token by referring this article, and then I’m trying to create a crud function such creating post, displaying posts, etc… . However, I have an error when I fetched the url which displays posts(IE, fetching url I defined as “index” method on views.py of app for auth manipulation), I have 401 error even though I can access by using url of the backend without any error even on terminal.
I found some config codes which are related to authentication and permission for manipulation of authentication with token on settings.py causes this error, since when I delete these codes, the crud function works. But obviously authentication function no longer works (index method on views.py retrieve only token, another informations are filled blank) by this solution.
//fetch method on frontend
try{
const res = await fetch(`${base_url}/accounts/current_user/`,{
method:'GET',
headers:{
Authorization:`JWT ${localStorage.getItem('token')}`
}
})
const data = await res.json();
setUsername(data.username);
console.log(data)
}catch(err){console.log(err)};
//fetch posts on frontend
const getProblems = async() =>{
const res = await fetch(base_url+'/problems/index');
const data = await res.json();
setProblems(data);
}
//views.py on app for auth manipulation
#api_view(['GET'])
def get_current_user(request):
serializer = GetFullUserSerializer(request.user)
print(serializer.data)
return Response(serializer.data)
//settings.py(related to auth, cors):
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication',
)
}
CORS_ORIGIN_WHITELIST = (
'http://localhost:3000',
)
CORS_ALLOW_CREDENTIALS = True
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER':
'new_sns.utils.custom_jwt_response_handler',
}
I have another files such serializer.py, urls.py,but they are absolutely same as the article I extracted.
I guess I misunderstand something around configuration. I would like to hear some of suggestions. Please let me know if you think if there may be problems on another files which I didn't attach on here.
Thanks.
Try changing JWT ${localStorage.getItem('token')} to Authorization:`Bearer ${localStorage.getItem('token')}.
In case this won't work, try djangorestframework-simplejwt - package recommended on DRF's docs.
JSON Web Token is a fairly new standard which can be used for
token-based authentication. Unlike the built-in TokenAuthentication
scheme, JWT Authentication doesn't need to use a database to validate
a token. A package for JWT authentication is
djangorestframework-simplejwt which provides some features as well as
a pluggable token blacklist app.
django-api-logger and axios-jwt may also come handy.

Forbidden (403) Post Request in a "Build React App" but work fine with React App running on "http://localhost:3000/" and PostMan

I am using Django Rest Framework and React. When I run the react app on "localhost:3000", every post request is accepted and worked fine. But After I build the react app with "npm run build". Then, all POST requests are being Forbidden(403) on "localhost:8000". but still, everything is fine on "localhost:3000"
Django has cross-site request forgery (CSRF) protection. In each form it uses a small extra element that contains a csrf token, and then validates this.
You can extract the csrf token with a JavaScript function specified in the Django documentation:
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
then you can make a fetch with:
fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken
},
body: {
// …
}
})
There can be many reasons:
Do you have CORS set? I recommend django-cors-headers
# update backend/server/server/settings.py
# ...
INSTALLED_APPS = [
#...
'corsheaders', # add it here
#...
]
# define which origins are allowed
CORS_ALLOWED_ORIGINS = [
"http://localhost:3000",
"http://127.0.0.1:3000"
]
# add to middleware
MIDDLEWARE = [
#...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
#...
]
Please check ALLOWED_HOSTS in settings.py
Please try to run Django server with DEBUG=True and check more info about the request, you can check it in developer tools in web browser
Please check at what address your requests are send? I'm using different server address for development and production:
import axios from "axios";
if (window.location.origin === "http://localhost:3000") {
axios.defaults.baseURL = "http://127.0.0.1:8000";
} else {
axios.defaults.baseURL = window.location.origin;
}
As Willem Van Onsem wrote, it can be the issue with CSRF, but CSRF is enabled by default only for session based authentication (what authentication are you using?)
Please take a look at the following articles:
React Token Based Authentication to Django REST API Backend
Docker-Compose for Django and React with Nginx reverse-proxy and Let's encrypt certificate
Github Repository with example Django+React project: Django+React Boilerplate

Angular 2 POST giving 403 CSRF error posting to Django server

I am struggling with 403/CSRF issues when trying to use Angular 2 to POST to my Django server.
Both my server code and my Angular code are running on the same 127.0.0.1 server.
When the Angular code is run the server returns a 403 4612 error
My Django View code looks like this (I am using the Django REST Framework):
rom django.utils import timezone
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.models import User
from .models import Item, Seen, Keyword, Flag
from django.utils.decorators import classonlymethod
from django.views.decorators.csrf import csrf_exempt
from rest_framework import viewsets
from items.serializers import ItemSerializer, UserSerializer
from rest_framework.authentication import SessionAuthentication
class CsrfExemptSessionAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
return # To not perform the csrf check previously happening
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all().order_by('-date_added')
serializer_class = ItemSerializer
authentication_classes = (CsrfExemptSessionAuthentication, )
#permission_classes = [IsAccountAdminOrReadOnly]
"""
Use the API call query params to determing what to return
API params can be:
?user=<users_id>&num=<num_of_items_to_return>&from=<user_id_of_items_to_show>
"""
def get_queryset(self):
this_user = self.request.query_params.get('user', None)
restrict_to_items_from_user_id = self.request.query_params.get('from', None)
quantity = self.request.query_params.get('num', 20)
if restrict_to_items_from_user_id is not None:
queryset = Item.objects.filter(owner=restrict_to_items_from_user_id, active=True).order_by('-date_added')[0:int(quantity)]
elif this_user is not None:
queryset = Item.objects.filter(active=True, credits_left__gt=0).exclude(pk__in=Seen.objects.filter(user_id=this_user).values_list('item_id', flat=True))[0:int(quantity)]
else:
queryset = Item.objects.filter(active=True, credits_left__gt=0)[0:int(quantity)]
return queryset
My Angular 2 code that does the POST looks like this:
import {Injectable, Inject} from 'angular2/core';
import {Http, Headers, HTTP_PROVIDERS} from 'angular2/http';
import {UserData} from './user-data';
import 'rxjs/add/operator/map';
#Injectable()
export class ConferenceData {
static get parameters(){
return [[Http], [UserData]];
}
constructor(http, user) {
// inject the Http provider and set to this instance
this.http = http;
this.user = user;
}
load() {
// Example of a PUT item
let body = JSON.stringify({ url: 'fred', item_type: 'P', owner_id: 2 });
let headers = new Headers();
headers.append('Content-Type', 'application/json');
this.http.post('http://127.0.0.1:8000/api/items/', body, {
headers: headers
})
.subscribe(
data => {
alert(JSON.stringify(data));
},
err => this.logError(err.json().message),
() => console.log('Authentication Complete')
);
}
}
I find CSRF issues really difficult to get to grips with!
EDIT: Added CORS settings
My CORS settings look like this:
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_WHITELIST = (
'veeu.co',
'127.0.0.1'
)
CORS_ORIGIN_REGEX_WHITELIST = ()
CORS_URLS_REGEX = '^.*$'
CORS_ALLOW_METHODS = (
'GET',
'POST',
'PUT',
'PATCH',
'DELETE',
'UPDATE',
'OPTIONS'
)
CORS_ALLOW_HEADERS = (
'x-requested-with',
'content-type',
'accept',
'origin',
'authorization',
'x-csrftoken'
)
CORS_EXPOSE_HEADERS = ()
CORS_ALLOW_CREDENTIALS = False
Rather than disabling the CSRF protection, you can add the token as a header to your ajax requests. See the docs, in particular the last section for AngularJS.
You might have to use csrf_ensure for the initial Django view, to ensure that Django sets the csrf cookie.

AngularJS + django REST api

I'm attempting to build an api with DRF.
Client is a cordova app backed with AngularJS.
When I try to post some user object using $resource I'm getting a 403 forbidden response from django.
Below is some code which I think is relevant for the issue:
The API Call:
$rootScope.user =
User.get({id: response.id}).$promise.then(angular.noop, function (e) {
if (e.status == 404) { //If not found, register the user.
$rootScope.user = new User();
Object.keys(response).forEach(function (key) {
$rootScope.user[key] = response[key];
});
$rootScope.user.$save(); //Fails here! 403.
}
else
console.log(JSON.stringify(e.msg));
});
The User factory:
.factory('User', function ($resource, serverConstants) {
return $resource(serverConstants.serverUrl + '/users/:id');
})
django view:
# Users
class UserSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.CharField(max_length=100,required=True)
email = serializers.EmailField(required=False,allow_blank=True)
joined = serializers.DateField(required=False,default=datetime.date.today)
class Meta:
model = models.User
fields = ('joined', 'id', 'email')
def get_validation_exclusions(self):
exclusions = super(UserSerializer, self).get_validation_exclusions()
return exclusions + ['owner']
class UserViewSet(viewsets.ModelViewSet):
queryset = models.User.objects.all()
serializer_class = UserSerializer
PS: I've configured angular to use CSRF cookie and django to allow CORS
Thanks in advance!
Your /user/:id endpoint requires authenticated requests.
You need to authenticate your client's requests using one of the methods specified on the previous link.
Given your app runs in a WebView and then has a builtin cookies handling, SessionAuthentication is the more straightforward to implement.
If you want the endpoint to not require authentication, you can set its permission_classes attribute like so:
from rest_framework.permissions import AllowAny
class UserViewSet(viewsets.ModelViewSet):
queryset = models.User.objects.all()
serializer_class = UserSerializer
permission_classes = (AllowAny, )
I guess with DRF you mean the django-rest-framework.
If yes, have a look here:
http://www.django-rest-framework.org/api-guide/authentication/
You can make the view public but using AllowAny.
from rest_framework.permissions import AllowAny
from rest_framework import generics
restapi_permission_classes = (AllowAny,)
class MyListView(generics.ListCreateAPIView):
serializer_class = MyObjectSerializer
permission_classes = restapi_permission_classes
queryset = MyObject.objects.all()
However I'd recommend you to use proper authentication once you are done with testing. I've been using the token authentication.
Have a look at this post for more details:
Django Rest Framework Token Authentication

How to add current user data when saving in django model

I am creating small Django/AngularJS app. User can create account, view all created posts and add his own posts.
The current version of app is here: GitHub
Models: models.py
from django.db import models
from django.contrib.auth.models import User
from datetime import datetime
# Post model
class Post(models.Model):
date = models.DateTimeField(default=datetime.now)
text = models.CharField(max_length=250)
author = models.ForeignKey(User)
Views: views.py
from django.shortcuts import render
from rest_framework import generics, permissions
from serializers import UserSerializer, PostSerializer
from django.contrib.auth.models import User
from models import Post
from permissions import PostAuthorCanEditPermission
...
class PostMixin(object):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [
PostAuthorCanEditPermission
]
def pre_save(self, obj):
"""Force author to the current user on save"""
obj.author = self.request.user
return super(PostMixin, self).pre_save(obj)
class PostList(PostMixin, generics.ListCreateAPIView):
pass
class PostDetail(PostMixin, generics.RetrieveUpdateDestroyAPIView):
pass
...
Serializers: serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
from models import Post
class UserSerializer(serializers.ModelSerializer):
posts = serializers.HyperlinkedIdentityField(view_name='userpost-list', lookup_field='username')
class Meta:
model = User
fields = ('id', 'username', 'first_name', 'last_name', 'posts', )
class PostSerializer(serializers.ModelSerializer):
author = UserSerializer(required=False)
def get_validation_exclusions(self, *args, **kwargs):
# Need to exclude `user` since we'll add that later based off the request
exclusions = super(PostSerializer, self).get_validation_exclusions(*args, **kwargs)
return exclusions + ['author']
class Meta:
model = Post
But when I create request (main.js) to add new post to DB like this:
var formPostData = {text: $scope.post_text};
$http({
method: 'POST',
url: '/api/posts',
data: formPostData,
headers: {'Content-Type': 'application/json'}
})
it raises error:
Request Method: POST
Request URL: http://127.0.0.1:8000/api/posts
Django Version: 1.7.6
Exception Type: IntegrityError
Exception Value:
NOT NULL constraint failed: nucleo_post.author_id
I thought, that this code adds author to the post model before saving. But it doesn't work correctly now.
def pre_save(self, obj):
"""Force author to the current user on save"""
obj.author = self.request.user
return super(PostMixin, self).pre_save(obj)
So I need some help...
this is because the request does not have the user information. For request.user to work, the request should include the authorization related information. Inclusion of this information depends on what authorization mechanism you are using. If you are using token or session based authentication then token or session key should be the part of request header or query param(depends on server implementation). If you are using rest_framework's login view then you should pass username:password along with the request.

Resources