Ver código fonte

CreateApiKey response now returns the base64 encoded credentials (#77351)

This PR adds a new "encoded" field to the response of CreateApiKey API.
It is the base64 encoded value of "id:api_key". The field is added for
the convenience of the API consumers so that no extra
computation/encoding is needed. Sometimes the extra computation/encoding
can be error prone or not feasible for simple clients.

Resolves: #50235
Yang Wang 4 anos atrás
pai
commit
00c3b5c8d9

+ 20 - 2
client/rest-high-level/src/main/java/org/elasticsearch/client/security/CreateApiKeyResponse.java

@@ -15,7 +15,9 @@ import org.elasticsearch.common.xcontent.ConstructingObjectParser;
 import org.elasticsearch.common.xcontent.XContentParser;
 
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.time.Instant;
+import java.util.Base64;
 import java.util.Objects;
 
 import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
@@ -53,6 +55,10 @@ public final class CreateApiKeyResponse {
         return key;
     }
 
+    public SecureString getEncoded() {
+        return new SecureString(encode(id, key).toCharArray());
+    }
+
     @Nullable
     public Instant getExpiration() {
         return expiration;
@@ -78,14 +84,26 @@ public final class CreateApiKeyResponse {
                 && Objects.equals(expiration, other.expiration);
     }
 
+    private static String encode(CharSequence id, CharSequence key) {
+        return Base64.getEncoder().encodeToString((id + ":" + key).getBytes(StandardCharsets.UTF_8));
+    }
+
     static final ConstructingObjectParser<CreateApiKeyResponse, Void> PARSER = new ConstructingObjectParser<>("create_api_key_response",
-            args -> new CreateApiKeyResponse((String) args[0], (String) args[1], new SecureString((String) args[2]),
-                    (args[3] == null) ? null : Instant.ofEpochMilli((Long) args[3])));
+        args -> {
+            final String id = (String) args[1];
+            final String key = (String) args[2];
+            if (args[4] != null && false == args[4].equals(encode(id, key))) {
+                throw new IllegalArgumentException("the encoded value does not match id and api_key");
+            }
+            return new CreateApiKeyResponse((String) args[0], id, new SecureString(key.toCharArray()),
+                    (args[3] == null) ? null : Instant.ofEpochMilli((Long) args[3]));
+    });
     static {
         PARSER.declareString(constructorArg(), new ParseField("name"));
         PARSER.declareString(constructorArg(), new ParseField("id"));
         PARSER.declareString(constructorArg(), new ParseField("api_key"));
         PARSER.declareLong(optionalConstructorArg(), new ParseField("expiration"));
+        PARSER.declareString(optionalConstructorArg(), new ParseField("encoded"));
     }
 
     public static CreateApiKeyResponse fromXContent(XContentParser parser) throws IOException {

+ 14 - 4
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java

@@ -2052,11 +2052,11 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             // end::create-api-key-execute
 
             // tag::create-api-key-response
-            SecureString apiKey = createApiKeyResponse.getKey(); // <1>
+            SecureString encoded = createApiKeyResponse.getEncoded(); // <1>
             Instant apiKeyExpiration = createApiKeyResponse.getExpiration(); // <2>
             // end::create-api-key-response
             assertThat(createApiKeyResponse.getName(), equalTo(name));
-            assertNotNull(apiKey);
+            assertNotNull(encoded);
             assertNotNull(apiKeyExpiration);
         }
 
@@ -2093,6 +2093,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             assertNotNull(future.get(30, TimeUnit.SECONDS));
             assertThat(future.get().getName(), equalTo(name));
             assertNotNull(future.get().getKey());
+            assertNotNull(future.get().getEncoded());
             assertNotNull(future.get().getExpiration());
         }
     }
@@ -2146,11 +2147,11 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             // end::grant-api-key-execute
 
             // tag::grant-api-key-response
-            SecureString apiKey = apiKeyResponse.getKey(); // <1>
+            SecureString encoded = apiKeyResponse.getEncoded(); // <1>
             Instant apiKeyExpiration = apiKeyResponse.getExpiration(); // <2>
             // end::grant-api-key-response
             assertThat(apiKeyResponse.getName(), equalTo(name));
-            assertNotNull(apiKey);
+            assertNotNull(encoded);
             assertNotNull(apiKeyExpiration);
 
             apiKeyVerifier.accept(apiKeyResponse);
@@ -2194,6 +2195,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             assertNotNull(future.get(30, TimeUnit.SECONDS));
             assertThat(future.get().getName(), equalTo(name));
             assertNotNull(future.get().getKey());
+            assertNotNull(future.get().getEncoded());
             assertNotNull(future.get().getExpiration());
 
             apiKeyVerifier.accept(future.get());
@@ -2213,6 +2215,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
         CreateApiKeyResponse createApiKeyResponse1 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
         assertThat(createApiKeyResponse1.getName(), equalTo("k1"));
         assertNotNull(createApiKeyResponse1.getKey());
+        assertNotNull(createApiKeyResponse1.getEncoded());
 
         final ApiKey expectedApiKeyInfo = new ApiKey(createApiKeyResponse1.getName(), createApiKeyResponse1.getId(), Instant.now(),
             Instant.now().plusMillis(expiration.getMillis()), false, "test_user", "default_file", metadata);
@@ -2369,6 +2372,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
         CreateApiKeyResponse createApiKeyResponse1 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
         assertThat(createApiKeyResponse1.getName(), equalTo("k1"));
         assertNotNull(createApiKeyResponse1.getKey());
+        assertNotNull(createApiKeyResponse1.getEncoded());
 
         {
             // tag::invalidate-api-key-id-request
@@ -2413,6 +2417,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             CreateApiKeyResponse createApiKeyResponse2 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
             assertThat(createApiKeyResponse2.getName(), equalTo("k2"));
             assertNotNull(createApiKeyResponse2.getKey());
+            assertNotNull(createApiKeyResponse2.getEncoded());
 
             // tag::invalidate-api-key-name-request
             InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyName(createApiKeyResponse2.getName(),
@@ -2437,6 +2442,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             CreateApiKeyResponse createApiKeyResponse3 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
             assertThat(createApiKeyResponse3.getName(), equalTo("k3"));
             assertNotNull(createApiKeyResponse3.getKey());
+            assertNotNull(createApiKeyResponse3.getEncoded());
 
             // tag::invalidate-realm-api-keys-request
             InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingRealmName("default_file");
@@ -2460,6 +2466,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             CreateApiKeyResponse createApiKeyResponse4 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
             assertThat(createApiKeyResponse4.getName(), equalTo("k4"));
             assertNotNull(createApiKeyResponse4.getKey());
+            assertNotNull(createApiKeyResponse4.getEncoded());
 
             // tag::invalidate-user-api-keys-request
             InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingUserName("test_user");
@@ -2483,6 +2490,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             CreateApiKeyResponse createApiKeyResponse5 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
             assertThat(createApiKeyResponse5.getName(), equalTo("k5"));
             assertNotNull(createApiKeyResponse5.getKey());
+            assertNotNull(createApiKeyResponse5.getEncoded());
 
             // tag::invalidate-user-realm-api-keys-request
             InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingRealmAndUserName("default_file", "test_user");
@@ -2508,6 +2516,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             CreateApiKeyResponse createApiKeyResponse6 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
             assertThat(createApiKeyResponse6.getName(), equalTo("k6"));
             assertNotNull(createApiKeyResponse6.getKey());
+            assertNotNull(createApiKeyResponse6.getEncoded());
 
             InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(createApiKeyResponse6.getId(), false);
 
@@ -2551,6 +2560,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             CreateApiKeyResponse createApiKeyResponse7 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
             assertThat(createApiKeyResponse7.getName(), equalTo("k7"));
             assertNotNull(createApiKeyResponse7.getKey());
+            assertNotNull(createApiKeyResponse7.getEncoded());
 
             // tag::invalidate-api-keys-owned-by-authenticated-user-request
             InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.forOwnedApiKeys();

+ 7 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/security/CreateApiKeyResponseTests.java

@@ -19,8 +19,10 @@ import org.elasticsearch.test.ESTestCase;
 import org.elasticsearch.test.EqualsHashCodeTestUtils;
 
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.util.Arrays;
+import java.util.Base64;
 
 import static org.hamcrest.Matchers.equalTo;
 
@@ -31,6 +33,7 @@ public class CreateApiKeyResponseTests extends ESTestCase {
         final String name = randomAlphaOfLength(5);
         final SecureString apiKey = UUIDs.randomBase64UUIDSecureString();
         final Instant expiration = randomBoolean() ? null : Instant.ofEpochMilli(10000);
+        final String encoded = Base64.getEncoder().encodeToString((id + ":" + apiKey).getBytes(StandardCharsets.UTF_8));
 
         final XContentType xContentType = randomFrom(XContentType.values());
         final XContentBuilder builder = XContentFactory.contentBuilder(xContentType);
@@ -44,6 +47,9 @@ public class CreateApiKeyResponseTests extends ESTestCase {
         } finally {
             Arrays.fill(charBytes, (byte) 0);
         }
+        if (randomBoolean()) {
+            builder.field("encoded", encoded);
+        }
         builder.endObject();
         BytesReference xContent = BytesReference.bytes(builder);
 
@@ -51,6 +57,7 @@ public class CreateApiKeyResponseTests extends ESTestCase {
         assertThat(response.getId(), equalTo(id));
         assertThat(response.getName(), equalTo(name));
         assertThat(response.getKey(), equalTo(apiKey));
+        assertThat(response.getEncoded().toString(), equalTo(encoded));
         if (expiration != null) {
             assertThat(response.getExpiration(), equalTo(expiration));
         }

+ 1 - 1
docs/java-rest/high-level/security/create-api-key.asciidoc

@@ -36,5 +36,5 @@ expiration.
 --------------------------------------------------
 include-tagged::{doc-tests-file}[{api}-response]
 --------------------------------------------------
-<1> the API key that can be used to authenticate to Elasticsearch.
+<1> the API key credentials that can be used to authenticate to Elasticsearch.
 <2> expiration if the API keys expire

+ 13 - 51
x-pack/docs/en/rest-api/security/create-api-keys.asciidoc

@@ -133,60 +133,38 @@ API key information.
 [source,console-result]
 ----
 {
-  "id":"VuaCfGcBCdbkQm-e5aOx",       <1>
-  "name":"my-api-key",
-  "expiration":1544068612110,        <2>
-  "api_key":"ui2lp2axTNmsyakw9tvNnw" <3>
+  "id": "VuaCfGcBCdbkQm-e5aOx",        <1>
+  "name": "my-api-key",
+  "expiration": 1544068612110,         <2>
+  "api_key": "ui2lp2axTNmsyakw9tvNnw", <3>
+  "encoded": "VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw=="  <4>
 }
 ----
 // TESTRESPONSE[s/VuaCfGcBCdbkQm-e5aOx/$body.id/]
 // TESTRESPONSE[s/1544068612110/$body.expiration/]
 // TESTRESPONSE[s/ui2lp2axTNmsyakw9tvNnw/$body.api_key/]
+// TESTRESPONSE[s/VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==/$body.encoded/]
 <1> Unique `id` for this API key
 <2> Optional expiration in milliseconds for this API key
 <3> Generated API key
+<4> API key credentials which is the Base64-encoding of the UTF-8 
+representation of the `id` and `api_key` joined by a colon (`:`).
 
 To use the generated API key, send a request with an `Authorization` header that
-contains an `ApiKey` prefix followed by the API key credentials. The credentials
-are a Base64-encoded string in UTF-8 format that you create by combining the
-`id` and `api_key` with a colon (`:`). For example:
+contains an `ApiKey` prefix followed by the API key credentials
+(the `encoded` value from the response).
 
 [source,shell]
 ----
-curl -H "Authorization: ApiKey <credentials>" \
+curl -H "Authorization: ApiKey VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==" \
 http://localhost:9200/_cluster/health\?pretty <1>
 ----
 // NOTCONSOLE
 <1> If your node has `xpack.security.http.ssl.enabled` set to `true`, then you
 must specify `https` when creating your API key
 
-.Use UTF-8 encoding
-****
-When converting the concatenated String of `id` and `api_key` into bytes, the
-format must be UTF-8. Authentication will fail if you use UTF-16 or UTF-32
-encoding.
-
-If you're concatenating `id` and `api_key` and then getting the bytes of that
-String from the command line (like in <<concat-api-key,this example>>), the
-`echo` command defaults to ASCII formatting, which is equivalent to UTF-8
-encoding.
-
-However, some other tools require an explicit encoding when converting a String
-into bytes. For example, in Java, you might use something like the following
-code, which assumes that `result` is the response from the create API key API.
-This conversion ensures that the bytes of the concatenated string is in UTF-8
-format:
-
-[source,java]
-----
-var bytes = (result.id + ":" + result.api_key).getBytes(StandardCharsets.UTF_8);
-var header = "ApiKey " + Base64.getEncoder().encodeToString(bytes);
-----
-****
-
-On a Unix-like system, the following command combines the `id` and `api_key`
-from the previous response. The concatenation of these parameters should be in
-UTF-8 format:
+On a Unix-like system, the `encoded` value can be created with the following
+command:
 
 [[concat-api-key]]
 [source,shell]
@@ -195,19 +173,3 @@ echo -n "VuaCfGcBCdbkQm-e5aOx:ui2lp2axTNmsyakw9tvNnw" | base64 <1>
 ----
 <1> Use `-n` so that the `echo` command doesn't print the trailing newline
 character
-
-The command outputs a Base64-encoded String:
-
-[source,shell]
-----
-VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==
-----
-
-Use this String in a request to authenticate with your cluster:
-
-[source,shell]
-----
-curl -H "Authorization: ApiKey VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==" \
-http://localhost:9200/_cluster/health\?pretty
-----
-// NOTCONSOLE

+ 3 - 1
x-pack/docs/en/rest-api/security/get-api-keys.asciidoc

@@ -78,11 +78,13 @@ API key information. For example:
 {
   "id":"VuaCfGcBCdbkQm-e5aOx",
   "name":"my-api-key",
-  "api_key":"ui2lp2axTNmsyakw9tvNnw"
+  "api_key":"ui2lp2axTNmsyakw9tvNnw",
+  "encoded": "VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw=="
 }
 --------------------------------------------------
 // TESTRESPONSE[s/VuaCfGcBCdbkQm-e5aOx/$body.id/]
 // TESTRESPONSE[s/ui2lp2axTNmsyakw9tvNnw/$body.api_key/]
+// TESTRESPONSE[s/VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==/$body.encoded/]
 
 You can use the following example to retrieve the API key by ID:
 

+ 5 - 3
x-pack/docs/en/rest-api/security/invalidate-api-keys.asciidoc

@@ -82,13 +82,15 @@ API key information. For example:
 [source,console-result]
 --------------------------------------------------
 {
-  "id":"VuaCfGcBCdbkQm-e5aOx",
-  "name":"my-api-key",
-  "api_key":"ui2lp2axTNmsyakw9tvNnw"
+  "id": "VuaCfGcBCdbkQm-e5aOx",
+  "name": "my-api-key",
+  "api_key": "ui2lp2axTNmsyakw9tvNnw",
+  "encoded": "VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw=="
 }
 --------------------------------------------------
 // TESTRESPONSE[s/VuaCfGcBCdbkQm-e5aOx/$body.id/]
 // TESTRESPONSE[s/ui2lp2axTNmsyakw9tvNnw/$body.api_key/]
+// TESTRESPONSE[s/VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==/$body.encoded/]
 
 The following example invalidates the API key identified by specified `ids`
 immediately:

+ 3 - 1
x-pack/docs/en/rest-api/security/query-api-key.asciidoc

@@ -185,11 +185,13 @@ API key information. For example:
 {
   "id": "VuaCfGcBCdbkQm-e5aOx",
   "name": "application-key-1",
-  "api_key": "ui2lp2axTNmsyakw9tvNnw"
+  "api_key": "ui2lp2axTNmsyakw9tvNnw",
+  "encoded": "VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw=="
 }
 ----
 // TESTRESPONSE[s/VuaCfGcBCdbkQm-e5aOx/$body.id/]
 // TESTRESPONSE[s/ui2lp2axTNmsyakw9tvNnw/$body.api_key/]
+// TESTRESPONSE[s/VnVhQ2ZHY0JDZGJrUW0tZTVhT3g6dWkybHAyYXhUTm1zeWFrdzl0dk5udw==/$body.encoded/]
 
 Use the information from the response to retrieve the API key by ID:
 

+ 4 - 0
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/CreateApiKeyResponse.java

@@ -20,8 +20,10 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
 
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.time.Instant;
 import java.util.Arrays;
+import java.util.Base64;
 import java.util.Objects;
 
 import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
@@ -40,6 +42,7 @@ public final class CreateApiKeyResponse extends ActionResponse implements ToXCon
         PARSER.declareString(constructorArg(), new ParseField("id"));
         PARSER.declareString(constructorArg(), new ParseField("api_key"));
         PARSER.declareLong(optionalConstructorArg(), new ParseField("expiration"));
+        PARSER.declareString(optionalConstructorArg(), new ParseField("encoded"));
     }
 
     private final String name;
@@ -151,6 +154,7 @@ public final class CreateApiKeyResponse extends ActionResponse implements ToXCon
         } finally {
             Arrays.fill(charBytes, (byte) 0);
         }
+        builder.field("encoded", Base64.getEncoder().encodeToString((id + ":" + key).getBytes(StandardCharsets.UTF_8)));
         return builder.endObject();
     }
 

+ 11 - 16
x-pack/plugin/security/qa/security-basic/src/javaRestTest/java/org/elasticsearch/xpack/security/SecurityWithBasicLicenseIT.java

@@ -11,14 +11,11 @@ import org.elasticsearch.client.Request;
 import org.elasticsearch.client.RequestOptions;
 import org.elasticsearch.client.Response;
 import org.elasticsearch.client.ResponseException;
-import org.elasticsearch.core.Tuple;
 import org.elasticsearch.test.rest.yaml.ObjectPath;
 import org.elasticsearch.xpack.security.authc.InternalRealms;
 
 import java.io.IOException;
-import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
-import java.util.Base64;
 import java.util.Map;
 
 import static org.hamcrest.Matchers.contains;
@@ -35,8 +32,8 @@ public class SecurityWithBasicLicenseIT extends SecurityInBasicRestTestCase {
         checkHasPrivileges();
         checkIndexWrite();
 
-        final Tuple<String, String> keyAndId = getApiKeyAndId();
-        assertAuthenticateWithApiKey(keyAndId, true);
+        final String apiKeyCredentials = getApiKeyCredentials();
+        assertAuthenticateWithApiKey(apiKeyCredentials, true);
 
         assertFailToGetToken();
         // Service account token works independently to oauth2 token service
@@ -50,7 +47,7 @@ public class SecurityWithBasicLicenseIT extends SecurityInBasicRestTestCase {
     public void testWithTrialLicense() throws Exception {
         startTrial();
         String accessToken = null;
-        Tuple<String, String> keyAndId = null;
+        String apiKeyCredentials = null;
         try {
             checkLicenseType("trial");
             checkSecurityEnabled(true);
@@ -58,15 +55,15 @@ public class SecurityWithBasicLicenseIT extends SecurityInBasicRestTestCase {
             checkHasPrivileges();
             checkIndexWrite();
             accessToken = getAccessToken();
-            keyAndId = getApiKeyAndId();
+            apiKeyCredentials = getApiKeyCredentials();
             assertAuthenticateWithToken(accessToken, true);
-            assertAuthenticateWithApiKey(keyAndId, true);
+            assertAuthenticateWithApiKey(apiKeyCredentials, true);
             assertAddRoleWithDLS(true);
             assertAddRoleWithFLS(true);
         } finally {
             revertTrial();
             assertAuthenticateWithToken(accessToken, false);
-            assertAuthenticateWithApiKey(keyAndId, true);
+            assertAuthenticateWithApiKey(apiKeyCredentials, true);
             assertFailToGetToken();
             assertAddRoleWithDLS(false);
             assertAddRoleWithFLS(false);
@@ -176,13 +173,12 @@ public class SecurityWithBasicLicenseIT extends SecurityInBasicRestTestCase {
         return ObjectPath.evaluate(tokens, "access_token").toString();
     }
 
-    private Tuple<String, String> getApiKeyAndId() throws IOException {
+    private String getApiKeyCredentials() throws IOException {
         Response getApiKeyResponse = adminClient().performRequest(buildGetApiKeyRequest());
         assertThat(getApiKeyResponse.getStatusLine().getStatusCode(), equalTo(200));
         final Map<String, Object> apiKeyResponseMap = entityAsMap(getApiKeyResponse);
         assertOK(getApiKeyResponse);
-        return new Tuple<>(ObjectPath.evaluate(apiKeyResponseMap, "api_key").toString(),
-            ObjectPath.evaluate(apiKeyResponseMap, "id").toString());
+        return ObjectPath.evaluate(apiKeyResponseMap, "encoded").toString();
     }
 
     private void assertFailToGetToken() {
@@ -208,12 +204,11 @@ public class SecurityWithBasicLicenseIT extends SecurityInBasicRestTestCase {
         }
     }
 
-    private void assertAuthenticateWithApiKey(Tuple<String, String> keyAndId, boolean shouldSucceed) throws IOException {
-        assertNotNull("API Key and Id cannot be null", keyAndId);
+    private void assertAuthenticateWithApiKey(String apiKeyCredentials, boolean shouldSucceed) throws IOException {
+        assertNotNull("API Key credentials cannot be null", apiKeyCredentials);
         Request request = new Request("GET", "/_security/_authenticate");
         RequestOptions.Builder options = request.getOptions().toBuilder();
-        String headerValue = Base64.getEncoder().encodeToString((keyAndId.v2() + ":" + keyAndId.v1()).getBytes(StandardCharsets.UTF_8));
-        options.addHeader(HttpHeaders.AUTHORIZATION, "ApiKey " + headerValue);
+        options.addHeader(HttpHeaders.AUTHORIZATION, "ApiKey " + apiKeyCredentials);
         request.setOptions(options);
         if (shouldSucceed) {
             Response authenticateResponse = client().performRequest(request);

+ 3 - 0
x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/api_key/10_basic.yml

@@ -113,6 +113,7 @@ teardown:
   - is_true: expiration
   - set: { id: api_key_id }
   - transform_and_set: { login_creds: "#base64EncodeCredentials(id,api_key)" }
+  - match: { encoded: $login_creds }
 
   - do:
       headers:
@@ -222,6 +223,7 @@ teardown:
   - is_true: expiration
   - set: { id: api_key_id_1 }
   - transform_and_set: { login_creds: "#base64EncodeCredentials(id,api_key)" }
+  - match: { encoded: $login_creds }
 
   - do:
       headers:
@@ -318,6 +320,7 @@ teardown:
   - is_true: api_key
   - is_true: expiration
   - transform_and_set: { login_creds: "#base64EncodeCredentials(id,api_key)" }
+  - match: { encoded: $login_creds }
 
   - do:
       headers: