Correct Application Structure to Safely Verify Cognito JWT Tokens - security

I'm building a React application that uses API Gateway and Lambda on the back-end. I'm going through the process of integrating Cognito authentication. I've completed the following:
Generate user pool
Upon login redirect to my React application with Auth Code
Extract Auth Code and send it to Token Enpoint
Receive back the id, access and refresh JWT tokens
I covered all that in detail in a post here: AWS Cognito Notes
What I'm confused about is the concept of verifying the signature of the JWT tokens. The AWS Docs describe how to do that here: https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
In short I have to download the JSON Web Key and then match it to the key on the tokens. It's not clear to me how this makes the process any safer. That key is publicly available. If I was conducting a man in the middle attack I could simply get that key and then attach it to my phony JWT token.
Which makes me think that I have a fundamental misunderstanding of this process. Should the Auth Code and JWT tokens not be sent to the React app in the first place?
Should I be setting the User Pool redirect URL to API Gateway, and have that trigger a Lambda function (that contains the client secret), to retrieve the JWT Tokens and THEN send the JWT Tokens to the React app? But then I would have the same problem of not knowing if the Tokens were legit I think? The tokens much be verified AT THE CLIENT, right?
If anyone has any insight on this or could point me to a good article I would much appreciate it.

You can use an API Gateway Authorizer to do this for you. It will check the header for ID Token and check if it is valid for your userpool. Expired and invalid tokens are rejected.
You can read more about this here: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html

The answer to this was that the JWK can be stored in any capacity with the client. The JWK is publicly available, however the public key cannot be used to fake the signature itself, because the signature can only be generated with the PRIVATE key.
I did a full write up on this topic here:
https://ncoughlin.com/posts/verify-decode-cognito-jwt-tokens/

Related

Securing a POST Request in NodeJS from Backend Application

I'm wanting to receive a POST request sent from a backend java application that sends user account information (e.g. username, email, etc.) and will create an account with the given information. What method can I go about where the api I have can only be used from my java backend (so people can't just start creating accounts themselves)? I assume a method would be using an api key but I have had trouble finding resources on how to implement that. Could someone help me and steer me in the right direction?
The most common method is by the use of API Keys.
You would have a list of valid clientsIds for your NodeJS Server (Note that the JAVA backend server would act as a client for your Node JS server when creating users).
Along with the list of valid clients, you would also need to store a valid API Key for each client.
The Java Backend would send you its clientId and the API Key in the POST request. You would need to validate the client id and the API Key for that client.
Once both are validated, you can assume the request to be from your Java backend.
Both the list of valid clientIds and the associated API Keys should be stored in environment variables. The API Keys should be different for the same client in different environments.
Another way I have implemented this in the past, and I consider to be more secure, is by use of RS256 Algo JWT Tokens with the use of Public and Private keys.
Your Java backend should generate a Public/Private key pair and provide the Node backend the public key.
Along with the POST Request, the Java Backend will generate and sign JWT token using the Private Key and send it to your NodeJS Backend.
Your NodeJS backend should verify the token using the public key.
The JWT Token generated by the Java backend should be short lived.
Every time the Java Backend has to send a POST request, it should generate a new JWT token.
The use of JWTs are more secure as they can be short lived and you dont have to worry about a token being leaked. If the API Key gets leaked, you will have to co-ordinate between the two backends to start using a new API Key. However, if a JWT token gets leaked, the same token cannot be used once it expires. It would be safe to have the JWT token short-lived, may be 1-2 minutes. Since the Private and Public keys are not being sent along with each request, there is a lesser chance of it being leaked.

NodeJS Authentication for Multiple RESTful API services

I would like to seek help from you guys.
I have multiple RESTful API services running in NodeJS Express. Each API is hosted by different NodeJS with different port. And in our front-end, it is accessible via reverse proxy.
We are now moving to a secured services. However, there's a problem in authenticating with the services. The front-end should request 1 auth token upon login that can be used in accessing the internal API services (API is also accessible in our public domain /api/).
How can we solve this problem?
Current Setup
You could issue a signed JWT token upon login (see https://jwt.io/).
The front end application has to send the token back with each API call.
Each API server has to know in advance the key (public key or symmetric key depending on the type of the signature algorithm) and use the key to check the signature of the token is valid.
If it is valid, the API server knows it can trust the token content, and decide whether to serve the request. The token would contain the identity of the user, expiration time, ...
Note that signing the token does not encrypt the data: if you need to convey sensitive data through the token, it needs to also be encrypted
Thus it is not necessary to establish a session and then share the session across the many API instances. Knowing the key is enough to trust the token content
To pass the JWT in API calls you can use a header:
Authorization : Bearer theBase64EncodedToken
Of course, it is up to you to check if this scheme satisfies the security concerns related to your application.

Correct Micro Service JWT flow

I am currently building out a micro service architecture and started with the auth server and client. I also wanted to confirm the best flow of authenticating a user with a token.
In the above image. Step 3 is were I start getting confused.
I thought of 2 solutions to the problem.
One every api passes the token to the auth server and waits to get approval that the token stored inside matches the db and it is still valid.
Two is to include a secret phrase in the JWT token and just have the API service parse and check for itself if the token is valid.(The secret phrase would be so that if a hacker tried to fake a token and it parsed to a valid id somehow the phrase would be off without the secret code used to encrypt the token. Which I don't even know if it is possible. If not then I guess 2 would be the best course of action)
A hacker cannot create a valid JWT token if he does not know the the signing key. If he somehow manages to get that signing key it is reasonable to assume that he is able to get your "secret phrase" also.
About the checking: JWT tokens can be checked by the API service as they contain all the information needed (except the signing key that must be known by the API service). The expiration can be checked here also. Anyway, you also need the information stored inside the token, like user ID. You should do this if you want better scalability.
The only reason why you would need to check a JWT token against a third Auth service is to see if it has been invalidated; for this you need a central service although you could replicate the list of invalid tokens to all the API services for better resilience.
You really don't have to forward the request to Auth-server to validate the JWT token. A JWT token is like a bill note, once it's signed it can be validated by anyone who is sharing the key.
I would recommend you to have an edge service in front of all your API-services. The edge service either shares the key by which JWT token is signed by Auth service or has the public key to verify the signature.
Once the signature is verified, the edge service can extract the required information from the token and add it to request header. Your downstream services can consume this information according to their need.
You can use Https to enforce that your request isn't intercepted by anyone over the network. In case, even if someone tries to mess up with the JWT token, the signature won't match and you can detect that. Please go through JWT/KONG: Cannot create JWTs with a shared secret to know more about creating-parsing the JWT token with public-private keys.

Restricting traffic to server from only my mobile application

I have a requirement to secure my JAX-RS resources and only accept requests that originate from authorized mobile applications. Is this possible? How can this be done?
All of my resources are protected already with user authentication, the goal here is to reduce user ID fishing attempts. I know one solution would be to keep the response error with an invalid user ID generic, but the application is very large and at the moment this isn't possible.
One idea I came up with is to use JWT tokens signed with a shared secret. Then I could add an Authorization filter on the server to check the signature. If it doesn't validate then discard the request. Does this sound like a viable option?
My concern is the security of the shared secret on a mobile device, could it be compromised with a rooted device?
Using tokens is the preferred way. But the secret key is not shared. Only the server has access to it. That secret key is used to generate the message authentication code(MAC) of the JWT. Since secret key is only known by the server, no one else can generate a JWT with a valid signature. Secret may be persisted or application scoped.
Once a client is authenticated using credentials, server must send a signed JWT to the client.
That JWT must contains necessary information to identify the client and state(if necessary).
Then client send that token in a header field along with all the other requests.
Server validates the JWT using secret key and process the request.
Even though client can change the JWT body, he cannot get it verified. That's the whole point of using a signature.

Generate token after login nodejs API

I am creating an API using nodejs and express. I need to provide local username/password authentication. I may need to provide additional authentication in the future so I am using passportjs as it seems the most flexible/plug-able.
The API will be used by a web application as well as a mobile application. Instead of having to pass the username/password with every single api request I was thinking I could let the user login and provide the client with a token. The client can store the token and provide that on each api request.
I have looked at using JWT tokens ie, http://coderead.wordpress.com/2012/08/16/securing-node-js-restful-services-with-jwt-tokens/. However I am not really sure how to create a secure token with JWT. I have read that using the username in a token is a bad idea. Has anyone use JWT in node to create tokens. Got an example?
Any other modules for node that I can take a look at for token generation.
node-uuid is the module you are looking for. I use it to authenticate the users and any task that requires a random and unique identifier. Encoding the credentials in the token is generally a bad idea.
It was already built into nodes crypto pacakge.
http://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback

Resources