To pitch my own project, OpenPubkey[0], it is designed for exactly this use case. OpenPubkey let's you add a public key to an ID Token (JWT) without needing any change at the IDP.
1. Alice generates an ephemeral key pair (if she is using a browser she can generate the key pair as a "non-extractable key"[1]).
2. Alice gets ID Token issued by Google that commits to their public key,
3. Alice signs her API request data to Service A and sends her ID Token to Service A.
4. Service A checks the ID Token (JWT) is issued by Google and that the identity (alice@gmail.com) is authorized to make this API call, then it extracts Alice's public key from the ID Token and verifies the signature on the data in the API call. Then it passes the signed data to Service B.
5. Service B verifies everything again including that the data is validly signed by Alice. Service B could then write this data and its cryptographic prominence into the database.
Technically OpenPubkey uses a JWS, but it is a JWS composed of a JWT (ID Token) with additional signatures. OpenPubkey signed messages, like the ones passed via the API are also JWS.
I'm working on a system where each service in the path adds their signatures to the signed message so you can cryptographically enforce that messages must pass through particular services and then check that at during the database write or read. Using signature aggregation, you don't get a linear increase in verification cost as the number of signatures increase. It doesn't seem to add much overhead to service meshes since they are already standing up and tearing down mTLS tunnels.
The main question to me is how much autonomy do you want to give to your services. There are cases in which you want services to query each other without those services having to prove that the call originated from a specific authorized user.
1. Alice generates an ephemeral key pair (if she is using a browser she can generate the key pair as a "non-extractable key"[1]).
2. Alice gets ID Token issued by Google that commits to their public key,
3. Alice signs her API request data to Service A and sends her ID Token to Service A.
4. Service A checks the ID Token (JWT) is issued by Google and that the identity (alice@gmail.com) is authorized to make this API call, then it extracts Alice's public key from the ID Token and verifies the signature on the data in the API call. Then it passes the signed data to Service B.
5. Service B verifies everything again including that the data is validly signed by Alice. Service B could then write this data and its cryptographic prominence into the database.
Technically OpenPubkey uses a JWS, but it is a JWS composed of a JWT (ID Token) with additional signatures. OpenPubkey signed messages, like the ones passed via the API are also JWS.
I'm working on a system where each service in the path adds their signatures to the signed message so you can cryptographically enforce that messages must pass through particular services and then check that at during the database write or read. Using signature aggregation, you don't get a linear increase in verification cost as the number of signatures increase. It doesn't seem to add much overhead to service meshes since they are already standing up and tearing down mTLS tunnels.
The main question to me is how much autonomy do you want to give to your services. There are cases in which you want services to query each other without those services having to prove that the call originated from a specific authorized user.
[0]: https://github.com/openpubkey/openpubkey
[1]: https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey/e...