We are trying to update our web application stack to leverage ServiceStack auth throughout. Our MVC web app currently has its own user authentication (regrettably still using Membership) and then authenticates calls against our restricted back-end Servicestack microservices using server-server tokens (generated using IdentityServer 4). In addition to calls from the web application, these microservices receive calls from batch processes (Windows Services), which also perform a server-server auth. The microservices are not network isolated and thus the server-server auth ensures that only authorized clients are able to make API calls.
We are trying to simplify and standardize our architecture and use ServiceStack user auth on the front-end and then pass the authenticated session down through the ServiceInterface layer into our back-end microservices using JWT tokens. In addition to simplification, we are seeking to have the user session context available in the microservices layer for business logic (we do this using our own mechanism now). The new approach is working great in prototyping, but it now makes the back-end services accessible to any authenticated user.
Is there a recommended/standard approach for using JWT to pass user sessions across microservice boundaries, while simultaneously restricting access to back-end microservices to only internal authorized clients (other than physical network restriction)? Is this a violation of the basic JWT principles? I assume the Windows Services would authenticate using a standard mechanism (credentials, API Key, etc.) and use the resulting JWT tokens to make calls. If there are any recommendations for best practices here that would be helpful too. Thanks in advance for any suggestions!
I don’t know of any standards/specs when it comes to this kind of design, but publicly exposed only designed for server to server auth commonly use some kind of shared secret either as API key or signing.
Your ‘backend’ microservice JwtAuthProvider could use a custom ValidateToken to check for either a straight secret or a signed string that could be validated on the request and your other services could inject this in a custom header.
If you’re microservices are many with additional rules around who can call what, you might need to flesh out this approach further to make it easier to manage, but if you can get away with a single shared key/secret, that would be easier to test/prototype at least.
As you’ve pointed out, network separation is likely a more straight forward way of doing this but could lead to unknown access if those network rules changed in the future. Some kind of custom signing of the request between backends is pretty common with webhooks for example and similar could be applied here outside the JWT payload. I’m not as familiar with the JWT spec, so there could be a more standardized way of doing it but hope this has helped in some way at least.
Yeah using an API Key for protected B2B integrations is pretty common where you wouldn’t want it to expire and be able to revoke manually.
If you want to use JWT’s you’d typically use the JWT aud (Audience) Claim for embedding the purpose and capabilities of the JWT token. So your backend server APIs might be configured with and require a backend audience (i.e. JwtAuthProvider.Audience) that your IdentityServer would include when issuing the token to control whether the user is able to access your backend APIs.
Thank you, Demis and Darren - I appreciate the feedback! I was looking at a similar approach of providing a special key in the JWT token, to be validated by back-end services, but wasnt sure if it was hacking around the intended structure of JWT. I’m glad to hear this is actually a reasonable approach.