I've created a back-end Django application and am currently working on hooking up an Angular front-end that requests data from API's set up using Django rest framework and have run into the problem that my logged in users (in the back-end, them being logged in on the front-end doesn't matter) get 403 on all post-requests to the API. Which I figure is because of the CSRF protection in Django.
I have look at a thread that tackled this exact problem for this and have attempted to implement the solution from this thread with no luck.
Logged out users can send post-requests to register or log in just fine. But when a logged in user tries to it gets a 403. Sending the same data while browsing the DRF API is successful, which is why I think it's related to CSRF. To be a little more clear, if I'm logged in as a user on the front-end and try to log-in or register a new user (those parts aren't blocked off in development) I get a 403 response. If I'm logged in on the browsable API and send the exact same post-content it works with successfully attempting to log me in again, or register a new user.
Can it be because of the custom header I'm adding that may be replacing the CSRF header? In that case, how can I remake it since that header is needed or else I get a 415 unsupported media type.
Here is the Angular service that provides the function that sends a login request to the server:
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { map, catchError } from 'rxjs/operators'
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Userlogon } from '../_models/Userlogon';
import { User } from '../_models/User';
const HttpOptions = {
headers: new HttpHeaders({ "Content-type": "application/json" })
};
@Injectable({
providedIn: 'root'
})
export class AuthService {
loginobject: any;
private usersUrl= '/api/v1/accounts/';
private logonUrl= '/api/v1/auth/login/';
private logoutUrl= '/api/v1/auth/logout/';
constructor(
private http: HttpClient
) { }
login(username: string, password: string) {
this.loginobject=JSON.stringify({"username": username, "password": password});
return this.http.post<any>(this.logonUrl, this.loginobject, HttpOptions)
.pipe(map(user => {
if (user){
console.log("If statement satisfied");
localStorage.setItem('currentUser', JSON.stringify(user));
}
return user;
}));
}
logout(){
localStorage.removeItem('currentUser');
return this.http.get<any>(this.logoutUrl).subscribe(response => console.log(response));
}
}
Here is the app module where I've implemented the provider options:
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { HttpModule, XSRFStrategy, CookieXSRFStrategy } from '@angular/http'
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { RegisterComponent } from './register/register.component';
import { LoginComponent } from './login/login.component';
import { AlertComponent } from './_directives/alert.component';
import { ProfileComponent } from './profile/profile.component';
import { AuthGuardService } from './_guards/auth-guard.service';
import { AlertService } from './_services/alert.service';
import { AuthService } from './_services/auth.service';
import { UserService } from './_services/User.service';
@NgModule({
declarations: [
AppComponent,
RegisterComponent,
LoginComponent,
AlertComponent,
ProfileComponent,
],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
AppRoutingModule,
HttpClientModule,
HttpModule
],
providers: [
{
provide: XSRFStrategy,
useValue: new CookieXSRFStrategy('csrftoken', 'X-CSRFToken')
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
Here is the template that Django in processes:
{% load static %}
{% csrf_token %}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>AngularWebapp</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<app-root></app-root>
{% block javascript %}
<script type="text/javascript" src="{% static 'runtime.js' %}"></script><script type="text/javascript" src="{% static 'polyfills.js' %}"></script><script type="text/javascript" src="{% static 'styles.js' %}"></script><script type="text/javascript" src="{% static 'vendor.js' %}"></script><script type="text/javascript" src="{% static 'main.js' %}"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
{% endblock %}
</body>
</html>
And lastly the Django view-code:
from django.shortcuts import render
from django.contrib.auth import authenticate, login, logout
from rest_framework import permissions, viewsets, status
from rest_framework.views import APIView
from rest_framework.response import Response
from authentication.models import Account
from authentication.permissions import IsAccountOwner
from authentication.serializers import AccountSerializer, LoginSerializer
from getorgname import scrapeorg, idgenerator
import json
# Create your views here.
class AccountViewSet(viewsets.ModelViewSet):
lookup_field='username'
queryset=Account.objects.all()
serializer_class= AccountSerializer
def get_permissions(self):
if self.request.method in permissions.SAFE_METHODS:
return (permissions.AllowAny(),)
if self.request.method == 'POST':
return (permissions.AllowAny(),)
return (permissions.IsAuthenticated(), IsAccountOwner(),)
def create(self, request):
serializer=self.serializer_class(data=request.data)
if serializer.is_valid():
newuser=serializer.validated_data
Account.objects.create_user(
username=newuser.get('username'),
email=newuser.get('email'),
org_name=scrapeorg(newuser.get('org_number')),
org_number=newuser.get('org_number'),
cid=idgenerator(),
utype=newuser.get('utype'),
password=newuser.get('password'),
)
#Account.objects.create_user(**serializer.validated_data)
return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
return Response({
'status': 'Bad request',
'message': 'Account could not be created with received data',
}, status=status.HTTP_400_BAD_REQUEST)
class LoginView(APIView):
def post(self, request):
#data=json.loads(request.body)
serializer=LoginSerializer(data=request.data)
if serializer.is_valid():
data=serializer.validated_data
username=data.get('username')
password=data.get('password')
account=authenticate(username=username, password=password)
if account != None:
login(request, account)
serialized = AccountSerializer(account)
return Response(serialized.data)
else:
return Response({
'status': 'Unauthorized',
'message': 'Username %s and password invalid' % username
}, status=status.HTTP_401_UNAUTHORIZED)
return Response({
'status': 'Bad request',
'message': 'Invalid data for a login request',
}, status=status.HTTP_400_BAD_REQUEST)
class LogoutView(APIView):
def get_permissions(self):
return (permissions.IsAuthenticated(), IsAccountOwner(),)
def get(self, request):
logout(request)
return Response({
'status': 'Success',
'message': 'Successfully processed logout request',
})