We have a simple requirement at this time to replace username/password authentication on our web services with authentication using a token. Our customers do not want their web services to be disrupted by password policy that requires changing a password every n months for example. We are doing work to migrate to using an IDP for SSO, but at this time we do not reference an IDP.
I was wondering whether this is a valid/secure implementation, and what issues people see arising from this:
User logs into our application. From their User profile, run a process that generates an RSA private/public key pair. The customer is provided a JWK representation of the full key (private and public), and the application stores the public key in the User profile.
The User stores their JWK in a secure location, and using a provided library, their Web Service calls create a JWT signed with their private key. They will be able to control the claims, expiration, etc. of the JWT, although the claims would have to match what is expected on the Web Service side.
The User runs the web service, inserting the JWT in the Authorization header (Bearer ). The server inspects the JWT to determine the User (I know this is not standard, as we should not be inspecting any part of the HWT before validating), looks up the public key based upon the User, and then validates the JWT using the public key, and assuming success, serves the request.
In this way, we can authenticate without a username/password, the customer maintains control over their credentials (they can generate a new JWK any time they wish), and we do not have the liability of storing/maintaining the User’s private key.
If this is an acceptable way to go, should we consider a way to issue an access token upon initial presentation of the JWT? Should we cache the JWT on the server after the initial authentication, so as to avoid the overhead of validating the JWT upon each web service call? Of course there would be issues if we include a nonce or sign part of the web service content, but I am assuming that using an access code would be vulnerable to the same issues.
The JWT being provided is already an access token so in general issuing another access token upon initial presentation of the JWT seems that it would not bring much to the table. However, it always depends on the exact details.
What you described is an authentication system based on bearer access tokens; however, the issuing of tokens is done in a custom way instead of following a framework like OAuth2. The overall security is tied to how the private key is communicated to the user, how the user stores that key and how you validate the token.
It seems that you’re interested in putting the responsibility of owning the private key into each user so although this is not a common approach it may work for you. It’s important that you also ensure that the fact that the user can control all the JWT payload does not put your system at a risk of being abused. For example, you can’t make any access control decisions based on the content (with the possible exception of the user identifier as that is validated by having the public key) of the token because that is fully controlled by the user.
In conclusion, you’re rolling your own bearer token authentication system/protocol instead of reusing something publicly available and publicly reviewed (think OAuth2). This implies that you may forget something fundamental that leaves the entire system at risk so the general recommendation is to stick to available frameworks/protocols.
As a final suggestion I would suggest you to go through the available documentation for API Authorization and to consider the use of OAuth2 end-user based flows to get access tokens meant to call resource servers (aka API’s).
Thanks for your thoughtful response. I think this confirms my notion that this is a reasonable approach for authentication to replace BASIC username/password authentication, similar to certificate sharing. Once we move to SSO and OAuth2/OIDC, we may change the way these JWT are generated so they come from a server.
Currently any user of our system is able to use their credentials to call our web services, so this does not represent a loosening or expansion of control. We won’t pay much attention to the claims (since we don’t have control), other than requiring the use of standard values.
Thanks for your thoughtful response. I think this confirms my notion that this is a reasonable approach for authentication to replace BASIC username/password authentication, similar to certificate sharing. Once we move to SSO and OAuth2/OIDC, we may change the way these JWT are generated so they come from a server.
Currently any user of our system is able to use their credentials to call our web services, so this does not represent a loosening or expansion of control. We won’t pay much attention to the claims (since we don’t have control), other than requiring the use of standard values.