amplify-js: Auth.currentAuthenticatedUser() does not return Cognito User.

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

Authentication, REST API

Amplify Categories

auth

Environment information

# Put output below this line

System:
    OS: macOS 13.0.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 126.13 MB / 16.00 GB
    Shell: 5.8.1 - /bin/zsh
  Binaries:
    Node: 16.18.0 - ~/.nvm/versions/node/v16.18.0/bin/node
    npm: 8.19.2 - ~/.nvm/versions/node/v16.18.0/bin/npm
    Watchman: 2023.04.03.00 - /opt/homebrew/bin/watchman
  Browsers:
    Brave Browser: 111.1.49.132
    Chrome: 112.0.5615.137
    Edge: 112.0.1722.58
    Firefox: 111.0.1
    Safari: 16.1
  npmPackages:
    cors: ^2.8.5 => 2.8.5 
    express: ^4.18.2 => 4.18.2 
    google-auth-library: ^8.7.0 => 8.7.0 
    hardhat: ^2.12.2 => 2.12.2 
  npmGlobalPackages:
    @aws-amplify/cli: 11.0.3
    corepack: 0.14.1
    eas-cli: 2.8.0
    nodemon: 2.0.20
    npm: 8.19.2
    serve: 14.1.2

Describe the bug

Login with google token using Auth.currentAuthenticatedUser() only returns google token like so:

{ “id”: “us-east-1:xxxxx”, “name”: “xxx”, “token”: “xxxxxx” }

Expected behavior

it should return cognito user, or id,access and refresh tokens to create cognito user.

Reproduction steps

  1. install aws-amplify
  2. Login with google and get a valid google id token then pass it to Auth.federatedSignIn
  3. Get the current user with Auth.currentAuthenticatedUser

Code Snippet

// Put your code below this line.
    const googleToken = response.credential;
    const googleData = parseToken(googleToken);
    console.log('googleData:', googleData);
    const { email, given_name, exp } = googleData;
    Auth.federatedSignIn(
      'google',
      {
        token: googleToken,
        expires_at: exp
      },
      {
        name: given_name
      }
    ).then((response) => {
      console.log('federatedSignIn res', response);
      // Use the Auth.currentAuthenticatedUser() method to check if the user is already authenticated
      Auth.currentAuthenticatedUser()
        .then((cognitoUser) => {
          console.log('cognitoUser:', cognitoUser);
          const cognitoToken =
            cognitoUser.signInUserSession.accessToken.jwtToken;
          // Use the Cognito token to authenticate requests to your backend API
          console.log('cognitoToken:', cognitoToken);
        })

Log output

// Put your logs below this line
{
    "id": "us-east-1:xxxxx",
    "name": "xxx",
    "token": "xxxxxx"
}

aws-exports.js

No response

Manual configuration

Amplify.configure({
  Auth: {
    // (required) only for Federated Authentication - Amazon Cognito Identity Pool ID
    identityPoolId: 'us-east-1:xxxx',

    // (required)- Amazon Cognito Region
    region: 'us-east-1',

    // (optional) - Amazon Cognito Federated Identity Pool Region
    // Required only if it's different from Amazon Cognito Region
    identityPoolRegion: 'us-east-1',

    // (optional) - Amazon Cognito User Pool ID
    userPoolId: 'us-east-1_xxx',

    // (optional) - Amazon Cognito Web Client ID (26-char alphanumeric string, App client secret needs to be disabled)
    userPoolWebClientId: 'xxx',

    // (optional) - Enforce user authentication prior to accessing AWS resources or not
    mandatorySignIn: false,

    // (optional) - Manually set key value pairs that can be passed to Cognito Lambda Triggers
    clientMetadata: { myCustomKey: 'myCustomValue' },

    // (optional) - Hosted UI configuration
    oauth: {
      domain: 'myapp.auth.us-east-1.amazoncognito.com',
      scope: [
        'phone',
        'email',
        'profile',
        'openid',
        'aws.cognito.signin.user.admin'
      ],
      redirectSignIn: `${window.location.origin}/login-google`,
      redirectSignOut: 'http://localhost:3000/',
      clientId: 'xxxxx',
      responseType: 'code' // or 'token', note that REFRESH token will only be generated when the responseType is code
    }
  }
});

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

is there a way to create valid cognito user with Auth.federatedSignIn response. The response is like so :

{
    "identityId": "us-east-1:xx-xxxxx",
    "accessKeyId": "xxxx",
    "secretAccessKey": "xx/xxx/x+xxxx",
    "sessionToken":"xxxxxx"
}

About this issue

  • Original URL
  • State: closed
  • Created a year ago
  • Comments: 20 (8 by maintainers)

Most upvoted comments

Thanks for your response. I have tried possible methods like so:

the method I call for google sign in:

  const handleGoogleLoginSuccess = async (response) => {
    console.log('response:', response);
    const googleToken = response.credential;
    const googleData = parseToken(googleToken);
    console.log('googleData:', googleData);
    const { email, given_name, exp } = googleData;

    try {
      const cognitoResponse = await Auth.federatedSignIn(
        'google',
        { token: googleToken, expires_at: exp },
        {
          name: given_name
        }
      );
      console.log('cognitoResponse:', cognitoResponse);
    } catch (error) {
      console.log('error:', error);
    }
  };

the response from these methods are like so:

  useEffect(() => {
    const unsubscribe = Hub.listen('auth', ({ payload: { event, data } }) => {
      switch (event) {
        case 'signIn':
          console.log('signIn:', data);
          break;
        case 'signOut':
          console.log('signOut:', data);
          break;
        case 'customOAuthState':
          console.log('customOAuthState:', data);
          break;
        case 'tokenRefresh':
          console.log('tokenRefresh:', data);
          break;
      }
    });

    Auth.currentAuthenticatedUser()
      .then((currentUser) => {
        console.log('currentUser', currentUser);
        //   {
        //     "id": "us-east-1:xxx-xxx-xx-xx-xxxx",
        //     "name": "İrfan",
        //     "token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjJkOWE1ZWY1YjEy......" google token
        // }
      })
      .catch((err) => console.log('No currentAuthenticatedUser', err));

    Auth.currentSession()
      .then((session) => {
        console.log('currentSession', session);
      })
      .catch((err) => console.log('No currentSession', err)); // No currentSession No current user

    Auth.currentCredentials()
      .then((creds) => {
        console.log('creds', creds);
        //   {
        //      "id": "us-east-1:xxx-xxx-xx-xx-xxxx",
        //     "accessKeyId": "xxxxx",
        //     "secretAccessKey": "/xxxxxx+",
        //     "sessionToken": "IQoJb3JpZ2luX2VjEFcaCXVzLWVhc3QtMSJGMEQCIGdf5gYxDJgwDnby.........",
        //     "expiration": "2023-05-19T23:07:13.000Z",
        //     "authenticated": true
        // }
      })
      .catch((err) => console.log('No creds', err));

    Auth.currentUserCredentials()
      .then((userCreds) => {
        console.log('userCreds', userCreds);
        //   {
        //      "id": "us-east-1:xxx-xxx-xx-xx-xxxx",
        //     "accessKeyId": "xxxxx",
        //     "secretAccessKey": "/xxxxxx+",
        //     "sessionToken": "IQoJb3JpZ2luX2VjEFcaCXVzLWVhc3QtMSJGMEQCIGdf5gYxDJgwDnby.........",
        //     "expiration": "2023-05-19T23:07:13.000Z",
        //     "authenticated": true
        // }
      })
      .catch((err) => console.log('No userCreds', err));

    Auth.currentUserInfo()
      .then((userInfo) => {
        console.log('userInfo', userInfo); //userInfo null
      })
      .catch((err) => console.log('No userInfo', err));

    Auth.currentUserPoolUser()
      .then((poolUser) => {
        console.log('poolUser', poolUser);
      })
      .catch((err) => console.log('No poolUser', err)); // No poolUser No current user

    return unsubscribe;
  }, []);

and when I call other way of federated sign in like so:

            Auth.federatedSignIn({
              provider: CognitoHostedUIIdentityProvider.Google
            });

any methods return anything but fall in catch block.

and these are logged at console:

ConsoleLogger.ts:105 [ERROR] 26:55.622 AuthClass - No current user

NotAuthorizedException: Unauthenticated access is not supported for this identity pool.

Is there a way to use sessionToken and secretAccessKey?

Sure. I am following Authorization code grant flow.

  1. step: call Auth.federatedSignIn with responseType: ‘code’. It returns a “code” in url.
  2. step: send a new request to oauth2/token endpoint with grant_type = ‘authorization_code’. It is supposed to return id_token_ access_token and refresh_token.

Below is detailed explanation:

Amplify.configure({
  Auth: {
    identityPoolId: ENV.COGNITO_IDENTITY_ID,
    region: ENV.COGNITO_REGION,
    identityPoolRegion: ENV.COGNITO_REGION,
    userPoolId: ENV.COGNITO_USER_POOL_ID,

    userPoolWebClientId: 'xxxxx', 
    mandatorySignIn: false,
    oauth: {
      domain: ENV.COGNITO_DOMAIN,
      scope: [
        'phone',
        'email',
        'profile',
        'openid',
        'aws.cognito.signin.user.admin'
      ],
      redirectSignIn: ${window.location.origin}/login-google,
      redirectSignOut: 'http://localhost:3000/',
      clientId: 'xxxxx', 
      responseType: 'code' // or 'token', note that REFRESH token will only be generated when the responseType is code
    }
  }
});

await Auth.federatedSignIn({
        provider: CognitoHostedUIIdentityProvider.Google
      });

when login with above method (Auth.federatedSignIn), it creates a new user in user pool and returns id_token and access_token when responseType is ‘token’. However i need refresh token too. otherwise i can not create a new CognitoUser object at client side ( so not able to update user attributes as well since not possible to create a valid CognitoUser with id_token and access_token only). Hence i need that REFRESH TOKEN too. According to docs, for example this one in order to get refresh token after federated sign in once should configure responseType as this : responseType: ‘code’.

when you configure responseType: ‘code’ you will get “code” and “state” variables in the url in return. The docs says that it is possible to get id_token, access_token and refresh_token all together by using this “code” with sending a request to /oauth2/token endpoint. It also emphasizes that if the client app that was used requires a secret, the Authorization header for this request is set as “Basic BASE64(CLIENT_ID:CLIENT_SECRET)“, where BASE64(CLIENT_ID:CLIENT_SECRET) is the base64 representation of the app client ID and app client secret, concatenated with a colon. whatever i did, i failed to get those tokens since i got always invalid_request. If I send a new request with already used “code”, this time it returns “invalid_grant”. The sample request is here like so:

const AUTH_DOMAIN = 'https://xxx.auth.us-east-1.amazoncognito.com';

    const grantType = 'authorization_code';
    const clientId = 'xxx'; 
 const clientSecret = 'xxxx',
    const redirectUri = ${window.location.origin}/login-google; 
    
    axios
      .post(
       ${AUTH_DOMAIN}/oauth2/token,

        new URLSearchParams({
          grant_type: grantType,
          code: code,
          state: state,
          client_id: clientId,
          redirect_uri: redirectUri
        }),
        {
          headers: {
            Authorization: getBase64EncodedCredential(clientId, clientSecret)
          }
        }
      )
      .then((response) => {
        // handle success
        console.log(response.data);
      })
      .catch((error) => {
        // handle error
        console.error(error);
      });
      
      function getBase64EncodedCredential(cognitoAppId, cognitoAppSecret) {
    return 'Basic ' + btoaImplementation(cognitoAppId + ':' + cognitoAppSecret);
  }
  function btoaImplementation(str) {
    try {
      return btoa(str);
    } catch (err) {
      return Buffer.from(str).toString('base64'); 
    }
  }

Auth.currentAuthenticatedUser() returns only token of provider (here google token). So it is not useful fro me. it is something like this :

{
    "id": "us-east-1:xxxxx",
    "name": "xxx",
    "token": "xxxxxx"
}
**In short, I am seeking to get that refresh token along with id token and access token from cognito user pool.**

Jeremy Glesner addressed this problem here in his article and here in his issue

another issue opened here

Update: I was able to leverage Auth with aforementioned restrictions, by creating following flow:

on the client:

  1. user logs into google, get id_token
  2. send id_token over to the backend

on the backend: 3. verify id_token on the backend (for node.js I’ve used ‘google-auth-library’ npm package) 4. create a cognito user and record real email in some custom attribute, confirm user, set random password 5. send username/ random password to user

on the client: 6. Auth.signIn({ username, password} )

One caveat - I’ve noticed if we were to use Auth.federatedSignIn({ provider : “Google” }) cognito users are created with username google_192301930123 where email attribute could be actual email. When I try to create that, it tells me that username must match look like email. How are users such as google_192301930123 generated? Am I missing something trivial and the above flow is overkill?

Thanks in advance for any input on this matter

Unfortunately I am not able to share repo. yet I have already shared how I configured, logged in with google and tried to get refresh token.

If possible share me a working example of ### authorization code flow when login with google token (or any other social login). I need to see login with google where it returns authorization code that will be later exchanged with cognito tokens (id, access and refresh token).

if you are able to share that working example I could check it and try to understand where i am doing wrong for authorization code flow and share the result here.

any help would be much appreciated

Hi @nadetastic No, I am not saying it’s not creating Cognito User in user pool. It returns id_token and access_token which indicates its creating a new user in user pool.

What is problem here is that I cannot create a new Cognito User instance in front app.

As far as I understand, since i need to update user attributes so I have to create a valid cognito user and cognito session in front. So to be able initiate new cognito session in front app I need to id_token, access_token and refresh_token. Above approach that is exchange code with token using token endpoint always returns invalid_request.

  1. First method returns session token like so: { "identityId": "us-east-1:xx-xxxxx", "accessKeyId": "xxxx", "secretAccessKey": "xx/xxx/x+xxxx", "sessionToken":"xxxxxx" }

But still it seems it is not possible to create a new cognito session with this data

  1. Second method
    a. returns id_token, and access_token when configured responseType=“token”. I cannot create a valid session with these tokens b. returns code responseType=“code”. Docs states that only way to get refresh token along with other tokens is that using grant_type=“authorization_code”. I am trying to exchange authorization code with token but always get invalid_request. I have asked a lot of place but still stuck here. It is getting boring 😃

ps: It might be related about this issue. #8632

This detail is important to know. Thank you. I should say still i am unable to achieve creating Cognito User with Auth.federatedSignIn({ provider: ‘Google’ }). When I use responseType: ‘token’, It returns only id_token and access_token, there is no refresh token so i could not create a valid Cognito User and cognito session. when it comes to responseType: ‘code’, i am getting invalid_request error from token endpoit for grant_type :authorization_code. this is the config and codes for exchanging code with token.

const AUTH_DOMAIN = 'https://xxx.auth.us-east-1.amazoncognito.com';

    const grantType = 'authorization_code';
    const clientId = 'xxx'; 
 const clientSecret = 'xxxx',
    const redirectUri = `${window.location.origin}/login-google`; 
    
    axios
      .post(
        `${AUTH_DOMAIN}/oauth2/token`,

        new URLSearchParams({
          grant_type: grantType,
          code: code,
          state: state,
          client_id: clientId,
          redirect_uri: redirectUri
        }),
        {
          headers: {
            Authorization: getBase64EncodedCredential(clientId, clientSecret)
          }
        }
      )
      .then((response) => {
        // handle success
        console.log(response.data);
      })
      .catch((error) => {
        // handle error
        console.error(error);
      });
      
      function getBase64EncodedCredential(cognitoAppId, cognitoAppSecret) {
    return 'Basic ' + btoaImplementation(cognitoAppId + ':' + cognitoAppSecret);
  }
  function btoaImplementation(str) {
    try {
      return btoa(str);
    } catch (err) {
      return Buffer.from(str).toString('base64'); 
    }
  }

Yet I am getting error: “invalid_grant” for the same code. I do not understand where i am doing wrong. Any help for this would be much appreciated.