I'm trying to implement auth between a Next.js frontend and a Rails API backend, and I'm having difficulty understanding the how to properly do this securely.
I'm using the jwt-sessions gem on the rails side, which upon login returns an access_token
, a refresh_token
and a csrf
token. The csrf
can only be sent via header, and is required for all non-GET or HEAD requests. The access
and refresh
can be given via header or by cookies.
I can implement the backend to either return all the tokens in a JSON response, or set cookies, or a mix of both.
The problem is that, client-side I see no real way to store these tokens that is:
- Secure
- Available during SSR as well as in the browser
Here are the options I see
Option 1: Server-Set httpOnly Cookies
This is what the jwt-sessions
gem recommends. Keep CSRF in localStorage, but the other tokens in httpOnly cookies set by the server.
This seems the most secure, but then SSR via getInitialProps
won't work at all, because it can't send the cookies via fetch
. I can't even manually send them because I can't see them in JS.
Option 2: non httpOnly Cookies
This is what the official next example seems to do (albeit with a single, probably stateless token)
Either have the server or the browser set the cookie without httpOnly.
I can access it via SSR, but doesn't this open me up to XSS?
Option 3: Use Browser Storage
Just put all the tokens in localStorage
and/or sessionStorage
. This seems to be the worst option, as it won't work in SSR, and to my knowledge is not secure.
????
Am I missing something? Is non-httpOnly, like in the official example, OK? Is there a better approach? Or will I have to ignore SSR (and thus one of Next.js's killer features)?
jwt-sessions
gem? If not, thejwt-sessions
gem can't set cookies that the next.js server side rendering can see. – Cirdeccsrf
can only be sent via header, and is required for all non-GET or HEAD requests to the rail backend, so it needs to be passed to the next.js server side any time the server side might make a non-GET or HEAD request to the backend? – Cirdec