* Which Category is your question related to? *
Auth / Storage
* What AWS Services are you utilizing? *
S3 / Cognito
* Provide additional details e.g. code snippets *
I am trying to have users of my app upload images to a folder in an s3 bucket which then triggers a lambda function that adds the image as the profile picture in my dynamodb.
I am having an issue when restricting the access so that users can only mutate their own images.
I used the policy described here https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_s3_cognito-bucket.html
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::<BUCKET-NAME>"],
"Condition": {
"StringLike": {
"s3:prefix": ["app/"]
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::<BUCKET-NAME>/app/${cognito-identity.amazonaws.com:sub}",
"arn:aws:s3:::<BUCKET-NAME>/app/${cognito-identity.amazonaws.com:sub}/*"
]
}
]
}
in my amplify config I changed the default customPrefixes to reflect the folder.
customPrefix: {
private: ‘app/’,
protected: ‘app/’,
public: ‘app/’,
},
But if I try to upload
Amplify.Storage.put(`${congito.sub}/test.txt`', 'Hello')
.then (result => console.log(result))
.catch(err => console.log(err));
I am getting an unauthorized error.
However, if I am adjusting the policy by removing ${cognito-identity.amazonaws.com:sub} to:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::<BUCKET-NAME>"],
"Condition": {
"StringLike": {
"s3:prefix": ["app/"]
}
}
},
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::<BUCKET-NAME>/app",
"arn:aws:s3:::<BUCKET-NAME>/app/*"
]
}
]
}
it works fine. In my lambda trigger I see then that the userIdentity does not contain a cognito sub though, only a principalId.
{
"Records": [
{
"eventVersion": "2.1",
"eventSource": "aws:s3",
"awsRegion": "eu-west-1",
"eventTime": "2018-12-11T15:10:56.056Z",
"eventName": "ObjectCreated:Put",
"userIdentity": {
"principalId": "AWS:AROAXXXXXXXXXSJ7LGAIC:CognitoIdentityCredentials"
},
"requestParameters": {
"sourceIPAddress": XXXXXX
},
"responseElements": {
"x-amz-request-id": XXXX,
"x-amz-id-2": XXXX,
},
"s3": {
"s3SchemaVersion": "1.0",
"configurationId": "01b36196-568a-4ce7-97ab-a428e8c581eb",
"bucket": {
"name": "dh--sfa-api--image--images",
"ownerIdentity": {
"principalId": XXXXX
},
"arn": "arn:aws:s3:::dh--sfa-api--image--images"
},
"object": {
"key": "app/07e32422-5a14-4c9b-a286-dc440a72687f/test.txt",
"size": 5,
"eTag": "8b1a9953c4611296a827abf8c47804d7",
"sequencer": "005C0FD38006BD30DC"
}
}
}
]
}
What could I be doing wrong here ?
@philiiiiiipp - What happens when you try setting the restriction level within the .put call?
Yes I have, public, private and protected, but all with the same response to the preflight call.
Request Method: OPTIONS
Status Code: 403 Forbidden
What I also noticed is that it adds app/region/usersub to the path if I am using the protected level.
https://xxxx.s3.eu-west-1.amazonaws.com/app/eu-west-1%3A8f8ac3c6-65bd-4844-a4b6-5446c651684b/246dd1e6-44dc-43e0-bf61-bec0f4f526ef/example.png
this is my CognitoAuthRole, which should have all the necessary permissions and even beyond.
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "s3:*",
"Resource": "arn:aws:s3:::*"
},
{
"Sid": "VisualEditor2",
"Effect": "Allow",
"Action": "s3:*",
"Resource": "arn:aws:s3:::*/*"
},
Ok that is not entirely true, it seems as if allowing everything in my CogntioAuthUser works fine, it just took some time to update.
Still, the moment I restrict it
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::XXXXXX--images/app/${cognito-identity.amazonaws.com:sub}/*"
},
I am getting a 403
Request URL: https://XXXXXX.s3.eu-west-1.amazonaws.com/app/3908ef60-0476-41c6-a186-06b5c217cdcc/example1.png
Request Method: PUT
Status Code: 403 Forbidden
Being less restrictive works fine
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::XXXXXXX/app/*"
},
Have you tried adding a Condition to your less restrictive policy?
I found the issue in the end. Apparently it seems to be a common misconception that the ${cognito-identity.amazonaws.com:sub} is the ${congito.sub} while they are actually different.
When using the level: protected it automactically adds the ${cognito-identity.amazonaws.com:sub} which looks like eu-west-1:8f8ac3c6-65bd-4844-a4b6-5446c651684b where the uuid is not the ${congito.sub}.
So querying https://XXXXXX.s3.eu-west-1.amazonaws.com/app/${cognito:sub}/example1.png returns a 404, correct would have been https://XXXXXX.s3.eu-west-1.amazonaws.com/app/${cognito-identity.amazonaws.com:sub}/example1.png
For anyone wondering how to get the ${cognito-identity.amazonaws.com:sub}
Auth.currentCredentials().then( cred => console.log(cred.identityId) )
@philiiiiiipp why you didn't try this policy
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::{enter bucket name}/public/*",
"arn:aws:s3:::{enter bucket name}/protected/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::{enter bucket name}/private/${cognito-identity.amazonaws.com:sub}/*"
],
"Effect": "Allow"
},
{
"Action": [
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::{enter bucket name}/uploads/*"
],
"Effect": "Allow"
},
{
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::{enter bucket name}/protected/*"
],
"Effect": "Allow"
},
{
"Condition": {
"StringLike": {
"s3:prefix": [
"public/",
"public/*",
"protected/",
"protected/*",
"private/${cognito-identity.amazonaws.com:sub}/",
"private/${cognito-identity.amazonaws.com:sub}/*"
]
}
},
"Action": [
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::{enter bucket name}"
],
"Effect": "Allow"
}
]
}
If you use amplify-cli this policies are created for you when you enable Storage on your project.
I already had an s3 bucket setup, but I suppose the bigger reason was that I wanted to know exactly how it works :-).
Most helpful comment
I found the issue in the end. Apparently it seems to be a common misconception that the ${cognito-identity.amazonaws.com:sub} is the ${congito.sub} while they are actually different.
When using the
level: protectedit automactically adds the ${cognito-identity.amazonaws.com:sub} which looks like eu-west-1:8f8ac3c6-65bd-4844-a4b6-5446c651684b where the uuid is not the ${congito.sub}.So querying https://XXXXXX.s3.eu-west-1.amazonaws.com/app/${cognito:sub}/example1.png returns a 404, correct would have been https://XXXXXX.s3.eu-west-1.amazonaws.com/app/${cognito-identity.amazonaws.com:sub}/example1.png