简体   繁体   中英

Django Rest Framework doesn't recognize CSRF cookie sent from a a React app using Axios

I have been a backend developer for many years, but now I want to learn frontend also, so I choose React as my frontend framework to start. I have expended two days learning to send requests from a React app using Axios to a Django Rest Framework backend, and I have clashed with that csrf cookie issue. I have posted several questions so far, and I could, finally, been able to send a right formed request that is accepted by the backend...only to get a Forbidden (CSRF token missing or incorrect.) error.

I guess my approach to get and use the csrf token might not be the right one, so I would appreciate you to point my mistake and teach me to solve it.

First, I send a GET request to the backend with the only goal of getting a csrf token (which I do), and set a cookie to such token:

class App extends Component {
  render() {
    const axios = require('axios');
    axios.get('http://127.0.0.1:8000/es/api/hand_shake/')
    .then(function (response) {
      Cookies.set('csrftoken', response['data']['cookie']);
      console.log(response);
    })
    .catch(function (error) {
      console.log(error);
    })

    return (
      <div className="App">
        <LoginModal />
      </div>
    );
  }
}

export default App;

Second, in another module, I get the token from the cookie and use it in a POST request:

handleClick() {
    const axios = require('axios');
    var csrfCookie = Cookies.get('csrftoken');
    console.log(csrfCookie)
    axios.post('http://127.0.0.1:8000/es/api-auth/login/',
      {
        next: '/',
        username: 'admin@admin.com',
        password: 'Cancun10!',
      },
      {
        headers: {
          'x-xsrf-token': csrfCookie,
        },
        withCredentials: true,
      }
    )
    .then(function (response) {
      console.log(response);
    })
    .catch(function (error) {
      console.log(error);
    })
  }

I have, finally, got the backend to accept the request, but I get the error Forbidden (CSRF token missing or incorrect.) .

Is it right to get the token the way I am doing it, and then to use it the way I am attempting?

The only solution I found was to exempt login process from csrf validation, but I had to give up Django Rest Framework default authentication functionality (if anybody knows how to do it with DRF default authentication, please share) and implement my own custom authentication view.

I created a ViewSet and decorated it with csrf_exempt , like this:

from django.views.decorators.csrf import csrf_exempt

class LoginViewSet(viewsets.ViewSet):
    permission_classes = ()

    @csrf_exempt
    def post(self, request):
        from PhotosManagerApp.email_backend import EmailBackend

        email_backend_instance = EmailBackend()
        user = email_backend_instance.authenticate(request, username=request.data['username'], password=request.data['password'])
        if user:
            try:
                usr_token = Token.objects.get(user=user)
            except:
                usr_token = None
        else:
            usr_token = None

        return Response({'token': str(usr_token)})

You also have to decorate your view in urls.py :

path('login/', csrf_exempt(views.LoginViewSet.as_view({'post': 'post'}))),

I had many troubles with Django settings.py , until I found the right configuration:

from corsheaders.defaults import default_headers

CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_HEADERS = default_headers + (
    'xsrfheadername',
    'xsrfcookiename',
    'content-type',
    'x-csrftoken',
)

CORS_ORIGIN_ALLOW_ALL = True
CSRF_COOKIE_NAME = "csrftoken"

I had many troubles until I found that I had to add default_headers to CORS_ALLOW_HEADERS .

Now, just ensure to send your request with withCredentials = false :

handleClick() {
    const axios = require('axios');
    //axios.defaults.withCredentials = true;
    axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
    axios.defaults.xsrfCookieName = "csrftoken";
    axios.post('http://127.0.0.1:8000/es/api/login/',
      {
        username: 'admin@admin.com',
      },
    )
    .then(function (response) {
      Cookies.set('token', "Token ".concat(response.data.token), {"expires": 10})
      console.log(response);
    })
    .catch(function (error) {
      console.log(error);
    })
  }

This is what worked for me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM