* Which Category is your question related to? *
Storage
* What AWS Services are you utilizing? *
https://aws-amplify.github.io/docs/js/storage#get
* Provide additional details e.g. code snippets *
On the storage documentation page, it has this listing:
Storage.get('test.txt', {
level: 'protected',
identityId: 'xxxxxxx' // the identityId of that user
})
.then(result => console.log(result))
.catch(err => console.log(err));
How/where do you get the identityId
of a user that isn't the current logged-in user? What does this refer to?
Thanks!
Hi @mwarger
That identityId
is the one from the user that created/uploaded the file to the protected
area.
You can find more details in the File Access Levels section in the docs:
Files with protected access level are readable by all users but writable only by the creating user. In S3, they are stored under protected/{user_identity_id}/ where the user_identity_id corresponds to a unique Amazon Cognito Identity ID for that user.
@manueliglesias I understand how the access is structured. In my question above, I asked how/where do you get the identityId of a user that isn't the current logged-in user? What does this refer to?
It appears to be a pattern of the region:some-GUID - where the GUID comes from somewhere - is this the user sub or some other identifier?
Thanks!
@mwarger Thanks for the clarification.
What does this refer to?
The identityId
is the Id that a user is assigned through the Identity Pool
how/where do you get the identityId of a user that isn't the current logged-in user
In Amplify there is currently no way to get another user's info. This could come from your UI or an API (e.g. you have an app that shows photos uploaded by other users, you might call an api like listProtectedPhotos
that could return you the bucket/region/key/identityId for each photo).
I hope this makes some sense
In my situation, I'm using the default cognito identity pool setup. In that case, is it going to be the user sub that is the Identity ID?
If that's true - how do I derive the identity ID for use as the S3 bucket path? The paths in the format region:GUID don't appear to directly correspond to the user sub. Is there an AWS admin api that I could use in my version of the listProtectedPhotos
to retrieve the correct identityID for a given user?
@mwarger
There are a couple of options to achieve this now, issue #54 has a lengthy discussion with suggestions and a feature-request for Cognito.
This comment in particular might help you:
https://github.com/aws-amplify/amplify-js/issues/54#issuecomment-434401406
@manueliglesias I will give that a look - thank you for your time.
@mwarger I think there might be a bit of a documentation gap here that I will address, and the other issues that @manueliglesias pointed are slightly unrelated to your original question. For clarity, if you enable UnAuthenticated access on your Identity Pool then when you call Auth.currentCredentials
an IdentityId for the device w/ UnAuthenticated credentials will be retrieved. However, you do not need to specify the key as using Storage.configure({level:''})
will do this for you and use the underlying ID. For example:
```import Amplify, { Auth, Storage } from 'aws-amplify'
Amplify.configure(config)
const getCreds = async () => {
let creds = await Auth.currentCredentials() //This will give unauthenticated credentials object
creds.identityId;
}
getCreds();
Storage.configure({ level: 'private' });
Storage.put('test.txt', 'Hello') //Key will use the UnAuthenticated IdentityId from above
.then (result => console.log(result))
.catch(err => console.log(err));
```
The example that you pointed out in the documentation shows how you would "get other users’ objects" when using the protected level.
@undefobj The example I posted is what I meant - the documentation says that you can get other users objects, but this is not true without the user's identityId. I finally got this to work, but by saving off the individuals identityIDs when they login as part of their user object, and fetching that for when I want to retrieve an image that they have uploaded. Thank you @undefobj and @manueliglesias for your help with this.
Quick clarification on this as I was planning on implementing the same method as @mwarger.
Is it acceptable practice to save the other users' identityIDs in the database so anyone can view it? In other words, should identityID be publicly accessible like that?
Thank you.
@mwarger - For what I understood, the identityId
is the Cognito Identity Pool (or Cognito Federated Identities) on the other hand is a way to authorize your users to use the various AWS services. This article explains it well - https://serverless-stack.com/chapters/cognito-user-pool-vs-identity-pool.html
@wizawuza - once the user signs up on my app, I store the identityId
together w/ other attributes
on my DinamoDB via GraphQL where @auth(rules: [{ allow: owner }])
, So only the owner can see it and Admin can access it.
Then I created a second GraphQL API for ADMIN, but I also cannot access the users images/objects. I even created a role fro the Cognito Pool to have full access for all S3 buckets, but still not luck:
AWSS3Provider - list error AccessDenied: Access Denied
How did you do it @mwarger ?
Thanks
@rfdc When any user logs in, I store the identityId from their credentials into a dynamodb table. I also use this table to store their username and other information I want to show when displaying their info on the page. I can grab their name, the URL to the photo, and the identityId to allow me to make the call through amplify to display the photo.
@mwarger do you know if it's safe to store the identityId like that? Or would a random person having access to a user's identityId allow that random person to use that information in a malicious manner?
edit: pardon my ignorance on cognito stuff like this, but thank you in advance for any help you can provide.
In general, I'm not sure. The way my app is setup, there are multiple barriers to entry before you would be able to get the info. You have to have an account, and the account information is not queryable directly, only through other parts of the appsync schema I'm using. Plus, besides marking all pictures/urls as public, I don't see any other way around this issue. If they aren't public, and by design of the amplify library when they're set as protected, you MUST have the identity ID to access the URL and retreive the media (in this case, a user's profile picture). The only more secure method is to set the media as private, but that obviously won't do for a situation like mine where you want users to be able to see others users profile photos.
If there is another more secure way to do this, I'm all ears. And, as you can see from the thread above, there don't appear to be any other alternatives.
there don't appear to be any other alternatives.
Yeah I was afraid so. _Can anyone on the AWS team comment?_
Otherwise, I don't believe there's another method of doing S3 storage via Amplify so that people can upload while limiting alterations (e.g., delete/remove) by 3rd parties. But, obviously, if exposing identityId's is a huge security risk....
@wizawuza identityId is just a way to identify a user (unique id), you cannot do anything without aws credentials from that user.
@mwarger I created PR #2910 that supports list/get objects from protected
prefix, with that you can list all the objects under that prefix.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
@elorzafe @undefobj I am facing the same issue. I'm not sure if I'm missing something obvious here. The design seems to make it impossible to access a protected item from another user without compromising the secrecy of identity id's and making unneeded updates every time on login. The identity id is meant to be private from what I understand and should remain within Cognito.
Why not simply store using the "sub" instead of identity id? Sounds like the quickest and most forward solution. Please let us know before we go further with development.
@rawadrifai I believe you also referenced this question here: https://github.com/aws-amplify/amplify-cli/issues/1847
You must use the IdentityID to set per-user policies on an S3 object, as this is the only control that IAM gives for S3 policies at this level. The JWT controls that are available in AppSync (or API Gateway for that matter) are not available at runtime in an IAM policy on a bucket.
IdentityIDs aren't considered private information to my knowledge, but perhaps you have a separate concern. Amplify creates three folders which have appropriate scopes for this policy here: https://aws-amplify.github.io/docs/js/storage#file-access-levels
If you're looking to have a place where users can upload data and others can read, I would look at either the public or protected folders. If you're concerned about doing list operations on the protected bucket you could potentially write a Lambda function with a GraphQL resolver using the @function
directive, and have that function return results to the caller as appropriate and have it run a Storage.get()
on a returned key.
@undefobj Playing along the same scenario of a slack-like messenger. Take the use case where you have a private channel, with images uploaded to that channel. Now you want to restrict access to the members of the channel only... so a dynamic set of Cognito users that assume a certain role. How would you go about that?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been automatically closed because of inactivity. Please open a new issue if are still encountering problems.
I have an app where users upload images that need to be readable by all other users. So access level = protected.
When I want to read an image from storage I need to provide the identityId of the user that created the image.
In my case I have a table where I store the image S3 key and the identityId of the owner.
With that information I do a Storage.get
of the protected image.
const signedUrl = await Storage.get(fullImagePath, {
level: "protected",
identityId: identityId,
});
Most helpful comment
@mwarger I think there might be a bit of a documentation gap here that I will address, and the other issues that @manueliglesias pointed are slightly unrelated to your original question. For clarity, if you enable UnAuthenticated access on your Identity Pool then when you call
Auth.currentCredentials
an IdentityId for the device w/ UnAuthenticated credentials will be retrieved. However, you do not need to specify the key as usingStorage.configure({level:''})
will do this for you and use the underlying ID. For example:```import Amplify, { Auth, Storage } from 'aws-amplify'
Amplify.configure(config)
const getCreds = async () => {
let creds = await Auth.currentCredentials() //This will give unauthenticated credentials object
creds.identityId;
}
getCreds();
Storage.configure({ level: 'private' });
Storage.put('test.txt', 'Hello') //Key will use the UnAuthenticated IdentityId from above
.then (result => console.log(result))
.catch(err => console.log(err));
```
The example that you pointed out in the documentation shows how you would "get other users’ objects" when using the protected level.