Which Category is your question related to?
api
Amplify CLI Version
3.17.0
What AWS Services are you utilizing?
AppSync, Cognito
So I recently started using @auth
directive in my schema.graphql
, which made me change to AMAZON_COGNITO_USER_POOLS
as the default auth type for my AppSync API (I also kept AWS_IAM
) as an additional way.
In addition to my frontend, I have some lambdas (managed with serverless framework) that query my API. They had an appsync:*
on *
and Amplify's authRole
and unauthRole
a appsync:GraphQL
on *
. I was previously able to query the API with this piece of code:
require('isomorphic-fetch');
const AWSAppSyncClient = require('aws-appsync').default;
const { AUTH_TYPE } = require('aws-appsync/lib/link/auth-link');
const gql = require('graphql-tag');
const AWS = require('aws-sdk');
const gql = require('graphql-tag');
const { GRAPHQL_ENDPOINT, AWS_REGION } = process.env;
const appSyncClient = new AWSAppSyncClient({
url: GRAPHQL_ENDPOINT,
region: AWS_REGION,
auth: {
type: AUTH_TYPE.AWS_IAM,
credentials: AWS.config.credentials,
},
disableOffline: true,
});
const listVideosQuery = gql(`query ListVideos(
$filter: ModelVideoFilterInput
$limit: Int
$nextToken: String
) {
listVideos(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
status
createdAt
}
nextToken
}
}
`);
appSyncClient.query({
query: listVideosQuery,
}).then(console.log);
Note that I specify the auth type as AWS_IAM
, so I was expecting this to work like before.
But since I changed the default auth type and added a second one, I now have the following error:
Error: GraphQL error: Not Authorized to access listVideos on type Query
.
My schema.graphql
looks like this (with other types and fields, but shouldn't impact our case):
type Video
@model(
queries: { get: "getVideo", list: "listVideos" },
mutations: { delete: "deleteVideo", create: "createVideo", update: "updateVideo" },
subscriptions: null,
)
@auth(rules: [
{ allow: owner, operations: [create, update, delete] },
{ allow: groups, groups: ["Admin"], operations: [create, update, delete] },
])
{
id: ID!
title: String!
createdBy: String
createdAt: AWSDateTime
updatedAt: AWSDateTime
}
I tried a bunch of workarounds but nothing worked. So my question is:
What is the recommended way to query my API from my backend in a "god" mode, meaning being able to do everything (limited only by the IAM policy)?
Hi @ChristopheBougere, try this @auth
rule addition on your types:
@auth(rules: [
{ allow: private, provider: iam },
{ allow: owner },
{ allow: groups, groups: ["Admin"] },
])
If you want to also use an API Key along with IAM and Cognito, use this:
@auth(rules: [
{ allow: public, provider: apiKey },
{ allow: private, provider: iam },
{ allow: owner },
{ allow: groups, groups: ["Admin"] },
])
Notice I added new rules, and modified your original owner and groups rules.
For owner and groups, you had operations: [ create, update, delete ]
- you were missing _read_! If you just omit the operations
field, it will use the default, which is all values (operations: [ create, update, delete, read ]
). Since you didn't have the read
operation defined, no one was allowed to query anything, only perform mutations!
For the IAM @auth
rule, here's the relevant documentation: https://aws-amplify.github.io/docs/cli-toolchain/graphql?sdk=js#private-authorization.
You can use private
with userPools
and iam
. This will use the "AuthRole" IAM Role.
You can use public
with apiKey
and iam
. This will use the "UnAuthRole" IAM Role.
From the docs:
The private authorization specifies that everyone will be allowed to access the API with a valid JWT token from the configured Cognito User Pool. To be able to use private the API must have Cognito User Pool configured.
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 āAuthenticated Roleā from Cognito Identity Pools for private access. When used in conjunction with amplify add auth the CLI generates scoped down IAM policies for the āAuthenticatedā role automatically.
The public authorization specifies that everyone will be allowed to access the API, behind the scenes the API will be protected with an API Key. To be able to use public the API must have API Key configured.
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.
The term "public" is a bit of a misnomer and was very confusing to me. "Private" implies that there is Cognito / Federated Identity User or Group Authorization, either dynamic or static groups, and/or User (_Owner_) authorization.
"Public" is not the same as "Anonymous" as we normally correlate that term to - e.g. "Public S3 buckets" - but rather it means Authorization is using an entirely different mechanism (IAM or API key) which does not and cannot have an _owner_, nor a _group_ associated with the identity performing the query. This is actually where the mysterious "AuthRole" and "UnAuthRole" IAM roles are used š¤
_Disclaimer_: I am not affiliated with AWS or the Amplify team in any way, and while I try my best to give well-informed assistance, I recommend you perform your own research (read the docs over and over and over...) and do not take this as official advice š
Thank you so much for your detailed answer @rrrix š
For owner and groups, you had operations: [ create, update, delete ] - you were missing read! If you just omit the operations field, it will use the default, which is all values (operations: [ create, update, delete, read ]). Since you didn't have the read operation defined, no one was allowed to query anything, only perform mutations!
So the doc says otherwise:
When specifying operations as a part of the @auth rule, the operations not included in the list are not protected by default.
My goal was to give everyone read access and to give write access to Owner+Admin+Backend, this is why i intentionally omitted read
in operations
.
However, my backend (iam provider) wasn't working and when I tried your solution it did work! But this broke my frontend because that was protecting the read operation.
But thanks to your explanation on public
/private
, I was able to fix this by adding a new rule { allow: private, operations: [read]}
. So in the end, here is my complete @auth
rule:
@auth(rules: [
{ allow: owner },
{ allow: groups, groups: ["Admin"] },
{ allow: private, provider: iam, operations: [create, delete, read, update] },
{ allow: private, operations: [read]}
])
I am still doing some tests but this seems to work well š
Looks like everything works well.
Thanks again for your help @rrrix !
This was really helpful. Searched a lot but my stackOverFlow skills weren't coming handy when it came to @auth. Can you please also tell how is owner different from private ?
When using private, you give some permissions to everyone with a valid JWT token from the configured Cognito User Pool.
Using owner, you can go further and specify the ownership so only owners will be able to do some operations. Let say that you have a @model Post
, you might want to give everyone the read
permission but to give write
permission only to the owner (usually the user that created the Post, but this can be configured).
More information about @owner
directive here
Very informative issue, and it's already included in the new doc,
https://docs.amplify.aws/lib/graphqlapi/graphql-from-nodejs/q/platform/js
The code example shows to use { allow: private, provider: iam }
as mentioned here, and how to sign the request.
Most helpful comment
Hi @ChristopheBougere, try this
@auth
rule addition on your types:If you want to also use an API Key along with IAM and Cognito, use this:
Notice I added new rules, and modified your original owner and groups rules.
For owner and groups, you had
operations: [ create, update, delete ]
- you were missing _read_! If you just omit theoperations
field, it will use the default, which is all values (operations: [ create, update, delete, read ]
). Since you didn't have theread
operation defined, no one was allowed to query anything, only perform mutations!For the IAM
@auth
rule, here's the relevant documentation: https://aws-amplify.github.io/docs/cli-toolchain/graphql?sdk=js#private-authorization.You can use
private
withuserPools
andiam
. This will use the "AuthRole" IAM Role.You can use
public
withapiKey
andiam
. This will use the "UnAuthRole" IAM Role.From the docs:
@auth > private authorization
@auth > public authorization
The term "public" is a bit of a misnomer and was very confusing to me. "Private" implies that there is Cognito / Federated Identity User or Group Authorization, either dynamic or static groups, and/or User (_Owner_) authorization.
"Public" is not the same as "Anonymous" as we normally correlate that term to - e.g. "Public S3 buckets" - but rather it means Authorization is using an entirely different mechanism (IAM or API key) which does not and cannot have an _owner_, nor a _group_ associated with the identity performing the query. This is actually where the mysterious "AuthRole" and "UnAuthRole" IAM roles are used š¤
_Disclaimer_: I am not affiliated with AWS or the Amplify team in any way, and while I try my best to give well-informed assistance, I recommend you perform your own research (read the docs over and over and over...) and do not take this as official advice š