Best Practices for handling JWT passing to other microservices - security

We have a set of microservices where some are public facing for our UIs to call. The UI sends a JWT user token (obtained from the user login with keycloak) on requests to a microservice and that microservice may need to call another microservice. What is the best practice for sending a JWT from one microservice to the other. i.e.
microservices just pass through the original JWT user token to other microservices
microservices do a token exchange with the original JWT user for a JWT service token and inject the claims from the original into the new service token.
microservices obtain a JWT service token and sends that in the Authentication header and also sends the original JWT user token as some other header
microservices obtains a JWT service token and adds the original JWT user token as a claim in the new token.
some other mechanism not mentioned
Thanks.

that microservice may need to call another microservice
Personally I would not allow such inter-microservices calls.
Instead, I would introduce a kind of "facade" (typically named an "API Gateway") in front of all micro-services, whose API will be invoked by the front-end.
Every coarse-grained service exposed by this facade is likely, behind the scenes, to chain N calls, potentially to different (fine-grained) micro-services.
This architecture allows two distinct security schemes
JWT tokens for cross-layers communication (ex: FrontEnd -> API GW; API GW -> micro-services)
BASIC auth account + mutual TLS when backend -> backend communication is needed
Nice article:
https://medium.com/ballerina-techblog/securing-microservices-with-jwt-a16b738b110f
My 2 cents,

Related

OAuth clarification

I've followed a training in Go as an introduction to microservices architecture a while ago. Getting back to this project I realise that I need more context as we've been quickly digging into the details of the implementations at the time...
I've drawn a simplified sequence diagram of 2 simple use cases:
The user logs in
The user is already logged in and make a purchase
(you can comment / modify the diagram at your convenience)
https://drive.google.com/file/d/1gWgkhJipUvWrVidkl7YFt_xlDmZYn_CX/view?usp=sharing
Here are the questions I have:
Here we're dealing with user authentication but what about client authentication? In the case of a web front end client, can I imagine storing an api_key and an api_secret in the env variables for the server that will be hosting this client? Because there use cases where the user is not logged but some services still needs to be available, but at the same time I only want my known clients (the web front and the mobile app) to be able to access those services (putting aside API Gateway solutions, and maybe other API Shields which would probably add another security layer against DOS etc.)
If the client logs in using Google/Facebook, the front app will receive an id_token that needs to be passed to the backend which would then verify the token ( https://developers.google.com/identity/sign-in/web/backend-auth ). In this particular case my OAuth API would not be used. Could please you confirm that it's the way it should be handled?
Many thanks.
EDIT 02/05/2022
Intro / Context
First thing first, Authorization is not Authentication.
Authentication is the process of verifying who a user is,
while authorization is the process of verifying what they have access to.
And like #Max said, OAuth is designed to manage Authorization and Open ID Connect (OIDC) is an extension of OAuth to manage Authentication on top of it.
The diagram I've exposed in my question is known in the OAuth world as the password grant, and, from the official documentation :
Because the client application has to collect the user's password and
send it to the authorization server, it is not recommended that this
grant be used at all anymore.
Authorization of my App (to get access to my APIs)
From the API perspective, we just want to ensure that the incoming requests are coming from the server that is hosting the App. So, in my case, it's simple machine-2-machine communication from backend server to backend server and there's no action required from the user. So, I must implement the Client Credentials Flow
...which would lead me to this flow:
https://drive.google.com/file/d/1qE9JpWRSRPa8z5iNxm7ocGkeT0E149Sv/view?usp=sharing (Feel free to comment / rectify )
Authentication of a user
Because OAuth knows nothing about authentication, I need an OIDC flow. The easiest one is based on the Authorization Code Flow with PKCE from OAuth below (only about authorization) ...
... but the difference is that we're passing an additional scope named openid in the authentication request (step 3), when the app performs the 2nd request to the token endpoint (step 7), the auth server returns an ID Token (which is a JWT containing user info in the payload -> authentication) in addition to the access_token (which do not contain user info but just "random string"). There's other OIDC flows with their pros & cons depending on the situation but it's another topic on its own (https://openid.net/specs/openid-connect-core-1_0.html)
User already identified by Google/Facebook
In case the client logs in using Google, the front app will receive an id_token. This token could be sent to the app server which would then make a request to the API Gateway, which then call the Auth api which would be in charge of verifying the token by calling the 3rd party auth server ( https://developers.google.com/identity/sign-in/web/backend-auth ).
In case of Facebook, we get back an access token, so I don't know how to deal with it ...
https://developers.facebook.com/docs/facebook-login/web
https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow
Using Firebase, there's an onAuthStateChanged callback, so from the App perspective it will prevent request without the user being logged in, but from the API perspective, it doesn't guaranty that a request is coming from a logged in user...
https://firebase.google.com/docs/auth/web/manage-users#get_the_currently_signed-in_user
Warning: the answer below is not complete, it only serves to give a rough idea
Intro
OAuth2 is a protocol for authorization.
Grant Types
Over the OAuth2 protocol, you can use one of the "grant types" or "flow", one of these flows is illustrated in the picture you post and named password grant.
Each of these flows is realized for different scenarios, you rightly point the attention to how securely store the password on a web app.
For example for a front-end authentication (javascript / wasm) you can use a PKCE Flow where the secret_id is not used.
Endpoints
On OAuth2 there are two primary enpoints
Authorize endpoint - where you obtain the authorization code
Token endpoint - where you exchange the authorization code for the token(s)
Tokens
There are two types of tokens on OAuth2
Access Token
Refresh Token
The definition of token on OAuth2 is "an opaque string", you don't need to read it.
The access token is used against the API, this token has an expiration date, when it is expired the system use the refresh token to get another access_token whitout user interaction, when the refresh_token expire the user must re-authenticate again.
You can read the content of the access_token (which is a JWT token) from the JWT.io
Scopes
The Access token has, on its body, the scopes (i.e. Read email, read name, etc).
Scope is a mechanism in OAuth 2.0 to limit an application's access to a user's account.
Identity
On top of the OAuth2 are build other protocols OIDC aka IdToken aka Open Id Connect is one of them, in other terms OIDC protocol use the OAuth2 for establish an Authentication.
With the OIDC there is another token the id_token, this token came with the user's info and is NOT used has Authorizization in front the API.
There are also OIDC flows you can use to get the id_token and/or the access_token.
Conclusion
I suggest you read about OAuth2 from the references below and try different flows using the playground
References
Which oauth2 flow should I use
OAuth2
PKCE in more depth
My advice is to start with the data, which is the deeper area of OAuth.
USE AN AUTHORIZATION SERVER
This will enable you to keep your code simple. It will also handle Google / Facebook and many other forms of authentication for you, with zero impact on your code. The Curity Community Edition is a free and developer friendly option, though there are others. eg Keycloak, Ory Hydra.
PROTECT DATA
OAuth primarily gives you modern ways to protect data. Use scopes and claims to protect data across multiple microservices in a zero trust manner while securely maintaining user context. You will also need to manage joining identity and business data.
IMPLEMENT UI FLOWS CORRECTLY
Mobile apps use the AppAuth pattern. The current best practice for browser based apps is a Backend for Frontend approach. Both of these are tricky.
KEEP CODE STANDARDS BASED
All of the above Curity resources are based on OAuth related standards. If followed your apps will stay simple, with portable code, that can also work with other providers.
OAuth is very architectural though, and the best designs take time to learn, but you can learn the intricacies gradually. Our IAM Primer is a good starting point.

Is nested authentication and authorization with JWT using both machine-to-machine and user-based auth a thing?

Explain it to me like I'm five.
Is it typical to use both machine to machine authentication alongside user-based authentication? Meaning: if I have a gateway or proxy which accepts user requests, and it verifies the JWT that come in with a user request prior to processing or forwarding the request to application servers is it normal, or a mis-use to expect to use a machine-to-machine JWT to ensure that requests arriving at the application servers originated from the gateway? And furthermore is it normal, or a mis-use to wrap, or nest the user's JWT within the machine-to-machine JWT when making the request to the application server?
Is it simply more typical to just have the gateway validate the JWT signature and claims and just forward it to the various application servers as needed?
Is the desire to nest JWTs in this fashion overkill, or some misuse / case of "you're holding it wrong"?
If you have a bunch of back end microservices being called via a gateway then it is usual for the original access token to be forwarded - this provides user context to your APIs so that they can authorize correctly.
A better use of the reverse proxy is to swap confidential tokens for those with rich claims - see the Phantom Token Approach for how this works.
Note also that it is recommended for each individual API to validate the JWT - this is often described as a Zero Trust Architecture, which protects against man in the middle exploits.

How to do authentication in microservices?

I had an application developed in monolith architecture. It had all the authentication and authorization needed.My application front end is in angular and back end is in nodejs. I have jwt token generated from front end. Now I started implement micro services for each module but I don't know how to implement authentication and authorization so that all the services can use it. I read different article on it but still unable to find solution.
Below 3 step process need to be implemented for authentication & authorization solution :
You can create auth micorservice using spring oauth 2.0 which takes care of user authentication and generating the JWT token after successful authentication, Here existing monolithic code can be utilized upto some extent.
Now, Using API Gateway design pattern, JWT token can be authorized in each API call at API gateway layer only, Means We can validate the JWT on API gateway itself before passing it to the backend microservices.
Further, From API Gateway forward the coming Bearer token to the backend microservices where method level authorization can be taken care of.

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.

Looking for JWT Auth microservice example

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.

Resources