Dhyey Shah
Dhyey Shah

Reputation: 652

http response not setting cookie in the browser

TLDR:

The following response header doesn't set the cookie in browser:

Access-Control-Allow-Origin: *
Allow: GET, HEAD, OPTIONS
Content-Length: 7
Content-Type: application/json
Date: Tue, 27 Apr 2021 15:58:02 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.9.4
Set-Cookie: csrftoken=r5r2YcZZvJKs79cbLd24VSyNscpUsxJB6UuWiWO2TXriy6B4r8KDZrwSDyI091K1; expires=Tue, 26 Apr 2022 15:58:02 GMT; Max-Age=31449600; Path=/; SameSite=Lax
Vary: Accept, Cookie, Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

My request headers:

Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Host: 127.0.0.1:8000
Origin: http://localhost:3000
Pragma: no-cache
Referer: http://localhost:3000/
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36

I am new to Django, react and "http header" related stuff.

My django dev server runs at:

http://127.0.0.1:8000/

and my react dev server runs at:

http://127.0.0.1:3000
  1. In order to access the website, login is required. So, all unauthorized requests are 1st redirected to login page, by configuring react-router and following this template. So, till now, no api calls are made.
  2. In order to post login data, i need to have csrf token set by the server. But since i have not made any api calls, i created an endpoint /api/csrf/ explicitly, to set the csrf token.
# URL: /api/csrf/
class CSRFGet(APIView):
    """
    Explicitly set csrf cookie
    """

    @method_decorator(ensure_csrf_cookie)
    def get(self, request):
        return Response('hello')

I call this endpoint, when useProvideAuth hook is mounted.

function useProvideAuth() {
    const [token, setToken] = useState(null);

    const login = (username, password) => {
        return axios.post(
            '/auth/',
            {
                username: username,
                password: password
            })
            .then(response => {
                setToken(response.token) 
            })
    }

    useEffect(()=> {
        axios.get(
            '/csrf/'
        )
    },[])

    return {
        token,
        login,
    }
}
  1. To retrieve and set this cookie, i followed the official Django docs. I also enabled CORS policy using django-CORS-headers allow all origins.

Now, when i make a request to any page, it redirects to login page, and i can see api/csrf/ responds with:

Set-Cookie: csrftoken=LgHo2Y7R1BshM4iPisi5qCXhdHyAQK7hD0LxYwESZGcUh3dXwDu03lORdDq02pzG; expires=Tue, 26 Apr 2022 06:29:23 GMT; Max-Age=31449600; Path=/; SameSite=Lax

But, the cookie is not set at all. Why is it so?

Is my approach for getting csrf cookie correct? Please let me know, if i am making any security vulnerability with this approach.

Upvotes: 1

Views: 2222

Answers (2)

Dhyey Shah
Dhyey Shah

Reputation: 652

Turns out the issue was, i was using http://127.0.0.1:8000 to make api calls, where as my server was on http://localhost:8000. Because of this, host and origin, in my request headers didn't match the same domain.

Cookies can be allowed to be used under same domain, with different ports and subdomains, unlike Same-Origin policy, but cannot be used cross-domains.

In my case, I guess http://127.0.0.1:8000 & http://localhost:8000 were considered different domains, and thus the browser was not setting my cookie.

Thanks Anas Tiour, for stating about Allow-Credentials. I had tried that too, but still had no luck until i found out the actual reason.

Upvotes: 2

Anas Tiour
Anas Tiour

Reputation: 1432

Could you try adding the following to the django-cors-headers configuration and retry?

CORS_ALLOW_CREDENTIALS = True

Also, please note that the above configuration would probably not work if you are allowing all origins. See this Mozilla documentation: Credential is not supported if the CORS header ‘Access-Control-Allow-Origin’ is ‘*’

If you face such error, I suggest setting:

CORS_ALLOWED_ORIGINS = [
    "http://127.0.0.1:3000",
]

or something fancier like:

CORS_ALLOWED_ORIGIN_REGEXES = [
       r"^http://127.0.0.1:[0-9]{1,4}$",
]

Finally, make sure that you are using a django-cors-headers version >= 3.5 since the 2 above configuration had different aliases back then.

Let me know if it works, I am very curious.

Upvotes: 1

Related Questions