Reputation: 97
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.
Upvotes: 6
Views: 8745
Reputation: 61
When using Amplify 6, there are several possible issues to be solved when facing this problem.
(1)
First, if your data is accessible via role inheritance (i.e. { allow: groups, groups: ["Admin"] } or similar) you should consider setting your appsync auth type in src/amplifyconfiguration.json
to: "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS"
instead of "aws_appsync_authenticationType": "API_KEY"
.
[edit] If you make a new amplify push
you may get back the original configuration in src/amplifyconfiguration.json
. A workaround is doing:
import amplifyconfig from './amplifyconfiguration.json';
Amplify.configure({...amplifyconfig, aws_appsync_authenticationType: "AMAZON_COGNITO_USER_POOLS"});
(2)
Then, you may face an issue related to No current user
. This is the second step related to your cognito identity pool not having the necessary authorizations. Then, you may need to modify the IAM rule from your service authRole. Then, you need to go to IAM -> Roles -> amplify-<xxx>-authRole
and click the tab Tust relationships
and authorize your user pool with something like:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "<YOUR_IDENTITY_POOL_ID>"
},
"ForAnyValue:StringLike": {
"cognito-identity.amazonaws.com:amr": "authenticated"
}
}
}
]
}
Where you must replace <YOUR_IDENTITY_POOL_ID>
with the Identity pool ID that can be found in Amazon Cognito -> Identity pools
.
(3)
After that, believe or not, you still may face an issue of the type UnauthorizedException
, this may happen because your AWS AppSync service related to Auth does not have the right Additional authorization mode
. Then you must go to AWS AppSync -> Settings (of your AppSync API)
and then to Additional authorization modes
and add your respective Amazon Cognito User Pool with the right Authorization mode identifier.
Finally, you may be able to communicate with your graphql CLI!
Upvotes: 0
Reputation: 554
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".
When using the "Cognito User Pool" as default authorization method you can use the API as usual for private methods correctly.
This also fixed the subscriptions for me.
Upvotes: 1
Reputation: 305
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.
Upvotes: 10
Reputation: 97
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
}
Upvotes: 1