Amplify-cli: Issue with Multi-auth

Created on 22 Sep 2019  ·  26Comments  ·  Source: aws-amplify/amplify-cli

Describe the bug
Using the following schema:

type Post @model
  @auth (
      rules: [
          { allow: groups, groups: ["Admin"], queries: null },
          { allow: public }
      ]
  ) {
  id: ID!
  title: String!
  description: String
}

As a signed in user as Admin I am unable to perform queries. Here are the generated operations:

type Query {
    getPost(id: ID!): Post
        @aws_api_key
    listPosts(filter: ModelPostFilterInput, limit: Int, nextToken: String): ModelPostConnection
        @aws_api_key
}

type ModelPostConnection @aws_api_key {
    items: [Post]
    nextToken: String
}

Looks like @aws_cognito_user_pools is not being added to the schema.

graphql-transformer question

Most helpful comment

Guys I know this is closed - but thought it might be the best place to follow-up.

I'm trying to setup the IAM for unAuthenticated users. Amplify notes that this should be created automatically.

However I still get Identity Pool Configuration Errors, hence we unable to obtain valid credentials for the graphQL request. I can see the unAuth role linked and with permissions to the graphql endpoint that we need. Any idea what is missing from the configuration? I know the API key configuration mentioned above should solve the problem but I can't see a working example of the public IAM role.

{"__type":"InvalidIdentityPoolConfigurationException","message":"Invalid identity pool configuration. Check assigned IAM roles for this pool."}

All 26 comments

@dabit3 what version of the CLI are you using, since I fixed a similar one recently. Also please tell me the configured auth modes, which one is the default.

But I'm curious, you're disabling queries in the rules, why are you expecting to generate @aws_cognito_user_pools? If the rule is not applied to a given operation, then the operation will not get that attribute added.

Hey, I am on 3.9.0 of the CLI.

I'm disabling queries because I have a use case where only authenticated users can read from the API but only Admin can make changes or mutations.

But I can't see authenticated users added as an @auth directive.

Adding { allow: private, operations: [read] } rule will be needed.

By default auth is not restricted, for the default auth mode configured for the service, but once you add an @aws_api_key to a type in the schema, that removes the default auth mode as allowed, this is how the service behaves, and that's the pattern what we're following as well.

Ah, ok so by adding { allow: private, operations: [read] } this will enable / add @aws_cognito_user_pools?

Perhaps this directive would describe better what you wrote above:

@auth (
    rules: [
          { allow: private, operations: [read] } # allow authenticated users to read 
        { allow: groups, groups: ["Admin"], operations: [create, update, delete] }, # allow admins to mutate
        { allow: public } # API key users can do everything?
    ]
)

Awesome, yes that's what I ended up with too. Thanks for your help on this, sorry to be bugging you on the weekend!

@dabit3 are we missing something in the docs that could have avoided this confusion?
cc @kaustavghosh06

@undefobj after reviewing the docs no, it's just a mistake on my part.

@attilah @dabit3 Hi, sorry to drag this on. I found this thread helpful for setting up my auth rules but I'm still having some issues.

I want authenticated users to be able to create, update, and delete their own posts. However, I want both authenticated and unauthenticated users to be able to read all posts.

There is a home screen where the posts should all be displayed. After logging in, users see a dashboard to create a new post or see all of their own posts that they can update or delete.

Reading through the documentation, I understood this to be the set up that I want:

Method 1

@auth(rules: [
    # Defaults to use the "owner" field.
    {allow: owner},
    # Then allows public read access
    {allow: public, provider: apiKey, operations: [read]}
  ])

However, reading the thread above it seems like I would need:

Method 2

@auth(rules: [
    # Defaults to use the "owner" field.
    {allow: owner, operations: [read, create, update, delete]},
    # Then allows public read access
    {allow: public, provider: apiKey, operations: [read]}
  ])

Or maybe:

Method 3

@auth(rules: [
    # Defaults to use the "owner" field.
    {allow: owner, operations: [read, create, update, delete]},
    # Allow authenticated users to read 
    {allow: private, operations: [read]}, 
    # Then allows "public" read access
    {allow: public, provider: apiKey, operations: [read]}
  ])

Using Method 1:
I am able to log in and do all CRUD operations for that owner's posts. However, the owner can only see their own posts.

When logged out, listPosts results in:
No current user.

The listPosts method looks like:

    async listPosts() {
        await this.getUser();
        try {
            const result = await API.graphql(graphqlOperation(listPosts, {user: this.state.user.username}));
            this.setState({posts: result.data.listPosts.items});
        } catch (error) {
            console.error(error);
        }
    }

When I use Postman to listPosts (with my api key for authentication), listPosts works.

Using Method 2:
Works the same as Method 1.

Using Method 3:
Works the same as Method 1.

I would love a little help figuring out the auth rules. I've read through the documentation a few times and I am not sure what I am missing.

Thanks,
Emma

@emmafass what about this schema?

@auth(rules: [
    # Defaults to use the "owner" field.
    {allow: owner},
    # Then allows public read access
    {allow: public, provider: iam, operations: [read]}
  ])

Since you're using userPools, you can leverage the Unauthenticated role as an IAM provider, but also public with userPools provider could do the trick, and Amplify JS provide the way to get the credentials of the unauthenticated role.

Could you please try these options?

I'm not sure I totally understand how to do this.

@auth(rules: [
    # Defaults to use the "owner" field.
    {allow: owner},
    # Then allows public read access
    {allow: public, provider: iam, operations: [read]}
  ])

Gives me the error

@auth directive with 'iam' provider found, but the project has no IAM authentication provider configured.
There was an error pushing the API resource
InvalidDirectiveError: @auth directive with 'iam' provider found, but the project has no IAM authentication provider configured.

Then trying public with userPools provider gives me:

@auth(rules: [
    # Defaults to use the "owner" field.
    {allow: owner},
    # Then allows public read access
    {allow: public, provider: userPools, operations: [read]}
  ])

Error:

@auth directive with 'public' strategy only supports 'apiKey' (default) and 'iam' providers, but found 'userPools' assigned.
There was an error pushing the API resource
InvalidDirectiveError: @auth directive with 'public' strategy only supports 'apiKey' (default) and 'iam' providers, but found 'userPools' assigned.

I have userPools as my default authorization mode and apiKey as the only additional authorization mode.

Should I add iam?

Sorry, my bad,

The second suggestion was incorrect. For the first one, please run amplify api update and when configure advanced settings and add IAM as a second provider for your API, that will get rid of the error message.

I ran amplify api update and added IAM as an additional authorization provider.

My auth rules are set up like:

@auth(rules: [
    # Defaults to use the "owner" field.
    {allow: owner},
    # Then allows public read access
    {allow: public, provider: iam, operations: [read]}
  ])
 ```

listPosts is now like:

async listPosts() {
try {
const result = await API.graphql(graphqlOperation(listPosts));
this.setState({posts: result.data.listPosts.items});
} catch (error) {
console.error(error);
}
}
```

When logged out, I am still not seeing any posts and getting No current user error.
When logged in, I am still only seeing the posts that user owns.

I am not very familiar with IAM. What additional set up should I do to get it working?

The documentation says: When used in conjunction with amplify add auth the CLI generates scoped down IAM policies for the “UnAuthenticated” role automatically.

I tried running amplify update auth and got:

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 unintende
d side effects.

Using service: Cognito, provided by: awscloudformation
 What do you want to do? (Use arrow keys)
❯ Apply default configuration with Social Provider (Federation) 
  Walkthrough all the auth configurations 

It seems like the auth configuration I want is:

@auth(rules: [
    # Defaults to use the "owner" field.
    {allow: owner, provider: userPools, operations: [read, create, update, delete]},
    # Next allow public access with an API Key
    {allow: public, provider: apiKey, operations: [read]}
  ]) 

and to list Posts for everyone to see I just needed to change the request to be like:

const createdPosts = await API.graphql({
  query: queries.listPosts,
  variables: null,
  authMode: 'API_KEY'
});

as explained in the documentation here

This is working for me locally!! Getting the results that I would expect, plus I am able to use Postman to test requests.

Thank you for the help @attilah

@emmafass how did you add the secondary (apiKey) auth provider with amplify cli? it's not working for me as described here.

@rlimberger if it is not coming up as an option when you run amplify update api then you probably are still using an old version of the CLI.

Could you check what version you are using? In order to install the latest version I had to run: sudo npm install -g @aws-amplify/cli --unsafe-perm=true.

More details on that command here

im using UserPool. Some Query must be public then i configure like this on schema.

Product @model @auth(rules: [{allow: public, provider: apiKey, operations: [read]}]) 

but not auth users cant access the query. "No current user"

@anarerdene Since UserPool is your default auth provider, you need to double check that you correctly added API Key as your secondary auth provider.

In the AWS Console, the AWS AppSync service has a settings tab. There you can view your configured auth providers.

If you do not see the API Key auth provider, I suggest adding it via the CLI. Run the command amplify update api.

Hi @emmafass .
i already configured.
image

I have a no clue.

this is my AppsyncSDK client code.

const client = new AWSAppSyncClient(
    {
      url: config.aws_appsync_graphqlEndpoint,
      region: config.aws_appsync_region,
      auth: {
        type: config.aws_appsync_authenticationType,
        jwtToken: async () => (await Auth.currentSession()).getIdToken().getJwtToken()
      },
      disableOffline: true
    },
    {
      cache: new InMemoryCache().restore(initialState || {}),
      ssrMode: true,
      connectToDevTools: true
    }
  );

or i need multiple clients ?

@anarerdene I don't think you need multiple clients but I could be wrong about that.

My next guess would be that you aren't passing the authMode in you GraphQL queries.

const result = await API.graphql({
        query: queries.listProducts,
        variables: null,
        authMode: "API_KEY"
});

Did you make sure to do this?

@emmafass reason is i cant use Amplify GraphQL Client. I'm using AWS AppSync SDK because i need Query cache.

If i change auth type COGNITO_USER_POOLS to ApiKey it works.


function createApolloClient(initialState) {
  const client = new AWSAppSyncClient(
    {
      url: config.aws_appsync_graphqlEndpoint,
      region: config.aws_appsync_region,
      auth: {
        type: config.aws_appsync_authenticationType,
        jwtToken: async () => (await Auth.currentSession()).getIdToken().getJwtToken()
      },
      disableOffline: true
    },
    {
      cache: new InMemoryCache().restore(initialState || {}),
      ssrMode: true,
      connectToDevTools: true
    }
  );

  const clientPublic = new AWSAppSyncClient(
    {
      url: config.aws_appsync_graphqlEndpoint,
      region: config.aws_appsync_region,
      auth: {type: AUTH_TYPE.API_KEY, apiKey: config.aws_appsync_apiKey},
      disableOffline: true
    },
    {
      cache: new InMemoryCache().restore(initialState || {}),
      ssrMode: true,
      connectToDevTools: true
    }
  );

  return clientPublic;
}

From the docs, this is not clear:

The @auth directive allows the override of the default provider for a given authorization mode. In the sample above iam is specified as the provider which allows you to use an “UnAuthenticated Role” from Cognito Identity Pools for public access, instead of an API Key. When used in conjunction with amplify add auth the CLI generates scoped down IAM policies for the “UnAuthenticated” role automatically.

I selected allow unauthenticated access during add auth with the cognito user pool option. Is there something else that needs to be done?

I'm getting this error when I run amplify mock:

Failed to start API Mock endpoint InvalidDirectiveError: @auth directive with 'iam' provider found, but the project has no IAM authentication provider configured.

my statement matches the code in the docs:

{ allow: public, provider: iam, operations: [read] }

Edit: I get the same error message when attempting to compile the graphql schema.

So i stopped the error by adding IAM as an additional authentication strategy to appsync. If this is the correct setup the docs should say so.

Guys I know this is closed - but thought it might be the best place to follow-up.

I'm trying to setup the IAM for unAuthenticated users. Amplify notes that this should be created automatically.

However I still get Identity Pool Configuration Errors, hence we unable to obtain valid credentials for the graphQL request. I can see the unAuth role linked and with permissions to the graphql endpoint that we need. Any idea what is missing from the configuration? I know the API key configuration mentioned above should solve the problem but I can't see a working example of the public IAM role.

{"__type":"InvalidIdentityPoolConfigurationException","message":"Invalid identity pool configuration. Check assigned IAM roles for this pool."}

Was this page helpful?
0 / 5 - 0 ratings