AWS Cognito with google provider, how to handle the response from google? - amazon-web-services

So I'm using cognito User Pools with Google as an Identity provider. I configured everything and I'm sending the data to google, which redirects back to the amazon auth endpoint, which then redirects back to my app with the following parameters in the URL:
AccessToken
ExpiresIn
IdToken
TokenType
Now, I have no idea how to use these to actually create a Cognito "session" that goes into the javascript api workflow and just becomes automatic (sending, receiving and renewing the token)
How do I achieve this?

Using IDToken to Authenticate Your API
After you authenticate, you can cache the IDToken in Browser and use it when the logged in user requests for API Calls from your web or mobile application. Then you can validate the token at your API endpoints for each request.
Validating IDToken
For more information about using the tokens and validating it at API endpoints, refer AWS documentation on Using Tokens with User Pools. There is a new library introduced by AWS called aws-amplify which simplify some of the implementations. If you are using AWS API Gateway for your API, you can use a custom authorizer Lambda function to do the validation.
Storing the IDToken and Security
In addition, the IDToken is a stateless token, which means it doesn't have a session in connection with AWS Cognito Once issued. Which means you cannot revoke it after issuing. Therefore it's important to keep its expiry short.
To allow your authentication API to issue new IDTokens before it gets expired, you might need to store the Refresh Token in your backend and use it to issue new tokens before the IDToken expires to implement a sliding window.
Note: Storing the IDToken in browser, you can decide in using multiple storage options available (E.g; Cookies, LocalStorage). However, I'm not going to comment on the security implications since it is itself a long discussion where there are still opinionated areas. The standard approach currently available, is to set up a serverside Cookie and implement CSRF. It will be challenging to implement unless you use the IDToken just to initiate the authentication and use a session instead, for the recurring requests.

Related

What is the best way to authorize an AWS API call for a logged in user?

I have a spring boot based web application which already authenticates the user. I would like to create some AWS APIs using AWS api gateway and a springboot rest app deployed on EC2. The user will log into the existing application and call the new AWS APIs from the browser ajax calls (like an SPA I guess but I have a server and can use client credentials/secrets if needed). What is the best way to handle security for the APIs. My first thought was to generate a JWT using a shared server side secret for hash and verify it with an AWS lambda. But this seems a bit non standard. Oauth2 might be better but might also be over kill. Not sure the performance would be good. Few requirements
token passed to the API should be a user specific token and have an expiration (and hence a refresh token)
The user is already logged into my web app and calling the api should not issue a login challenge.
AWS API Gateway should be able to verify the token before passing it to the application
Any token passed to the API should probably be generated on the logged in web application. Server knows the user is authenticated and should generate the user access token on behalf of the user. The AWS api should be able to figure out what privileges the user has based on the user principle or scopes in the token
I've thought about using Cognito AWS but I dont want to require the users to preexist in a user pool. Cognito seems geared more for the authentication rather than authorization and I would not be using the cognito login functionality. I dont know if its possible to use the oauth2/token endpoint alone with cognito.

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).

AWS Cognito - where in code should I process oAuth authorization code?

I have got one beginners questions. I am just starting with AWS Cognito User Pools and set oAuth "Authorization Code Grant" workflow succesfully - so now, after the login, I am succesfully redirected and get the authorization code in the url. The next step is to exchange it for the actual JWT token, but the problem is that I miss some broader perspective and dont know whats the best practice in this case. I know the javascript code to exchange the code for token, but I dont know where it should run.
Shall I use Triggers functionality in my user pools, tie Lambda function to some event (which one? "Post Authentication"?) and let this Lambda function exchange the code for token and save it as a session?
Or shall I put all this functionality into the url I redirect to after a successful login? Is there some best practice for this situation?
Thank you
The question to ask is: "Now the user is authorized.. now what?"
The AWS services that you want to make available to your users will determine what type of authentication flow you need to use. Since your question is fairly broad.. I will give you the high level of two types of authentication flow.
Federated Identities:
You can configure federated identities to grant users AWS IAM credentials (AccessKey and AccessSecret). When you set up a Federated Identity Pool, you specify the identities that can receive IAM credentials (for example: Your cognito pool, google, any other openID provider) and the IAM policy which will grant access to specified resources. This guide will review how to do this in your application code.
API Gateway Custom Authorizer You can also configure a Custom Authorizer with the source being your Cognito User Pool. This will allow you to make a request using the jwt #id_token received from authenticating against the user pool.
Specific answers to questions:
Shall I use Triggers functionality in my user pools, tie Lambda function to some event (which one? "Post Authentication"?) and let this Lambda function exchange the code for token and save it as a session? - Generally Triggers are used to store user data for analytics or server side processing. If you are using Cognito for authentication you don't need to use Lambda
Or shall I put all this functionality into the url I redirect to after a successful login? Is there some best practice for this situation? - I would recommend using a library such as AWS Amplify to manage Authentication with Cognito.
Most common approach I have seen with recommendation for code grant flow is the approach you mentioned,
shall I put all this functionality into the urlĀ I redirect to after a
successful login
The advantage of this is that you can store the refresh token server side and use it to refresh the id_token before it expires (There are several places which states not to store the refresh token client side).
However, if you look at the implementation in AWS AmplifyJS library for Code Grant, it does this in client side storing the refresh token also in local storage and refreshes the id_token from client side using JavaScript.
So both approaches are there while using both in different projects, I feel the latter is easy to implement and maintain.

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 federated identities + permanent access key for programmatic API

I have an API service, which I'm going to deploy using AWS API Gateway with Cognito authorizer + Lambda as backend. This service will be used by our javascript client. Also, it should be exposed to end users as raw endpoints for programmatic access.
While it was quite easy to enable signup/login in js client using federated identities, I can't figure out the way to provide users with private access token to include directly in http headers.
Here are two authentication flows, I'd like to get in the end:
The flow for js client user:
User signs up with Facebook or Google.
User verifies his identity.
After login, user goes to the Profile/API Keys section in the interface.
User copies access token and can include it in http request header in any http client (httpie, curl, language libraries whatever)
The flow for admin created user:
Admin creates user.
Access token is generated for that user.
Admin passes generated access token the user.
User can include it in http headers to make request, as in previous flow.
An access token should be permanent, and can be regenerated by user at any time (think of Stripe API access keys).
The point here is to eliminate additional steps for the user to start using service programmatically. The closest thing in AWS docs so far is developer-authenticated-identities, but user should utilize AWS sdk anyway.
One possible way to accomplish this task is to use Custom authorizer instead of Cognito authorizer in API Gateway. Custom authorizer could implement logic based on e.g. auth header name and decide to either authorize in Cognito or to user API access token in database. I'm not sure, if it is possible, and if it is the major drawback is to reimplement Cognito authentication flow in lambda function.
The question is how can I accomplish such API access token (re)generation using Cognito or API Gateway?
The first flow should be possible with User Pools. Cognito User Pools now has a federation feature where you can federate using Facebook/Google and receive access token/refresh token depending on the flow used.
For admin created user, the user would need to authenticate before tokens are issued but this can be achieved by creating the user with a temporary password and signing the user in with that password, after which it can be changed and logged in again to receive access/refresh token.
The refresh token use case is that it can be used against the Cognito APIs to receive a new access token. When the refresh token expires (default is 30 days but it is configurable), the user would have to authenticate again.