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
Related
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.
I'm using cognito to authenticate to node-js using amazon-cognito-identity-js I logged in and it returns me an access_token, id_token and refresh_token but none of them work when I'm using the open id scope with authorization code grant.
I don't know if I need to call another service or do another process to get access at the gateway
but when I generate a token using client_credentials flow the api gateway works
If I understood correctly, the scopes you are setting are OAuth 2.0 scopes and require using the OAuth 2.0 endpoints (e.g. the hosted UI, or an external IdP federation). Your code seems to be using the non OAuth 2.0 flow (e.g. I assume InitiateAuth with SRP). If you decode the JWT I believe you will see it has only the aws.cognito.signin.user.admin scope.
For using custom scopes you will need to
send the access token (not the id token)
use token you got from the token endpoint (e.g. using the hosted UI or federation) - they will contain the scopes you set in the screenshot
For using the open_id scope, same as above but send the id token, not the access token, and remove the custom OAuth scopes in API Gateway (if you put them it will expect an access token)
p.s. custom scopes work great with the client credentials flow, but less with the authorization code flow if it doesn't have a client secret.
Lastly I recommend you take a look at AWS Amplify as it will handle a lot of that for you behind the scenes as well as include security features such as PKCE out of the box.
Relevant github issue: https://github.com/aws-amplify/amplify-js/issues/3732
Let's say you are developing a client side JavaScript SPA app (Angular), a backend API for this app (ASP.NET Core in my case) and you use an identity provider that implements Open ID Connect protocol (I'm using IdentityServer4).
Apparently the recommended way for securing the app is to use the OIDC implicit flow between the JavaScript app and the identity provider and if successful the JavaScript app gets an id token and an access token.
Now according to this documentation the JavaScript app is supposed to pass the access token in the headers whenever it calls the API. And I'm not sure what purpose does the id token serve in this case (besides customizing the UI in your JavaScript app)?
But this is the confusing part: The documentation also says you should never use the access token for authentication. But that is the token that my API receives in the request headers, how can it authenticate the user then? If my API receives Post(newRecord), and the API needs to internally fix some audit information on newRecord (i.e newRecord.CreatedBy = CurrentUsername), how can it do that without authenticating the caller??
I think I'm missing a piece of the puzzle. Please any help is deeply appreciated.
Short answer/suggestion
Use Access Token to access API endpoints. From API endpoints, you must use token introspection endpoint to validate token validity (active state) as well as obtain subject who authenticated at authorization server. IdentityServer provide support for this. Documentation is available from here.
Explanation
ID token is intended to be used by receiving client. It will allow your client to authenticate the end user and provide user specific customizations. This is the whole purpose of OpenID Connect (OIDC). On the other hand OAuth 2.0 provide an authorization framework. It replaces user credentials with access tokens, thus improving API access security (compared to basic authentication and storing user credentials everywhere). Hence OIDC is built on top of OAuth 2.0, you get both ID Token and Access token with OIDC flow.
But as you have figured out (and mentioned before), ID token is intended for client. There are could be exceptional cases where ID token get passed between client and a server. This is mainly when both are controlled by same party. But the access token is the key to access API endpoints, correctly.
Access tokens can be an opaque string or JWT. When it's a JWT, API can read and understand the token (self-contained). When it's opaque, only way to validate token at API endpoint is to use token introspection endpoint. If API can validate token validity, then it could grant access (authorize) request. Furthermore, if user details (subject) are available (through JWT or as introspection response), then user specific checks can be executed. This further extends to token scope values. But at the end of the day, you are authorizing the API request and not authenticating the user at API endpoint. That's the highlight. Hope things are clear now.!
I am wanting to build a authentication/authorization service using NodeJS, Mongo, and JWT. This service would be a micro-service that handles requests not only from my API Gateway before allowing requests, but from other services that might want to check auth and roles. I am assuming that all other services will use this Auth Service to validate the JWT as well as roles, etc.
Hopefully this diagram better explains what I am looking for.
Can anyone point me to a resource that might help me learn how to do this with NodeJS?
If you have a single client application then you can do following steps
Make one microservice for authentication that generates jwt token.
The jwt contains all essential user information in its payload, ie Role, UserId etc.
The jwt token will be sent in Authorization header for every authorised request.
Before processing any request you can validate and decode the jwt token using middlewares. Now you can set the user's info in req object easliy and can easily access users role and its id in your controller.
if the token is not valid then you can throw error in middlewares and it will provide json response of unauthorised.
You can call the authentication api to validate and decode your token or you can write 3 to 4 line of code in every microservice in middleware.
Here are some links for sample implementation of jwt, you should customize these sample code according to above steps.
5-steps-to-authenticating-node-js
authenticate a nodejs api with json web tokens
If you have multiple client applications
You should use openid connect standard that provides single sign on solution to authenticate multiple application with same username and password.
here is a openid connect playground to understand the authorization flow.
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