I am working on a cloud native microservices based application deployed in AWS. This application should use a OIDC based IdP (preferably AWS Cognito). The authentication and authorization flow are as follows. Once the user logs in using authorization code flow, the IdP generates one ID token and access token. The access token is sent to the backend services for the backend calls. The backend service can fetch the user information through /userinfo endpoint if needed. Additionally, the access token has the required group claim and the scopes to determine if the right access is present.
Now if the backend service needs to call downstream backend internal microservices on behalf of the logged in user, then should I create a
new token with client credentials grant with a reduced scope needed for the service call? or
Should I send the initial access token of the user only?
In the first case the user context is lost in the new access token and if the downstream service requires the user context, then how will the downstream service get the user information?
In the second case the downstream service is not called with the exact specific scope needed for that service and is not security best practice.
However, there is also one more grant call exchange grant (https://www.rfc-editor.org/rfc/rfc8693.html) which supports creating a new access token with the user context from the initial token without relogging in the user (delegation mode in https://www.rfc-editor.org/rfc/rfc8693.html). Is it supported in AWS Cognito? If not, then how can I achieve the same functionality if I use AWS Cognito?
It kind of depends on the level of trust between microservices. By default, if they are part of the same unit, aim for this type of setup:
Orders microservice, requires an orders scope
Shipping microservice, requires a shipping scope
Customer website, uses scopes orders and shipping
You can then flow the user identity securely, if Orders calls Shipping, by forwarding the access token. Meanwhile each API verifies the JWT on every request and checks for the scopes it needs.
Each API should also check for an audience claim has the expected value, representing a set of related APIs, such as api.example.com. I believe currently though, AWS Cognito does not issue an audience claim to access tokens.
If Shipping is a less trusted subdivision of your company, token exchange would be more appropriate. Eg if you have concerns about a Shipping service abusing Orders privileges.
Avoid over-reliance on scopes, or too many of then. Use claims for the main authorization. In Cognito you can look up these values after verifying the JWT but before your API creates its claims principal
Cognito only has quite limited support for more advanced token behaviour, such as issuing custom claims, reference tokens, or token sharing techniques. If you need them, the preferred option is to choose an authorization server that supports the standards.
Related
I am currently working on building a mobile app which will require the users be authenticated, and have them specific privileges on what they can do (for example, a logged in user will only be able to manage some of their data held in the backend database).
I think I do understand the differences between ID token (user identity) and Access token (for authorizing between the mobile phone client and the back-end), but I am not sure what I intend to do conforms to the best practices regarding authentication and authorization.
Let me summarize what I am planning to do:
front end will be a Flutter/Dart mobile app
authentication will be done through Google Firebase Authentication service
my back end will be hosted in AWS:
API Gateway will serve a unique graphQL endpoint
API Gateway has an associated Lambda authorizer function which processes requests prior to the actual "application" Lambda functions
"application" Lambda function serves GraphQL requests (using python ariadne library), including authorization business logic
Database is a managed PostGreSQL inside AWS
The authentication / authorization workflow is as follow:
Not all of this has been implemented yet, but on the paper I think it could work.
However, it goes against several good practices regarding token usage:
the ID token is sent along an API call which is advised against (see this nice auth0 blog post)
I make no usage of access token between the front end and the back-end (could the AWS policy generated at step 5 fullfill this role?)
I do not leverage the audience claims in tokens
Note: as GraphQL exposes a single endpoint, I am not sure of what the content of the access token should be
I am new to Authentication / Authorization and would gladly have some insight regarding the best way to actually make everything work as per best practices, accounting to the following contraints:
authentication is outside of AWS
the APIs are GraphQL
I want to user Google Identity Platform as the CIAM solution for our GKE-based cloud service. We have a requirement to allow 3rd parties to access our cloud APIs using credentials they obtain via OAuth.
For example, our cloud service provides APIs that Google Assistant or Amazon Alexa can access on behalf of our users. Therefore, we want to provide an OAuth-based token manager that uses the identities of our customers as defined in the Google Identity Platform.
Is this type of OAuth service possible using Google Identity Platform, or the underlying Firebase service that drives it?
Based on the documentation for Google Assistant, you will need to implement your own OAuth2 endpoints. In the authorization code flow, you need two endpoints:
The authentication endpoint needs to sign in the user and get their permission to allow the third party (eg. Google) to call the customer's API on their behalf. If the user gives permission then they return an authorization code - which could be implemented by creating a custom token with Cloud Identity Platform.
Token exchange endpoint is also needed, which has two functions. The first is to exchange the authorization code created by the first endpoint for a refresh token and an ID token. The second is to exchange a refresh token for a new ID token. Both of these functions can be delegated to Cloud Identity Platform.
Additional note:
I would suggest to use custom claims to ensure that these tokens can only be used for the intended purposes, ie. to perform actions which the Google Assistant needs to do. Users shouldn't be allowed to perform other actions, eg. changing the user's password or providing authorization codes to other third parties.
Also make sure that this endpoint can't be used by malicious third parties. For example, you can check that the redirect URL provided matches what is expected, since this is where the authorization code will be sent to.
I'm playing around with securing an AWS API Gateway with AWS Cognito User Pool tokens. The API Gateway authorizer settings allows me to require that an HTTP request contains both an x-api-key header and an authorization header (containing an JWT token). I've attached a picture of this.
It seems to me that requiring an API key along with an OAuth token is redudant. What practical security advantage might there be by requiring clients of this endpoint to provide both?
Api Key is more used along with Usage Plan to control
which stages can a given client access.
Set throttling limit. ex: 100 calls per minute.
Set Quota limit. ex: 100K calls per day.
Api key doesn't actually contain any user specific details, its a static value typically created per application/client.
Personally I don't even consider this AWS Api Key as equivalent to old school Basic Auth header and never used for securing the api at all.
On the other hand Authentication Header is usual access_token and/or id_token, generated when a particular user is successfully authorized/authenticated against auth server and gains access to APIs.
From the Docs:
Don't rely on API keys as your only means of authentication and
authorization for your APIs. For one thing, if you have multiple APIs
in a usage plan, a user with a valid API key for one API in that usage
plan can access all APIs in that usage plan. Instead, use an IAM role,
a Lambda authorizer, or an Amazon Cognito user pool.
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).
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.