Amplify-js: Bug in amazon-cognito-identify-js

Created on 29 Mar 2019  Â·  30Comments  Â·  Source: aws-amplify/amplify-js

Hey, we have installed the version 3.0.10 amazon-cognito-identify-js and we noticed a bug.
If we use this version, an additional API call to https://cognito-idp.eu-central-1.amazonaws.com/ will be fired. This call breaks our app, because we receive a 400er.
Is that true, that a new call was added?

The Version 3.0.9 works great. In this version just one call gets fired. X-Amz-Taget: AWSCognitoIdentityProviderService.GetUser

With the 3.0.10 Version an additional call with X-Amz-Taget: AWSCognitoIdentityProviderService.InitiateAuth gets fired.

Auth investigating pending-close-response-required

Most helpful comment

Just wanted to jump in quickly as I also ran into this over the weekend when my app's auth started breaking for seemingly no reason. Managed to track it to the same issue outlined here, and am currently working around it by pinning my deps to the older version. I have a somewhat more traditional React amplify app where this is working in 3.0.9 and failing in the newer version without any other changes due to the extra call that returns a 400 level error causing amplify to think the session is invalid and removing all tokens. Just wanted to confirm this is not isolated to one custom app. :)

All 30 comments

@ffjanhoeck the IntiateAuth request is triggered by the refreshSession function which is added into the GetUser function in the latest version. This function is aimed to get the latest accessToken and idToken when getting the user data.

Can you paste the request and response body of your IniateAuth request here?

@powerful23 Sure but first let me explain what we do, because at this point we don't use aws-amplify as expected.
We have a backend service which is able to login into cognito. This backend service sends us the idToken, refreshToken and accessToken back to the frontend. After the frontend receives the tokens we do the following:

public loginWithTokens(tokenModel: TokenModel): Promise<CognitoUserSession> {
        let stage = EnvironmentManagement.getStage();
        if (stage === StageTypes.LOCAL) {
            stage = StageTypes.DEVELOPMENT;
        }

        // set the new tokens in the store
        const key = `CognitoIdentityServiceProvider.${stageSettings[stage].clientId}`;
        localStorage.setItem(`${key}.LastAuthUser`, tokenModel.username);
        localStorage.setItem(`${key}.${tokenModel.username}.idToken`, tokenModel.idToken);
        localStorage.setItem(`${key}.${tokenModel.username}.refreshToken`, tokenModel.refreshToken);
        localStorage.setItem(`${key}.${tokenModel.username}.accessToken`, tokenModel.accessToken);

        return this.getCurrentSession();
    }

The getCurrentSession function looks like this:
/** * Returns the current session if a user is logged in. The session contains * all cognito tokens and more. */ public async getCurrentSession(): Promise<CognitoUserSession> { return await Auth.currentSession(); }

This worked in the amazon-cognito-identity-js version 3.0.9 pretty fine!
Now you may ask, why we do this... That's because we have two products. An old product and a new product. We want to migrate the users from the old product to the new one - but the old users don't know the login credentials to the new product. So the backend service is handling the login for us.

Lets get back to your question, if I can paste the request here. I copied the call with the chrome debugger - rightclick > copy as cURL (bash). You can copy it in postman if you want
curl 'https://cognito-idp.eu-central-1.amazonaws.com/' -H 'Referer: http://localhost:3000/login?apiStage=production&apiVersionTag=stable&token=d84d6ab7-6584-404c-b436-32739a7998a9' -H 'Origin: http://localhost:3000' -H 'X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth' -H 'X-Amz-User-Agent: aws-amplify/0.1.x js' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36' -H 'Content-Type: application/x-amz-json-1.1' --data-binary '{"ClientId":"57brn8csff678o6aee3k1ia00n","AuthFlow":"REFRESH_TOKEN_AUTH","AuthParameters":{"REFRESH_TOKEN":"eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.QNX_xKfiYfClQ8MnKcaNUvmUXA3LGpn28DLDjVhmUAsla8r2t-clMuqQ8Es2L_zrTcuYobLgiiU-hMx9onzYY3a03KAFJZrt4oUHZQrOU9u_5tZfTYUXrQ6dB0bAuWXCP_fEKcRb-V5xUekT54-vmqTydsKt2irTLi3fCZqclunDteGiY3Tel6MTdsJ7VqJ58S7E85sJnzcNp_wj5KxVCh7UnhfoOAQqgJE6m57N1RNE-1VtsGTGysB2wcMJJzjZWgxAv-qfAOR102cxBJh4h6G3nWwaUYEm1MGd-QHF1DXyFaecYc9agH6b3X3zrQ80cDB3PrHkaB19YHU0f-DAqg.mJXNDqWpNT0Kq9rP.SmlB3aVE_obJ_JCqhUWsMqv7U98-U6rRpraRzqlUf4RrrYxUlPLR--bMjMPo2Wx-aI6dtdhQVmcgfSgyzqMJiog2Hfi7rusgBsN9MNPzafQ4gqoYI9PswE-iWlZAfEqM8u3GMfbl-B-gJelaP7cNLFarTVLxNo9vstDCLk_DNTGsvZy-FZXbvJp583d1Ubo-rp93J4APBmia37ehxkkfSeLXECvTANg9Tze8CMmrKJhTrrbUvl4YlP3xnA5qEN7Hwf8yilDO0FGgymuT9bWTNID67IzPnFbtZs7Sf15tb5JupObTFq7N6274hwHcy_5mZwIJsMMcOhXLvPLr3QBw28hHzy6KzxX29GOjcBwzW_o0L-7n1-iLchbHnQAU_kfxo1Cwn0D5UOtTKOL3bFFa83UcaSIqJtYQOS-b8IBonSQxEFaSW3VmQjKhp5tFh98v-a7QNrZXy1mz9Sf2IqJGBR_AZpEKr3SuGI77GL7LJid4VfroSRHkBCWhN2piEyVWVYhkR1NzFnI2mWBCFlaBOR5WcFGMLuvI21YWmV2V1_yzWKKoYkwjZWJ8mDlYJyAiAAzGmRJMlBbYNVP1Kkh-yMM0A__1aVHdSoBaS320HPuSJVgSqYTqu6IiIfUdjNm5Xb502xIoOX5fSYXIthwtL-78II_qhBBXsZbvXcWUmDaBIv18czuXn8LzKs3hTJsL6Bmpt5dhamh0gb5BJFN3x8J8UWF8KNqYZCrD6nqW26LDTcK5Cy1VRo0BHJ6Y_vi3FXGld3CQcYQQOzHiA2eJcnxbruQB52NJynkHRQKqXOarNyDvIpIN-Q_MFet01S6BV2Ph37upztkBwI_n1fJeadtUFgFz6M9pt0JUWPplbzbQw4g_vAEKzSRjZPQrLqMxrnCOFlvcIW4x221NRkg-oWCSP5Fz6prI7eLHlj_SByn0M3E5f-TsUuSGjI-n9z_SoPd-VbKEIifNOO2g2lZAERr_czlkYnksAnYLAy2WdCqFnyjec9Uir-3ilij_L_umbzzbDHEiffJUFDtqHH1fprbhtDqo5f-y_c5AN3f0_itjSTM-xvJp_GCJtzWftcYgiY1vCJFFhQB-E9_-QjXFfQPuxkAQmgMROnxhHLSyh9udYnkJp3ayHm5imRP3tGxt2K4fUUN6X35Cl_kSoY-3VeTzH5FC_Ndw25-bpxxgUxPH0okBK8dJTbrJelm7fEvpw-9C8uRlFYxcnCg_6qlXjUt4wEVozz27aBmqDdz-CBYTSVQw7-3Xr7ZqP7QeoYrAEtxAG39ZuLwXex3vWzkOipT-amkOYcZttG6xcyBT2iJfSWbbFnWXyt0dCZzeP9N1tIhMeXWp_a9zhGlcwZ1Tv8XOTKtp-3POj6G6-lLeNd96GB02QnpnWnyiB9NIBIWiRnaoshHrv6P4kRU5A1tm-5DggU7gjuAZYc1Y.Y3vixW2vWUhCQQ0LeRqyLA"}}' --compressed

The response is:
{"__type":"NotAuthorizedException","message":"Invalid Refresh Token"}

we fixed it temporary with yarn resolutions. But we get this warning in console.
_warning Resolution field "[email protected]" is incompatible with requested version "amazon-cognito-identity-js@^3.0.10"_

Hopefully anyone can help us :)

@powerful23 Did you got any updates on this issue?

@ffjanhoeck sorry for the late response. Questions:

  • Is the cognito client id the same in both front and server side?
  • How long does the refresh token live?
  • When using the old version(3.0.9), can you try:
const user = Auth.currentAuthenticatedUser();
user.refreshSession();

to see if you could refresh the session with the old version?

Just wanted to jump in quickly as I also ran into this over the weekend when my app's auth started breaking for seemingly no reason. Managed to track it to the same issue outlined here, and am currently working around it by pinning my deps to the older version. I have a somewhat more traditional React amplify app where this is working in 3.0.9 and failing in the newer version without any other changes due to the extra call that returns a 400 level error causing amplify to think the session is invalid and removing all tokens. Just wanted to confirm this is not isolated to one custom app. :)

@bduff9 can you also provide more info about this issue? Normally the refreshSession won't fail as long as the refresh token is valid.

So, in my React app, with 3.0.9, everything works and I can see the tokens in the localStorage. With 3.0.10, I see a response for AuthenticationResult, then UserAttributes (and the localStorage values are created), then I see a NotAuthorizedException from a REFRESH_TOKEN_AUTH. This is all one after the other, and this latest call which returns a 400 code, then clears all the localStorage tokens so sign in fails.

The only difference between my auth working/not working is pinning this package to 3.0.9.

To answer your question to @ffjanhoeck , in my working example with 3.0.9, calling refreshToken manually causes the same behavior (400 error response and signing me out).

@powerful23 When I do the call manually causes the same behavior for me too (400 error).
Now I try to find out if the frontend and the backend are using the same clientId and userpool.

@powerful23 We have two app clients registered in cognito. The backend is using a different clientId as the frontend :x

@ffjanhoeck can you try not caching the refreshToken in the client side as it won't work with the client in the front?

@bduff9 are you also using two clients in your app like @ffjanhoeck ?

@powerful23 No I am not, only using cognito for auth so no backend and only 1 client

Also running into this error when logging in after upgrading to aws-amplify 1.1.24, adding resolution for "amazon-cognito-identity-js": "3.0.9" does fix it, but would like to not have to do this.

const user = await Auth.currentAuthenticatedUser();
user.refreshSession();

I tried this, however I got Unhandled Rejection (TypeError): Cannot read property 'getToken' of undefined

@johnbowdenatfacet Yes its because this code is wrong. refreshSession has two arguments. You have to pass down the RefreshToken object. You can get it from any session.

@ffjanhoeck can you try not caching the refreshToken in the client side as it won't work with the client in the front?

Sorry, I don't get it what you mean. I don't cache the refreshToken :x

@ffjanhoeck

Sorry, I don't get it what you mean. I don't cache the refreshToken :x

Because this line in your code will do this:

localStorage.setItem(`${key}.${tokenModel.username}.refreshToken`, tokenModel.refreshToken);

@powerful23 I do this because cognito does this too, when a user log in.
What do you think should happen if I remove this line?

     try {

        const user = await Auth.currentAuthenticatedUser();

        if (!user) return;

        user.getSession((err, session) => {

          if (err) return;

          const refresh_token = session.getRefreshToken();

          user.refreshSession(refresh_token, (refresh_err, resfresh_session) => {

            console.log('refresh session.', refresh_err, resfresh_session);

            if (!refresh_err) {

              user.setSignInUserSession(resfresh_session);

            }

          });

        });

      } catch (err) {

        console.log(err);

      }

Gives me the error:
{"__type":"NotAuthorizedException","message":"Invalid Refresh Token."}

     try {

        const user = await Auth.currentAuthenticatedUser();

        if (!user) return;

        user.getSession((err, session) => {

          if (err) return;

          const refresh_token = session.getRefreshToken();

          user.refreshSession(refresh_token, (refresh_err, resfresh_session) => {

            console.log('refresh session.', refresh_err, resfresh_session);

            if (!refresh_err) {

              user.setSignInUserSession(resfresh_session);

            }

          });

        });

      } catch (err) {

        console.log(err);

      }

Gives me the error:
{"__type":"NotAuthorizedException","message":"Invalid Refresh Token."}

You have the same issue like @bduff9 and me :)

@ffjanhoeck if the refreshToken is not cached in local, then when the session expires, it will just throw error saying Cannot retrieve a new session. Please authenticate. instead of trying to refresh the session. Then you should refresh the tokens in the backend and send them to the frontend.

@powerful23 Ok I can test it but I don't think that this will work, because the received refresh token is completly new generated from the backend.

@ffjanhoeck

We have two app clients registered in cognito. The backend is using a different clientId as the frontend

Since the refresh token is generated using another client in your backend, I don't think it would work in the client side.

@powerful23 So you think we have to use the same client ids in both to fix it?
In my opinion you should revert the change of 3.0.10 and release it as a major version.

@ffjanhoeck yes, unless you do have reason to have two different clients.

After digging around and testing, it looks like when "Device Tracking" is turned on, refreshSession won't work as this (the deviceKey) isn't implemented. Not sure if this is documented any where.

@powerful23 We start now to investigate some time to change the clientID in the Backend, so that the backend will use the same clientID as the frontend.

And do you agree with the answer from @johnbowdenatfacet?

@johnbowdenatfacet @ffjanhoeck it's true that after turning on Device Tracking, the refresh token can only be used in the device which contains the correct device key:

A successful authentication by a user generates a set of tokens – an ID token, a short-lived access token, and a longer-lived refresh token. The access token only works for one hour, but a new one can be retrieved with the refresh token, as long as the refresh token is valid. With device tracking, these tokens are linked to a single device. If a refresh token is used on any other device, the call fails. In a scenario where, for example, a device is stolen, the ForgetDevice API can be used to forget that specific device, and as a result, all future calls to revalidate that device’s refresh tokens will fail.

from https://aws.amazon.com/blogs/mobile/tracking-and-remembering-devices-using-amazon-cognito-your-user-pools/

The device key is handled automatically by the cognito sdk, I can confirm that it's attached in the request of refreshing session in the code. I can do a test to do a further confirm.

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.

This issue has been automatically closed because of inactivity. Please open a new issue if are still encountering problems.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

romainquellec picture romainquellec  Â·  3Comments

rayhaanq picture rayhaanq  Â·  3Comments

oste picture oste  Â·  3Comments

guanzo picture guanzo  Â·  3Comments

DougWoodCDS picture DougWoodCDS  Â·  3Comments