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

Graphql create API not working for FB login users #9353

Closed kgoyal98 closed 2 years ago

kgoyal98 commented 2 years ago

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

Authentication, GraphQL API

Amplify Categories

auth

Environment information

``` # Put output below this line ```

Describe the bug

I created an amplify application with email signup which is working perfectly. Recently, I added FB signin option in the application. The FB signin is successful. The get user API is also working. However, the create graphql APIs are not working with the error: Not Authorized to access createUser on type User

User graphql definition:

type User @model
  @auth(rules: [
      {allow: groups, groups: ["admin"]},
      {allow: owner, ownerField: "id", operations: [create, update, delete]},
      {allow: private, operations: [read]}
    ])
    @key(fields: ["id"])
    @key(name: "byUsername", fields: ["username"], queryField: "usersByUsername") {
  id: ID!
  username: String!
  name: String!
}

Expected behavior

The create user graphql API should work for FB authenticated users as well.

Reproduction steps

N/A

Code Snippet

// Put your code below this line.

Log output

``` // Put your logs below this line ```

aws-exports.js

const awsmobile = {
    "aws_project_region": "ap-south-1",
    "aws_appsync_graphqlEndpoint": "https://****.appsync-api.ap-south-1.amazonaws.com/graphql",
    "aws_appsync_region": "ap-south-1",
    "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",
    "aws_cognito_identity_pool_id": "ap-south-1:****",
    "aws_cognito_region": "ap-south-1",
    "aws_user_pools_id": "ap-south-1_****",
    "aws_user_pools_web_client_id": "***",
    "oauth": {
        "domain": "<domain>.amazoncognito.com",
        "scope": [
            "phone",
            "email",
            "openid",
            "profile",
            "aws.cognito.signin.user.admin"
        ],
        "redirectSignIn": "https://<domain>,http://localhost:3000",
        "redirectSignOut": "https://<domain>,http://localhost:3000",
        "responseType": "token"
    },
    "federationTarget": "COGNITO_USER_AND_IDENTITY_POOLS",
    "aws_user_files_s3_bucket": "",
    "aws_user_files_s3_bucket_region": "ap-south-1"
};

export default awsmobile;

Manual configuration

amplify/backend/backend-config.json

{
  "api": {
    "*****": {
      "service": "AppSync",
      "providerPlugin": "awscloudformation",
      "output": {
        "authConfig": {
          "defaultAuthentication": {
            "authenticationType": "AMAZON_COGNITO_USER_POOLS",
            "userPoolConfig": {
              "userPoolId": "*****"
            }
          },
          "additionalAuthenticationProviders": [
            {
              "authenticationType": "AWS_IAM"
            }
          ]
        }
      }
    }
  },
  "auth": {
    "*****": {
      "service": "Cognito",
      "providerPlugin": "awscloudformation",
      "dependsOn": [],
      "customAuth": false
    }
  },
  "storage": {
    "images": {
      "service": "S3",
      "providerPlugin": "awscloudformation"
    }
  }
}

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

No response

chrisbonifacio commented 2 years ago

Hi @kgoyal98 thanks for raising this issue. I was not able to reproduce the issue, I was able to create a user record while logged in as a facebook user in my app. If you try creating a user with a normal username/email and password login, do you experience the same behavior?

chrisbonifacio commented 2 years ago

for reference: https://staging.d13c7lh58sja73.amplifyapp.com/

aws-exports.js

const awsmobile = {
    "aws_project_region": "us-east-1",
    "aws_cognito_identity_pool_id": "us-east-1:0842f275-a91d-41bc-988e-4f26de400f07",
    "aws_cognito_region": "us-east-1",
    "aws_user_pools_id": "us-east-1_B5dxgXBp1",
    "aws_user_pools_web_client_id": "6smtsec602ivrka2fhlc2cmtch",
    "oauth": {
        "domain": "tibxlj6drokt-staging.auth.us-east-1.amazoncognito.com",
        "scope": [
            "phone",
            "email",
            "openid",
            "profile",
            "aws.cognito.signin.user.admin"
        ],
        "redirectSignIn": "http://localhost:3000,https://staging.d13c7lh58sja73.amplifyapp.com/",
        "redirectSignOut": "http://localhost:3000,https://staging.d13c7lh58sja73.amplifyapp.com/",
        "responseType": "code"
    },
    "federationTarget": "COGNITO_USER_POOLS",
    "aws_cognito_username_attributes": [
        "EMAIL"
    ],
    "aws_cognito_social_providers": [
        "FACEBOOK"
    ],
    "aws_cognito_signup_attributes": [
        "EMAIL"
    ],
    "aws_cognito_mfa_configuration": "OFF",
    "aws_cognito_mfa_types": [
        "SMS"
    ],
    "aws_cognito_password_protection_settings": {
        "passwordPolicyMinLength": 8,
        "passwordPolicyCharacters": [
            "REQUIRES_LOWERCASE",
            "REQUIRES_NUMBERS",
            "REQUIRES_SYMBOLS",
            "REQUIRES_UPPERCASE"
        ]
    },
    "aws_cognito_verification_mechanisms": [
        "EMAIL"
    ],
    "aws_appsync_graphqlEndpoint": "https://dolgi5qhhvcm3e5d4lygyglgyq.appsync-api.us-east-1.amazonaws.com/graphql",
    "aws_appsync_region": "us-east-1",
    "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",
    "aws_appsync_apiKey": "da2-agesyzefiva45mndtrfvkd3qei"
};

export default awsmobile;
kgoyal98 commented 2 years ago

@chrisbonifacio If I create a user with a normal email and password login, it is working fine. I think there is some configuration issue in my Amplify app. I can see some keys in your aws-exports.js which are not present in mine. How did you configure your amplify app?

chrisbonifacio commented 2 years ago

Here's the codebase for the project I used to reproduce: https://github.com/chrisbonifacio/amplify-dse-repros/tree/main/studio/starter

Might be worth comparing how we are both signing into Facebook. Are you authenticating against the User Pool or Identity Pool? For example, when you signup/signin as a FB user, do your users appear in the Cognito console under User Pool?

Personally, I am using the Amplify UI components, specifically the Authenticator component, to authenticate which seems to be against Cognito User Pools.

I noticed that your federationTarget is COGNITO_USER_AND_IDENTITY_POOLS whereas mine is COGNITO_USER_POOLS

If you are actually authenticating with an Identity Pool user, and your authenticationType for AppSync is AMAZON_COGNITO_USER_POOLS, which it seems to be, that would explain why the user is unauthorized to perform the mutation, but is allowed to read/get because of the { allow: private, operation: read } rule as they are still authenticated.

kgoyal98 commented 2 years ago

@chrisbonifacio The FB user is coming in user pool. I do not understand user identity page since I never used it as amplify handled everything internally.

I am using this code (in nodejs) to do FB signin: await Auth.federatedSignIn({ provider: CognitoHostedUIIdentityProvider.Facebook });

I am creating user using

await API.graphql(graphqlOperation(mutations.createUser, {
        input: {
            'id': userID,
            'username': username,
            'name': name
        }
    })) as Promise<{ data: types.CreateUserMutation }>;

Here is the walkthrough of amplify authentication.

$ amplify auth update
Please note that certain attributes may not be overwritten if you choose to use defaults settings.

You have configured resources that might depend on this Cognito resource.  Updating this Cognito resource could have unintended side effects.

Using service: Cognito, provided by: awscloudformation
 What do you want to do? Walkthrough all the auth configurations
 Select the authentication/authorization services that you want to use: User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Storage features for images or o
ther content, Analytics, and more)
 Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM) No
 Do you want to enable 3rd party authentication providers in your identity pool? Yes
 Select the third party identity providers you want to configure for your identity pool: Facebook

 You've opted to allow users to authenticate via Facebook.  If you haven't already, you'll need to go to https://developers.facebook.com and create an App ID. 

 Enter your Facebook App ID for your identity pool:  371436797834763
 Do you want to add User Pool Groups? No
 Do you want to add an admin queries API? No
 Multifactor authentication (MFA) user login options: OFF
 Email based user registration/forgot password: Enabled (Requires per-user email entry at registration)
 Please specify an email verification subject: Your verification code
 Please specify an email verification message: Your verification code is {####}
 Do you want to override the default password policy for this User Pool? No
 Specify the app's refresh token expiration period (in days): 30
 Do you want to specify the user attributes this app can read and write? No
 Do you want to enable any of the following capabilities? 
 Do you want to use an OAuth flow? Yes
 What domain name prefix do you want to use? 24ehrd4ndpbr
 Which redirect signin URIs do you want to edit? 
 Do you want to add redirect signin URIs? Yes
chrisbonifacio commented 2 years ago

Well, it turns out the issue might actually be just that you're including the id in the createUser inputs. Try leaving that out, it should still be set to the user's sub automatically.

I just tried including it and got the Unauthorized error. Must've missed that.

With ID argument

request

Screen Shot 2021-12-14 at 3 16 59 PM

response

Screen Shot 2021-12-14 at 3 17 04 PM

Without ID argument

request

Screen Shot 2021-12-14 at 3 17 31 PM

response

Screen Shot 2021-12-14 at 3 17 36 PM
kgoyal98 commented 2 years ago

@chrisbonifacio I am still getting the same error. In your case, it happened because the sub of your user is not what you supplied but facebook_... If you supply the correct sub, you won't get any error.

chrisbonifacio commented 2 years ago

In the screenshots I shared, trying to use a FB user's Cognito sub failed. By leaving out the id in the input, the username was used automatically instead and that worked.

this doesn't work for you?

await API.graphql(
  graphqlOperation(createUser, {
    input: {
      username,
      name,
    },
  })
);
kgoyal98 commented 2 years ago

@chrisbonifacio No, it didn't work.

chrisbonifacio commented 2 years ago

Can you share the code where you're importing and configuring Amplify?

For reference, this is how I'm configuring mine

import { Amplify } from "aws-amplify";
import awsConfig from "./aws-exports";

const isLocalhost = Boolean(
  window.location.hostname === "localhost" ||
    // [::1] is the IPv6 localhost address.
    window.location.hostname === "[::1]" ||
    // 127.0.0.1/8 is considered localhost for IPv4.
    window.location.hostname.match(
      /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    )
);

// Assuming you have two redirect URIs, and the first is for localhost and second is for production
const [localRedirectSignIn, productionRedirectSignIn] =
  awsConfig.oauth.redirectSignIn.split(",");

const [localRedirectSignOut, productionRedirectSignOut] =
  awsConfig.oauth.redirectSignOut.split(",");

const updatedAwsConfig = {
  ...awsConfig,
  oauth: {
    ...awsConfig.oauth,
    redirectSignIn: isLocalhost
      ? localRedirectSignIn
      : productionRedirectSignIn,
    redirectSignOut: isLocalhost
      ? localRedirectSignOut
      : productionRedirectSignOut,
  },
};

Amplify.configure(updatedAwsConfig);
kgoyal98 commented 2 years ago
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';

const isLocalhost = Boolean(
  window.location.hostname === "localhost" ||
  // [::1] is the IPv6 localhost address.
  window.location.hostname === "[::1]" ||
  // 127.0.0.1/8 is considered localhost for IPv4.
  window.location.hostname.match(
    /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
  )
);

// Assuming you have two redirect URIs, and the first is for localhost and second is for production
const [
  localRedirectSignIn,
  productionRedirectSignIn,
] = awsconfig.oauth.redirectSignIn.split(",");

const [
  localRedirectSignOut,
  productionRedirectSignOut,
] = awsconfig.oauth.redirectSignOut.split(",");

const updatedAwsConfig = {
  ...awsconfig,
  oauth: {
    ...awsconfig.oauth,
    redirectSignIn: isLocalhost ? productionRedirectSignIn : localRedirectSignIn,
    redirectSignOut: isLocalhost ? productionRedirectSignOut : localRedirectSignOut,
  }
}

Amplify.configure(updatedAwsConfig);
chrisbonifacio commented 2 years ago

Interestingly, using the Cognito user's username (which happens to be the sub if you're using email login mechanism) might be the best way to approach this with your schema. Making the call like this worked for me for multiple social providers (amazon, fb, google) as well as a cognito user. I was able to create and delete with all of them using the same exact schema in this issue's description.

// mutation
const res = await API.graphql(
  graphqlOperation(createUser, {
    input: {
      id: (await Auth.currentAuthenticatedUser()).username,
      username,
      name,
    },
  })
);
kgoyal98 commented 2 years ago

@chrisbonifacio With the above code, I am getting ConditionalCheckFailedException "The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 9RES2F6P0CRL50QOARQ82DCVMJVV4KQNSO5AEMVJF66Q9ASUAAJG, Extended Request ID: null)"

chrisbonifacio commented 2 years ago

What kind of user is trying to make that request?

Also are you using email or username login mechanism for Cognito users?

EDIT: If the mutation is failing with a Cognito user, make sure that there isn't already a User record with their ID. That DynamoDB error can happen when trying to create a record with the same ID.

kgoyal98 commented 2 years ago

Facebook logged in user is making that request

I am using email login mechanism.

kgoyal98 commented 2 years ago

You are right. There was a user record with the same ID. I am able to create the user with username instead of sub.

github-actions[bot] commented 1 year 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.