springspring-bootspring-securityhexagonal-architecture

Spring Boot: Use incoming access token for outgoing requests. (Internally pass access token from RestController to Component.)


I am wondering what is the best way in a Spring Boot application to internally pass an access token from an incoming request through the different layers, before using it in an outgoing request?

The situation is the following: I am developing a Webapp (React) and Service A (Spring Boot). Authentication is handled using Keycloak.

enter image description here

Depending on the use case Service A has to make a request to Service B (plain Java). Service B is completely outside of my control. All I got from it is an OpenAPI spec for the API. (I can also not change this OpenAPI spec.)

Service B must be called with an access token issued to the user that made the original request to Service A. (Therefore client credential flow for inter service communication cannot be used.)

The code of Service A is organised according to Hexagonal Architecture. The request is received by a controller (which extracts the access token) and than passed to a service which makes a request to Service B.

enter image description here

I cannot call Service B from the Webapp directly.

Currently I'm using the naive approach to just pass the access token as a method parameter around. But it doesn't feel right to pollute all methods with a parameter that is not related to the business logic of the application.

Is there a better way to internally pass the access token around? Extracting it in the AccountController and have access to it in the AccountAdapterRest?


Solution

  • The "incoming" request authorization should be accessible from Spring security-context.

    In case of OAuth2, this request is authorized with a Bearer token and the Bearer string is stored in the default Authentication implementations (JwtAuthenticationToken when using a JWT decoder and BearerTokenAuthentication when using introspection).

    You can retrieve it from anywhere as follow:

    static Optional<JwtAuthenticationToken> getCurrentRequestAuthentication() {
        if(SecurityContextHolder.getContext().getAuthentication() instanceof JwtAuthenticationToken jwtAuth) {
            return Optional.of(jwtAuth);
        }
        return Optional.empty();
    }
    
    static Optional<String> getBearerToken() {
        return getJwtAuthentication().map(JwtAuthenticationToken::getToken).map(Jwt::getTokenValue);
    }
    

    As a side note, it is not quite recommended anymore to have Javascript based applications be configured as OAuth2 public clients. You should have a look at the BFF pattern where such application never access the tokens: it is secured with session on a middleware on the server which can be configured as a confidential client, retrieve and store tokens in a secured place and replace session cookie with OAuth2 access token before forwarding a request to the resource server(s). I have written a tutorial on Baeldung with spring-cloud-gateway configured as a BFF.