skip to Main Content

I have a netlify frontend at app.mydomain.com, and a django ninja REST API at api.mydomain.com. When I submit to my login endpoint, the api returns successfully with the access key (which I store in app state) and a refresh token in the form of a secure, httponly cookie. I can see this cookie is returned by looking in the response headers in dev tools. The cookie however is not stored by the browser at all, I’ve gone through numerous other questions/answers and I believe I have implemented everything required, but it’s still not working.

My login API call from the frontend is:

await fetch(
  AUTH_URL_OBTAIN,
  {
    method: RequestMethod.POST,
    headers: {"Content-Type": "application/json"},
    body: JSON.stringify({username: formData.email, password: formData.password}),
    credentials: "include",
  },
);

On the backend, the cookie is set like this:

response.set_cookie(
    key="refresh",
    value=refresh_token,
    expires=datetime.fromtimestamp(refresh_token_payload["exp"], timezone.utc),
    httponly=True,
    samesite="none",
    secure=True,
    path="/api/auth/web/token-refresh",
    domain=".mydomain.com",
)

I also have the following settings set (substituting the values in for environment variables):

CSRF_TRUSTED_ORIGINS = ["https://app.mydomain.com"]
CORS_ALLOWED_ORIGINS = ["https://app.mydomain.com"]
CORS_ORIGIN_WHITELIST = ["https://app.mydomain.com"]
CORS_ALLOW_CREDENTIALS = True

The login response provides the access token (which works as expected – I am able to make API calls using this just fine, and have credentials: include on all fetch requests) and the response headers are below:

Access-Control-Allow-Credentials: true

Access-Control-Allow-Origin: https://app.mydomain.com

Content-Length: 661

Content-Type: application/json; charset=utf-8

Cross-Origin-Opener-Policy: same-origin

Date: Thu, 06 Jun 2024 14:06:22 GMT

Referrer-Policy: same-origin

Server: daphne

Set-Cookie: refresh=ey...3uQ; Domain=.mydomain.com; expires=Sat, 06 Jul 2024 14:06:22 GMT; HttpOnly; Max-Age=2592000; Path=/api/auth/web/token-refresh; SameSite=none; Secure

Vary: origin

X-Content-Type-Options: nosniff

X-Frame-Options: DENY

I’m at a bit of a loss at this point – any advice would be very appreciated, thank you!

2

Answers


  1. Browser security policies can block cookies from/to ajax requests. See

    For a cross-origin API, I’d rather not use cookies at all. Imagine any API client other than a browser! Cookies are meant to track browsing sessions in a website, not storing state in arbitrary http clients.

    Just include the refresh token in the response body and store it in your app state as well.

    Login or Signup to reply.
  2. I’ve implemented many solutions of this type, where the cookie is used to enable an API message credential and is hence issued on the API side of the architecture.

    It looks to me like your problem is the expires property of the cookie. This causes the cookie to be persistent and I expect it is set incorrectly. Everything else looks right.

    I think you might fix your problem if you remove that cookie property. Doing do is also a security best practice and results in a session cookie that is removed when the browser is closed.

    Also, it is not portable to make assumptions about refresh token formats or payloads. Instead, a refresh token is considered expired when you try to use it at the authorization server’s token emdpoint and receive a response with an invalid_grant error code. Therefore, when using cookies that represent tokens, always derive expiry from the underlying token.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search