public TokenResponse exchangePkceCode(
      URI tokenEndpoint, String code, String redirectUri, String clientId, String codeVerifier) {

    var body =
        UrlFormBodyBuilder.create()
            .param("grant_type", "authorization_code")
            .param("redirect_uri", redirectUri)
            .param("client_id", clientId)
            .param("code", code)
            .param("code_verifier", codeVerifier)
            .build();

    var headers =
        List.of(
            new Header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON),
            new Header(HttpHeaders.CONTENT_TYPE, UrlFormBodyBuilder.MEDIA_TYPE));

    var req = new Request(tokenEndpoint, "POST", headers, body);

    var res = httpClient.call(req);
    if (res.status() != 200) {
      throw HttpExceptions.httpFailBadStatus(req.method(), tokenEndpoint, res.status());
    }

    return JsonCodec.readValue(res.body(), TokenResponse.class);
  }
@Test
  void exchangePkceCode_badStatus(WireMockRuntimeInfo wm) {
    var path = "/auth/token";
    stubFor(post(path).willReturn(badRequest()));

    var base = URI.create(wm.getHttpBaseUrl());

    var tokenEndpoint = base.resolve(path);

    var e =
        assertThrows(
            HttpException.class,
            () -> client.exchangePkceCode(tokenEndpoint, null, null, null, null));

    assertEquals(
        "http request failed: bad status 'POST %s' status=400".formatted(tokenEndpoint),
        e.getMessage());
  }