We handle this the other way around - protect the APIs with OAuth, and require that the OAuth tokens embed a proof that the user exists. Then the microservices don't need to check.
For example, with this approach:
- a user logs in (in our case via SAML) and then clicks a link in the UI or whatever that initates the chain of microservices calls.
- the UI generates an OAuth token from the user's SAML assertion using the grant type as per RFC 7522 (the elegantly named "Security Assertion Markup Language (SAML) 2.0 Profile for OAuth 2.0 Client Authentication and Authorization Grants").
- our authorization server handles that grant type by creating an OAuth token which is a signed (tamper-proof) JWT containing the user id.
- that token can now be used to call microservices API(s), which can then extract the user id from the token, and can have trust that the user exists.
Re your specific case (A calls B), we handle this with a custom OAuth grant type (we call it "inherited").
This grant type creates an OAuth token with an embedded user identity by starting with a different OAuth token with embedded user identity.
This allows microservice calls to be chained together, e.g. when UI calls A calls B calls C:
- First UI generates an OAuth token using RFC 7522
- UI calls A passing the token
- A generates an OAuth token using the inherited grant type
- A calls B
- B generates an OAuth token using the inherited grant type
- etc.
In this whole chain of events, the user's identity has only been checked once.
All of the above is about a scenario where the microservices calls are ultimately initiated by a logged in user.
It could be that in your case the calls were a result of something else - maybe a batch job that performs some actions on behalf of users every midnight. In that case, there's no SAML assertion to start from. I'd suggest you could still use a similar approach though - embed user details in the OAuth token - even if RFC 7522 is not relevant.