Name:
interface
Value:
Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.

Page updated Apr 29, 2024

File access levels

Amplify Flutter v1 is now in Maintenance Mode until April 30th, 2025. This means that we will continue to include updates to ensure compatibility with backend services and security. No new features will be introduced in v1.

Please use the latest version (v2) of Amplify Flutter to get started.

If you are currently using v1, follow these instructions to upgrade to v2.

When adding the Storage category, you configure the level of access users have to your S3 bucket. You can configure separate rules for authenticated vs. guest users. When using the Storage category to upload files, you can also specify an access level for each individual file: guest, protected, or private.

  • Guest Accessible by all users of your application
  • Protected Readable by all users, but only writable by the creating user
  • Private Readable and writable only by the creating user

Guest access does not mean that your files are totally public. A "guest" is a user of your application who has not yet signed in. To enable access at this level, you will still be required to configured Authentication in your app. The user must be able to assume an unauthenticated role from your Cognito Identity Pool.

For protected and private access, the [IDENTITY_ID] below corresponds to the unique ID of the user. Once the user has signed in, the [IDENTITY_ID] can be retrieved from the session by accessing the identity id. See Accessing credentials to retrieve the identity id, and use this as the unique ID of the authenticated user.

The default access level for the Storage category is guest. Unless you specify otherwise, all uploaded files will be available to all users of your application. This means that a user who is using your application but has not signed in will have access. Anyone else who is not using your application will not be able to access your files.

Protected access

After the user has signed in, create an options object specifying the protected access level to allow other users to read the object:

import 'package:amplify_flutter/amplify_flutter.dart';
Future<void> uploadProtectedFile({
required String filePath,
required String key,
}) async {
final awsFile = AWSFile.fromPath(filePath);
const options = StorageUploadFileOptions(
accessLevel: StorageAccessLevel.protected,
);
try {
final uploadResult = await Amplify.Storage.uploadFile(
localFile: awsFile,
key: key,
options: options,
).result;
safePrint('Uploaded file: ${uploadResult.uploadedItem.key}');
} on StorageException catch (e) {
safePrint('Something went wrong uploading file: ${e.message}');
rethrow;
}
}

This will upload with the prefix /protected/[IDENTITY_ID]/ followed by the key.

For other users to read the file, you must specify the access level as protected and the identity ID of the user who uploaded it in the options.

import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';
Future<void> downloadProtectedFile({
required String key,
required String targetIdentityId,
required String downloadPath,
}) async {
final awsFile = AWSFile.fromPath(downloadPath);
final options = StorageDownloadFileOptions(
// specify that the file has a protected access level
accessLevel: StorageAccessLevel.protected,
// specify the identity ID of the user who uploaded this file
pluginOptions: S3DownloadFilePluginOptions.forIdentity(
targetIdentityId,
),
);
try {
final result = await Amplify.Storage.downloadFile(
key: key,
localFile: awsFile,
options: options,
).result;
safePrint('Downloaded file is located at: ${result.localFile.path}');
} on StorageException catch (e) {
safePrint('Something went wrong downloading the file: ${e.message}');
rethrow;
}
}

Private Access

Create an options object specifying the private access level to only allow an object to be accessed by the creating user

import 'package:amplify_flutter/amplify_flutter.dart';
Future<void> uploadPrivateFile({
required String filePath,
required String key,
}) async {
final awsFile = AWSFile.fromPath(filePath);
const options = StorageUploadFileOptions(
accessLevel: StorageAccessLevel.private,
);
try {
final uploadResult = await Amplify.Storage.uploadFile(
localFile: awsFile,
key: key,
options: options,
).result;
safePrint('Uploaded file: ${uploadResult.uploadedItem.key}');
} on StorageException catch (e) {
safePrint('Something went wrong uploading file: ${e.message}');
rethrow;
}
}

This will upload with the prefix /private/[IDENTITY_ID]/, followed by the key.

For the user to read the file, specify the same access level (private) and key you used to upload:

import 'package:amplify_flutter/amplify_flutter.dart';
Future<void> downloadPrivateFile({
required String key,
required String downloadPath,
}) async {
final awsFile = AWSFile.fromPath(downloadPath);
const options = StorageDownloadFileOptions(
accessLevel: StorageAccessLevel.private,
);
try {
final result = await Amplify.Storage.downloadFile(
key: key,
localFile: awsFile,
options: options,
).result;
safePrint('Downloaded file is located at: ${result.localFile.path}');
} on StorageException catch (e) {
safePrint('Something went wrong downloading the file: ${e.message}');
rethrow;
}
}

Customization

Customize Object Key Path

You can customize your key path by defining a prefix resolver:

import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';
// Define your own prefix resolver, which implements the `S3PrefixResolver`.
class MyPrefixResolver implements S3PrefixResolver {
const MyPrefixResolver();
Future<String> resolvePrefix({
required StorageAccessLevel accessLevel,
String? identityId,
}) async {
if (accessLevel == StorageAccessLevel.guest) {
return 'myPublicPrefix/';
}
final String accessLevelPrefix;
if (accessLevel == StorageAccessLevel.protected) {
accessLevelPrefix = 'myProtectedPrefix/';
} else {
accessLevelPrefix = 'myPrivatePrefix/';
}
final targetIdentityId = identityId ?? await getCurrentUserIdentityId();
return '$accessLevelPrefix$targetIdentityId/';
}
Future<String> getCurrentUserIdentityId() async {
final authPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey);
final authSession = await authPlugin.fetchAuthSession();
return authSession.identityIdResult.value;
}
}

Then, configure the Storage plugin with your custom prefix resolver:

final storagePlugin = AmplifyStorageS3(
prefixResolver: const MyPrefixResolver(),
);
...
await Amplify.addPlugin(storagePlugin);

Add the IAM policy that corresponds with the prefixes defined above to enable read, write and delete operation for all the objects under path myPublicPrefix/:

{
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": ["arn:aws:s3:::your-s3-bucket/myPublicPrefix/*"]
}
]
}

If you want to have custom private path prefix like myPrivatePrefix/, you need to add it into your IAM policy:

{
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::your-s3-bucket/myPrivatePrefix/${cognito-identity.amazonaws.com:sub}/*"
]
}
]
}

Passthrough PrefixResolver

If you would like no prefix resolution logic, such as performing S3 requests at the root of the bucket, you can use the PassThroughPrefixResolver provided by the amplify_storage_s3 package.

import 'package:amplify_storage_s3/amplify_storage_s3.dart';
final storagePlugin = AmplifyStorageS3(
prefixResolver: const PassThroughPrefixResolver(),
);
await Amplify.addPlugin(storagePlugin);