How to secure JWT tokens with a DRF backend supporting both mobile and SPA clients?
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/
: SupportsPOST
, requires validusername
andpassword
, returns a JWTaccess
andrefresh
/auth/refresh/
: SupportsPOST
, requires a validrefresh
token, returns new access and refresh tokens/protected/endpoint/
: Requires a valid access token
From reading on CSRF I have gathered:
- CSRF is only needed if cookies are involved, so only use for the SPA client
- To persist the JWT token:
SPA
: Persist in anhttpOnly
cookie withsecure=true
andsame-origin
setApp
: Persist in however the app persistent state works
So 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.
Questions:
- Am I correct CSFR is only needed when cookies are involved?
- If the answer to the previous question is "yes", can I avoid storing the JWT token in a cookie altogether for SPA, but still somehow securely persist SPA login across browser sessions and page refreshes?
- If the answer to the previous question is "no", then how can I support both types of clients while also preventing CSRF attacks?
- If I cannot get around CSRF tokens for SPA, how should I store the CSRF cookie (do I even need it to be in a cookie?)? I assume it will need at least
same-origin
to be set, to avoid leaking it to other domains, but does it also need to behttpOnly
andsecure=true
? I've read conflicting opinions on that. - How should I implement CSRF with DRF? The documentation for SessionAuthentication mentions CSRF but it is vague and even states that the default DRF CSRF implementation is not suitable for login views.
- Where does
CORS
fit into all of this? Can I use it to fix the problems I have?
EDIT:
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...