Why does AWS CognitoSync ListDatasets require IdentityId? - amazon-web-services

If a user can only access his or her dataset, why is IdentityId a field which has to be set on this API call (ref). Couldn't the IdentityId be inferred from the AWS credentials? Or if they don't have to match, then technically wouldn't that mean that any malicious user could access that dataset, if he or she had the origin user's Cognito ID? Or does the call to ListDatasets re-validate the Cognito ID against the identity provider's token? In which case, what should I cache in my website as a cookie, so that a user won't have to log in every time?
Right now I am caching the CognitoId, but I am worried that if exposed that data will be compromised (or brute force attack), and if it isn't a security hole, then will I have to have the user re-authenticate against their provider each time they want to sync their data? What is the solution here?
Any help would be appreciated.
Edit 1:
I think what I have found is a caching problem with CognitoSync being used in the web browser and this question which may point to the same issue. Here is the auth flow I am expecting:
Login to Web Federated Auth Provider (OIDC, such as Google) => returns temporary token.
Use token to get temporary credentials and IdentityId from Cognito.
Cache Something.
Activate CognitoSync Manager to synchronize.
Some Time Later:
Using the cached something, rerun synchronization.
Problems:
If I cache the OIDC provider token, I can regenerate the AWS credentials, except it expires after 1 hour.
If I cache the AWS credentials, I can reissue Sync, except the credentials als expire.
If I cache the IdentityId I can't synchronize, So what do I cache?
Conclusion:
If AWS Credentials are needed to issue synchronization call, why does the listDatasets require IdentityId again in the API call, can't AWS CognitoSync reverse lookup the credentials to determine what the IdendityId assigned to those credentials are?
Does the [cognito sdk](https://github.com/aws/amazon-cognito-js) actually work?
Am I forced to use DynamoDB directly to do synchronization, if I want to avoid the Auth flow every time a user wants to synchronize?
There is a [Caching Provider](http://docs.aws.amazon.com/AWSAndroidSDK/latest/javadoc/com/amazonaws/auth/CognitoCachingCredentialsProvider.html) for Android SDK, does something like this not exist for the browser? (Does that even work?)
[Developer docs](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityCredentials.html) reference refreshing the token, this can only be done when the current token is still active :(.

It's not quite either. Cognito will validate the identity id given against the identity id for which the AWS credentials were created. So, for your concern about spoofing, it would not allow user A to get user B's sync data just by knowing their identity id.

There seems to be two issues:
Cognito validates the AWS Credentials against the IdentityId, so there is no security hole. However it still doesn't make sense for the API to contain that as a parameter, Amazon probably will needed to update that API.
The problem that you are running into is that identity provider should provide a way to get the necessary access token for each request authorization without requiring the user to grant permissions everytime. Authorize with the provider and then take the access token (or id_token) and send it Cognito as the Login token for that provider each time you want to synchronize. You actually don't need to cache anything.
For Google the request looks like:
var nonce = createNonce();
setCookie('GoogleAuthNonce', nonce, 1);
var paramString = $.param({
response_type:'id_token',
scope: 'openid', //email, profile',
client_id: GOOGLE_CLIENT_ID,
nonce: nonce,
login_hint: 'service_email_address',
prompt: 'none',
redirect_uri: 'http://service.com'
});
window.location.href = `https://accounts.google.com/o/oauth2/v2/auth?${paramString}`;

Related

Aws Cognito: Immediately revoke user token. Could this work?

I've read many posts online but to no avail until I came upon this post. I would like to check if my implementation has any loophole or if I've missed out anything?
My Use Case:
Admin logged out a user (UserA) using AdminGlobalSignOut
UserA will be denied immediately - not wait for token's expiration - but on the very next aws resource access
My Implementation:
I uses the Identity Pool to setup temp cred for my authenticated users and sort of as my authorization server. And the critical part when setting up Identity Pool is to set ServerSideTokenCheck = true
My user will authenticate against Cognito and get Aws temp credential via Identity Pool. My user will only have access to aws Lambda, and nothing more. The respective lambda will then have access to the rest of aws resource (e.g. DynamoDB)
Admin do a global sign out to revoke UserA's token. Now ... this is the issue coz tokens are valid for 60 minutes, and with current aws design, signed out user can still access to aws with the revoked token. But with ServerSideTokenCheck, it will always check if the token is signed out. I've 2 invoke methods which I used for my project:
[Direct Lambda Invoke]
When client invoke the lambda function directly (using the temp credential from Identity Pool), immediate the client will get NotAuthorizedException: Token is inactive - Problem solved
[Api Gateway]
a. Add a new custom lambda authorizer
b. In the lambda, call the GetCredentialsForIdentity api with the IdToken. This will give a NotAuthorizedException (code: 400) - Problem solved
I've tested my implementation and it works. The only possible issue is that GetCredentialsForIdentity has a quotas of 200 request per second. Did I missed out any other consideration factors?

AWS API Gateway authorizer google sign in

I have an API Gateway/lambda REST API that is being accessed from a react web app. I need to add authentication using google as an identity provider. The app should also keep the user signed in.
I understand when the user first grants access to the (react) client app, it should send the ID token to my backend, which should then verify the token.
Since my architecture is serverless, I assume the verifying should be done in the API Gateway authorizer function, which then grants access to the API on successful verification of the token.
My question is, how do I then create a persistent session? Should I be saving anything to my database about the user? Does the token need to be verified on every API call?
Should the authorizer be checking if the user is already registered or if it's a new user?
It would be easiest to use AWS Cognito for this. Configure a user pool as an authorizer for your API gateway and then configure Google as an identity provider for that user pool. This link might be helpful: https://docs.aws.amazon.com/cognito/latest/developerguide/google.html. Cognito even has a hosted UI if you want to use it for signing users in.
As for your question about persisting user sessions, they usually get persisted in local storage in the browser or in a cookie or some similar mechanism. You can also persist them on the server-side in a database like you were mentioning but that isn't really for authentication purposes.
The user session will contain an access token. The access token is short-lived, meaning you can only use them for an hour usually. After that you have to use a separate refresh token to generate a new access token. And to be extra safe the refresh token itself will expires after a few days (and you have to sign back in).

Is storing aws cognito JWT key in frontend javascript insecure?

I was reviewing a website's javascript files to find an endpoint i wanted,during this i found a line like this.
DASHBOARD_API_Token=eyJraWQiOiJVS2paTnB3SWdBK292QzB3RWdxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
When i decoded the Above JWT i found entities like kid, username, useremail, iss = http://cognito-idp.us-east-1.amazonaws.com/{pool-id}
I have read few amazon resources which tell that its possible to fetch aws credentials using the above token. Given that i only have the jwt token what is possible and is it that i have to contact the website to revoke the current token?
Well, AWS Cognito is quite an interesting beast when it comes to its JWT tokens and what you can do with them.
Firstly, when you authenticate the user against Cognito User Pool, you get 3 different tokens: AccessToken, IdToken, and RefreshToken. Both AccessToken and IdToken are valid for exactly 1 hour (and you can't change it). This is why you get RefreshToken, which you can use to get new pair of AccessToken and IdToken by calling, for example, InitiateAuth. RefreshToken is by default valid for 30 days, but you can change it in the settings of your Cognito User Pool.
AccessToken
This is the most powerful token that is given by Cognito. If you possess a valid AccessToken, you can call several quite sensitive Cognito APIs.
One very surprising example is that you can call DeleteUser API by providing AccessToken and it will completely remove your account from Cognito User Pool. The "nicest" thing here is that app developer can't stop you from doing it if you've got a valid AccessToken. AWS devs are saying that "its by design". That was one of the strongest reasons why we decided not to pass Cognito Access Token onto the application frontend.
Speaking of the payload of AccessToken, the only potentially sensitive claim is the client_id, which contains the Cognito Client Id, which was used to authenticate the user. If this client does not have a client secret (which should probably be considered a security flaw in the first place), then the client_id can be used to call quite wide range of Cognito APIs. However, Cognito considers client_id to be public information. All other claims are considered public, you can see the full list of claims here.
IdToken
Contains the information about the player. It may contain some personal information about the player depending on what standard claims and custom attributes were configured in your Cognito User Pool, however it is also possible to suppress or override those claims using Pre Token Generation Lambda Trigger. Using this trigger allows one to hide or mask player attributes, which are considered sensitive and should not appear in IdToken.
As far as I'm aware, you cannot user IdToken to call Cognito APIs.
RefreshToken
Can only be used to request new AccessToken and IdToken, cannot be used to call Cognito APIs. In our particular use case we had to avoid passing RefreshToken to the app frontend, since it can be used to get AccessToken against public Cognito API (meaning that we cannot stop the user from doing that).
Using Cognito tokens to get AWS credentials
This is only possible if Cognito User Pool has been explicitly integrated with Cognito Identity Pool (which assumes that app developers knew what they were doing). When you exchange your Cognito JWT tokens, you get temporary AWS credentials with permissions bounded to a specific IAM role configured in Cognito Identity Pool. It is completely legitimate flow and intended behaviour. Moreover, if Cognito Identity Pool is configured to allow unauthenticated access, you can even get temporary AWS credentials without having Cognito JWT tokens (but those will be tied to another, presumably, more restrictive IAM role).
Using Cognito tokens with API Gateway
AWS API Gateway resources can be integrated with Cognito User Pools. Depending on how the integration is done, you can use either an AccessToken or IdToken from the integrated Cognito User Pool.
Revoking the tokens
You can revoke all tokens issued for your account in Cognito User Pool by calling GlobalSignOut API and providing a valid AccessToken. As this API is public, you don't need to contact the website if you possess a valid AccessToken.

AWS Cognito get new id_token

I'm using implicit grant for one particular client (web-based admin portal) for an application that uses Cognito-integrated auth for other parts of the application (API Gateway, etc).
The problem is, the id token I get from Cognito Oauth expires after one hour, but I still need that token to send to API Gateway to signify that I have an authenticated user.
How do I get a new id token (remember, I'm using implicit grant, so I don't have a refresh token)?
Do I have to re-send the user's credentials to Cognito's auth endpoint every hour? That seems extravagantly un-secure for several reasons.
What am I missing?
Thanks in advance!
If you use implicit grant there is no way of refreshing the tokens (Storing credentials and using it to get new id tokens doesn't count here since its takes away the purpose of using OpenID Connect).
This is why the code grant is there. Unless you use code grant, refreshing the tokens are not possible.
You can use AWS Amplify library to simplify the login where it takes cares of the refreshing and other heavy liftings if you use the code grant including using federated identities to access AWS resources such as S3.

Is a single AWS Cognito Region, us-west-2 for example, suitable for serving Canada, US and Puerto Rico?

I'm looking at my options for a managed sign-on service and AWS Cognito looks promising.
I notice that it's user pools etc do not currently replicate across regions. I wanted to confirm that 1 region is sufficient us-west-# for example (or us-east-#) would be sufficient for an application that has users spread across Canada, the US and Puerto Rico.
In general, not only in the case of Cognito, the closer your users are in the data center that hosts your services the better. And this is only so you can minimize the propagation delays between your clients and the data center hosting your service.
Therefore, if you have to choose one region, choose the one that the majority of your clients are closer to.
AWS Cognito, does not replicate userPools across regions at the moment. Therefore, if you want to use the AccessToken against that userPool you need to go to the region that the userPool resides.
Now, every other service that accepts accessTokens, will accept your token inside AWS, outside AWS in any region.
I'm adding this supplementary detail to the question as a reference for the token types that Cognito returns. As I just found it by googling some of the info in the answer above.
Using the AccessToken against the userPool would be done for things like updating the user's account information. Which would be required to use the region the pool resides in since pools are not replicated.
http://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html
ID Token
The ID token is represented as a JSON Web Key Token (JWT). The token contains claims about the identity of the authenticated user. For example, it includes claims such as name, family_name, phone_number, etc. For more information about standard claims, see the OpenID Connect specification. A client app can use this identity information inside the application. The ID token can also be used to authenticate users against your resource servers or server applications. When an ID token is used outside of the application against your web APIs, you must verify the signature of the ID token before you can trust any claims inside the ID token.
The ID token expires one hour after the user authenticates. You should not process the ID token in your client or web API after it has expired.
Access Token
The access token is also represented as a JSON Web Key Token (JWT). It contains claims about the authenticated user, but unlike the ID token, it does not include all of the user's identity information. The primary purpose of the access token is to authorize operations in the context of the user in the user pool. For example, you can use the access token against Amazon Cognito Identity to update or delete user attributes. The access token can also be used with any of your web APIs to make access control decisions and authorize operations in the context of the user. As with the ID token, you must first verify the signature of the access token in your web APIs before you can trust any claims inside the access token.
The access token expires one hour after the user authenticates. It should not be processed after it has expired.
Refresh Token
The refresh token can only be used against Amazon Cognito to retrieve a new access or ID token.
By default, the refresh token expires 30 days after the user authenticates. When you create an app for your user pool, you can set the app's Refresh token expiration (days) to any value between 1 and 3650.