0

I am working on a project where the server provide only an access token without the refresh token for some company needs. Referring to official docs, the following code will automatically refresh the access token when it's expired if a refresh token exists. if not it will retrieve a new one for every request.

Here is the optimal code :

@Bean
WebClient webClient(ClientRegistrationRepository clientRegistrations,
        OAuth2AuthorizedClientRepository authorizedClients) {
    ServletOAuth2AuthorizedClientExchangeFilterFunction oauth =
            new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
    // (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
    // oauth.setDefaultOAuth2AuthorizedClient(true);
    // (optional) set a default ClientRegistration.registrationId
    // oauth.setDefaultClientRegistrationId("client-registration-id");
    return WebClient.builder()
            .apply(oauth2.oauth2Configuration())
            .build();
}

Here is the code that i am using :

@Configuration
public class OauthEmployeConfig{

    /**
    ** ... String baseUrl, String accessUrl for the access token url
    **/

    @Bean
    public WebClient webClient(UserRegistration userRegistr) {

        ClientRequest clientRequest = ClientRequest
            .create(HttpMethod.POST, URI.create(accessUrl))
            .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
            .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
            .headers(headers -> headers.setBasicAuth(userRegistr.getClientId(), userRegistr.getClientSecret()))
            .body(BodyInserters.fromFormData("grant_type", userRegistr.getAuthorizGrantType())
                .with("scope", userRegistr.getScope().replaceAll(",", "")))
            .build();

        return WebClient.builder()
            .baseUrl(baseUrl)
            .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
            .filter((request, next) -> next.exchange(clientRequest)
                .flatMap(response -> response.body(org.springframework.security.oauth2.core.web.reactive.function.OAuth2BodyExtractors.oauth2AccessTokenResponse()))
                .map(accessToken -> accessToken.getAccessToken().getTokenValue())
                .map(token -> setBearer(request, token))
                .flatMap(next::exchange))
            .filter(logRequest())
            .filter(handleResponseError())
            .build();
    }

    private ClientRequest setBearer(ClientRequest request, String token) {
    return ClientRequest.from(request)
        .header("Authorization", "Bearer " + token).build();
    }


    private static ExchangeFilterFunction handleResponseError() {
        return ExchangeFilterFunction.ofResponseProcessor(
            response -> response.statusCode().isError()
                ? response.bodyToMono(String.class)
                    .flatMap(errorBody -> Mono.error(new RuntimeException(errorBody, response.statusCode())))
                : Mono.just(response));
    }

     private static ExchangeFilterFunction logRequest() {
        return ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
          clientRequest.headers().forEach((name, values) -> values.forEach(value -> LOG.info("{}={}", name, value)));
          return Mono.just(clientRequest);
        });
    }
}

I would like to know if there is an approach to get the access token once and keep it for every request while it's not expire, and when it's the case I get a new one!

Abder KRIMA
  • 3,418
  • 5
  • 31
  • 54
  • you have written a filter that will fetch the token on each request, so obviously the code will fetch a token on each request. Fetch the token manually, use it for a number of requests, when you get a 401 from the service, you fetch a new one. Storing a token as a global for all requests is bad practice, keeping a global state is bad practice in reactive programming, its also bad practice when it comes to scaling, and is a bottleneck in highly threaded environments. – Toerktumlare Apr 17 '20 at 09:18
  • I know if i store the token globally is a bad idea. So in my case , have you a simple example to solve my situation? Thank you! – Abder KRIMA Apr 17 '20 at 11:58
  • No because there is no ”simple solution”. I have no idea how your request are performed. Fetch a token on the initial request, performe the requests needed. Throw away the token. The tokens are supposed to be short lived, the shorter the better. – Toerktumlare Apr 17 '20 at 12:01
  • 1
    one way to achieve your goal is to put your access token in redis with TTL. make sure the TTL time you will set is the expiration time of the access token. Redis will delete the token once your set TTL time is over. so you don't have to worry about the expired token. Now, every time you need a token, ask redis. if redis return something then go with it. If it returns null then fetch new token and save it to redis again and go with the token. – Shoshi Apr 17 '20 at 19:44

0 Answers0