Cannot get axios to include CSRF token from cookie in cross-domain request

Viewed 610

Disclaimer - I looked at a lot of similar questions on stackoverflow.

I have a backend running on "https://api.website.com" and a frontend running on "https://admin.website.com" The backend's CORS policy is setup like this:

appCorsResourcePolicy = CorsResourcePolicy {
    corsOrigins        = Just (["http://localhost:8082", "https://admin.website.com"], True)
  , corsMethods        = ["OPTIONS", "GET", "PUT", "POST", "PATCH", "DELETE"]
  , corsRequestHeaders = ["Authorization", "Content-Type", "Cookie", "X-XSRF-TOKEN", "Origin", "X-Requested-With", "Accept",
                          "Accept-Language", "Content-Language"]
  , corsExposedHeaders = Just ["Set-Cookie", "X-XSRF-TOKEN"]
  , corsMaxAge         = Nothing
  , corsVaryOrigin     = True
  , corsRequireOrigin  = False
  , corsIgnoreFailures = False
}

After performing GET request to "https://api.website.com", frontend successfully receives XSRF-TOKEN cookie (secure = false, httpOnly = false, sameSite = Lax) set for domain "api.website.com" (I also tried SameSite = None, secure = true). Now, I cannot make axios (or manually) add token from cookie to X-XSRF-TOKEN header when making POST request to "https://api.website.com". Here's how I try to make requests:

declare module 'axios' {
  export interface AxiosRequestConfig {
    crossDomain?: boolean;
  }
}

const xsrfCookieName = 'XSRF-TOKEN'
const xsrfHeaderName = 'X-XSRF-TOKEN'

export const api = axios.create({
  baseURL: endpoint,
  timeout: 5000,
  xsrfCookieName,
  xsrfHeaderName,
  withCredentials: true,
  crossDomain: true
})

//...

export const postWarehouses = (warehouse: string) =>
  api
  .post('/admin/warehouses', warehouse, { withCredentials: true })

Also I tried to manually add header from cookie:

function attachCsrfToken(request: AxiosRequestConfig) {
  const csrfToken =
    ['POST', 'PUT', 'PATCH'].includes(request.method ?? 'GET') &&
    document.cookie &&
    document.cookie.length
      ? document.cookie.match(new RegExp(`${xsrfCookieName}=([^;]+)`))
      : {} as RegExpMatchArray
  if (csrfToken && csrfToken.length > 1) {
    request.headers[xsrfHeaderName] = csrfToken[1] ?? ''
  }
  return request
}

api.interceptors.request.use(attachCsrfToken)

Apparently, frontend can only read cookies on domain "https://admin.website.com". So what should I do?

My backend sets necessary headers:

access-control-allow-credentials: true
access-control-allow-origin: https://admin.website.com
access-control-expose-headers: Set-Cookie, X-XSRF-TOKEN

I also control creation of XSRF-TOKEN cookie, by the way. I would prefer to avoid "proxying" solution - merging my domains.

1 Answers

Have you tried testing if the issue is still present on mobile ? I had the same problem and it was basically the dev and the production csrf token conflicting and the issue was only for me, not the users, I solved it by deleting all the cookie for 127.0.0.1, web.site and api.web.site

Related