How do I make an AWS AppSync GraphQL API publicly accessible while using Amazon Cognito for authentication? - amazon-web-services

Currently I am using Amazon Cognito for authentication in an AWS Amplify project, so only signed-in users have access to the api.
But I want to have some api calls publicly accessible.
How do I go about this?

I just solved this exactly same problem. This is what I did:
Update your API by running amplify update auth and select IAM as your users handler (everything else go with default)
Login to your AWS console -> Appsync and modify access to IAM (instead of Cognito Pool)
Go to the IAM console and create IAM policies for both AUTH and UNAUTH users (search them on the list by typing the name of your Appsync app)
Locate the AUTH user and attach the following policy (update it with your info):
AUTH USER
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "appsync:GraphQL",
"Resource": [
"arn:aws:appsync:<AWS region>:<AWS account ID>:apis/<app sync endpoint ID>/*"
]
}
]
}
Locate the unauth user and attach the following Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "appsync:GraphQL",
"Resource": [
"arn:aws:appsync:<AWS region>:<AWS account ID>:apis/<app sync endpoint ID>/types/Query/fields/<your Query name>",
"arn:aws:appsync:<AWS region>:<AWS account ID>:apis/<app sync endpoint ID>/types/Query/fields/<your Query name>",
"arn:aws:appsync:<AWS region>:<AWS account ID>:apis/<app sync endpoint ID>/types/Query/fields/<your Query name>"
]
}
]
}
And now the thing that is not documented (people transitioning from Cognito Pools to IAM ) You need to import {AUTH_TYPE}
import AWSAppSyncClient, {AUTH_TYPE} from "aws-appsync";
and use it to load the credentials in the AppSync initialization
const client = new AWSAppSyncClient(
{
disableOffline: true,
url: aws_config.aws_appsync_graphqlEndpoint,
region: aws_config.aws_cognito_region,
auth: {
// IAM
type: AUTH_TYPE.AWS_IAM,
credentials: () => Auth.currentCredentials(),
});
Hope this helps.

For AppSync APIs - API Keys are considered "unauthenticated"
See the below documentation:
https://docs.aws.amazon.com/appsync/latest/devguide/security.html#api-key-authorization

Related

AWS IAM user credential always authenticated as anonymous

I am creating a simple API Gateway and trying to apply its auth. I created an IAM user (called postman-user) and created its credential (as AccessKeyId and SecretAccessKey).
My IAM User policy is like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "execute-api:*",
"Resource": "*"
}
]
}
and in my api gateway I applied the resource policy as below:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<my account id>:root",
"arn:aws:iam::<my account id>:user/postman-user"
]
},
"Action": "execute-api:Invoke",
"Resource": "arn:aws:execute-api:us-west-2:<my account id>:<my api g id>/*"
}
]
}
I applied the key id and secret key id in postman:
enter image description here
then the problem comes. no matter how I call the api endpoint using aws credential of this IAM user, I always got this error:
User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:us-west-2:******
I thought it was postman failed to sign this AWS sigV4, then I tried this in python:
url = 'https://<apig id>.execute-api.us-west-2.amazonaws.com/beta/query/'
auth = AWSRequestsAuth( aws_access_key='<my key id>',
aws_secret_access_key='<my secret key>',
aws_host='ec2.amazonaws.com',
aws_region='us-west-2',
aws_service='api')
response = requests.get(url, auth=auth)
This error is just forever for me
User: anonymous is not authorized to perform: execute-api:Invoke on resource: arn:aws:execute-api:us-west-2:******
Anyone can tell me what I missed ? I clicked on deployAPI in resource to stage beta 100 times ...
tried python, tried postman, nothing works
it sounds like there is something missing on the api plane. It may be the you havent configured IAM auth right on the http method you try to use. I may also be that the resource policy is not attached to the api gateway. Note if the policy is updated and reattached you need to redeploy the api gateway.
Link:
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-resource-policies-create-attach.html
This is an API Gateway config issue:
Resources -> click on the method -> Method Request -> Authorization: it used to be None, changing to to AWS IAM made this work.

How can I access DynamoDB table from AccountA with using AccountB's User CLI credentials?

I have created one table in Dynamodb and setup role in IAM with following policy attached:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "dynamodb:*",
"Resource": "arn:aws:dynamodb:ap-south-1:**AccountAID**:table/employee"
}]
}
I have added trusted entity AccountB ID in the role. Then I have also created policy in AccountB for the access of dynamodb table created in AccountA with following policy:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "*",
"Resource": "arn:aws:iam::**AccountAID**:role/DynamodbCrossAccountAccessRole"
}]
}
I have configured profiles of AccountA user and AccountB user and with the credentials of AccountA's profile user, I can list tables of dynamodb but while trying to get with AccountB's profile user I always returns with empty-table list.
Krunal-MacBook-Air:.aws krunal$ aws dynamodb list-tables --profile Krunal
{
"TableNames": [
"employee"
]
}
Krunal-MacBook-Air:.aws krunal$ aws dynamodb list-tables --profile Krunal2
{
"TableNames": []
}
Can anyone help me out of this why am I not able to access dynamodb with AccountB's profile?
Policies are attached to the account users respectively.
Based on the comments.
The issue was solved by assuming the role in AccountB. The useful links showing how to do this are:
Cross-Account Access Control with Amazon STS for DynamoDB
Delegate Access Across AWS Accounts Using IAM Roles
Based on the comments and with the help of provided documents, I can access to my AccountB's resources. I have configured generated API Key, Secret Key and Tokens in credentials file of my profile and it's working as expected.

AWS Cognito Groups and AWS Api Gateway

I am starting with serverless on AWS, and I am using AWS Cognito for user authentication and authorization. For what I saw on the documentation and examples out there, you can make groups for allowing certain users to be able to use an Api Gateway endpoint, attaching a role and a policy to that group. I try this, and then made a simple client and try with two different users, and both are able to get a 200 status code instead of one of them getting that it is unauthorize. For creating the role I went to IAM, create role, role for identity provider access, grant access to web identity providers, and then I choose Amazon Cognito and choose my user pool of Cognito.
Trust Relationship:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "us-east-1_8TAUVKbGP"
}
}
}
]
}
Policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"my-arn-resourse-from-api-gateway"
]
}
]
}
Then I assigned this role to my admin group and add a user to that group, which should allow access to that Api Gateway resource by attaching that policy to the user when it signs in. But when I try with a user not in that group it still works. By the way, on my Api Gateway resource in the request I put for authorization my cognito pool.
Thanks very much!
Ok finally I got it to work perfectly! The problem was in my IAM Role, in the trust relationship document
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "identity_pool_id"
},
In that part instead of using my Identity Pool Id, I was using my User Pool Id. Once that was fixed, I got the credentials back, and tried in Postman using those credentials and it worked perfectly. Then I change the same user to another role, and access was denied as planned! The most importante part for using role authorization on an easy way, is as agent420 said to use AWS_IAM method for securing the api, then the rest is handle by aws.
You would need to use the AWS_IAM method instead for your use case. Also, in this case all your API requests would need to be SIGv4 signed. You can use Postman (chrome extension) for testing. It includes an option for AWS credentials.
I try what you said! I think I am in the right way but AWS.config.credentials is returning sessionToken, and accessKeyId both with null. This is the code I am using:
let poolData = {
UserPoolId : 'my_user_pool_id',
ClientId : 'my_app_client_id'
};
let authenticationData = {
Username : 'username',
Password : 'password',
};
let userPool = new CognitoUserPool(poolData);
let userData = {
Username : 'username',
Pool : userPool
};
let authenticationDetails = new AuthenticationDetails(authenticationData);
let cognitoUser = new CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: (result) => {
console.log(result);
AWS.config.region = 'my_region';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId : 'my_identity_pool_id',
Logins : {
'cognito-idp.my_region.amazonaws.com/my_user_pool_id' : result.getIdToken().getJwtToken()
}
});
console.log(AWS.config.credentials);
},
onFailure: (error) => {
}
});
The result from the authenticateUser returns the expected tokens. The problem I think is when retrieving the CognitoIdentityCredentials.
Thanks very much!

IAM policy for API Gateway invocation based on Cognito Identity ID

I want to allow Cognito authenticated users to invoke API Gateway endpoint but restrict them to their own resources like
'/users/<IdentityID>/*'.
I have prepared an IAM role like this.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": [
"arn:aws:execute-api:ap-northeast-1:*:MyAPIID/*/*/users/${cognito-identity.amazonaws.com:sub}*"
]
}
}
But on this setting, I get a 403 error when I try to invoke.
If I replace the ${cognito-identity.amazonaws.com:sub} to actual Identity ID (like ap-northeast-1%3Ad8515ae9-62b5-4cba-af5c-195f5d7e1d07), it works.
We cannot use ${cognito-identity.amazonaws.com:sub} on API Gateway resource, can we?
That is correct. Currently, it's only a shortcut for S3 and DynamoDB.

Cognito Role and AWS S3 Bucket policy for mobile and web access

Our goal is to create S3 bucket and IAM role policies that will only allow S3 access to our logged in users.
We are hosting private files on an S3 bucket that will be accessed from both a web and mobile app. We are attempting to add a layer of security with Amazon Cognito, using an unauthenticated role, so that any users signed into our application can access the S3 bucket.
Using the AWS-SDK for JS and following the basic AWS.config.credentials setup, we can see 1 identity accessed and the number of syncs in our Amazon Cognito Identity Dashboard. Since we seem to be connecting to the IdentityPool, I'm thinking that our policies may need some tweaking so that logged in users that have the unauthenticated Cognito role can access the S3 bucket.
IAM Role policy for Cognito_IdentityPoolUnauth_Role
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SID_NUMBER",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::OUR_BUCKET_NAME/*"
]
}
]
}
S3 BUCKET POLICY
{
"Version": "2012-10-17",
"Id": "http referer policy example",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::IAM_NUMBER:role/Cognito_IdentityPoolUnauth_Role"
},
"Action": "s3:*",
"Resource": "arn:aws:s3:::OUR_BUCKET_NAME/*"
}
]
}
When you attempt to access the files directly from the browser, no credentials are sent with the request. It has the same effect as trying to hit S3 directly without having any code to get credentials from Cognito. In order to use the Cognito credentials, you need to make the request using the javascript SDK. Below is a example request using the javascript SDK taken from this page
var s3 = new AWS.S3();
var params = {Bucket: 'myBucket', Key: 'myKey'};
s3.getSignedUrl('getObject', params, function (err, url) {
console.log("The URL is", url);
});
If you call this after getting Cognito credentials, it will use Cognito credentials to create a signed url to access the key myKey in bucket myBucket. You can take an approach similar to this to listObjects in your bucket and then generate signed urls (signed with Cognito credentials) that your end users can click on to view the contents of each key in your bucket.