aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.38k stars 2.1k forks source link

Sensitive information are being persisted in local storage #3436

Open jiachen247 opened 5 years ago

jiachen247 commented 5 years ago

Describe the bug All cognito session tokens id, access and refresh tokens are being persisted into localstorage. This goes against all industry security best practice of storing sensitive infomation in signed httponly cookies.

To see why its bad practice this article presents a summary.

Also known as Offline Storage, Web Storage. Underlying storage mechanism may vary from one user agent to the next. In other words, any authentication your application requires can be bypassed by a user with local privileges to the machine on which the data is stored. Therefore, it's recommended not to store any sensitive information in local storage.

jacintoArias commented 4 years ago

Is this being ignored? or is there somewhere we can track the status of this?

mlabieniec commented 4 years ago

We are evaluating this. This argument can be ambiguous as well. For example see thread here: https://www.reddit.com/r/Frontend/comments/cubcpj/local_storage_vs_cookies_for_auth_tokens/ in addition to the comments/discussion in the above thread you referenced.

However, it's an interesting problem in this particular space (client-side/browser), with the ultimate solution of simply not storing / using a persistent session (only storing in memory, which you can do with the SDK currently). Obviously, the down-side to this is needing to login in every time the page is refreshed. This issue exists storing in any storage medium i.e. localstorage, cookies / even httpOnly; even though JS can't access httpOnly tokens, this cookie is still sent with every request, so if JS is injected into your app (considering the main argument on this is generally XSS), API requests will technically still be made as an authenticated user.

That said, the underlying Cognito SDK is using temporary credentials, these credentials need to be refreshed and Signature V4 signed in order to make authenticated AWS API calls. Also, these credentials are scoped to your authenticated/unauthenticated identities. So essentially, if someone was to first access your computer, and get into your web browser, they would need to create a signed request using an AWS SDK with your secret key and access key before the credentials have expired. You can also invalidate these credentials in the event of this via the IAM console.

However, it's still an ambiguous topic with problems in all areas. We will be leaving this issue open to track feedback on this as we evaluate the path forward and most secure approach available when utilizing a client-side / serverless architecture with amplify.

jacintoArias commented 4 years ago

Hi, thanks for reopening.

I agree that the opinions and strategies here are not universally valid.

In my case I am using amplify to manage authentication with cognito by just retrieving and using Access Bearer tokens to call API Gateway with cognito authorizers, so no IAM temporary credentials for me.

However the access token is temporary as well and the same principle applies (the attacker could use the token just until it expires). My main concern is the refresh token, which has a much longer expiration time and can be used to reclaim fresh credentials until the attacker is spotted and the token are revoked.

I will be much more confident by following a strategy similar to the silent authentication implemented in Auth0, or knowing that there is a good reason to have a refresh token in the localStorage of my angular SPAs.

https://auth0.com/docs/tokens/refresh-token/current

Otherwise, can anyone confirm if implementing the Authorization code grant pattern leverages the problem of storing such refresh token?

mlabieniec commented 4 years ago

Ah i see, Assuming you are referring to this as to what you are using? https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html

The temporary credentials I am referring to are the ones that are signing all your AWS requests i.e. secret key and access key. Are you using the Amplify Auth Category, initialized by the CLI with your project, as in, are the services (API Gateway, Cognito etc...) in your application using authenticated / unauthenticated IAM roles?

jacintoArias commented 4 years ago

Exactly, we are providing a manual cognito configuration to the amplify SDK for angular and we are using such cognito user pool as an authorizer for our APIs (both using direct integration and lambda authorizers depending on the microservice)

We use just the user pool with an OAuth Authorization Code Grant, so amplify redirects to the hosted UI and parses the corresponding callback query param, it is already very nifty compared with our previous implementation without amplify.

mlabieniec commented 4 years ago

Ah ok understood now thanks! So, you are not using federated identities at all and user pools directly. I was actually thinking that potentially the lambda authorizer could be an interesting solution in this case, even to potentially add a layer of security. We are working on some authentication work now that's still in the design phase, so this is a good thread to add to these discussions, will track/keep this up-to-date here as we move along, thanks for the details.

mlabieniec commented 4 years ago

@jacintoArias do you have device tracking turned on in Cognito User Pools? This will actually add additional security to the refresh token which it will only work on the specific device: https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-device-tracking.html

jacintoArias commented 4 years ago

Tracking the device will definitely improve the security of the refresh token and fits in our requirements, thanks for the recommendation we will give it a try.

We are currently migrating from a lambda authorizer to directly integrating with cognito beacuse we precisely think that cognito's implementation of token validation could be more advanced than our own custom token validation. Basic authorization is being done by filtering the endpoints using oauth claims in the token.

I will be very interested in knowing if If you think that a lambda authorizer is a better and more secure approach than leveraging the cognito integration directly...

mlabieniec commented 4 years ago

Great to hear! Yes i think the device tracking is a good solution here. For the authorizer i think the Cognito one is good for what you are doing, i was more referring to a possible solution where perhaps we could utilize to add a layer of additional validation, however the device tracking does a good job of locking down the refresh token per device.

Jun711 commented 4 years ago

@mlabieniec I wonder how accurate device tracking is. Is there an easy way for users to reset the tracking id?

evbo commented 4 years ago

What about SPA use-cases, requiring a user to stay logged in greater than 1 hour but no more than 8 hours?

Would the Cognito team (I presume amplify works closely with them) take as feedback to please allow either:

Either of the above enhancements would be arguably safer since it limits the TTL of the very sensitive refresh token information.

Secondary concerns that compound vulnerability:

Similar security concerns expressed here: https://github.com/aws-amplify/amplify-js/issues/3224 https://github.com/aws-amplify/amplify-js/issues/1218 https://github.com/aws-amplify/amplify-js/issues/3774 https://github.com/aws-amplify/amplify-js/issues/2213 https://github.com/aws-amplify/amplify-js/issues/1735

mlabieniec commented 4 years ago

@Jun711 If you delete local storage, a new device ID will get generated.

@evbo will look into this for you and get some details from the Cognito team on these requests/concerns.

evbo commented 4 years ago

@mlabieniec thank you.

Just wanted to emphasize that my second suggestion: increasing access token expire is really the preferred solution in lieu of silent auth features being supported.

Here's some additional useful context for the Cognito team. I hope they receive this feedback well:

  1. The security issue at hand is well stated here: https://github.com/aws/amazon-cognito-auth-js/issues/92

  2. Configuring access token expire is inline with the spec (there is no documented min/max required): https://tools.ietf.org/html/rfc6749#section-4.2.2

  3. Configurable access token expire for Cognito is a fairly popular discussion on SO/forums as well:

mlabieniec commented 4 years ago

@evbo I have discussed with the cognito team this feature and they have informed me that this is in their backlog as a feature. I do not have a completion date on this yet tho will keep this issue open to track progress from their side.

ajhool commented 4 years ago

The issue linked by @evbo suggests that the Implicit Grant flow is not suitable for SPAs due to this limitation from cognito:

Quoted from linked issue:

I would say that without Cognito implementing prompt=none on the /oauth2/authorize endpoint, and whilst the cognito cookie on .auth..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.**

Does this imply that the Implicit Grant flow that Amplify provides is also subject to the same limitation and therefore should not be used for Single Page Applications?

Am I understanding correctly that all Authorized requests will start failing after 60 minutes in a browser?

KhirodPanda commented 4 years ago

I was going through aws amplify doc .. I see a statement about “if you want a secure storage of token than local storage then you can do that ”. Is this helps in solving the problem ?

KhirodPanda commented 4 years ago

Hello , I see in amplify documentation there is a recommendation of using store if we want to store the token in a more secured way .. has any one tried that ? Is thy something will help in getting out of this security concern ?

agostbiro commented 4 years ago

There is a lot of FUD in this thread, so I thought it would be helpful to provide a summary of the topic:

  1. Protecting users from unauthorized access to their computers (physically or through malicious code) is outside the scope of the web application security model. Arguments against local storage based on this threat model can be discarded.

  2. Any information stored in local storage can be stolen by an attacker exploiting an XSS vulnerability. So when JWT tokens are stored in local storage AND an attacker successfully exploits an XSS vulnerability, the attacker can send the tokens to their server and use them to log in from a new device. (This is in addition to all the other bad things an attacker exploiting an XSS vulnerability can do.)

The solution to mitigate the second point is to set the remembered devices functionality to "Always On" in Cognito. Doing this will make Cognito verify the device identifier associated with the refresh token on log in, thus preventing an attacker from signing in with a stolen refresh token on an other device. (docs)

evbo commented 4 years ago

@abiro I agree that the storage vulnerabilities you've pointed out aren't the primary concern, but instead I think if Cognito could relax the TTL configuration limits it would enable developers to diminish the attack surface substantially, but unfortunately there's no free way of sending Cognito this kind of feedback directly.

I think many just want to follow the OAuth documentation:

Access tokens must be kept confidential in transit and in storage. The only parties that should ever see the access token are the application itself, the authorization server, and resource server. The application should ensure the storage of the access token is not accessible to other applications on the same device. The access token can only be used over an https connection, since passing it over a non-encrypted channel would make it trivial for third parties to intercept.

These sentiments have been echoed in regards to both Access and Refresh tokens by leading competitors of Cognito: Auth0, Storm Path

So where I agree a lot of FUD and hot debates occur is surrounding the decision on how authentication proofs are stored. While there's no perfect answer, I think many agree that one very simple tuning mechanism is the TTL of these tokens, which can allow the trade off between attack surface and accessibility to be decided on a case by case basis.

For instance, to fulfill my particular need for a TTL ~8 hours for a basic SPA, even a 24 hr Refresh token was overkill, so the least attack surface I could provide at a reasonable development cost was to only persist the Refresh token in an HTTP only cookie with a path: /refresh (so CSRF is quite limited and bandwidth isn't hogged) and with the Access token only in memory. This was a bit more moving parts than I would have liked, but minimized the amount of sensitive information storage inline with the documentation.

So what could be better? Simply configuring an 8 hour Access Token would be 16 hours safer and also obviate the need for refreshing tokens. So simply being able to configure TTL beyond the current limits (1 hr for access, >= 24 hr for refresh) would be nice, as that way it is tailored to individual needs better and the worry over storage methods can be lessened.

Also, I like your suggestion of Device Tracking. Still, it requires the promise of anonymity to be trusted by users so not a silver bullet in that regard.

amzhang commented 4 years ago

@abiro Device tracking keys/secrets are also stored in localStorage, so vulnerable to the same type of risks as the refresh tokens stored there. But I assume other meta-data about IP, OS, etc are also used to confirm device.

Luke-1988 commented 4 years ago

@mlabieniec any update here please? Thanks

judedaryl commented 4 years ago

All these problems would really be solved by a prompt=none option. The safest way to store is in transient memory. The best way to handle token expirations in my experience was the iframe solution, this is currently a feature in oidc-client.js, but i have had project where i built a "oauth2" client from scratch and have had success refresh tokens silently using the iframe solution.

Works like this.

Your main context creates an iframe and points it to the idp together with the configs (clientId, redirect, etc..). Make sure the redirectUri is set to a blank route or a blank static .html file

function silentRefresh() {
    return new Promise((res) => {
        let iframe = document.getElementById('my-silent-refresh-iframe');
        iframe.style = 'width: 0px; height: 0px; overflow: hidden';
        iframe.addEventListener('message', (mes) => {
            // the token is in the ``mes`` payload
            res();
            // empty out the frame or destroy after
            iframe.textContent = '';
        })
        iframe.src = 'https://my.idp?clientId=....&redirectUri=https://mysite.com/silent_refresh.html'
    })

}

Your silent request context can then send the hash or parsed hash back to the main context using window.top.postMessage

<script>
    window && window.top.postMessage(parseHash(window.location.hash))

    function parseHash(hash) {
        var data = hash.substr(1);
        var dict = data.split("&");
        return dict.reduce((props, val) => {
            var map = val.split('=');
            props[map[0]] = map[1];
            return props;
        }, {})
        return oidcResponse;
    }
</script>

MAIN PROBLEM: If cognito asks for user credentials this strategy just doesn't work. The iframe would just be stuck at the login page of cognito or some other federated idp.

The prompt=none solves this issue. I hope this thread is still being monitored by the amplify team or the cognito team

themrshubh commented 3 years ago

The solution to mitigate the second point is to set the remembered devices functionality to "Always On" in Cognito. Doing this will make Cognito verify the device identifier associated with the refresh token on log in, thus preventing an attacker from signing in with a stolen refresh token on an other device. (docs)

I tried using the remembered device functionality in cognito to mitigate this problem. However, I believe that cognito only verifies the device identifier when using the refresh token to generate a new access token.

However, if I use a different device to access my resource server using an access token provided to an authenticated client on another device, it lets me through. According to my understand, this will leave the access token in the local storage vulnerable.

Is there a workaround for this problem yet?

gary-archer commented 3 years ago

There are 3 related problems here:

I have an Online AWS Cloudfront Deployed SPA, which uses Cognito and which anyone on this thread can run.

The SPA is also runnable locally via this GitHub repo. This is an AWS implementation of Curity's Token Handler Pattern.

amzhang commented 3 years ago

@gary-archer I wonder if it might run into the max cookie-size limit. Some of these tokens are pretty large. To get around that you could encrypt the token, store the key in the cookie, store the encrypted content the browser's storage, and pass both to the proxy.

rspuler commented 3 years ago

@gary-archer: Do we need Lambda@Edge for this or could we not just use lambda behind API Gateway to do the same?

gary-archer commented 3 years ago

Hi @rspuler. The key point around wrapping refresh tokens in cookies is that a same domain cookie works best, since these days browsers such as Safari will aggressively drop cross site cookies - especially on POST requests. I would have used a normal API Gateway lambda otherwise, as you suggest.

You can run my AWS hosted SPA from my Quick Start Page, to see the desired end behaviour. My demo SPA stores access tokens in memory and refresh tokens in cookies. It has zero cross site cookie problems - as you can see by:

Disgruntled commented 3 years ago

Having Authorization servers put tokens into cookies is directly against the Oauth Spec - Tokens are returned in response bodies.

This basically re-introduces the security problem that existed before 'httponly' cookies - if someone can pull off XSS, they can steal the users credentials and act as the user without the need to persist inside their browser. This is particular problematic with Singe Page Applications as they typically have a well documented or at least easier to understand API than traditional web applications.

AWS, is there a plan to do some enhanced XSS protection here? I know cookies aren't going to fly, but I've seen some examples of folks using webworkers to hold refresh tokens such that JS code cannot read tokens.

evbo commented 3 years ago

@gary-archer I mentioned it in comments above and it's been emphasized in newly updated docs from Auth0 that all efforts should be made to avoid persisting tokens client-side in SPA applications, BUT for everyone like us who must (e.g. to maintain login across browser refresh or multiple tabs) then the best thing you can do is limit the expiration of said tokens:

To reduce security risks if your SPA is using implicit... ...or hybrid flows, you can reduce the absolute token expiration time. This reduces the impact of a reflected XSS attack (but not of a persistent one).

@mlabieniec Good news, Cognito team implemented much more flexible token expiration configuration. Thanks for relaying feedback to them.

Disgruntled commented 3 years ago

@gary-archer Have you also considered putting session management responsibilities onto the OpenID Provider/Authorization Server(or whatever system the user uses to authenticate to them).

What I mean is if the users browser had a session cookie (samesite/secure/httponly) for the OP/AS, there would be no need to persist the refresh token to storage. Upon seeing an unauthenticated user/no tokens, the SPA would redirect to the OP/AS but the user would already have a session set to that system.

The major downside here is that there is a need to manage session state at OP/AS, but that challenge doesn't seem insurmountable.

ledbruno commented 3 years ago

We have the same problems and designed something as you said @Disgruntled . We were willing to use kong and this plugin https://github.com/nokia/kong-oidc/pull/181

But, in order to all the things work together i need to use (authorization code grant flow), and this is only avaible through cognito hosted UI. Amplify JS does not Support that.

With this scenario, we will need to implement some kind of Session Management Facade Server. Amplify as a productivity tool, is not doing OK for us.

ravenscar commented 3 years ago

I originally opened up a similar ticket in the old repo, almost three years ago: https://github.com/amazon-archives/amazon-cognito-auth-js/issues/92

I sure am surprised that this is still an issue and prompt=none hasn't been implemented, we have been using auth0 for years now and I guess we are spoiled, but now are moving back to cognito for price reasons (auth0 OTP SMS prices are high).

We have an enterprise account with AWS so I recently raised the issue and I though I would put the reply here in case others are interested.

Currently the "prompt" parameter functionality is not supported by Cognito. I deeply regret any inconvenience caused due to this limitation. Please consider my sincere apologies on behalf of AWS regarding this.

Unfortunately, the current workaround seems the only possible way to help. You will either need to use the Authorization code grant flow and have the desired expiry value for your refresh tokens and use your server mechanism for secure storage of these tokens. If you would like to still continue with the implicit grant flow then you will need to extend your ID token duration to 24 hours. This is something which has been supported and extended recently.

...

I understand that you are aware of these workarounds and the feature of supporting "prompt" parameter is important to you. Please be advised that "prompt" parameter was already in team's feature request backlog. Although the new feature was requested, it will not be possible for me to share the exact road-map for the completion of the request as the development process goes through multiple stages before the new feature is released. Thus, I'm unable to give you an ETA for when this feature may become available. We take customer's feedback seriously, as this allows AWS to better prioritize new features/improvements down the road. I have added your voice to the request making sure it gets the appropriate attention it deserves. I will perform my due diligence in ensuring that your case is used as supporting evidence to escalate this feature.

Can't say I am particularly surprised but I am disappointed.

I might as well throw in our hacked together solution to this.

We have an SPA but we built a very small separate app on the same domain, that is only used for auth.

This has no dependencies outside of react, it has our own implementation of Cognito's FrankenSRP and uses the browser's Crypto.subtle and BigInt. This calls the API directly (not via the sdk) and delivers the id/access cookies to the app via inter frame communication, where they are only store in memory.

The refresh token is stored in an http only secure cookie which limited to a /refresh path. We store this by calling a lambda with the token and the token is then pushed back as a cookie. When we want to refresh we call another lambda on the /refresh path which sends the cookie back to another very small app with no dependencies outside of our SRP lib, this refreshes the cookie from the users browser, then delivers the new id/access token via the iframe.

We store device information in local storage.

This way we never store the refresh token in a place accessible to javascript, the XSRF options are limited as all that can achieve is pushing a new access/id token to the SPA in the iframes parent. We use a CSP for the iframe, and also in order for it to work the device information has to be delivered to it from the SPA.

In mobile we just use native calls and store the refresh token in the keychain, and use native code to refresh when we need to.

jeremiahsmall commented 3 years ago

Ok, nice to see @sammartinez self-assigning this. Hopefully he's going to have a PR for https://github.com/amazon-archives/amazon-cognito-auth-js/issues/92 soon? The secret in local storage precludes us from using Cognito for React SPAs. We'd like to be able to, since most of the other infrastructure is on AWS.

mohanr commented 2 years ago

I gathered these 4 points from this long thread.

  1. Implement browser best practice, such as a Content Security Policy that prevents tokens being sent out of the browser, via connect-src
  2. Make sure API access is designed so that a user can never elevate their own privileges by grabbing a token via browser tools

Could you point out any spec. or material to assess how we should implement the above 2 ?

  1. Store refresh token in an external DB. But DB access requires another authentical mechanism. Right ?
  2. PEN test

PKCE is what we want to use.

michaelbrewer commented 2 years ago

The Ultimate Guide to handling JWTs on frontend clients has some good recommendations for how to store your JWT

ravenscar commented 2 years ago

@michaelbrewer this guide looks sensible but it is suggesting features such as silent refresh, which cognito doesn't have. A lot of the decisions would make you change from the Cognito UI as well which will mean you lose access to a bunch of OAuth stuff like code flow etc, so if using Cognito you will have to reimplement code flow using your own server side crypto and storage.

What I am saying is that I don't think you can follow the practices of this guide, or good practices in general, whilst using Cognito and/or Amplify.

rspuler73 commented 2 years ago

I have to agree with @ravenscar. Much of the OAuth/OIDC stuff is only provided with the Cognito Hosted UI which is pretty useless in any customer facing application as it is very limited in what can be customized (e.g. lack of i18n support). So there is no way to use it as an OIDC without implementing everything yourself.

dittmarconsulting commented 2 years ago

Luckily I found this thread as I was scratching my head why I could read the payload of the access token with this one-liner:

console.log(base64.decode(token.split('.')[1]))

I get something like that (modified)

{"kid":"IDq0iinUEq4\/yp4PfebvmcShx1JSDz2fH96iKMVJumE=","alg":"RS256"}{"sub":"00757755-3cbc-46e6-9212-2510b86b5e1e","event_id":"6bc5abff-9u79-4036-af26-964bed08a6da","token_use":"access","scope":"aws.cognito.signin.user.admin","auth_time":1635149829,"iss":"https:\/\/cognito-idp.ap-southeast-2.amazonaws.com\/ap-southeast-2_7rJr7xrje","exp":1635153429,"iat":1635149829,"jti":"1w2a054e-bef8-402c-8509-2cbeed0ef71b","client_id":"5t4p9qpgpn469vs5s97ocsnq8n","username":"00677799-3cbc-67o9-9212-2510b86b5e1e"}
Antonio-Riccelli commented 1 year ago

Hi. What is the current status on this, please?

bearsworth commented 1 year ago

I posted for a feature request on a similar note. But moving forward, instead of using async storage, can't you guys just use encrypted storage solutions out there?

wz2b commented 1 year ago

The thing I find spooky is that, in addition to the content in the tokens themselves, Auth also stores UserAttributes which has all of your cognito attributes (even the custom ones) in it. So someone hacking the local storage later might only get expired keys, but they also get your name, phone number, e-mail. There's even more information in the idToken. At work we'd call this Personal Identifiable Information (PII) and, like others who made this comment, I think that storing that forever is a problem.

You can avoid this by using sessionStorage (I guess) but like everyone has pointed out that's pretty inconvenient, too.

I kind of feel like there's no good answer to this.

wz2b commented 1 year ago

I should add this, though for everyone who is nervous about this: if you call Auth.signOut() the local storage is cleared.

zuluaica18 commented 11 months ago

It is important to emphasize that if your application is going to go through security tests with a company that trusts the owasp definitions, you would have serious problems with the output of your project. Keep that in mind!

It seems to me that aws and owasp have a lot to talk about...

zuluaica18 commented 11 months ago

I would like to see an alternative storage that is cookie httponly , and amplify could create the lambda to manage the oauth flow with the server, I would pay more infrastructure but it would be a beautiful option for some, this would bring the advantage of passing the strict security tests based on owasp from my company. Meanwhile, amplify escapes my use case.

kevoj7 commented 7 months ago

Using nextjs 13.4+, it stores it in cookies now!! I've been playing with it lately and works nicely so far:

This package should do it

Docs: https://docs.amplify.aws/lib-v1/q/platform/js/

JacobDel commented 7 months ago

We are evaluating this. This argument can be ambiguous as well. For example see thread here: https://www.reddit.com/r/Frontend/comments/cubcpj/local_storage_vs_cookies_for_auth_tokens/ in addition to the comments/discussion in the above thread you referenced.

However, it's an interesting problem in this particular space (client-side/browser), with the ultimate solution of simply not storing / using a persistent session (only storing in memory, which you can do with the SDK currently). Obviously, the down-side to this is needing to login in every time the page is refreshed. This issue exists storing in any storage medium i.e. localstorage, cookies / even httpOnly; even though JS can't access httpOnly tokens, this cookie is still sent with every request, so if JS is injected into your app (considering the main argument on this is generally XSS), API requests will technically still be made as an authenticated user.

That said, the underlying Cognito SDK is using temporary credentials, these credentials need to be refreshed and Signature V4 signed in order to make authenticated AWS API calls. Also, these credentials are scoped to your authenticated/unauthenticated identities. So essentially, if someone was to first access your computer, and get into your web browser, they would need to create a signed request using an AWS SDK with your secret key and access key before the credentials have expired. You can also invalidate these credentials in the event of this via the IAM console.

However, it's still an ambiguous topic with problems in all areas. We will be leaving this issue open to track feedback on this as we evaluate the path forward and most secure approach available when utilizing a client-side / serverless architecture with amplify.

I get the argument for storing refresh tokens, but why are the cognito user attributes shown in plain sight? Why not use local storage for the refresh tokens and cookies for the attributes?

TeoTN commented 5 months ago

Using nextjs 13.4+, it stores it in cookies now!! I've been playing with it lately and works nicely so far:

This package should do it

Docs: https://docs.amplify.aws/lib-v1/q/platform/js/

Not sure how you got to this result, I'm using "aws-amplify": "6.0.9" and I can see all the data in local storage.

EDIT: My bad, I just found out that you can change token-saving mechanism.

rdsedmundo commented 4 months ago

Although it's possible to switch to a CookieStorage, it doesn't come without its problems.

See: https://github.com/aws-amplify/amplify-js/issues/1545 (closed without being addressed) https://github.com/aws-amplify/amplify-js/issues/10498

hasan-web commented 4 months ago

Any update on this ????

LydGol90 commented 2 months ago

In case it helps anyone, I use react native and we have a similar problem in that the default is to use react native async storage which is not secure. I migrated to ios keychain and android keystore using the following and it seems to be working. We will shortly be getting this re-tested by a pen-test company so I can let you know if it meets their standards. Caveat that the below code is rough and not thoroughly tested yet.

import { Amplify, type ResourcesConfig } from 'aws-amplify';
import { defaultStorage, KeyValueStorageInterface } from 'aws-amplify/utils';
import { cognitoUserPoolsTokenProvider } from 'aws-amplify/auth/cognito';
import {
  getAllGenericPasswordServices,
  getGenericPassword,
  resetGenericPassword,
  setGenericPassword,
} from 'react-native-keychain';

class MyCustomStorage implements KeyValueStorageInterface {
  async setItem(key: string, value: string): Promise<void> {
    await setGenericPassword(key, value, { service: key });
  }
  async getItem(key: string): Promise<string | null> {
    const data = await getGenericPassword({ service: key });
    if (data) {
      return data.password;
    } else {
      // Migrate existing users from old storage to new storage
      const oldData = await defaultStorage.getItem(key);
      if (oldData) {
        await setGenericPassword(key, oldData, { service: key });
        await defaultStorage.removeItem(key);
        return oldData;
      }
    }
    return null;
  }
  async removeItem(key: string): Promise<void> {
    await resetGenericPassword({ service: key });
  }
  async clear(): Promise<void> {
    const storedServices = await getAllGenericPasswordServices();
    const promises = storedServices.map(service =>
      resetGenericPassword({ service }),
    );
    await Promise.all(promises);
  }
}

cognitoUserPoolsTokenProvider.setAuthConfig(authConfig);
cognitoUserPoolsTokenProvider.setKeyValueStorage(new MyCustomStorage());

Amplify.configure(
  {
    Auth: authConfig,
  },
  { Auth: { tokenProvider: cognitoUserPoolsTokenProvider } },
);