AWS - Cognito Identity with nodejs - What to do with tokens - node.js

So I'm trying to use Cognito Identity in my nodejs API. My goal in using Cognity Identity is to be able to give users a secure way to create a user account and log in. I'd like to use my API to make aws cognito calls to verify users by identifying them with their email address. My API will then give users access based on who they are, which is based on their email.
I was able to create a user, verify the user's email, and log in to get an AccessToken, IDToken, and RefreshToken. That's great, but at this point I'm not entirely sure what to do with these tokens. I'd imagine I can somehow use them to verify a user every time they make a call to my API, but I'm uncertain how to do that.
I'm imagining authentication flow going something like this:
User logs in with their password -> My API makes a call to aws to get tokens -> My API passes those tokens back to the user's mobile device -> Their mobile device stores these tokens -> AccessToken is used to verify all API calls until it expires -> RefreshToken is used to get a new set of tokens if AccessToken expires -> If RefreshToken is expired user must log in with username/password again.
Is that incorrect or an improper way of doing this? If I have the right idea, then how do I use the tokens to accomplish this? I wasn't able to find any documentation on the authentication process once a user gets their tokens. The only thing I can find that seems it might be able to accomplish this is here: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#initiateAuth-property

Your next step depends on what service you use and how you use it.
For example, for an API-driven application with Lambda / API Gateway, you'd use Amazon Cognito User Pools for your API resource methods and send the ID token as an Authorization header with your API call. (Yes, the name is misleading. It should be Authentication since the authorization logic is actually implemented in your Lambda function)
Then, your Lambda function can access the identity claim properties from the user pool using the context object (when you enable Lambda proxy integration) as:
const email = context.authorizer.claims.email;
or
const cognitoGroups = context.authorizer.claims['cognito:groups'];
If you haven't enabled Lambda proxy integration, you should make sure to pass those values in your body-mapping template.
You can find code examples here and here.

There is a good reference github application that demonstrates various aspects of cognito and how integrates with lambda, API Gateway, DynamoDB and other AWS services.
Here's a link:
https://github.com/awslabs/aws-serverless-auth-reference-app

Related

How to modify AWS Cognito JWT Tokens, we are using NodeJS technical question

I am using AWS Cognito for user management. I am getting too much information in JWT Token (in payloads). I do not want to pass it on to receiving API Client. So my question is how do I reduce the data or get the data which is only necessary for the user. Sending that much data is the security issue.
I don't find any documentation or console controls to modify the same.
For example I only want:
"exp": 0000000000,
"at": 0000000000,
"username":"exampleUsername"
It sounds like you have a client that might be third party, or you trust less. You can setup a separate Cognito UserPool App Client for this application. Then write a Pre Token Generation Lambda to suppress token fields you do not want to expose to this client, and assign it to the correct UserPool App Client.
https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-pre-token-generation.html
You probably already know this, but you cannot edit a token after it is issued without invalidating the token.

Link a third party application account in Cognito

I need to link a third party application credentials to the one the user uses to connect to my application through Cognito. In other words: user connects to application A (my application), then application A asks user for credentials to connect to application B. Application A then interact with application B with the credentials provided by the user. I would like to link those identities but let the user connect to application A only with credentials I provided him (and not from the ones he uses for B).
AWS has AdminLinkProviderForUser in Cognito which doesn't satisfy the last requirement.
Application B offers multiple authentications: Form, Basic HTTP, SSO, OAuth2 and OpenID.
Is there any way to link the two identities in the way described above and store it in Cognito for future use?
If I am understanding you correctly - what you need is for the user to log into your application with credentials they have for another application and then store the login they have for that application and provide them with new login details where those login details link them to your application and the that application.
Have a look here: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-identity-federation.html
However taking that approach the users will still login with application B details. But Cognito provides that out of the box.
If for some reason you do not want to take that approach - and you want to have a force change on the password you must do the link yourself.
A suggested approach would be as follows (Making use of Lambda Triggers in Cognito User Pools):
Pre-signup lambda trigger:
Get the user credentials
Use the credentials to make the call in that lambda function to
application B.
Get the authentication response from that call
Pass this as a param to your Cognito user attributes
Make a call using AdminCreateUser(https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminCreateUser.html)
Now you have the user stored with the auth data from application B as a custom attribute.
This is not the best way to do it though because auth data usually needs to get refreshed which will add extra complexity. You could do that refresh in a Pre-Authentication Trigger that updates that custom attribute for the user.
Honestly, if application B is custom, I think the best approach for you will be to use OIDC Provider approach https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-oidc-idp.html

Include user details in AWS Cognito Oauth2 token

I'm using AWS cognito with a NodeJS backend API and want to include user details in the access token return from /oauth2/token end point with scopes defined in the user pool client app.
Also if I use adminInitiateAuth API, there is no way to include the scopes in the return access token. So is it possible to have both user details and scopes in a one access token?
Cognito does not support custom claims in access tokens, which I think is a really good design choice.
You can manage extra / custom data fairly easily in your APIs and UIs in a much more extensible manner via claims caching:
https://authguidance.com/2017/10/03/api-tokens-claims/
There is a node sample of mine that does this here:
https://github.com/gary-archer/oauth.websample2
It is a pattern often implemented by API gateways, such as AWS:
https://authguidance.com/2018/12/16/serverless-api-deployment/
Amazon Cognito returns multiple tokens uppon succesful autehntication : an ID token, an Access Token, and optionaly a Refresh Token.
Question 1 : user details in Token
The ID Token contains the some PII details, such as the use name and email address.
https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html
When using AWS amplify, you can use Auth.currentAuthenticatedUser() to retrieve user details from Cognito
https://aws-amplify.github.io/docs/js/authentication
Question 2 : adminInitiateAuth
This is currently not supported
https://github.com/aws-amplify/aws-sdk-android/issues/477

How can we integrate a customer's serverless application with own user base with our OAuth server?

We have a simple setup: An authorization server based on OAuth 2.0, which currently only supports the client_credentials grant type. Then we have an API, the resource server, which is protected by requiring an access token from our OAuth server.
All use-cases for our API so far, have been pure machine-to-machine communcation, where it's simply our customers servers running batch jobs.
Today I had a meeting with a new customer. They have an SPA that seemingly does not have its own backend server. It uses AWS for authenticating, and seems to return a JWT, but from what I can tell, they make a lot of API calls directly to publicly available services, and then the logic is all performed in the SPA.
We ideally would've liked them to simply register a single OAuth client with us, so that when users make a request that needs one of our APIs, the request is first routed to their server, which performs the lookup, and then uses their client's credentials to contact our server. But they would prefer to not have to set up a backend. In this case I'm kind of at a loss for how we sensibly let them integrate with our system. They would prefer to send their user's JWT to our system, but I don't think they understand that we would need their key to verify the user's signature in that case, and we don't want to have to create new APIs simply for this purpose.
Would very much appreciate any advice on this issue - thanks very much in advance for any help.
We ideally would've liked them to simply register a single OAuth
client with us, so that when users make a request that needs one of
our APIs, the request is first routed to their server, which performs
the lookup, and then uses their client's credentials to contact our
server.
Your recommended approach is the right way of doing it with Client Credentials Grant.
They would prefer to send their user's JWT to our system, but I don't
think they understand that we would need their key to verify the
user's signature in that case, and we don't want to have to create new
APIs simply for this purpose.
If the previous approach doesn't work, I'm afraid that this is the only way of doing it. You will need to implement a Proxy to validate the JWT (You should be able to get their public key to verify the signature). You can do this in AWS itself, using AWS API Gateway + Lambda to verify the JWT to and forward the request to your existing backend with Client Credentials Grant where you don't need to pay anything upfront.

Getting Google User ID in Node.js in Firebase Functions for Assistant App

I'm getting started with Google Cloud Platform.
I'm developing an Android App that will collect information and store it in a Firebase App. The idea is that a Google Assistant function can query this information and read it back- eg
OK Google, Talk To Simons App, Tell me the last time XYZ was done
The problem I've got however, is to get this being multi user.
I've got the Android App collecting data and putting it into the cloud. I'm using the FirebaseUser and using getUid() in the Android app to get a unique id which is a 28 character string like uVHkia8RRgWD8GGPVvW4AUDUK2.
I've setup Actions on Google, got it hooked into API.AI with Web Fulfilment and got Node.js working in firebase functions.
Unfortunately, the UserID I get back looks more like:HTge48H0CF2FC5jJQCigFBc-UCQ
The problem is that this UserID is not the same as the UserUID I got from Filebase User.
Im using
let ApiAiApp = require('actions-on-google').ApiAiApp;
const app = new ApiAiApp({request: request, response: response});
and
const userId = app.getUser().userId;
What am I missing? I see references to OAuth2, Account Linking etc. I'm not entirely sure what to do at this point. All I need to do at the moment, is get the User UID like I get from FirebaseUser.getUID() in order to look the data up in the Firebase Database.
I think the rest should be straight forward.
There's loads of documentation on linking to Firebase from the Assistant, but very little on actually authenticating. So far I've not setup an flows, like Authorization Code Flows, Implicit Flows, or set a username/password against API.AI and the Fulfilment option. Quite frankly Im not sure what needs to go where at this point.
The user provided by app.getUser().userId is designed as a persistent anonymous identifier. It is roughly the equivalent of a cookie that one sets in a web server - you can use it to determine if you've seen this user before, and what else they've done in your Action, but it does not inheriently relate to any outside account.
Unfortunately, the best solution you have at this point is to use Account Linking which requires you to setup an OAuth2 server. As part of this setup, you will need to create three components:
A login web page for your service. You'll provide the URL for this page to Google as part of Account Linking. When the user accesses your Action, and your action indicates they need to log in, they'll be redirected to this URL on their mobile device. On this page the user will log in - once logged in, you now know their Firebase ID and will create an auth code for this user. You'll then redirect them back to a different URL and pass this auth code (along with some other info) as a parameter.
A token exchange endpoint. Google will call this URL with the auth code above. You'll verify this code, determine who the user is, and send back a unique access token. (You'll also send back a refresh token that Google will use in the same way to get an updated access token.)
An auth token handler in your webhook. When API.AI calls your webhook, it will include the access token as one of the fields in the request. You can use this access token to get the Firebase ID for the user.
One thing to note for the auth code, the access token, and the refresh token is that you'll need to have some way to map from these codes/tokens to the Firebase ID. There are two good ways to do this:
The code/token can be a JSON Web Token (JWT). This takes the Firebase ID, and other information you wish to retain about the user, and puts it in a standard format. It then creates a cryptographic signature, to make sure it hasn't been tampered with, and encodes it in a standard format. When you need to determine the ID from the JWT, you can decode it, verify the signature is valid, and read the value.
Generate a random string and store this in your database against the Firebase ID. When you get the token, you can then look up in your database the ID that this string was assigned to.
There are many additional details about an OAuth implementation and about each of these steps. See https://developers.google.com/actions/identity/oauth2-code-flow for more details.

Resources