Skip to content
This repository has been archived by the owner on Mar 12, 2020. It is now read-only.

Refresh access and id tokens in a React/Angular SPA #92

Open
ravenscar opened this issue Feb 20, 2018 · 39 comments
Open

Refresh access and id tokens in a React/Angular SPA #92

ravenscar opened this issue Feb 20, 2018 · 39 comments

Comments

@ravenscar
Copy link

Summary

I would say that without Cognito implementing prompt=none on the /oauth2/authorize endpoint, and whilst the cognito cookie on <your_domain>.auth.<region>.amazoncognito.com/ expires after 60 minutes instead of 30 days (or what is set for the user pool) it is unsuitable to be used as an out-of-the-box solution for Single Page Applications.

Details

The recommended OAuth2 flow for a Single Page Application (SPA) is the Implicit Grant. This flow, by design, does not issue a refresh token to the web app.

There are many issues in this project around refreshing tokens that seem to be resolved by switching to Authorization Code Grant instead, which is OK for some native apps and some non-SPA web apps (e.g. where there are round trips to a server) but is unsuitable for SPA apps.

It is considered unsafe and bad practice for a web application to store a refresh token in session storage, local storage, or in javascript accessible memory as this opens up the app to many attacks which could result in the refresh token being exposed to a malicious third party which is now able to obtain ID/API tokens for the user.

The OpenID Provider (OP) usually sets an http only secure cookie on user login, and this is used for refreshing in this case the OP is awscognito and the cookie would be set on <your_domain>.auth.<region>.amazoncognito.com/. Cognito does set this cookie.

The typical process for refreshing tokens in an SPA is to call the OP's authorize endpoint with a prompt=none in the query string. This would normally occur in an embedded iframe in the SPA so as to not alter the UI and would either return new tokens which can be stored or an error, which deletes the tokens.

This is not a standard or requirement as it is up the the OpenID Provider to determine how a user is authorized, however if this is not implemented and it means that an SPA must choose to log in whenever the access token expires, or to adopt poor practices and store refresh tokens in the SPA (there is another option where we could have our own server which stores the refresh tokens and can refresh api/id tokens and deliver them to the SPA).

This partially works using cognito hosted UI, there is a cognito cookie stored when you login at <your_domain>auth.<region>.amazoncognito.com/login?client_id=<your_client_id>&response_type=token&redirect_uri=<your_redirect> and if you subsequently call <your_domain>auth.<region>.amazoncognito.com/oauth2/authorize?client_id=<your_client_id>&response_type=token&redirect_uri=<your_redirect> it will redirect with new tokens, with updated timestamps, without requiring interaction.

Unfortunately the cognito token expires after 60 minutes so it can only be used to extend the session to a maximum of 120 minutes. Also since the prompt parameter is not implemented there is no way for the iframe to get an error so it cannot be used in an iframe.

References

@hhp21
Copy link

hhp21 commented Feb 23, 2018

Any thoughts on this? It seems to me that it makes implicit grant totally impractical to use.

@a053a
Copy link

a053a commented Mar 1, 2018

I'd prefer if AWS allows the configuration the id_token and session cookie expiry TTL based on your own requirements.

@cervantek
Copy link

cervantek commented Jul 2, 2018

This seems like a deal breaker for using Cognito User Pools for SPA development. I am seeing the cognito cookie valid for 8 hours though now, so that is a little better. I agree though that without the prompt=none option, the re-authorize cannot be transparent to the user.

@ppasmanik
Copy link

Why there is no response from aws team on this??? This is making implicit flow absolutely useless. I am not sure how @cervantek got cognito cookie to work for 8 hours. I see only 1 hour, so you are stuck with maximum 2 hour for tokens if you do a workaround and make a call to oauth2/authorize endpoint for hosted login.

@luomavaltteri
Copy link

Wondering the same thing! This is a MUST feature for any real-world use.

@gercorr-gw
Copy link

gercorr-gw commented Aug 1, 2018

This seems to be a vital feature for user experience. @cervantek do you have details on how to increase the cognito cookie timeout? Though even that would not fully solve the issue.

@nihakue
Copy link

nihakue commented Aug 1, 2018

The document that you referenced assumes that refresh tokens last indefinitely, but I believe that with Cognito User Pools you can configure the refresh token TTL

I don't know what security experts think about e.g. reducing the refresh token TTL to 1 day and storing it in the local storage of a SPA, but it seems no worse than increasing the TTL of the access token.

@a053a
Copy link

a053a commented Aug 1, 2018

AWS needs to implement this properly by giving us the ability to configure cognito cookie timeout. If implemented, then using a proper Oidc JS library would get a new id_token and token periodically.

@Beretta1979
Copy link

We need this feature too

@ppasmanik
Copy link

I agree with @nihakue . After talking to AWS team we went with that solution - setting refresh token ttl to 1 day, and doing refresh token every hour with auth code flow.

@a053a
Copy link

a053a commented Sep 12, 2018

@ppasmanik @nihakue Although this is technically possible, it’s considered bad practice to store refresh tokens for SPA apps. AWS really needs to close in on this so they don’t keep putting applications at risk.

@rajwilkhu
Copy link

Could someone from the AWS team respond to this? Came across this issue whilst trying to implement silent refresh. I am now considering moving away from Cognito to something that supports the implicit flow better for SPAs.

@luomavaltteri
Copy link

luomavaltteri commented Sep 26, 2018

Could someone from the AWS team respond to this? Came across this issue whilst trying to implement silent refresh. I am now considering moving away from Cognito to something that supports the implicit flow better for SPAs.

@rajwilkhu It's been very quiet for a long time. Choose something that supports silent refresh.

@maziarz
Copy link

maziarz commented Oct 16, 2018

While this may sound (very) crazy. What if the refreshToken had a more granular TTL, such as minutes or hours. Wouldn't this get us slightly closer to a silent auth/refresh solution?

@a053a
Copy link

a053a commented Oct 16, 2018

@maziarz while it might help, it's actually the wrong solution. Refresh tokens shouldn't be used in SPA apps; rather, use the session cookie controlling the refresh, e.g. cognito to use session cookie to see if session cookie is still valid and reissue another token and update session cookie if warranted.

@maziarz
Copy link

maziarz commented Oct 16, 2018

@Salmonz its not that i disagree, i ran into this problem 1.5 years ago and ended up implementing Cognito with passport.js in the back utilising secure cookies. But eventually it removed all benefits from being truly "serverless" and having low maintenance on a SPA. I do not believe that anyone has solved this issue in an elegant way, even auth0 is relying on a valid SSO session in order for this to actually work with prompt=none. It does not look like Cognito team are going to introduce a cookie mechanism any time soon..

@a053a
Copy link

a053a commented Dec 5, 2018

AWS - do you have an update on this issue? Seems to me that the sign-in cookie expiry should be configurable in the AWS portal for a cognito instance, or per client configuration.

@adlaika
Copy link

adlaika commented Jan 11, 2019

Any update on this?

@carvercj
Copy link

Any updates??

@vanpra1
Copy link

vanpra1 commented Feb 18, 2019

Any progress on this?

@valecarlos
Copy link

any updates?

@AshUK
Copy link

AshUK commented Mar 15, 2019

+1 for silent renew support on implicit grants

@dpistole
Copy link

Sub'd, this is a deal breaker for using cognito user pools with SPA, would be nice to hear something from AWS.

@sjbthfc2
Copy link

AWS: please give us some sort of update on this issue? What workarounds can you propose for SPA apps, do you have any plans to deal with this? The silence is deafening!

@jeremiahsmall
Copy link

I guess we'll have to use Auth0 again.

@sebastienfi
Copy link

The typical process for refreshing tokens in an SPA is to call the OP's authorize endpoint with a prompt=none in the query string. This would normally occur in an embedded iframe in the SPA so as to not alter the UI and would either return new tokens which can be stored or an error, which deletes the tokens.

The iFrame flow does not work on the latest Safari.
Auth0 warns about it here: https://auth0.com/docs/api-auth/token-renewal-in-safari

So I guess all we have left is

there is another option where we could have our own server which stores the refresh tokens and can refresh api/id tokens and deliver them to the SPA

@sebastienfi
Copy link

I agree with @nihakue . After talking to AWS team we went with that solution - setting refresh token ttl to 1 day, and doing refresh token every hour with auth code flow.

@ppasmanik Can you please elaborate?

@ravenscar
Copy link
Author

@sebastienfi

I believe they are using the Authorization Code Grant instead of the Implicit Grant to get a code that can be exchanged for a refresh token, storing the refresh token in the SPA, and refreshing the access/id tokens hourly.

Storing the refresh token client side in a web app would normally be considered very insecure however they have limited the lifetime of the refresh token to 24 hours, which is a tradeoff they have had to make as AWS provides no silent auth.

I guess the upshot is that users would have to authenticate daily rather than hourly.

@sebastienfi
Copy link

@ravenscar Thank you for mentioning me - and for these clarifications!

@ppasmanik
Copy link

ppasmanik commented Apr 25, 2019 via email

@jeremiahsmall
Copy link

Storing secrets in local storage is the entire problem. The workarounds described are too insecure for some to consider.

@ppasmanik
Copy link

ppasmanik commented May 6, 2019 via email

@leotohill
Copy link

Any thoughts on this? It seems to me that it makes implicit grant totally impractical to use.

Actually, I think you mean that the auth code grant is impractical. Only the auth code grant provides a refresh token. Also, the implicit grant is inadvisable these days.

@leotohill
Copy link

@ppasmanik , "You need both unexpired token and refresh token to renew a token."
Actually, that's not correct. You need only the refresh token. See section 6 of https://tools.ietf.org/html/rfc6749, also https://www.oauth.com/oauth2-servers/making-authenticated-requests/refreshing-an-access-token/

@ravenscar
Copy link
Author

ravenscar commented Jul 12, 2019

Actually, I think you mean that the auth code grant is impractical. Only the auth code grant provides a refresh token. Also, the implicit grant is inadvisable these days.

@leotohill not sure I follow you here. Auth code grant cannot be used securely in a SPA because it returns a refresh token which cannot be securely stored.

When implemented correctly by an auth provider implicit grant can allow refresh without the token being stored in a place inaccessible to javascript, this is desirable if you are not auditing every node module in your dependency tree.

Do you have a recent source saying why this is suddenly inadvisable? What is the alternative "safer" way of storing refresh tokens in an app where all javascript code cannot be trusted.

@leotohill
Copy link

@ravenscar yes, this ietf best practice document.. The auth code grant is a more secure flow, and the refresh token can (should be) discarded, or not issued in the first place if that's an option.

Not sure i fully understand what you are saying about the auth provider "allow refresh without...", but could this work anyway, even if the token is acquired via the auth code flow?

I too find that this advice makes things challenging. I'm thinking of setting up an server where the refresh token can be stored server-side, and the server executes the refresh operation, returning the new token to the client. This would require a cookie-based session with the server.

@ravenscar
Copy link
Author

@leotohill interesting read but not sure it's worth trading implicit grant headaches for CORS headaches. Maybe in 5 years if CORS allows multiple origins.

I am not sure if the silent refresh would work with auth-code flow. I can't see a reason it wouldn't, but I guess since it's optional it's up to the implementer.

Your idea seems to be re-implementing the out-of-the-box functionality of other auth providers such as Auth0. I'd advise just to use their services and forget that cognito user pools exists.

Even when you get past this hurdle you have a dozen more, such as discovering that the sign-up and log-in emails are case sensitive, so joe@gmail.com can't login if he signed up as Joe@gmail.com.

Nothing AWS has produced has disappointed me as much as cognito user pools. Shouldn't event be in beta.

@ajhool
Copy link

ajhool commented Aug 31, 2019

@ravenscar @sjbthfc2 @ppasmanik @sebastienfi

The last commit to this repo was 6 months ago. Please submit a ticket to the Support Center from your AWS Console (top right -> Support -> Suppor Center) if you want to actually get AWS' attention on this issue -- and they have a contractual obligation to respond to you within 24 hours (or less if this is a critical vulnerability and you are on a higher support plan)

I submitted a ticket to AWS support center, which hopefully has a channel to a different Cognito team than this open-source sdk team.

AWS makes a(n arbitrary IMO) distinction between open-sourced and closed-source/backend services, so I urge everybody else to open support tickets. Particularly if you work at a large enough company that you have the top-tier of customer service or work at a mid-large company and depend on cognito.

I agree with the other sentiment that cognito is a fundamental service, a fundamentally flawed service, and the one that makes me wonder if AWS has stretched itself too thin but the alternative providers in this area all have issues of their own, too.

@evbo
Copy link

evbo commented Oct 4, 2019

fyi, AWS' official client library for cognito was able to confirm that better support for SPA authentication use-cases is in the Cognito team's backlog:
aws-amplify/amplify-js#3436 (comment)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests