Search code examples
javaspringspring-bootspring-security

Handling Spring Security Oauth2 on a multiple services


I have an auth service that uses oauth2 along with keycloak. Then i have 2 different services that both need to know which user is accessing them (directly). Right now i have these options:

  1. Auth service sets a jsession cookie by default. So it is stateful. I haven't found a way to make it fully stateless as it needs to save the code from oauth2. I will extract jwt token from it and set it as another cookie. Then i will set my other 2 services as stateless and call them using Bearer token. That works. I found this to be slower, as on every call my services will verify the token with keycloak. I also won't be able to make calls to auth service using Bearer token as it expects cookie-based session.

  2. I share my session via redis for every service that i need to. That way i can access all of my services (on same domain) without any need for setting my headers. I can also make internal calls to other services using that same cookie sent from client. And i found it to be much faster. However im not aware of consequences that may lead to.

  3. Make other services stateless, dont use security in them, route all my requests through gateway, gateway will convert my session cookie into jwt internally, services will simply decode jwt's as they are always signed by keycloak already since its routed through gateway.

I was wondering what is a common scenario for you guys? Is it a common practice to share sessions? And what scenario is preferable?


Solution

  • Option 3 is almost the way I'd go: you should keep resources access control on the resources servers which is responsible for keeping it. This resource servers know about the resource internal and business rules. Also, it's pretty easy to test access control rules on resource servers.

    As you pointed out in your edit, if access tokens are JWTs, then resource servers do not need a round trip to the authorization server for each request. A request from resource server to authorization server is required only to fetch tokens public signing key (at startup and each time it is rotated). Tokens are then validated locally by the resource server, with this key.

    OAuth2 is thought to work as follow:

    • OAuth2 authorisation server have state (user authentication status)
    • OAuth2 clients have state (store tokens)
    • OAuth2 resource server can (should?) be stateless (receive token with each request)

    The sessions on authorization server and client(s) are different things and the authorization server should not share its sessions with other actors (but you could use spring-session to share session between clients).

    Latest recommendations are to use only "private" OAuth2 clients, which means clients running on server you trust (no, the OAuth2 client should not be a single page or mobile application).

    For single page (Angular, React, Vue, ...) and mobile applications, Spring Security team encourages the use of the BFF pattern. I wrote a tutorial for that using spring-cloud-gateway with the TokenRelay= filter.

    If you are using a server-side rendering engine (Thymeleaf, JSF, ...), then the MVC application can be configured as confidential client and directly send requests (authorized with access tokens) to resource servers. I wrote another tutorial for that. In this tutorial, the resource server and client are merged in a single application but, as the security requirements are very different, each has its own SecurityFilterChain bean.