I am developing an application that uses the Django REST Framework to provide a REST API.
I intend to protect it using token authentication, with the help of simple_jwt
.
Example API overview:
/auth/login/
: Supports POST
, requires valid username
and password
, returns a JWT access
and refresh
/auth/refresh/
: Supports POST
, requires a valid refresh
token, returns new access and refresh tokens/protected/endpoint/
: Requires a valid access tokenFrom reading on CSRF I have gathered:
SPA
: Persist in an httpOnly
cookie with secure=true
and same-origin
setApp
: Persist in however the app persistent state worksSo far so good, however, this would mean that my REST API needs to both require CSRF tokens for requests coming from the SPA and not require/ignore CSRF tokens for requests coming from mobile app clients.
Since this is logically impossible I was first thinking of implementing 2 separate APIs. I.e.:
/spa/auth/...
, /spa/protected/...
: For SPA clients, where all endpoints require a CSRF token/mobile/auth/...
, /mobile/protected/...
: For mobile clients, where all endpoints are CSRF exempt.But doing this just means that my CSRF protection is useless, since a malicious actor can just target my mobile API instead of my SPA API.
I also read about a bit about CORS but I am not sure how it fits into all of this.
same-origin
to be set, to avoid leaking it to other domains, but does it also need to be httpOnly
and secure=true
? I've read conflicting opinions on that.CORS
fit into all of this? Can I use it to fix the problems I have?I've thought about this more - what if I subclass the CSRF middleware and allow it to be bypassed if there is an Authorization
header present? Then the request can be granted or denied by the authentication middleware directly (effectively I am assuming that if there is an Authorization header I am dealing with a mobile client, which does not need CSRF protection).
Is that a bad idea?
In case it isn't, then how should the LoginView look like? It should still be protected against CSRF but neither the mobile, nor the SPA clients will have a token to set as the Authorizaton
header...
I wasn't aiming for a self-answer, but after more research and a lack of useful responses from people here I have settled on the following approach:
X-MyApp-Auth: Bearer <token>
<userid>:<password>
pair (HTTP Basic Auth) instead of a JWT token. On successful authentication, I shall return a JWT access and refresh tokens in the response body.Additionally:
forbidden request headers
(for example - Origin
, Referer
, Sec-Fetch-Dest
, etc) in the request and present, I shall consider the client to be an SPA.same-origin
and secure=true
set unless the server is in debug mode)Token storage:
Rationale