Closed pepie closed 8 hours ago
Hello @pepie - Your storage backend allows identities to write to media/public/*
, but there is no identityId in the path. Paths that have access set for an identity should have an identity Id in the path. You can see an example of this under the "Owners" tab here: https://docs.amplify.aws/flutter/build-a-backend/storage/authorization/#access-definition-rules
One option is to update the backend to the following:
export const storage = defineStorage({
name: 'gallery',
access: (allow) => ({
'media/public/{entity_id}/*': [
allow.guest.to( ['read'] ),
allow.entity('identity').to(['read', 'write', 'delete']),
],
}),
});
And then update the upload function to the following.
final result = await Amplify.Storage.uploadFile(
localFile: AWSFile.fromStream( imageFile.openRead(), size: 0 ),
path: StoragePath.fromIdentityId((id) => "media/public/$id/test.jpg"),
options: options,
onProgress: onProgress ?? (progress) {
safePrint('upload progress ${progress.fractionCompleted}');
},
).result;
Let me know if you have other questions.
Thanks @Jordan-Nelson ,
That's what my actual code looks like. I simplified it for the sake of the ticket, because I get the same error regardless of my configuration. Upload fails for both authenticated and non-authenticated users.
I even called Amplify.Auth.getCurrentUser() right before the upload function, to make sure the user was still logged in.
Here's the full code:
`//(backend): amplify/storage/resource.ts
export const storage = defineStorage({
name: 'gallery',
access: (allow) => ({
'media/public/{entity_id}/*': [
allow.guest.to( ['read'] ),
allow.entity('identity').to(['read', 'write', 'delete']),
allow.groups(['Admins','Staff','SystemAdmins']).to( ['read','write','delete'] )
],
'media/protected/{entity_id}/*': [
allow.authenticated.to(['read']),
allow.entity('identity').to(['read', 'write', 'delete']),
allow.groups(['Admins','Staff','SystemAdmins']).to( ['read','write','delete'] )
],
'media/private/{entity_id}/*': [
allow.entity('identity').to(['read', 'write', 'delete']),
allow.groups(['Admins','Staff','SystemAdmins']).to( ['read','write','delete'] )
]
}),
});
// calling function
AuthUser authUser = await Amplify.Auth.getCurrentUser(); // TESTING ONLY - Just to double check user is still signed-in.
print(authUser); //this works - so the user credentials is not the issue
final result = await Amplify.Storage.uploadFile(
localFile: AWSFile.fromStream( imageFile.openRead(), size: 0 ),
path: StoragePath.fromIdentityId( (id)=> "media/public/${id}/test.jpg"),
options: options,
onProgress: onProgress ?? (progress) {
safePrint('upload progress ${progress.fractionCompleted}');
},
).result;
// Amplify Config
if( !Amplify.isConfigured ) {
final apiPlugin = AmplifyAPI(
options: APIPluginOptions(
modelProvider: ModelProvider.instance
),
);
final authPlugin = AmplifyAuthCognito();
final s3StoragePlugin = AmplifyStorageS3();
final datastorePlugin = AmplifyDataStore(
modelProvider: ModelProvider.instance,
options: DataStorePluginOptions(
authModeStrategy: AuthModeStrategy.multiAuth,
errorHandler: ((error)=> print("DataStore Error: ${error.toString()}")),
),
);
await Amplify.addPlugins([
authPlugin,
datastorePlugin,
apiPlugin,
s3StoragePlugin
]);
// configure amplify
try {
await Amplify.configure( amplifyConfig );
log("Amplify configuration completed");
} on AmplifyAlreadyConfiguredException catch(error, stacktrace){
debugPrintStack(stackTrace: stacktrace);
debugPrint('Tried to reconfigure Amplify; this can occur when your app restarts on Android.');
}catch(error, stacktrace){
log('Amplify config error: $error');
debugPrintStack(stackTrace: stacktrace);
}finally {
log("Cloud configuration successful!");
}
}else{
log("Cloud configuration skipped - already present");
}
@pepie I am not able to reproduce that behavior. I have created a storage backend with your definition above and used the code snippet you are using to upload the file and the file uploads successfully. Below are the full details for my app.
Backend defintion
Flutter app code
Can you share the IAM policy definition for your Authenticated role on your S3 bucket? It should look similar to the one below. You can find it my navigating to IAM -> Roles in the AWS Console and then searching for your app name. There will be several roles listed. The one you should look at is for authenticated users and will be named something like "
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "s3:GetObject",
"Resource": [
"arn:aws:s3:::<app_name>/media/public/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::<app_name>/media/protected/*/*",
"arn:aws:s3:::<app_name>/media/protected/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::<app_name>/media/private/${cognito-identity.amazonaws.com:sub}/*"
],
"Effect": "Allow"
},
{
"Condition": {
"StringLike": {
"s3:prefix": [
"media/public/${cognito-identity.amazonaws.com:sub}/*",
"media/public/${cognito-identity.amazonaws.com:sub}/",
"media/protected/*/*",
"media/protected/*/",
"media/protected/${cognito-identity.amazonaws.com:sub}/*",
"media/protected/${cognito-identity.amazonaws.com:sub}/",
"media/private/${cognito-identity.amazonaws.com:sub}/*",
"media/private/${cognito-identity.amazonaws.com:sub}/"
]
}
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::<app_name>",
"Effect": "Allow"
},
{
"Action": "s3:PutObject",
"Resource": [
"arn:aws:s3:::<app_name>/media/public/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::<app_name>/media/protected/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::<app_name>/media/private/${cognito-identity.amazonaws.com:sub}/*"
],
"Effect": "Allow"
},
{
"Action": "s3:DeleteObject",
"Resource": [
"arn:aws:s3:::<app_name>/media/public/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::<app_name>/media/protected/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::<app_name>/media/private/${cognito-identity.amazonaws.com:sub}/*"
],
"Effect": "Allow"
}
]
}
@pepie if you are still facing this issue please share the IAM policy. Thanks.
Thanks for your patience, @Jordan-Nelson .
I had four policies created. The first three were had a type of 'Customer Managed' and followed this naming convention
amplify-<app name>-sandbox-< ID > - amplifyDataAuthRolePolicy< ID >
with type 'Customer Managed'
Each had one attached entity.
The last one, which I think is the one you're interested in, was is named 'storageAccess< ID >' and has a type of 'Customer Inline' with zero attached entities.
Here's the content of that policy
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "s3:GetObject",
"Resource": [
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket<*ID*>/kio-media/public/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket<*ID*>/kio-media/protected/*/*",
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket<*ID*>/kio-media/protected/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket<*ID*>/kio-media/private/${cognito-identity.amazonaws.com:sub}/*"
],
"Effect": "Allow"
},
{
"Condition": {
"StringLike": {
"s3:prefix": [
"kio-media/public/${cognito-identity.amazonaws.com:sub}/*",
"kio-media/public/${cognito-identity.amazonaws.com:sub}/",
"kio-media/protected/*/*",
"kio-media/protected/*/",
"kio-media/protected/${cognito-identity.amazonaws.com:sub}/*",
"kio-media/protected/${cognito-identity.amazonaws.com:sub}/",
"kio-media/private/${cognito-identity.amazonaws.com:sub}/*",
"kio-media/private/${cognito-identity.amazonaws.com:sub}/"
]
}
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket<*ID*>",
"Effect": "Allow"
},
{
"Action": "s3:PutObject",
"Resource": [
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket<*ID*>/kio-media/public/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket<*ID*>/kio-media/protected/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket<*ID*>/kio-media/private/${cognito-identity.amazonaws.com:sub}/*"
],
"Effect": "Allow"
},
{
"Action": "s3:DeleteObject",
"Resource": [
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket<*ID*>/kio-media/public/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket<*ID*>/kio-media/protected/${cognito-identity.amazonaws.com:sub}/*",
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket<*ID*>/kio-media/private/${cognito-identity.amazonaws.com:sub}/*"
],
"Effect": "Allow"
}
]
}
Note:
I also get a few warnings when launching the sandbox
current credentials could not be used to assume 'arn:aws:iam::< ID >:role/cdk-hnb659fds-deploy-role-< ID >-us-east-1', but are for the right account. Proceeding anyway.
Here's the bucket policy that was generated
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": {
"AWS": "*"
},
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket< ID >",
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket< ID >/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::< ID >:role/amplify-backend-kio-sandb-CustomS3AutoDeleteObjects-< ID >"
},
"Action": [
"s3:PutBucketPolicy",
"s3:GetBucket*",
"s3:List*",
"s3:DeleteObject*"
],
"Resource": [
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket< ID >",
"arn:aws:s3:::amplify-backend-kio-sandbox-kiomediabucket< ID >/*"
]
}
]
}```
@pepie - In the IAM policy it looks like the root folder name is kio-media
. The resource.ts file snippet you shared and the Amplify Flutter code snippets showed media
as the root folder.
Can you confirm the resource.ts file you shared is accurate? Have you made changes since your last deployed? If your resource.ts file is accurate and you have successfully deployed recently, can you open an issue at https://github.com/aws-amplify/amplify-backend for this?
With the given IAM policies you should be able to update the Amplify Flutter code snippet to use kio-media
in place of media
. For example:
final result = await Amplify.Storage.uploadFile(
localFile: AWSFile.fromStream( imageFile.openRead(), size: 0 ),
path: StoragePath.fromIdentityId( (id)=> "kio-media/public/${id}/test.jpg"),
options: options,
onProgress: onProgress ?? (progress) {
safePrint('upload progress ${progress.fractionCompleted}');
},
).result;
Let me know if you have other questions. Thanks.
Yes, I mentioned this in the post. I changed "kio-media" to "media" to simplify the GitHub post, but all the paths configurations are set to 'kio-media'.
I also noticed that your sample backend.js code did not include a config for data
import { defineBackend } from "@aws-amplify/backend";
import { auth } from "./auth/resource";
import { storage } from "./storage/resource";
defineBackend({
auth,
storage,
});```
I don't know if this has any impact, but I have defaultAuthorizationMode set to 'userPool' in data/resource.ts.
```export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: 'userPool',
apiKeyAuthorizationMode: {
expiresInDays: 360
}
},
});```
The only other difference I have is a post-confirmation resource.
I'll try your code again in a clean project and open a ticket if it fails.
Also, can you confirm your flutter amplify library version?
I am using the latest version (2.2.0) for all, except for amplify_authenticator.
variables:
amplify_version: &lify_version 2.2.0
amplify_api: *amplify_version
amplify_authenticator: ^2.1.0
amplify_auth_cognito: *amplify_version
amplify_datastore: *amplify_version
amplify_flutter: *amplify_version
amplify_storage_s3: *amplify_version```
@pepie Okay, let me know if you still face issues after redeploying.
I had used amplify flutter v2.1.0 as that was the latest available at the time. v2.2.0 was released on Thursday.
Lastly, the Sandbox deployment is successful. However, I do get the following warnings:
@pepie Okay, let me know if you still face issues after redeploying.
I had used amplify flutter v2.1.0 as that was the latest available at the time. v2.2.0 was released on Thursday.
Yes, I started with v2.1.0 and upgraded to 2.2.0, but I still get the same error. I'll try with a clean flutter project and see if the problem persists.
Lastly, the Sandbox deployment is successful. However, I do get the following warnings:
I am not seeing any warnings in the comment.
Hey @Jordan-Nelson, I created a cleaned project and was able to upload an image successfully. I'll have to review my project to see the differences, but it seems this issue is on my side.
This new project has no custom authorizations yet, so I'm guessing something in my auth config is incorrect. Thank you for your help!
@pepie I am glad it is working now. I will close this issue out.
Description
Hello! I'm unable to upload to S3 with ampllify_storage_s3 v2.1.0 + Amplify Gen2. This happens for guest and authenticated users, public or secured buckets.
Error: "S3 access denied when making the API call"
Tutorial Reference: https://docs.amplify.aws/flutter/build-a-backend/storage/upload-files/
Please review.
Categories
Steps to Reproduce
Screenshots
No response
Platforms
Flutter Version
3.22.2
Amplify Flutter Version
2.1.0
Deployment Method
Amplify CLI
Schema