@@ -52,6 +52,12 @@ public class KeyFlowAuthenticator implements Authenticator {
5252 /**
5353 * Creates the initial service account and refreshes expired access token.
5454 *
55+ * <p>NOTE: It's normal that 2 requests are sent, it's regular OkHttp Authenticator behavior.
56+ * The first request is always attempted without the authenticator and in case the response is
57+ * Unauthorized(=401), OkHttp reattempt the request with the authenticator. See <a
58+ * href="https://square.github.io/okhttp/recipes/#handling-authentication-kt-java">OkHttp
59+ * Docs</a>
60+ *
5561 * @deprecated use constructor with OkHttpClient instead to prevent resource leaks. Will be
5662 * removed in April 2026.
5763 * @param cfg Configuration to set a custom token endpoint and the token expiration leeway.
@@ -65,6 +71,12 @@ public KeyFlowAuthenticator(CoreConfiguration cfg, ServiceAccountKey saKey) {
6571 /**
6672 * Creates the initial service account and refreshes expired access token.
6773 *
74+ * <p>NOTE: It's normal that 2 requests are sent, it's regular OkHttp Authenticator behavior.
75+ * The first request is always attempted without the authenticator and in case the response is
76+ * Unauthorized(=401), OkHttp reattempt the request with the authenticator. See <a
77+ * href="https://square.github.io/okhttp/recipes/#handling-authentication-kt-java">OkHttp
78+ * Docs</a>
79+ *
6880 * @deprecated use constructor with OkHttpClient instead to prevent resource leaks. Will be
6981 * removed in April 2026.
7082 * @param cfg Configuration to set a custom token endpoint and the token expiration leeway.
@@ -81,6 +93,12 @@ public KeyFlowAuthenticator(
8193 /**
8294 * Creates the initial service account and refreshes expired access token.
8395 *
96+ * <p>NOTE: It's normal that 2 requests are sent, it's regular OkHttp Authenticator behavior.
97+ * The first request is always attempted without the authenticator and in case the response is
98+ * Unauthorized(=401), OkHttp reattempt the request with the authenticator. See <a
99+ * href="https://square.github.io/okhttp/recipes/#handling-authentication-kt-java">OkHttp
100+ * Docs</a>
101+ *
84102 * @param httpClient OkHttpClient object
85103 * @param cfg Configuration to set a custom token endpoint and the token expiration leeway.
86104 */
@@ -91,6 +109,12 @@ public KeyFlowAuthenticator(OkHttpClient httpClient, CoreConfiguration cfg) thro
91109 /**
92110 * Creates the initial service account and refreshes expired access token.
93111 *
112+ * <p>NOTE: It's normal that 2 requests are sent, it's regular OkHttp Authenticator behavior.
113+ * The first request is always attempted without the authenticator and in case the response is
114+ * Unauthorized(=401), OkHttp reattempt the request with the authenticator. See <a
115+ * href="https://square.github.io/okhttp/recipes/#handling-authentication-kt-java">OkHttp
116+ * Docs</a>
117+ *
94118 * @param httpClient OkHttpClient object
95119 * @param cfg Configuration to set a custom token endpoint and the token expiration leeway.
96120 * @param saKey Service Account Key, which should be used for the authentication
@@ -129,6 +153,9 @@ protected KeyFlowAuthenticator(
129153
130154 @ Override
131155 public Request authenticate (Route route , @ NotNull Response response ) throws IOException {
156+ if (response .request ().header ("Authorization" ) != null ) {
157+ return null ; // Give up, we've already attempted to authenticate.
158+ }
132159 String accessToken ;
133160 try {
134161 accessToken = getAccessToken ();
0 commit comments