'Unauthorized' error when using AWS amplify with grahql to create a new user

7.8k views Asked by At

So I think this issue comes from me not quite understanding the relationship between AWS cognito user pools and the auth rules in a graphql schema.

When I run the code below, I get the message "Not Authorized to access createUser on type User".

import React from 'react';
import { Auth, API, graphqlOperation } from 'aws-amplify';
import { withAuthenticator } from "@aws-amplify/ui-react";

// This was created automatically from the schema by aws amplify
const CreateUser = /* GraphQL */ `
  mutation CreateUser(
    $input: CreateUserInput!
    $condition: ModelUserConditionInput
  ) {
    createUser(input: $input, condition: $condition) {
      id
      username
      conversations {
        items {
          id
          convoLinkUserId
          convoLinkConversationId
          createdAt
          updatedAt
        }
        nextToken
      }
      messages {
        items {
          id
          authorId
          content
          messageConversationId
          createdAt
          updatedAt
        }
        nextToken
      }
      createdAt
      updatedAt
    }
  }
`;

async function signIn(username, password) {
  try {
      const user = await Auth.signIn(username, password);
      const { attributes } = user;
      console.log("User", attributes)
      return user
  } catch (error) {
      console.log('error signing in', error);
  }
}

async function createUser(id) {
  // creating a new user in the dynamodb table
  try {
    const newUser = {input: {username: id, id}}
    console.log("Creating new user", newUser)
    await API.graphql(graphqlOperation(CreateUser, newUser))
  } catch (err) {
    console.log('Error creating user! :', err)
  }
}

async function testApiCalls() {
  await signIn("[email protected]", "notarealpassword123") // runs successfully
  await createUser("[email protected]") // where the error happens
}

function App() {
  testApiCalls()

  return (
    <div className="App">
      Hello
    </div>
  );
}

export default withAuthenticator(App);

Other relevant code would be my index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import Amplify, { Auth } from 'aws-amplify';
import AWSAppSyncClient from 'aws-appsync'
import aws_config from './aws-exports';
import { ApolloProvider } from '@apollo/client';

Amplify.configure(aws_config);
aws_config.graphql_headers = async () => { const currentSession = await Auth.currentSession(); return { Authorization: currentSession.getIdToken().getJwtToken() }; };


const client = new AWSAppSyncClient({
  url: aws_config.aws_appsync_graphqlEndpoint,
  region: aws_config.aws_appsync_region,
  auth: {
    type: aws_config.aws_appsync_authenticationType, // AMAZON_COGNITO_USER_POOLS
    jwtToken: async () => (await Auth.currentSession()).idToken.jwtToken
  }
});

const WithProvider = () => (
  <ApolloProvider client={client}>
      <App/>
  </ApolloProvider>
)

ReactDOM.render(
  <WithProvider/>,
  document.getElementById('root')
);

And the schema definition for the User object:

type User 
  @model 
  @auth(rules: [{ allow: owner, ownerField: "id", queries: null }]) {
  id: ID!
  username: String!
  conversations: [ConvoLink] @connection(name: "UserLinks")
  messages: [Message] @connection(name: "UserMessages")
    createdAt: String
    updatedAt: String
}

Ultimately, I'm trying to make something similar to this example. I've tried reading the aws amplify docs but haven't been able to properly understand how the graphql operations are effected by the authentication.

3

There are 3 answers

0
patwards On

I'm pretty sure that the solution was adding @aws_cognito_user_pools to the schema definition for User. I also changed it to allow the owner to do whatever they want, but before they were unable to query.

type User 
  @model 
  @auth(rules: [{ allow: owner}])
  @aws_cognito_user_pools {
  id: ID!
  username: String!
  conversations: [ConvoLink] @connection(name: "UserLinks")
  messages: [Message] @connection(name: "UserMessages")
    createdAt: String
    updatedAt: String
}
1
BrunoLoops On

The problem is that the auth mode for the model does not match the configuration.

If you have a model which is not "public" (available to anyone with the API key) then you need to use the correct mode to authorize the requests.

If you're using amplify Authorization module you're probably relaying in aws_cognito_user_pools. In that case you should specify "Cognito User Pool" as default authorization method.

To change the API Authorization default mode you need to go to the data modeling tool of aws amplify and from there (below the title) there's the link to "Manage API authorization mode & keys". Manage API authorization mode & keys

When using the "Cognito User Pool" as default authorization method you can use the API as usual for private methods correctly. enter image description here This also fixed the subscriptions for me.

1
WyoBuckeye On

I just spent several hours battling this same issue. For me, I had to specify the authMode on the graphql request.

Rather than doing something like this:

await API.graphql(graphqlOperation(createFamily, {input: family}))

I had to use this:

await API.graphql({
        query: createFamily,
        variables: {input: family},
        authMode: 'AMAZON_COGNITO_USER_POOLS'
      })

I did try the solution from user patwords. However, nothing I did on the schema was effective (including adding @aws_cognito_user_pools as indicated).

Unfortunately, the Amplify documentation does not do a good job documenting the process. I hope this helps someone else save a bit of time.