I'm having a deeper look into OAuth 2.0 and especially the Client Credentials Flow that is intended to authenticate a client application or device without user interaction (the client is accessing its own resources, not acting on the behalf of a user).
The current best practices tell me to authenticate a client with an assertion token. In short: instead of a shared secret the client uses an assertion token to identify itself. This token is created by the client and signed using the private key of a certificate owned by the client. The authentication server needs to know the public key to validate this assession token.
Cyber security guidelines tell me to rotate / cycle secrets regularily. This means that I have to tell the authentication about the new public key.
What am I supposed to do when I want to rotate the secret?
The 2 obvious solutions are:
- The client calls the Client Configuration Endpoint at the authentication server and the server adds the new public key to the list of secrets valid for this client.
- The client publishes a jwks_endpoint and the server retrieves the new public key directly from the client when none of the registered keys are valid for the token (probably only once, the server can persist the new key in its database).
Both solutions have pros and cons. I cannot see any objectively "best" solution.
Are there any guidelines as to how secret rotation is best implemented? Have I overlooked a third solution?
My main concern is security. I cannot allow a malicious software to register its own secrets or jwks_endpoint at the authentication server or to replace a genuine client at a registered jwks_endpoint. But I cannot allow clients being locked out because their registered secrets expired and for whatever reasons they were not able to register a new secret in time, either.
If the most secure solution takes more time to implement, I'm willing to invest that time. Apart from that, I'd like to reduce network traffic and accelerate the authentication process as much as possible.
There's a related question on StackExchange from 2015 that didn't answer my question.