I am trying to build a service where users are able to upload photos to an S3 database using presigned URLs given to them via API gateway. For each user, I was planning on submitting the photo information through the presigned URL and identifying the user who sent it as metadata via the ID found in the access token granted by AWS Cognito.
However, I am not sure how to secure it so that users can only upload photos as themselves and not as others. It seems to me that malicious users can simply modify the frontend code to change the user ID and submit photos as someone else.
I'm wondering if it is possible to create a presigned URL with some sort of ID so that they can only submit content as themselves? Or is there a better way?
How about this solution:-
There is one question that is not mentioned, how do you plan to differentiate legit users and non-legit users, or is it open to everyone?
Use Amazon Cognito to authenticate users.
users will try to query for URL (upload s3 through an interface), API gateway will verify authentication.
if authentication is successful; then only lambda will generate an s3 resigned URL.
This solution is a little costly however it serves you the purpose of making it secure, where if the user is authenticated then only lambda will generate a signed URL.
You should not worry about the identity of the user, or someone sending a false identity, because a sub claim will be present as part of the token, if someone tries to change that, the cognito will not verify it.
A heads up:- if you are trying to make this service global, then you can implement a backend database like dynamodb, and add a manual/automatic step to add a attribute to identity users who are privileged and add logic to lambda to find users who is privileged and then generate resigned URL exclusively for that privileged user
.https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html, https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-enable-cognito-user-pool.html
Related
I have spent 2 days trying to understand the idea behind Amazon Cognito Access Control for a site I am trying to create. Its very simple:
The site allows users to create posts
Anyone (anonymous users and signed in users) should be able to see posts
Only signed in users should be able to create posts
Only the original author should be able to edit his own posts
Now heres the things I think I have understood about amazon cognito:
Cognito user pools are meant for rest interfaces and identity pools are used to create temporary amazon roles with specific policies so people logged on to my service can access aws services such as dynamodb or s3 buckets directly
In order to make an authorized request to my rest interface, a user has to sign in using the cognito UI and receive an access token, that has to be sent with every request to the Rest API to verify the request
The verification can be done automatically using the cognito authorizer right in API Gateway or by redirecting the access token to lambda and verifying it directly there
If its done with the cognito authorizer, the username and other information cannot be read from within lambda
If I use AWS Amplify to handle request authorization on the client side, the access tokens are renewed automatically
But theres still some questions I have about the whole process:
Are Cognito usernames guaranteed to be unique? And if there not, how can I let the user choose a guaranteed unique username after registration or using a google login?
Are access tokens equivalent to session cookies?
If I am meant to handle access tokens on inside a lambda script, is there a library or service I can use? Because I haven't found a way to verify the token or get the user associated with the token
Why doesn't the built in authorizer redirect the user information to the lambda script?
Is there a prebuilt lambda authorizer that I could plug into API Gateway directly, that would allow both signed in and anonymous users to make requests to it and redirects user information to the lambda script? Because I couldn't find one in the repository or anywhere else?
So, I dont have all the answers, but I have some that will hopefully direct you:
Are Cognito usernames guaranteed to be unique
A User cannot signup if a username/email is already used. You need to specify which field to use for username (ie email or username)
Are access tokens equivalent to session cookies?
No, an access token is normally communicated within the authorization header of a request via a bearer token strategy and the cookie is within the cookie header. Some services will validate a cookie, others (especially machine to machine) will validate an authorization header. In some cases, developers may decide to make these the same, but its not always this way.
If I am meant to handle access tokens on inside a lambda script, is there a library or service I can use.
If you are looking to get context (like the username, which is encoded within the JWT string) of an access token, then you can use JWT decode functions. By the time the request hits lambda, the Authorizor has already validated it, so you do not need to do the same again.
Why doesn't the built in authorizer redirect the user information to the lambda script
Because not all services need/want it. Its better for consuming services to do what they need with the context after they decode the token
Is there a prebuilt lambda authorizer that I could plug into API Gateway directly, that would allow both signed in and anonymous users to make requests to it and redirects user information to the lambda script?
This question shows a problem with your understanding of rest. An API should leverage CRUD actions; so Create may only be granted to contributors, Read may be granted publicly, Update may only be granted to owners, Delete may only be granted to owners. The above is a generalization, but you need an API strategy; when you develop this strategy, you will realize there is no "easy" button.
In general, I would have 2 API Gateways, one for read-only purposes and another for managing content. This keeps it simple and allows you to scale in different ways (ie you may want your contributors to not be blocked by read-only uses in situations of scale issues).
Also, adopting a path based strategy can help with simplifying who has access to what resources. Take the below example:
a user sends a POST request to /api/blog/ to create a new blog (resulting in /api/blog/:blogId). The /api/blog/ endpoint is not "owned" by any specific contributor. Later the user makes an update to /api/blog/1234-abcd...now your service must make an additional call to see if that resource is owned by the user (this is called the "entitlement" process). In psuedo-sql, you would SELECT created_by FROM blogs WHERE id='1234-abcd' and then see if the created_by field matches your user id.
Anywho, you get the point :) This becomes even MORE complex if you allow teams/multiple users the ability to modify resources...and new we get into RBAC (role based access control), which is a much longer topic.
Sorry for the digression, but hopefully this gives you more direction.
Have a simple 1 page s3 website, my goal is to force users to go through Cognito to get there.
Couple questions:
Does the callback URL simply tell cognito where to go after login? Is there any kind of authentication here? Would the user be able to go straight to my website if they knew the S3 endpoint?
If that first option doesn't work, which I don't think it does, can I use this https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_s3_cognito-bucket.html?
Or do I have to make cognito generate a JWT token and write the needed javascript (is amplify mandatory?) to make it work? If I go this route is cloudfront->api gateway, and the gateway handles the token a better option?
Have never done login type stuff before so not sure if there are multiple routes or I am even thinking about this correctly.
Thanks~
Callback URL is the address cognito sends its tokens to. You can use
javascript to read these tokens to exchange them for temporary AWS
credentials. So this URL will need to have a script which will
consume these tokens, get temporary credentials, and then use these
credentials to request aws resource access.
The first method is not optional. You have to get the tokens and
exchange them for AWS credentials.
Amplify is not mandatory, it will just make your life a lot easier.
Tokens expire in an hour and need to be refreshed. Amplify handles
all of this for you.
Additionally, cognito offers something called as hosted GUI, which will create a login page for you and let you define redirect/callback URL.
I have a use case where I need arbitrary clients to receive AWS credentials (key and secret) that I generate and pass to it. The credentials should expire after a few minutes. The clients need to post to an s3 bucket.
The clients will not be a part of any AWS account and cannot use any multi factor auth. This seems to prevent me from using IAM roles.
It seems that the Security Token Service is what Amazon provides for similar use cases, but I can't massage it to get what I need out of it. I either need a role ARN, or to pass the session token on to the clients to use in their requests. The clients can have no concept of a session token- only AWS key/secret.
In short, I want to be able to generate a temporary AWS key/secret pair that needs no multifactor auth or session token.
Is this possible? Thanks!
This is exactly the use-case for Uploading Objects Using Pre-Signed URLs - Amazon Simple Storage Service.
Basically:
Your application determines whether the user is authorized to upload/download a file
It generates a Pre-signed URL that includes an expiration time
The clients use the URL to upload/download to S3
After the expiry time, the URL no longer works
I would like to create my own HTML page to allow users to login using the IAM credentials. I could not find any reference how to do it without getting the credential token first. I would like to host this on an AWS S3 bucket.
A combination of Javascript web app and using Javascript SDK for AWS cognito could work to serve your purposes.
Example of creating a page using Javascript to log in users with Facebook login and access an S3 bucket.
http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/getting-started-browser.html
https://aws.amazon.com/developers/getting-started/browser/
Using Cognito
Amazon Cognito lets you easily add user sign-up and sign-in to your mobile and web apps. With Amazon Cognito, you also have the options to authenticate users through social identity providers such as Facebook, Twitter, or Amazon, with SAML identity solutions, or by using your own identity system. In addition, Amazon Cognito enables you to save data locally on users devices, allowing your applications to work even when the devices are offline. You can then synchronize data across users devices so that their app experience remains consistent regardless of the device they use.
https://aws.amazon.com/cognito/
AWS S3 documentation clearly explains, it's not possible.
You can host a static website on Amazon S3. On a static website,
individual web pages include static content. They may also contain
client-side scripts. By contrast, a dynamic website relies on
server-side processing, including server-side scripts such as PHP,
JSP, or ASP.NET. Amazon S3 does not support server-side scripting.
If you mean, you would like to grant access to HTML content for only certain users without their IAM, you could explore S3 pre-signed object URL option.
IAM username/password credentials are only for use with the AWS console.
There is no exposed API that you can use to validate IAM credentials.
Q: Is there an authentication API to verify IAM user sign-ins?
No. There is no programmatic way to verify user sign-ins.
https://aws.amazon.com/iam/faqs/
So since parse is shutting down we are moving our website / mobile app that we've been developing to AWS. We are primarily going to use the following services:
SNS, SES, Dynamo, S3, Lambda.
Now I am still a bit confused on:
what cognito is used for? Do we really need cognito to authenticate users and use DynamoDB, S3, SNS ? Or can we just use specific APIs for each of these services and connect directly (using Js SDK)?
If we do have to use cognito how do we save local data i.e logged in user/ identity? is that what cognito sync is for or do we have to use cookies ?
In summary why do I need cognito when I can directly connect to DynamoDB using the JavaScript SDK?!
Thank you in Advance.
Amazon Cognito can be decomposed in two sub-services: Amazon Cognito Identity and Amazon Cognito Sync.
Think of the former as an authentication service and a credentials provider. The latter is just a service to store user data and keep it synchronized between multiple devices.
What is the purpose of Amazon Cognito Identity?
Suppose that you have a table in DynamoDB. Let's say that you have a web application that will store an item on that table.
You can create an user in IAM, embed the credential information on the web application, and then put the item on the table using the AWS SDK.
There are three things going on here:
The credentials are embedded in the application
The credentials do not expire.
Every user in your application has the same access rights on your table
This may be fine for some applications, but Amazon Cognito Identity offers a solution to these common problems.
Let me explain Cognito Identity's workflow:
An user registers an account on your application, sending all the information (username, password, other data...) to your server.
The server stores the user in some back-end database (it could be a DynamoDB table) and creates a new identity on the Cognito service. This identity is then mapped to this user.
The user can now login into your application. The user logins and sends username and password to your server. (This process could be done automatically after account registration)
The server checks the username and password against your back-end database. If everything is right, then the server makes a request to Amazon Cognito for a temporary access token.
The web application receives the token and makes a request to Amazon Cognito (using that access token) to get the user credentials. These credentials are basically a temporary IAM user that was created specifically for this user. It will have an expiration (usually an hour).
The web application uses these credentials to make operations on AWS, such as putting an item on a DynamoDB table, or calling a Lambda.
When the credentials expire, the user must re-login into the application. This might be done automatically or not, depending on your application's requirements.
On the Amazon Cognito dashboard, you can configure roles and policies for your "identities" (an user in Cognito). This way you can specify which services it can access. It even allows you to create access roles for your users (Admin users may be able to access some services that normal users should not).
I should also note that Amazon Cognito can be easily adapted to support Facebook / Google+ / Amazon accounts, which will be mapped to the same identity, so the user can login via multiple sources.
What is the purpose of Amazon Cognito Sync?
Consider it like a DynamoDB table where you store information for a specific user. These information is shared between multiple devices and is always synchronized. This means that when a web application updates an user value, then the mobile application will automatically reflect this change.
There is a limit on how much user data you can store (I don't remember now), so it's not something you would use to persist information (such as an user password), but rather a mean to share information.