"detail": "CSRF Failed: CCSRF token missing." when sending post data from angular 13 to django connected database

Question:

i need to send the post data from angular to DRF through angular form but geeting the error

i checked almost all the answers available on the internet but did not found and useful answer.

 "detail": "CSRF Failed: CSRF token missing."

//post logic sources.service.ts

import { Injectable } from '@angular/core';
import { sources } from './sources';
import { HttpClient } from '@angular/common/http';
import { Observable , of, throwError } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';
import { HttpHeaders } from '@angular/common/http';

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type':  'application/json',
//     Authorization: 'my-auth-token',
    cookieName: 'csrftoken',
    headerName:  'X-CSRFToken',
//     X-CSRFToken: 'sjd8q2x8hgjkvs1GJcOOcgnVGEkdP8f02shB',
//     headerName: 'X-CSRFToken',
//      headerName: ,
  })
};

@Injectable({
  providedIn: 'root'
})
export class SourcesService {
API_URL = 'http://127.0.0.1:8000/sourceapi.api';
  constructor(private http: HttpClient) { }

     /** GET sources from the server */
    Sources() :  Observable<sources[]> {
      return this.http.get<sources[]>(this.API_URL);
    }
      /** POST: add a new source to the server */
//       addSource(data: object) : Observable<object>{
//         return this.http.post<object>(this.API_URL,data, httpOptions);
//       }

addSource(source : sources[]): Observable<sources[]>{
  return this.http.post<sources[]> (this.API_URL, source, httpOptions);
 //console.log(user);
  }
  }


//add-source.component.ts

import { Component, OnInit } from '@angular/core';
import { sources } from '../sources';
import { SourcesService } from '../sources.service';
import { FormGroup, FormControl, ReactiveFormsModule} from '@angular/forms';

@Component({
  selector: 'app-add-source',
  templateUrl: './add-source.component.html',
  styleUrls: ['./add-source.component.css']
})
export class AddSourceComponent implements OnInit {
   // a form for entering and validating data
   sourceForm = new FormGroup({
    name : new FormControl(),
    url : new FormControl(),
    client : new FormControl(),
  });
  constructor(private sourcesService: SourcesService) { }

  ngOnInit(): void {
  }

  sourceData_post: any;
  saveSource(){
    if(this.validate_form()){
      this.sourceData_post = this.sourceForm.value;
      this.sourcesService.addSource(this.sourceData_post).subscribe((source)=>{
      alert('source added');
      });
      }

      else{
      alert('please fill from correctly');
      }
  }
  validate_form(){
    const formData = this.sourceForm.value;
    if(formData.name == null){
      return false;
      }else if(formData.url == null){
      return false;
      }else{
      return true;
      }
      }

}

// add-source.component.html


<div class="bread-crumb">
    <div>    <span>Add Source</span>   </div>
</div>
<div class="container flex">
        <div class="form">
            <form action="" [formGroup]="sourceForm" (ngSubmit)="saveSource()">

                <table>
                    <tr>
                        <td>Source Name:</td>
                        <td>
                            <input class="input" type="text" formControlName="name">
                        </td>
                    </tr>
                    <tr>
                        <td>Source URL:</td>
                        <td>
                            <input class="input" type="text" formControlName="url">
                        </td>
                    </tr>

                     <tr>
                        <td>Source client:</td>
                        <td>
                            <input class="input" type="text" formControlName="client">
                        </td>
                    </tr>
                    <tr>
                        <td colspan="2">
                            <div class="center">
                                <button type="submit">submit</button>
                            </div>
                        </td>
                    </tr>
                </table>
            </form>
        </div>
</div>

i tried

imports: [
  BrowserModule,
  AppRoutingModule,
  HttpClientModule,
  Ng2SearchPipeModule,
  FormsModule,
  ReactiveFormsModule,
  HttpClientXsrfModule,
  HttpClientXsrfModule.withOptions({
    cookieName: 'XSRF-TOKEN',
    headerName: 'X-XSRF-TOKEN',
    })

but did not help

Note :- this is angular 13

Asked By: Aakash Singh

||

Answers:

you need to exempt csrf in views.py

from django.views.decorators.csrf import csrf_exempt

and then

@csrf_exempt
def index(request):
pass
Answered By: Ajay K

(Partial answer)

You get this error message because the CSRF protection is activated by default and you don’t send the CSRF token. Someone wrote a good description of what CSRF is here

On the first GET request, the server sends you the CSRF token in a cookie, and you have to send it back on every request, as a cookie AND as a request header. The server will check that the CSRF value in the cookie matches with the CSRF value that is in the header.

It can be tedious to repeat that on every request so Angular has a builtin module for that : HttpClientXsrfModule that you configured here :

HttpClientXsrfModule.withOptions({
  cookieName: 'XSRF-TOKEN',
  headerName: 'X-XSRF-TOKEN',
})

One problem is that you override this behavior by setting again the header by hand here :

const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type':  'application/json',
    cookieName: 'csrftoken',
    headerName:  'X-CSRFToken',
  })
};
[...]

addSource(source : sources[]): Observable<sources[]>{
  return this.http.post<sources[]> (this.API_URL, source, httpOptions);

You don’t need that. Just leave it like this :

addSource(source : sources[]): Observable<sources[]>{
  return this.http.post<sources[]> (this.API_URL, source);

Another problem is that the name for the CSRF header/cookie is not standard. It can be CSRF, XSRF, or whatever you want. Of course, if you send it as CSRF and the server expects it as XSRF, it will not be detected.

As I can see from the comments on the question, the server sends you that

Set-Cookie: csrftoken=sjd8q2xsdfgfhjgfnVGEkdP8f02shB

So we are sure that the cookie name is csrftoken. So it should be the same in the configuration of the HttpClientXsrfModule. Can you try like this

HttpClientXsrfModule.withOptions({
  cookieName: 'csrftoken', // << This one is certain
  headerName: 'X-XSRF-TOKEN', // << For this one, I don't know yet
})

Can you try this with different values for the headerName ? Preferably csrftoken also ? header name and cookie name are often the same.

Update :

According to the Django documentation, the default CSRF header name is HTTP_X_CSRFTOKEN. So you can try this :

HttpClientXsrfModule.withOptions({
  cookieName: 'csrftoken',
  headerName: 'HTTP_X_CSRFTOKEN',
})
Answered By: Arnaud Denoyelle

The logic on the front-end side was correct, the reason for showing csrf token missing was from the Django rest framework.
once i removed the @api_view from my views.py and returned the json response it worked.

Answered By: Aakash Singh