I'm building an application which will support both browser and application access to REST resources.
- Applications will
POST
a username/password JSON body to a login endpoint which will return a signed JWT token in the response body. This token will be stored by the application and included via bearer auth, i.e.Authorization: Bearer {{ token }}
. - The browser will also use a JSON
POST
request, albeit probably to a different endpoint which will return the JWT token's headers and metadata without the signature in the response body, and will set aHttpOnly
cookie so that the actual credentials cannot be stolen via XSS.
HTML forms will not be used, but I know that I'll still need to make sure that mutable HTTP verbs include Content-Type: application/json
to prevent CSRF with forms that are architected to look like JSON bodies.
I'm struggling now to figure out how to do double-submit CSRF tokens with my application, as my HTML for the application, which is a single-page-application (SPA) in JavaScript, is served from a static hosting service. Embedding the token in a <meta>
attribute in the DOM is thus not possible.
What I think might work is to expose an endpoint like /api/csrf
with restricted CORS which sets a HttpOnly
CSRF token and returns JSON with the CSRF token in the body for double-submit verification before any API requests take place. The token will only be stored in browser memory and not in local storage or cookies accessible by JavaScript. The double-submit will occur by setting a header on REST requests like X-CSRF-Token
, which will be permitted by CORS.
I have two questions:
- Will this method of double-submit (obtain inaccessible cookie and obtain copy in JSON response, set HTTP header including token in JavaScript) work and protect my API against CSRF attacks, or does getting the token over REST somehow defeat the purpose?
- If I enable this CSRF protection for my API, will I also need to force non-browser applications to double-submit CSRF tokens as well? Or is there a safe way to do CSRF mitigation only on requests from browsers?