-
Notifications
You must be signed in to change notification settings - Fork 153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SPA and http-only cookies #52
Comments
Silently as in without the user noticing? Don't think so, because with http-only cookies, you cannot access them in JS, so you need the browser to explicitly visit another URL: the token refresh URL, where the server (Lambda@Edge) will read the JWT and do the refresh. The user will notice this redirect, even though the user is redirected back immediately. |
BTW if you deploy the solution in static site mode, all cookies will be HTTP only by default. |
Thank you very much for your prompt response. I was doing some tests. I hope I am not mistaken in the conclusions. This means that JS makes an API call, the lambda function checks if it is about to expire, if so, it forwards it to "/ refreshauth", where the lambda function silently updates the token from behind and returns 307 to URL of the original API (with the new cookie), due to the redirection settings of the fetch function it follows the new URL. Then the browser calls the redirect URL again, and finally the fecth function returns (with the original URL). To test this, I made a simple application, where a SPA is loaded and every 5 seconds it calls an API and updates a textarea with the result code and a little part of the JSON. As I show you in the following image, the application called the API, it updated the token, and for the spa application this was transparent. There was also no unwanted refresh page: I think this is a way to leave the token a little more protected in a SPA application. What do you think of this solution? Are there any considerations that I'm not considering? |
On the other hand, looking at the following line of code, I think there is a small bug: I understand that what is intended is to update the token 5 minutes before it expires. If so, 60 * 5 should be added, not subtracted. Or in any case subtract the variable exp - (60 * 5). In this way, either compare it for a future time, or compare it for a token with less expiration time. |
Indeed, nice catch! Wanna send a PR for it and grab the street creds? About your solution regarding refreshes. I can follow your general reasoning I think. But isn't a caveat that you would need to send all fetches to the refresh URL, and pass the actual URL you want to fetch to as a query param or so, so the refreshAuth function can return a redirect to it? While that will work, it is quite demanding on the front-end code I guess. |
Sorry, I think you don't explain me correctly. I'm going to explain it in other words and add a new option. I believe that these 2 options could be implemented to make the token refresh from SPA, without exposing the tokens. Option 1I will explain my previous solution again in other words, since my level of English did not help me. Assumption 1 - User has a token that expires in 20 minutes
Assumption 2 - The user has an expired or expired token in less than 5 minutes.
Conclusion:In both cases, the result for JS was the same: Some considerations:
Option 2Develop a new lambda function in a new URL, for example "/check-session". This function will verify that the token is correct and has not expired. With which you will have 3 ways:
Then from JS, you must do what is currently done (call this URL every 5 minutes): But instead of calling the authentication server, "/check-session" is called. You can also add it to return the payload of the jwt, instead of returning an empty 202. Conclusion:I think this is the best option. It does not imply many changes, it is transparent to the JS, and the tokens are well secured in only http cookies. |
On the other hand, in a while I will do PR with the small correction. |
Option 2 is actually already supported now I believe (maybe you are saying that). You could do a background XHR GET to index.html or any other page every 5 minutes, and that would trigger checkAuth, and token refresh if needed. Good suggestion! Maybe we should write a bit about it in README.md |
* #52: Time frame corrected to refresh token soon to expire. * tsconfig.tsbuildinfo file added to .gitignore
Storing tokens in non HttpOnly Cookie is a security vulnerability. No enterprise would allow it. We went through security review and this approach was rejected by InfoSec. Same vulnerability exists for Amplify implementation where they are storing tokens in non HTTPOnly cookies. |
This solution here supports storing cookies as HttpOnly, and the linked blog post also goes into this. It is all about trade offs ! Storing refreshToken at a specific path, if you want that, is possible too (ny the same mechanism). But it would need a small code change, because Ultimately, the solution here is a sample, that you can go on from, intended to show how you could do it, and speed you up. By all means change it to fit your security requirements, |
Sorry for opening this issue again, I am using this template to deploy a react app for employees to use in a company. Using non http-only cookies seems not best security practice. But in my case, the website is used by trusted employees. The website is not exposed on the internet to used by untrusted users, so this is not an issue. Am I right ? I am sending this jwt to an api gateway with cognito authorizer enabled that located outside of this cloudfront. |
Making cookies http-only is a way to protect against malicious JavaScript. If somehow malicious JavaScript got deployed on your site, it could e.g. steal the credentials from logged in users. But if your SPA needs to access the JWTs also because it uses them in client side fetches, then you have no choice but to make the cookies NOT httpOnly. A middle ground is to just make the refreshToken httpOnly with cookie settings such as: {
idToken: "Path=/; Secure; SameSite=Lax",
accessToken: "Path=/; Secure; SameSite=Lax",
refreshToken: `Path=/refreshauth; Secure; HttpOnly; SameSite=Lax`,
nonce: "Path=/; Secure; HttpOnly; SameSite=Lax",
} But then, if your JWTs expire, you must make some request to CloudFront so that Lambda@Edge triggers and refreshes your cookies (because you can't do it client side anymore, as you can't access the refresh token there). Such as: #190 (comment) |
I have a SPA application and it makes calls to an API. I had some doubts based on the examples.
Can the token be refreshed silently, using a SPA and http-only cookies?
Why does the SPA example use cookies shared with the client? In the event that it can be silently refreshed using http-only, I see no reason to expose the token and refresh token to js attacks.
The text was updated successfully, but these errors were encountered: