Search code examples
javaoauthjwtopenid-connectspring-authorization-server

Multiple instances of Spring Authorization Server


I'm currently building a backend using Spring to build a backend and authenticating using Spring Authorization Server. After some trial and error, this is now working and authenticates the user with my resource server(s).

I have a problem running multiple instance of the authorization server, with multiple possible solutions.

For all intents and purposes, I do not want to use a third-party auth provider like Okta or Auth0, but I have no issues with using a service such as Keycloak, etc.

PROBLEM:

When running multiple instances of the Spring Authorization Server behind a gateway/load balancer, the resource servers can't always authenticate. This is because the resource servers only have knowledge of the well known configuration endpoint of one of the instances, not all. This sometimes prevents the resource server from getting the correct key to decode the JWT token.

POSSIBLE SOLUTIONS:

  1. Have the key generated externally and dynamically loaded into the authorization server, so all auth servers use the same key, rather than the key being generated when the server instance is started. If this is the ideal solution, then is there any standard implementation methods for such functionality.

  2. Allow the resource servers to check well known configuration and/or JWK Set URI endpoint of all auth server instances.

  3. Use a pre-generated key stored in a file system, which would make it difficult to implement key rotation later. Furthermore, as the key is file-based, it would need a file share which is added costs and introduces extra vulnerabilities into the configuration of the auth server and could potentially leave the key exposed through improper configuration or access using tools such as Kubectl or over FTP/SSH if a user where to gain access.

SUMMARY:

At this point, the easiest option seems to be option 3, but it does come with added resource costs and added security vulnerabilities which need to be addressed. The most secure option seems to be option 2, but this would be a nightmare to maintain and just doesn't seem ideal at this time.

I have a feeling that Keycloak or something similar may be able to help with this problem however, I do not have any experience with Keycloak so would not be able to confirm this until I have some experience with Keycloak. I wouldn't like to spend multiple days learning Keycloak and it's implementation details, if it would not be of any help to me at the moment.

I've tried the hard-coded file method and this does work. This method requires creating files containing the public/private key which introduces issues regarding security, along with added costs for storing the key files in a shared location. This also makes it difficult to implement key rotation in Spring auth server, which is one piece of functionality I would like to implement and keep.

I've tried adding multiple well-known configuration endpoints in list style to the spring resource server configurations but these aren't picked up. This method would require that all instance URIs remain the same or are updated each time an auth server instance is brought up or shut down. For this reason, I don't see this as a viable long-term solution.

QUESTION:

How does one normally implement running multiple instances of an Spring authentication service, whilst sharing the same key but also implementing key rotation?


Solution

  • Handling tokens issued by several authorization servers

    Yes, forcing all of the authorization servers to share a single key set would solve your problem for validating the tokens signature, but this really isn't your only option, and would not solve other validation issues (like checking the value of the iss claim in the tokens).

    Your possible solution n°2 looks like standard multi tenancy: instead of the default authentication manager configured by Spring Boot for a single issuer, you provide with your own. In your case, "static" multi-tenancy seems enough: all issuers can be listed in configuration and the right public signing key can be retrieved based on the iss claim in the JWT.

    The doc linked above contains information for building such an authentication manager by yourself. You could also consider using this additional starter I maintain: it makes possible to configure "static" multi-tenancy with just application properties.

    Another solution to consider is using a n+1 authorization servers model: you use a single authorization server (the +1) as reference for your clients and resource servers, and implement a "login with" any of the (n) other authorization servers from the master one. In this model, clients and resource servers are single tenant as it only see tokens from the master authorization server (you can use Spring Boot default authentication manager in your resource server).

    Keys rotation

    According to the Runtime Expectations in the manual:

    As the authorization server makes available new keys, Spring Security will automatically rotate the keys used to validate JWTs.

    JWT decoders are configured with an authorization server JWKS endpoint (either explicitly or extracted from the OpenID configuration when just an issuer URI is provided). It can fetch the new public key when the old expires.