Comments (14)
On the other hand, in a while I will do PR with the small correction.
from cloudfront-authorization-at-edge.
Can the token be refreshed silently, using a SPA and http-only cookies?
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.
from cloudfront-authorization-at-edge.
BTW if you deploy the solution in static site mode, all cookies will be HTTP only by default.
from cloudfront-authorization-at-edge.
https://github.com/aws-samples/cloudfront-authorization-at-edge#spa-mode-or-static-site-mode
from cloudfront-authorization-at-edge.
Thank you very much for your prompt response.
I was doing some tests. I hope I am not mistaken in the conclusions.
Although it is as you say, the JS does not have the capacity to renew token when it expires. But there is one interesting property of the “fetch” function (JS) can be used, which is to set the “redirect” property to “follow “(by default browsers take it this way). https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
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).
Sorry if the explanation is poor or not well understood. I don't have good english.
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.
In the event that the SPA needs the JWT payload, a lambda function could be made that only returns the payload without revealing any extra information.
What do you think of this solution? Are there any considerations that I'm not considering?
from cloudfront-authorization-at-edge.
On the other hand, looking at the following line of code, I think there is a small bug:
https://github.com/aws-samples/cloudfront-authorization-at-edge/blob/master/src/lambda-edge/check-auth/index.ts#L32
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.
from cloudfront-authorization-at-edge.
60 * 5 should be added, not subtracted
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.
from cloudfront-authorization-at-edge.
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.
In both cases, in the authentication server it uses the “hybrid flow”, with a client secret (since it is only known by the server in the lambdas functions), and with offline access in the scope (to be able to refresh the token calling from lambdas functions). And Always the server is the one that updates the tokens.
Option 1
I will explain my previous solution again in other words, since my level of English did not help me.
To help my reasoning, I am going to use a different example from the one I mentioned earlier (POST method instead of GET method): A user has a form with a field that POSTs the URL “/api/machine”. Sending the name of a new machine. The post method in the API creates a machine and returns code 202 with the data of the created machine.
Assumption 1 - User has a token that expires in 20 minutes
- The user makes a post to the URL “/api/machine”, with the data of the machine.
- The lambda (check-auth) function verifies the token, as this is correct, it continues its course towards the API.
- The user receives an HTTP code “202”, with the information of the created equipment.
Assumption 2 - The user has an expired or expired token in less than 5 minutes.
- The user makes a post to the URL “/api/machine”, with the data of the machine.
- The lambda function (check-auth) verifies the token, as this is expired or soon to expire, and the user has a refresh token, redirect it to the lambda function that is responsible for renewing the token (refresh-auth).
- The browser receives a 307 (redirected to the lambda function). Because it is a redirect, the JS fetch function in the code if configured with the "redirect" attribute in "follow" (default value), calls the redirect without the JS code knowing.
- The browser redirects the post to the new address, which is serviced by the lambda refresh-auth function. The function is responsible for calling the identity server, and with the client id, the secret client and the refresh token, it requests a new token. If everything goes well you get new tokens (this is all from the lamba function). And again it sends a redirect (307) response to the original URL (“/ api / machine”), but with the new cookies.
- Again, the browser receives a 307 (redirected to the original / api / machine address).. Because it is a redirect, the JS fetch function in the code if configured with the "redirect" attribute in "follow" (default value), calls the redirect without the JS code knowing.
- The lambda function (check-auth) verifies the token, as this is correct, it continues its course towards the API.
- The user receives an HTTP code “202”, with the information of the created equipment.
Conclusion:
In both cases, the result for JS was the same:
Some considerations:
- Regarding the overhead you point out, these redirects only happen when the tokens expire (for example, if it were every 20 minutes, 2 redirects would happen every 20 minutes, I don't see it as a problem). It would be great to be able to do it behind the scenes without JS suffering these redirects, but for that the lambda function edge should update the token, call the api, and change the cookies (request and response event), I am new to lambda Edge, but I understand that this cannot be done.
- Is important to note that in JS the fetch functions must be configured so that the redirects automatically follow. In the case that they have another configuration, this would not work. With which it can be a great disadvantage, since it would be good for developers to forget about this. Or maybe the api returns redirect and the JS needs to see them, and this would be a problem.
- The refresh-auth function must tolerate all HTTP methods (not just the get).
Option 2
Develop 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:
- Token valid and not expired: Only returns Ok (202)
- Token valid, but expired or soon to expire: The function is responsible for calling the identity server, and with the client id, the secret client and the refresh token, requests a new token. If everything is correct, restart Ok (202), but with the new cookies.
- Token invalid or renewal fails: Authentication error 404. This must be handled by JS. Here there is no point in doing a redirect, since the api is only called from JS once it is authenticated.
Then from JS, you must do what is currently done (call this URL every 5 minutes):
https://github.com/aws-samples/cloudfront-authorization-at-edge/blob/master/src/cfn-custom-resources/react-app/src/App.js#L38
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.
from cloudfront-authorization-at-edge.
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
from cloudfront-authorization-at-edge.
Thank you so much for your patience. I think it is important that this is found in the documentation.
One last point: Although the token is refresh, either in the form calling the identity server every 5 minutes, or doing XHR GET to index.html every 5 minutes. I think that the check-auth function is not correct, because it can give that it is called in a time window that the token has not been updated yet, and JS will receive a redirect without knowing how to handle it.
Example:
I think that the API calls should only validate that the token is valid or if it is invalid return 404. It is not something that should be changed in the project, but I think it should be kept in the documentation.
from cloudfront-authorization-at-edge.
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.
Also RefreshToken should be stored at specific path (/refresh-token)
from cloudfront-authorization-at-edge.
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 check auth
lambda, which is attached to the default behavior, now checks to see if there is a refresh token
before forwarding to the refresh tokens endpoint. That would not work, if check auth
can't see the refresh token - so then it would send you to Cognito to sign-in again
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,
from cloudfront-authorization-at-edge.
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.
from cloudfront-authorization-at-edge.
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)
from cloudfront-authorization-at-edge.
Related Issues (20)
- Unable to change the code and update deployment HOT 8
- Issues with the refresh endpoint endlessly redirecting after signin HOT 33
- CloudFormation did not receive a response from your Custom Resource HOT 19
- Content Security Policy: The page’s settings blocked the loading of a resource at inline (“script-src”) HOT 2
- Refresh issue after token expires HOT 8
- On signout Required String parameter 'redirect_uri' is not present HOT 5
- Possible Open Redirect (CWE-601) in sample code HOT 2
- nonce cookies are not expired HOT 1
- [Feature request] Support multiple Cognito user pool clients HOT 4
- custom domain is not redirecting to cognito hosted ui HOT 1
- Getting blocked by CORS policy but unable to figure out the source HOT 5
- Node version bump HOT 7
- Custom IDP with Amplify and Auth at Edge HOT 9
- Fail on delete of the stack HOT 3
- Function must be in an Active state error on deploying the solution HOT 7
- Errors from Lambda when destroiyng the stack HOT 2
- Cognito TAGS HOT 1
- How Do I add User Pool attributes to Cookies? HOT 1
- A potential risk in cloudfront-authorization-at-edge which can be used to upload malicious code. HOT 4
- Having the ability to tune logs HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from cloudfront-authorization-at-edge.