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.42k stars 2.12k forks source link

GraphQL returning Undefined #8207

Closed suhailsaqan closed 3 years ago

suhailsaqan commented 3 years ago

Before opening, please confirm:

JavaScript Framework

React Native

Amplify APIs

GraphQL API

Amplify Categories

auth, api

Environment information

``` # Put output below this line System: OS: macOS 11.3 CPU: (8) x64 Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz Memory: 168.34 MB / 16.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 14.16.0 - /usr/local/bin/node Yarn: 1.22.10 - /usr/local/bin/yarn npm: 7.11.2 - /usr/local/bin/npm Browsers: Brave Browser: 90.1.23.73 Chrome: 90.0.4430.93 Safari: 14.1 npmPackages: @aws-amplify/api: ^3.2.30 => 3.2.30 @babel/core: ^7.13.16 => 7.13.16 @babel/runtime: ^7.13.17 => 7.13.17 @react-native-async-storage/async-storage: ^1.15.4 => 1.15.4 @react-native-community/eslint-config: ^2.0.0 => 2.0.0 @react-native-community/netinfo: ^6.0.0 => 6.0.0 @types/jest: ^26.0.23 => 26.0.23 @types/react: ^17.0.4 => 17.0.4 @types/react-native: ^0.64.4 => 0.64.4 @types/react-test-renderer: ^17.0.1 => 17.0.1 HelloWorld: 0.0.1 amazon-cognito-identity-js: ^4.6.0 => 4.6.0 aws-amplify: ^3.3.27 => 3.3.27 aws-amplify-react-native: ^4.3.2 => 4.3.2 babel-jest: ^26.6.3 => 26.6.3 dateformat: ^4.5.1 => 4.5.1 eslint: ^7.25.0 => 7.25.0 hermes-inspector-msggen: 1.0.0 jest: ^26.6.3 => 26.6.3 metro-react-native-babel-preset: ^0.66.0 => 0.66.0 (0.64.0) react: 17.0.1 => 17.0.1 react-native: 0.64.0 => 0.64.0 react-native-fast-image: ^8.3.4 => 8.3.4 react-native-flash-message: ^0.1.23 => 0.1.23 react-native-gesture-handler: ^1.10.3 => 1.10.3 react-native-image-crop-picker: ^0.36.0 => 0.36.0 react-native-indicators: ^0.17.0 => 0.17.0 react-native-maps: ^0.28.0 => 0.28.0 react-native-material-textfield: ^0.16.1 => 0.16.1 react-native-modal: ^11.10.0 => 11.10.0 react-native-modalize: ^2.0.8 => 2.0.8 react-native-phone-number-input: ^2.0.1 => 2.0.1 react-native-pose: ^0.9.1 => 0.9.1 react-native-reanimated: ^2.1.0 => 2.1.0 react-native-responsive-dimensions: ^3.1.1 => 3.1.1 react-native-safe-area-context: ^3.2.0 => 3.2.0 react-native-screens: ^3.1.1 => 3.1.1 react-native-splash-screen: ^3.2.0 => 3.2.0 react-native-storage: ^1.0.1 => 1.0.1 react-native-super-grid: ^4.1.1 => 4.1.1 react-native-vector-icons: ^8.1.0 => 8.1.0 react-navigation: ^4.4.4 => 4.4.4 react-navigation-animated-switch: ^0.6.4 => 0.6.4 react-navigation-hooks: ^1.1.0 => 1.1.0 react-navigation-stack: ^2.10.4 => 2.10.4 react-navigation-tabs: ^2.11.1 => 2.11.1 react-test-renderer: 17.0.1 => 17.0.1 rn-placeholder: ^3.0.3 => 3.0.3 typescript: ^4.2.4 => 4.2.4 (3.9.9) npmGlobalPackages: @aws-amplify/cli: 4.46.1 apollo-codegen: 0.20.2 expo-cli: 4.3.2 firebase-tools: 9.1.0 nodemon: 2.0.7 npm: 7.11.2 truffle: 5.2.6 yarn: 1.22.10 ```

Describe the bug

I have reported this issue before but then the code worked so it got closed. However, now it is happening again:

I am trying to query the API with the following code, however, it is erroring with an 'undefined' error.

The network is empty when I query the API: Screen Shot 2021-04-30 at 11 52 03 AM

This is the code:

const users = (await API.graphql(
        graphqlOperation(listUsers, {
          filter: {email: {eq: email}},
        }),
      )) as GraphQLResult<ListUsersQuery>;

Expected behavior

I expect it to return a graphql result.

Reproduction steps

  1. amplify add api
  2. created the schema
  3. perform the above graphql query
  4. gives error as undefined

Code Snippet

// Put your code below this line.

Log output

``` // Put your logs below this line LOG [DEBUG] 41:45.405 AuthClass - signIn MFA required LOG [DEBUG] 41:45.408 AuthClass - Getting current session LOG [DEBUG] 41:45.409 AuthClass - Failed to get user from user pool LOG [DEBUG] 41:45.409 AuthClass - Failed to get the current user No current user ```
suhailsaqan commented 3 years ago

Any progress on this? If you need more information to reproduce this let me know.

chrisbonifacio commented 3 years ago

Good afternoon :wave: would you mind trying to console log users and also wrap the query in a try/catch and console log the error you're getting if possible?

suhailsaqan commented 3 years ago

Hi 👋🏻. It does not return any user but throws an error. The error is undefined.

chrisbonifacio commented 3 years ago

I haven't been able to reproduce this issue in a React Native with the same amplify packages and versions. Have you been able to successfully create users before trying to query for them?

This is my code

package.json

{
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo start --android",
    "ios": "expo start --ios",
    "web": "expo start --web",
    "eject": "expo eject"
  },
  "dependencies": {
    "@aws-amplify/api": "3.2.30",
    "@react-native-community/netinfo": "6.0.0",
    "amazon-cognito-identity-js": "4.6.0",
    "aws-amplify": "3.3.27",
    "aws-amplify-react-native": "4.3.2",
    "expo": "~41.0.1",
    "expo-status-bar": "~1.0.4",
    "react": "16.13.1",
    "react-dom": "16.13.1",
    "react-native": "https://github.com/expo/react-native/archive/sdk-41.0.0.tar.gz",
    "react-native-web": "~0.13.12"
  },
  "devDependencies": {
    "@babel/core": "^7.9.0"
  },
  "private": true
}

schema.graphql

type User @model {
  id: ID!
  email: AWSEmail!
  phone: AWSPhone!
  first_name: String!
  last_name: String!
  profile_picture: AWSURL
}

App.js

import { StatusBar } from "expo-status-bar";
import React, { useEffect } from "react";
import { StyleSheet, Text, View } from "react-native";
import awsconfig from "./src/aws-exports";
import Amplify, { API, graphqlOperation } from "aws-amplify";
import { listUsers } from "./src/graphql/queries";

Amplify.configure(awsconfig);

export default function App() {
  const init = async () => {
    try {
      const email = "test@gmail.com";

      const users = await API.graphql(
        graphqlOperation(listUsers, {
          filter: { email: { eq: email } },
        })
      );
      console.log(users);
    } catch (error) {
      console.log(error);
    }
  };

  useEffect(() => {
    init();
  }, []);

  return (
    <View style={styles.container}>
      <Text>Open up App.js to start working on your app!</Text>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

console log

Screen Shot 2021-05-07 at 2 40 09 PM
suhailsaqan commented 3 years ago

No it's the same thing when i try to create them. I created a user from the console though and that works normally.

It also tells me that there is no user after logging in with Cognito. Is that normal? Screen Shot 2021-05-07 at 11 51 52 AM

chrisbonifacio commented 3 years ago

I don't think that's normal 😅 how are you logging in or using Auth in your project? If you're using Cognito user pools as the auth for your graphQL api, I don't think you'd be able to make any requests if you're not properly logged in.

Would you mind sharing more of the code? I'm thinking there's probably an issue with the way auth is set up in the project.

suhailsaqan commented 3 years ago

This is the basics of my code. I am first using Cognito to sign in then querying the users data from the api.

try {
      const user = await Auth.signIn(email, password);
      console.log('successfully signed in: ' + user.getUsername());
      const users = await API.graphql(
        graphqlOperation(listUsers, {
          filter: {email: {eq: email}},
        }),
      );
} catch ({message}) {
      console.log('sign in failed', message);
chrisbonifacio commented 3 years ago

Are you importing Auth from aws-amplify or from the amazon-cognito-identity-js package? If you are, we recommend importing Auth from Amplify and letting it take care of Auth. It uses the same cognito library under the hood but it will keep the logged in user in the same instance it will use to make the graphql calls.

suhailsaqan commented 3 years ago

Like this?:

import {Auth, API, graphqlOperation} from 'aws-amplify';
chrisbonifacio commented 3 years ago

Quick question: In your error log, you're getting "signIn MFA required", so the Auth.signIn probably isn't really signing you in. I think if you were to console log the user returned from it you'll see that there's a challengeName. It might be that your user doesn't have MFA setup yet. For that I recommend following this part of the documentation: https://docs.amplify.aws/lib/auth/mfa/q/platform/js#setup-totp

suhailsaqan commented 3 years ago

Does SMS verification not count as MFA? Because when a user signs up it sends an SMS message and I use this to confirm the user:

await Auth.confirmSignUp(username,confirmationCode); 

What’s the difference?

chrisbonifacio commented 3 years ago

That SMS/Email verification code is to verify the user's account/email. The MFA code is to add an extra layer of security to authenticating (logging in) the user. So, when they try to log in they need to also provide a security code (TOTP: Time-Based One Time Password) from an app like Google Authenticator.

There is Auth.confirmSignUp but then there is Auth.confirmSignIn which accepts the CognitoUser object from Auth.signIn, the MFA code from either SMS or TOTP depending on what you chose during the auth configuration, and a respective mfaType of either "SMS_MFA" or "SOFTWARE_TOKEN_MFA"

With your auth configuration, the user will not be fully authenticated in until Auth.confirmSignIn is successfully resolved.

chrisbonifacio commented 3 years ago

This is how I'm handling it in my reproduction code

After attempting to sign in, set up TOTP for the user with their CognitoUser from Auth.signIn, display the token to them (I set it to state to display for them)

async function getTotpToken() {
    try {
      const token = await Auth.setupTOTP(user);
      setTotpToken(token);
    } catch (error) {
      console.log(error);
    }
  }

The user should then take the token we gave them and use it in an app that generates one time passwords (I used the Google Authenticator browser extension for my reproduction)

You'll need to allow the user to input the generated one time password so you can verify it

async function verifyTotpToken() {
    const {token} = formState;
    try {
      const res = await Auth.verifyTotpToken(user, token);
    } catch (error) {
      console.log(error);
    }
  }

From then on, the user will need to be logged in like so after Auth.signIn, which will give you the CognitoUser and a challengeName meaning another step is required before fully authenticating.

async function confirmSignIn() {
    const {code} = formState;
    try {
      const confirmedSignIn = await Auth.confirmSignIn(
        user,
        code,
        'SOFTWARE_TOKEN_MFA',
      );
      console.log({confirmedSignIn});
      onConfirmSignIn();
    } catch (error) {
      console.log(error);
    }
  }

Otherwise, you'd keep getting this even after Auth.signIn might've seemed like it worked because it didn't error out and returned some user info

Screen Shot 2021-05-10 at 4 50 38 PM

Here's what it looks like when I call Auth.signIn, try to use Auth.currentAuthenticatedUser, and finally Auth.confirmSignIn. You'll see that I get The user is not authenticated until I call Auth.confirmSignIn, then it works.

Screen Shot 2021-05-10 at 4 53 30 PM
chrisbonifacio commented 3 years ago

I used software token for my reproduction so you'd just have to make sure you pass "SMS_MFA" to Auth.confirmSignIn if you used "SMS" in your auth config instead.

suhailsaqan commented 3 years ago

Thank you! I'll try that now but I was trying to save the users information through the API to the database right after they sign up. How can I do it like that because it does not let the user use the api until doing the confirmSignin?

suhailsaqan commented 3 years ago

Do I just run the confirmSignup then confirmSignin then the API queries on signup?

chrisbonifacio commented 3 years ago

I would suggest using a Cognito Post Confirmation trigger to create the user in your API. https://medium.com/hackernoon/how-to-add-new-cognito-users-to-dynamodb-using-lambda-e3f55541297c

github-actions[bot] commented 2 years ago

This issue has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs.

Looking for a help forum? We recommend joining the Amplify Community Discord server *-help channels or Discussions for those types of questions.