-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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 with Implicit Grant - handling token refresh #1218
Comments
(Putting this here to avoid duplication. I have the same problem and describe my setup below): Hey, it seems as though Amplify only handles credential refresh if we are using the 'code' / Auth flow. What is the guidance for automatically refreshing tokens if we are using 'token' / implicit flow, and have no refresh token? At the moment, Amplify just falls back to making unauthenticated requests. I would love to be able to use implicit flow and not have to worry about my session expiring. Ideally I would like to be able to do this silently without interrupting the user's workflows /refreshing the page. Is that something that is possible? I've found it very difficult to answer these questions, but would be happy to contribute to documentation if I could come up with a sanctioned solution. For reference, my setup is:
const url = `https://${domain}/oauth2/authorize?client_id=${clientId}&redirect_uri=${redirectSignIn}&response_type=${responseType}&prompt=none`;
window.location.assign(url);
I'm seeing that after my session expires, amplify tries to refresh my access token using the refresh token, but there isn't one since I'm using token / implicit flow. Failing that, it seems to give up and use guest user instead, which my identity pool doesn't (and won't) support. Thanks for your help! |
I am also having this same issue. Will a dev take a look at this please? |
A followup to my original post: After spending quite some time on the issue together with my colleagues and investigating different options, we came to a conclusion that this is simply not possible with Cognito. It is not an Amplify issue but rather Cognito as is. There is currently no way to perform a silent refresh when using the implicit flow. The reasons are following:
So... a bummer. I wish somebody would come here and bashed me to the ground that I'm wrong and stupid and there is actually a way, but I'm afraid that won't happen. |
I have the same issue as well. This is a real blocker for using Cognito with implicit flow. Has any one solved silent refresh using cognito? |
I'm also seeking for a resolution to handle refresh token for implicit flow since we are working on the SPA. Using authorization code flow can retrieve refresh token but it doesn't good because of security concern. In this example, you will see the response_type=code is required, but it should only be used for the mobile app. Many examples using authenticateUser API, as the result, a refresh token will be stored at the client site (on the browser) - it doesn't a best practice, does it? The authenicateUser API will be received the same response with the authorization code. How about if we implement a storage from the backend and apply authorization code (or using common authenticateUser API)? Do we have any disadvantage or any security concern we need to take care? |
This is unfortunately still not possible. I spend some days last year to resolve this problem, but ended up with a cookie-based solution utilising passport.js with nuxt.js. Not exactly ideal, but there was simply no other way around implicit grant with Cognito without having to re-login the user. |
It seems implicit flow is only possible with Cognito user pools, not identity pools. |
This SDK helps. |
I have long wondered, what is so bad about using auth code grant with an SPA? The only difference, from what I can see, is that the refresh token is stored in the user's browser. Obviously, if someone gets access to that refresh token, then they can impersonate the user for the duration of the refresh token. That refresh can then be revoked (globalSignout). And by comparison, the alternative is that you're using auth code grant and storing/handling the refresh token in your own backend. In which case, you still have to provide your user with a cookie or token to store in their browser, which could be used to access your backend for the expiration-duration of that cookie or token. What's the difference? Why is the first scenario any worse? |
Its not really that bad, since client_secret can be hidden and the refreshToken ttl can go all the way down to 1day. As far as i can see there is a lack of PKCE support in the aws-amplify SDK for Authorization Code Grant, unless HostedUI is utilised, which is not an option in my use-case. |
I am having a similar scenario and a similar issue. Has anyone solved this one yet? |
@vanpra1 The current parsing url to get authentication function is bounded with the use of hosted UI. In my case, I don't use the hosted UI. So I use the following codes to parse the URL and create the Auth user session. const oauth = {
I am using typescript, the _cognitoAuthClient is a private field of AuthClass. I didn't find an easy way of creating CognitoAuth from amazon-cognito-auth-js. So I ended up above codes to ignore the type checking. With code grant, it automatically hit the token endpoint to get access token, id token. @powerful23 does the above codes look reasonable to you? |
This is what I'm currently testing. Not sure if it's perfect or handles everything, but it seems to work. It's part of a Vue SPA using vue-router. This is called on every router page change. There are commented out lines that I used when figuring out everything.
|
This is still not supported. Marking as a feature request for syncing visibility with the Cognito team. |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
@claurin Can you elaborate on this? It seems to me this issue is affecting user pools. |
as I read here is not safe to store refresh tokens on an SPA because all of the application code and storage is easily accessible. What if I use code grant and delete the RefreshToken from the local storage and store it on the server? then, when the app whant to refresh the tokens, first it retrieve the refresh token, use it to refresh the other tokens, and then erase again from local storage? is not 100% safe, but no one could copy/paste the refresh token form localStorage. |
Why not have the server return a secure cookie containing the refresh token. The spa need not then worry about submitting a refresh token, the cookie will be submitted by the browser automatically, just adapt your server side code to check for it..? |
This is not really an issue related to amplify-js but rather Cognito. Quite a bummer that this has not been resolved within 2 years... |
Wow... Reported in 2018 and it's now 2021. Not a good look for even a small company. I would call this a BUGI have never ever seen an Implicit Grant Flow that:
Perhaps Amazon is planning to deliver my new token by Drone, which looks like will happen faster than resolving this pathetically embarrassing BUG. |
Yeah, this is a bit of a complete joke really. Imagine investing so much time into learning Cognito and all it's horrible flaws - just to get everything working... but not be able to silently refresh tokens on the Client. Seriously AWS, what is this. |
hey folks, we are actively working on this issue with the cognito team and will we will have a release as soon as the cognito service updates are out. We understand your frustration but please understand that we are actively working on this issue. |
@agileurbanite do you know when a fix will be released ? in days/weeks/months ? If you need someone to make some tests I can offer my help as i'm working on it. My Stack is C# .Net Blazor |
Also finding that secure key management is a blocker to using Cognito, which otherwise would serve my use case quite well. Interested to understand a timeline and what specific features are planned to address this. |
We are also hoping AWS can make some progress on this. We were hoping to use Blazor with Cognito but we're not having much luck whereas getting up and running with identity solutions from other providers is a breeze. This issue alone may push us towards embracing another cloud provider for our next project (despite our preference to continue working with AWS). Hopefully some progress can be made (quickly). |
+1 our team is hoping for a timely solution. This issue makes it impossible to build a nice SPA UX with Cognito. @agileurbanite @aws-amplify-ops do you happen to have an update on this? |
any news? |
Looks like @agileurbanite is no longer with AWS... does @aws-amplify-ops have someone else working on this item? |
More than a year later... any news? |
Don't pressure them! They've already invested 4 years of time and sweat to solve this issue for us ❤️ |
hey, guys spent the last 5 days trying to identify this, and ended up here. For people who faced the bugs, which other auth provider would you recommend. I don't want to spend too much time, going through the libraries to have to figure out what the issue is, which is what I did in this case. |
... And still no resolution... Now I understand why people are switching to alternative solutions from Cognito. Important issues are not solved for years. |
We switched to Supabase. (not affiliated, just a fan.) |
FusionAuth is also an option (not affiliated as well but they're actively developing and listening to users' feedback) |
TL;DR Use the authorization code flow with a stateless backend to store long-lived refresh tokens safely in As OP mentioned, it's really not possible to build a decent UX on top of Amazon Cognito using the implicit flow due to the lack of refresh tokens. Note that this behavior isn't a Cognito-specific limitation; the OAuth2 spec prohibits the implicit flow from returning refresh tokens:
As a result, the implicit flow causes users to be logged out whenever the short-lived access token expires. This leads to a similarly frustrating user experience as the AWS Console. Aside from the UX issues, the IETF's Best Current Practice (BCP) for browser-based OAuth2 apps requires the use of the authorization code flow. There are a couple of secure, viable alternatives that work well, but Amplify doesn't make them particularly easy. Both of these options require a (stateless) token-mediating backend API in order to keep refresh tokens inaccessible to client-side JavaScript code. This is an important defense in depth measure so that even if an app is vulnerable to cross-site scripting (XSS), an attacker won't be able to steal users' long-lived refresh tokens. Important Storing refresh tokens in local/session storage or client-accessible cookies means that JS code has access to them, and that includes any JS code an attacker is able to introduce via XSS. If refresh tokens are instead stored in Unfortunately, all of Amplify's built-in token storage mechanisms lack defense-in-depth against XSS. This is particularly troubling since Cognito doesn't rotate refresh tokens after use, a requirement in the BCP. If you don't want to follow current security best practices, consider using Amplify's built-in Authenticator. If you want to follow the recommendations of application security experts and protect refresh tokens from XSS exfiltration, consider the options below. Option 1: OAuth2 Authorization Code Flow w/ Token-Mediating BackendAdvantages
Disadvantages
The full solution with important security details is described in the BCP, but briefly, it entails the following steps:
When an access token expires:
In this solution, the registered Cognito user pool client is the backend API, not the frontend app. It's a confidential client that should use a client secret when communicating with the Cognito token endpoint. The allowed callback URL registered for the Cognito client should correspond to the backend API. When implementing this solution, be sure to pay attention to the BCP for details around CSRF protection, PKCE, etc. Option 2: User Pool API (non-OAuth2) w/ Token-Mediating BackendAdvantages
Disadvantages
This solution uses Cognito's The steps needed are similar to Amplify's
When an access token expires:
Both of these solutions take a fair amount of effort to implement, but if there's interest, I may open source an implementation. Please let me know if this would be useful, and which solution is more relevant! |
Thanks for this detailed write up of the options and considerations that go into deploying cognito. I am currently using some variation of option 2 but I have considered implementing option 1 soon, but with a stateful back-end that could issue cognito JWTs in exchange for a longer lived HTTP only session cookie. I think the value proposition of cognito is quite slim if you are just proxying calls from a back-end, at that point you may as well have implemented your own auth on top of an established library or product. Using the federation and social login features seem like one of the only reasons you'd use cognito, and thus you'd think option 1 would be generally more valuable. Still a shame you have to essentially manage your own proxy to make cognito useful. |
Do you want to request a feature or report a bug?
A bug/feature request together perhaps. This has to do with both Amplify and Cognito.
What is the current behavior?
When using Cognito + Amplify with a SPA javascript app (Vue app in my case), the only proper way to implement authentication is to use the Implicit Grant (reasoning for example here). Using the Implicit Grant, Amplify is unable to automatically refresh the tokens after they expire.
The sources (like the one linked above) recommend to use a silent authentication or silent refresh to renew the tokens. The problem is, that Amplify doesn't provide any way to do this - and Congnito doesn't support it either (there is no
prompt=none
support as described here). This makes it impossible to design the SPA properly and provide a nice user experience.What is the expected behavior?
I would expect that there is a way to renew the tokens when using Implicit Grant somehow.
I realize that this is probably more Congnito issue than Amplify issue, but since you guys probably work quite close together, I hope you could maybe provide some tips how to make Amplify properly usable with a SPA app. Thank you!
There is a discussion about the same thing in another repo, without a solution: amazon-archives/amazon-cognito-auth-js#92
The text was updated successfully, but these errors were encountered: