0

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

  • Your `HttpClientModule` looks fine but are you sure the cookie/header name is correct ? Depending on the backend, the expected token can be CSRF or XSRF etc. Also, can you check in the dev tools that this token is sent ? – Arnaud Denoyelle Nov 28 '22 at 09:51
  • Can you open the dev tools, take the first XHR request, and tell us what you see in the HTTP response headers ? Something related to the CSRF/XSRF in the Set-Cookie or a CSRF header sent by the server ? Also, your first XHR must be a GET – Arnaud Denoyelle Nov 28 '22 at 09:58
  • i am getting csrf token in my response from the views in the form of cookie but the problem is that i am not bale to make post request. – Aakash Singh Nov 28 '22 at 09:58
  • i am getting something likee this Allow: DELETE, POST, OPTIONS, GET Content-Length: 11157 Content-Type: text/html; charset=utf-8 Cross-Origin-Opener-Policy: same-origin Date: Mon, 28 Nov 2022 09:31:23 GMT Referrer-Policy: same-origin Server: WSGIServer/0.2 CPython/3.8.2 Set-Cookie: csrftoken=sjd8q2xsdfgfhjgfnVGEkdP8f02shB; expires=Mon, 27 Nov 2023 09:31:23 GMT; Max-Age=31449600; Path=/; SameSite=Lax Vary: Accept, Origin, Cookie X-Content-Type-Options: nosniff X-Frame-Options: DENY – Aakash Singh Nov 28 '22 at 10:01
  • fine. So, looking at the code, I suppose that you get the cookie from the first request (`this.http.get(this.API_URL)`). Is that correct ? Then, can you check that the CSRF cookie name corresponds to the one that you indicate here : `HttpClientXsrfModule.withOptions({ cookieName: 'XSRF-TOKEN',` ? – Arnaud Denoyelle Nov 28 '22 at 10:01
  • yes you got it.. – Aakash Singh Nov 28 '22 at 10:02
  • Update : so no, your cookie name is wrong. The cookie name as sent by the server is `csrftoken`. Can you update that in the code here : `HttpClientXsrfModule.withOptions({cookieName: 'XSRF-TOKEN',` ? Then we will have to check the header name also – Arnaud Denoyelle Nov 28 '22 at 10:03
  • i am getting cookie name as cookieName: csrftoken – Aakash Singh Nov 28 '22 at 10:04
  • Can you try with `HttpClientXsrfModule.withOptions({ cookieName: 'csrftoken', headerName: 'X-XSRF-TOKEN', })` ? Also, can you check that the cookie and the headers are sent in the POST request ? – Arnaud Denoyelle Nov 28 '22 at 10:06
  • sure , checking ... – Aakash Singh Nov 28 '22 at 10:07
  • in the request header i am getting cookieName: csrftoken headerName: X-CSRFToken – Aakash Singh Nov 28 '22 at 10:09
  • Hmm, nope :). Can you remove the httpOptions here : `return this.http.post (this.API_URL, source, httpOptions);`. The point of configuring `HttpClientXsrfModule` is to not have to deal with this manually. Can you replace it with only `return this.http.post (this.API_URL, source);` – Arnaud Denoyelle Nov 28 '22 at 10:15
  • Comments are not very practical for explaining things so I will just write a partial answer and update it until we find the correct code :) – Arnaud Denoyelle Nov 28 '22 at 10:17
  • did, but same issue, sure may be we can find the sol'n – Aakash Singh Nov 28 '22 at 10:18
  • Note : from what I understand from DRF, if you did not override anything, it is possible that the default CSRF header name is `HTTP_X_CSRFTOKEN`. If that is correct, the right configuration would be `HttpClientXsrfModule.withOptions({ cookieName: 'csrftoken', headerName: 'HTTP_X_CSRFTOKEN', })` – Arnaud Denoyelle Nov 28 '22 at 10:51

3 Answers3

1

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.

0

(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',
})
Arnaud Denoyelle
  • 29,980
  • 16
  • 92
  • 148
-1

you need to exempt csrf in views.py

from django.views.decorators.csrf import csrf_exempt

and then

@csrf_exempt
def index(request):
pass
Ajay K
  • 183
  • 11
  • 1
    I don't know Django at all but a CSRF exemption looks like a bad idea to me. Can you elaborate why it would be appropriate here ? – Arnaud Denoyelle Nov 28 '22 at 09:53
  • i already tried this ....@csrf_exempt @api_view(['GET', 'POST','DELETE']) @permission_classes((permissions.AllowAny,)) def Source_api(request, format=None): # import ipdb;ipdb.set_trace() if request.user.is_staff: if request.method == 'GET': sources = Source.objects.all() serializer = SourceSerializer(sources, many=True) return JsonResponse(serializer.data, safe=False) elif request.method == 'POST': serializer = SourceSerializer(data=request.data) if serializer.is_valid(): ............. – Aakash Singh Nov 28 '22 at 09:54
  • @ArnaudDenoyelle it's quick solution. Alternatively we can implement proper JWT. – Ajay K Nov 28 '22 at 09:54