title: Error 403 when feign client makes POST/PUT request to micro-service
body: <p>I've got a legacy app in java that can't use spring cloud. It uses a feign client to access a microservice throug the gateway.</p>
  <p>Gateway and service are generated by jhipster 5.7.2 with OAuth2/OIDC option.</p>
  <p>In my client, the a RequestInterceptor calls keycloak in order to get a token (direct access grant) and injects it int he header.</p>
  <p>It's ok when I make a GET request, but I receive a 403 after POST or PUT request.</p>
  <p>CORS is enabled on the gateway (but isn't used because the request isn't a cors request). I run it in dev mode.
  Zuul routes seem to be ok.
  I didn't change the config neither on the gateway nor on the service.</p>
  <p>Does anybody have an idea ?</p>
  <p>Below my feign client :</p>
  <pre><code>public interface SmartDocumentClient {
  
  @RequestLine(&quot;GET /api/ebox/test&quot;)
  //@Headers(&quot;Content-Type: application/json&quot;)
  public ResponseEntity&lt;HasEboxResponse&gt; test();
  
  @RequestLine(&quot;POST /api/ebox/test&quot;)
  @Headers(&quot;Content-Type: application/json&quot;)
  public ResponseEntity&lt;HasEboxResponse&gt; testPost(HasEboxRequest request);
  
  @RequestLine(&quot;PUT /api/ebox/test&quot;)
  @Headers(&quot;Content-Type: application/json&quot;)
  public ResponseEntity&lt;HasEboxResponse&gt; testPut(HasEboxRequest request); }
  </code></pre>
  <p>My client config :</p>
  <pre><code>T client = Feign.builder()
              .contract(new feign.Contract.Default()) //annotation openfeign pour éviter bug d'upload avec SpringMvc
              .client(new OkHttpClient())
              .encoder(new FormEncoder(new GsonEncoder())) //pour gérer le formData
              .decoder(new ResponseEntityDecoder(new ResponseEntityDecoder(new CustomFileDecoder(new CustomGsonDecoder()))))
              .requestInterceptor(interceptor)
              .options(new Request.Options(timeout, timeout))
              .target(SmartDocumentClient, url);
  </code></pre>
  <p>The interceptor :</p>
  <pre><code>public class GedRequestInterceptor implements RequestInterceptor {
  
  public static final String AUTHORIZATION = &quot;Authorization&quot;;
  public static final String BEARER = &quot;Bearer&quot;;
  
  private String authUrl;
  private String user;
  private String password;
  private String clientId;
  private String clientSecret;
  
  private RestTemplate restTemplate;
  private CustomOAuth2ClientContext oAuth2ClientContext;
  
  public GedRequestInterceptor(String authUrl, String user, String password, String clientId, String clientSecret) {
      super();
      this.authUrl = authUrl;
      this.user = user;
      this.password = password;
      this.clientId = clientId;
      this.clientSecret = clientSecret;
      restTemplate = new RestTemplate();
      //oAuth2ClientContext = new DefaultOAuth2ClientContext();
  }
  
  @Override
  public void apply(RequestTemplate template) {
      // demander un token à keycloak et le joindre à la request
      Optional&lt;String&gt; token = getToken();
      if (token.isPresent()) {
          template.header(HttpHeaders.ORIGIN, &quot;localhost&quot;);
          template.header(AUTHORIZATION, String.format(&quot;%s %s&quot;, BEARER, token.get()));
      }
  }
  
  private Optional&lt;String&gt; getToken() {
      if (oAuth2ClientContext.getAccessToken() == null || oAuth2ClientContext.getAccessToken().isExpired()) {
          MultiValueMap&lt;String, String&gt; map = new LinkedMultiValueMap&lt;&gt;();
          map.add(&quot;client_id&quot;, this.clientId);
          map.add(&quot;client_secret&quot;, this.clientSecret);
          map.add(&quot;grant_type&quot;, &quot;password&quot;); // client_credentials //password
          map.add(&quot;username&quot;, this.user);
          map.add(&quot;password&quot;, this.password);
          oAuth2ClientContext.setAccessToken(askToken(map));
      } 
      
      if (oAuth2ClientContext.getAccessToken() != null){
          return Optional.ofNullable(oAuth2ClientContext.getAccessToken().getValue());
      } else {
          return Optional.empty();
      }
  }
  
  private CustomOAuth2AccessToken askToken( MultiValueMap&lt;String, String&gt; map) {
          HttpHeaders headers = new HttpHeaders();
          headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
          
          HttpEntity&lt;MultiValueMap&lt;String, String&gt;&gt; request = new HttpEntity&lt;&gt;(map, headers);
  
          ResponseEntity&lt;CustomOAuth2AccessToken&gt; response = restTemplate.postForEntity(
                  this.authUrl, request, CustomOAuth2AccessToken.class);
          
          if (response != null &amp;&amp; response.hasBody()) {
              return response.getBody();
          } else {
              return null;
          }
  }
  </code></pre>
  <p>}</p>
  <p>And finally the ressource :</p>
  <pre><code>    @RestController
  @RequestMapping(&quot;/api&quot;)
  public class DocumentResource {
  
          private static String TMP_FILE_PREFIX = &quot;smartdoc_tmp&quot;;
  
          public DocumentResource() {
          }
  
          @GetMapping(&quot;/ebox/test&quot;)
          public ResponseEntity&lt;HasEboxResponse&gt; test() {
                  return ResponseEntity.ok(new HasEboxResponse());
          }
  
          @PostMapping(&quot;/ebox/test&quot;)
          public ResponseEntity&lt;HasEboxResponse&gt; testPost(@RequestBody HasEboxRequest request) {
                  return ResponseEntity.ok(new HasEboxResponse());
          }
  
          @PutMapping(&quot;/ebox/test&quot;)
          public ResponseEntity&lt;HasEboxResponse&gt; testPut(@RequestBody HasEboxRequest request) {
                  return ResponseEntity.ok(new HasEboxResponse());
          }
  
  }
  </code></pre>
  <p>Thanks !</p>