You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ap...@apache.org on 2023/03/29 10:46:22 UTC

[ignite-3] branch main updated: IGNITE-18607: add basic auth to the cli (#1821)

This is an automated email from the ASF dual-hosted git repository.

apkhmv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new c2f8f54bb2 IGNITE-18607: add basic auth to the cli (#1821)
c2f8f54bb2 is described below

commit c2f8f54bb234388ba45daf758432a29fd0893214
Author: Ivan Gagarkin <ga...@gmail.com>
AuthorDate: Wed Mar 29 14:46:15 2023 +0400

    IGNITE-18607: add basic auth to the cli (#1821)
---
 .../testframework/IntegrationTestBase.java         |   4 +
 .../commands/cluster/init/ItClusterInitTest.java   |  88 ++++++++++
 .../internal/rest/ItGeneratedRestClientTest.java   |   2 +-
 .../cluster/AuthenticationConfigConverter.java     |  64 ++++++++
 .../internal/cli/call/cluster/ClusterInitCall.java |   6 +-
 .../cli/call/cluster/ClusterInitCallInput.java     |  48 +++++-
 .../cli/call/cluster/status/ClusterStatusCall.java |   2 +-
 .../call/cluster/topology/LogicalTopologyCall.java |   2 +-
 .../cluster/topology/PhysicalTopologyCall.java     |   2 +-
 .../call/configuration/ClusterConfigShowCall.java  |   2 +-
 .../configuration/ClusterConfigUpdateCall.java     |   2 +-
 .../cli/call/configuration/NodeConfigShowCall.java |   2 +-
 .../call/configuration/NodeConfigUpdateCall.java   |   2 +-
 .../internal/cli/call/connect/ConnectCall.java     |   2 +-
 .../call/node/metric/NodeMetricSetListCall.java    |   2 +-
 .../node/metric/NodeMetricSourceEnableCall.java    |   2 +-
 .../call/node/metric/NodeMetricSourceListCall.java |   2 +-
 .../cli/call/node/status/NodeStatusCall.java       |   2 +-
 .../cli/call/node/version/NodeVersionCall.java     |   2 +-
 .../internal/cli/call/unit/DeployUnitCall.java     |   2 +-
 .../cli/call/unit/DeployUnitCallFactory.java       |   2 +-
 .../internal/cli/call/unit/ListUnitCall.java       |   2 +-
 .../internal/cli/call/unit/UndeployUnitCall.java   |   2 +-
 .../internal/cli/call/unit/UnitStatusCall.java     |   2 +-
 .../ignite/internal/cli/commands/Options.java      |  39 ++++-
 .../cluster/init/AuthenticationOptions.java        |  60 +++++++
 .../commands/cluster/init/ClusterInitOptions.java  |  14 +-
 .../ignite/internal/cli/config/CliConfigKeys.java  |  12 +-
 .../internal/cli/config/ini/IniConfigManager.java  |  12 ++
 .../ignite/internal/cli/core/ApiClientBuilder.java | 145 -----------------
 .../ignite/internal/cli/core/ApiClientFactory.java |  70 --------
 .../handler/IgniteCliApiExceptionHandler.java      |  34 ++--
 .../repl/registry/impl/JdbcUrlRegistryImpl.java    |   2 +-
 .../internal/cli/core/rest/ApiClientFactory.java   | 178 +++++++++++++++++++++
 .../internal/cli/core/rest/ApiClientSettings.java  | 103 ++++++++++++
 .../cli/core/rest/ApiClientSettingsBuilder.java    |  69 ++++++++
 .../core/rest/BasicAuthenticationInterceptor.java  |  44 +++++
 .../internal/cli/IgniteCliInterfaceTest.java       |  57 +++++++
 38 files changed, 824 insertions(+), 263 deletions(-)

diff --git a/modules/api/src/testFixtures/java/org/apache/ignite/internal/testframework/IntegrationTestBase.java b/modules/api/src/testFixtures/java/org/apache/ignite/internal/testframework/IntegrationTestBase.java
index 76cc8cde9d..4e01de34bc 100644
--- a/modules/api/src/testFixtures/java/org/apache/ignite/internal/testframework/IntegrationTestBase.java
+++ b/modules/api/src/testFixtures/java/org/apache/ignite/internal/testframework/IntegrationTestBase.java
@@ -122,6 +122,10 @@ public class IntegrationTestBase extends BaseIgniteAbstractTest {
 
         IgnitionManager.init(initParameters);
 
+        awaitClusterInitialized();
+    }
+
+    protected void awaitClusterInitialized() {
         for (CompletableFuture<Ignite> future : futures) {
             assertThat(future, willCompleteSuccessfully());
 
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/cluster/init/ItClusterInitTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/cluster/init/ItClusterInitTest.java
new file mode 100644
index 0000000000..c51fa03d77
--- /dev/null
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/cluster/init/ItClusterInitTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.commands.cluster.init;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import org.apache.ignite.internal.cli.commands.CliCommandTestNotInitializedIntegrationBase;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link ClusterInitCommand}.
+ */
+public class ItClusterInitTest extends CliCommandTestNotInitializedIntegrationBase {
+
+    @Test
+    @DisplayName("Init cluster with basic authentication")
+    void initClusterWithBasicAuthentication() {
+
+        // when
+        execute("connect", "http://localhost:10301");
+
+        resetOutput();
+
+        execute(
+                "cluster", "init",
+                "--meta-storage-node", CLUSTER_NODE_NAMES.get(0),
+                "--cluster-name", "cluster",
+                "--auth-enabled",
+                "--basic-auth-login", "admin",
+                "--basic-auth-password", "password"
+        );
+
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                () -> assertOutputContains("Cluster was initialized successfully")
+        );
+
+        // then
+        awaitClusterInitialized();
+
+        // basic authentication has been enabled
+        assertRestIsUnavailable();
+
+        // set basic authentication settings
+        execute("cli", "config", "set", "ignite.auth.basic.login=admin");
+        execute("cli", "config", "set", "ignite.auth.basic.password=password");
+
+        // REST is available
+        assertRestIsAvailable();
+    }
+
+    private void assertRestIsUnavailable() {
+        resetOutput();
+        execute("cluster", "config", "show");
+
+        assertAll(
+                () -> assertErrOutputContains("Authentication error"),
+                this::assertOutputIsEmpty
+        );
+    }
+
+    private void assertRestIsAvailable() {
+        resetOutput();
+        execute("cluster", "config", "show");
+
+        assertAll(
+                this::assertErrOutputIsEmpty,
+                this::assertOutputIsNotEmpty
+        );
+    }
+
+}
diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
index 13a48dfa01..2cd679b3a1 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/rest/ItGeneratedRestClientTest.java
@@ -51,7 +51,7 @@ import java.util.stream.IntStream;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.IgnitionManager;
 import org.apache.ignite.InitParameters;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.internal.testframework.WorkDirectory;
 import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
 import org.apache.ignite.internal.util.IgniteUtils;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/AuthenticationConfigConverter.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/AuthenticationConfigConverter.java
new file mode 100644
index 0000000000..8bdfd171be
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/AuthenticationConfigConverter.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.call.cluster;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import org.apache.ignite.rest.client.model.AuthenticationConfig;
+import org.apache.ignite.rest.client.model.AuthenticationProviderConfig;
+import org.apache.ignite.security.BasicAuthenticationProviderConfig;
+import org.jetbrains.annotations.Nullable;
+
+final class AuthenticationConfigConverter {
+
+    private AuthenticationConfigConverter() {
+    }
+
+    @Nullable
+    static AuthenticationConfig toAuthenticationConfig(org.apache.ignite.security.AuthenticationConfig config) {
+        if (config == null) {
+            return null;
+        }
+
+        List<AuthenticationProviderConfig> providerConfigs = config.providers().stream()
+                .map(AuthenticationConfigConverter::toAuthenticationProviderConfigDto)
+                .collect(Collectors.toList());
+
+        return new AuthenticationConfig()
+                .enabled(config.enabled())
+                .providers(providerConfigs);
+    }
+
+    private static AuthenticationProviderConfig toAuthenticationProviderConfigDto(
+            org.apache.ignite.security.AuthenticationProviderConfig provider
+    ) {
+        switch (provider.type()) {
+            case BASIC:
+                BasicAuthenticationProviderConfig config =
+                        (BasicAuthenticationProviderConfig) provider;
+
+                return new AuthenticationProviderConfig()
+                        .type(config.type().toString())
+                        .name(config.name())
+                        .login(config.login())
+                        .password(config.password());
+            default:
+                throw new IllegalArgumentException("Unexpected authentication provider type: " + provider.type());
+        }
+    }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCall.java
index 1142c772f5..4d5e1afaa4 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCall.java
@@ -18,13 +18,12 @@
 package org.apache.ignite.internal.cli.call.cluster;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.ClusterManagementApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
-import org.apache.ignite.rest.client.model.AuthenticationConfig;
 import org.apache.ignite.rest.client.model.InitCommand;
 
 /**
@@ -48,8 +47,7 @@ public class ClusterInitCall implements Call<ClusterInitCallInput, String> {
                     .metaStorageNodes(input.getMetaStorageNodes())
                     .cmgNodes(input.getCmgNodes())
                     .clusterName(input.getClusterName())
-                    //TODO support authentication in the CLI https://issues.apache.org/jira/browse/IGNITE-18607
-                    .authenticationConfig(new AuthenticationConfig().enabled(false))
+                    .authenticationConfig(AuthenticationConfigConverter.toAuthenticationConfig(input.authenticationConfig()))
             );
             return DefaultCallOutput.success("Cluster was initialized successfully");
         } catch (ApiException | IllegalArgumentException e) {
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
index fe911c7dda..cf80e12bcb 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/ClusterInitCallInput.java
@@ -17,9 +17,15 @@
 
 package org.apache.ignite.internal.cli.call.cluster;
 
+import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
+
 import java.util.List;
+import org.apache.ignite.internal.cli.commands.cluster.init.AuthenticationOptions;
 import org.apache.ignite.internal.cli.commands.cluster.init.ClusterInitOptions;
 import org.apache.ignite.internal.cli.core.call.CallInput;
+import org.apache.ignite.security.AuthenticationConfig;
+import org.apache.ignite.security.AuthenticationProviderConfig;
+import org.apache.ignite.security.BasicAuthenticationProviderConfig;
 
 /**
  * Input for {@link ClusterInitCall}.
@@ -29,17 +35,19 @@ public class ClusterInitCallInput implements CallInput {
     private final List<String> metaStorageNodes;
     private final List<String> cmgNodes;
     private final String clusterName;
+    private final AuthenticationConfig authenticationConfig;
 
     private ClusterInitCallInput(
             String clusterUrl,
             List<String> metaStorageNodes,
             List<String> cmgNodes,
-            String clusterName
-    ) {
+            String clusterName,
+            AuthenticationConfig authenticationConfig) {
         this.clusterUrl = clusterUrl;
         this.metaStorageNodes = metaStorageNodes;
         this.cmgNodes = cmgNodes;
         this.clusterName = clusterName;
+        this.authenticationConfig = authenticationConfig;
     }
 
     /**
@@ -86,6 +94,10 @@ public class ClusterInitCallInput implements CallInput {
         return clusterName;
     }
 
+    public AuthenticationConfig authenticationConfig() {
+        return authenticationConfig;
+    }
+
     /**
      * Builder for {@link ClusterInitCallInput}.
      */
@@ -98,6 +110,13 @@ public class ClusterInitCallInput implements CallInput {
 
         private String clusterName;
 
+        private AuthenticationConfig authenticationConfig;
+
+        public ClusterInitCallInputBuilder authenticationSettings(AuthenticationConfig authenticationConfig) {
+            this.authenticationConfig = authenticationConfig;
+            return this;
+        }
+
         public ClusterInitCallInputBuilder clusterUrl(String clusterUrl) {
             this.clusterUrl = clusterUrl;
             return this;
@@ -110,14 +129,31 @@ public class ClusterInitCallInput implements CallInput {
          * @return this builder
          */
         public ClusterInitCallInputBuilder fromClusterInitOptions(ClusterInitOptions clusterInitOptions) {
-            this.metaStorageNodes = clusterInitOptions.getMetaStorageNodes();
-            this.cmgNodes = clusterInitOptions.getCmgNodes();
-            this.clusterName = clusterInitOptions.getClusterName();
+            this.metaStorageNodes = clusterInitOptions.metaStorageNodes();
+            this.cmgNodes = clusterInitOptions.cmgNodes();
+            this.clusterName = clusterInitOptions.clusterName();
+            this.authenticationConfig = toAuthenticationConfig(clusterInitOptions.authenticationOptions());
             return this;
         }
 
         public ClusterInitCallInput build() {
-            return new ClusterInitCallInput(clusterUrl, metaStorageNodes, cmgNodes, clusterName);
+            return new ClusterInitCallInput(clusterUrl, metaStorageNodes, cmgNodes, clusterName, authenticationConfig);
+        }
+
+        private AuthenticationConfig toAuthenticationConfig(AuthenticationOptions options) {
+            if (options == null) {
+                return null;
+            }
+
+            return new AuthenticationConfig(options.enabled(), extractAuthenticationProviders(options));
+        }
+
+        private static List<AuthenticationProviderConfig> extractAuthenticationProviders(AuthenticationOptions options) {
+            if (!nullOrBlank(options.basicLogin()) && !nullOrBlank(options.basicPassword())) {
+                return List.of(new BasicAuthenticationProviderConfig("basic", options.basicLogin(), options.basicPassword()));
+            } else {
+                return List.of();
+            }
         }
     }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/status/ClusterStatusCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/status/ClusterStatusCall.java
index bb726633f4..f052bb156c 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/status/ClusterStatusCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/status/ClusterStatusCall.java
@@ -20,12 +20,12 @@ package org.apache.ignite.internal.cli.call.cluster.status;
 import jakarta.inject.Singleton;
 import org.apache.ignite.internal.cli.call.cluster.status.ClusterStatus.ClusterStatusBuilder;
 import org.apache.ignite.internal.cli.call.cluster.topology.PhysicalTopologyCall;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.call.UrlCallInput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.ClusterManagementApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 import org.apache.ignite.rest.client.model.ClusterState;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/LogicalTopologyCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/LogicalTopologyCall.java
index 8f721d3c54..540566c8b4 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/LogicalTopologyCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/LogicalTopologyCall.java
@@ -19,12 +19,12 @@ package org.apache.ignite.internal.cli.call.cluster.topology;
 
 import jakarta.inject.Singleton;
 import java.util.List;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.call.UrlCallInput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.TopologyApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 import org.apache.ignite.rest.client.model.ClusterNode;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/PhysicalTopologyCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/PhysicalTopologyCall.java
index 9036a6e320..c223cfcb22 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/PhysicalTopologyCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/cluster/topology/PhysicalTopologyCall.java
@@ -19,12 +19,12 @@ package org.apache.ignite.internal.cli.call.cluster.topology;
 
 import jakarta.inject.Singleton;
 import java.util.List;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.call.UrlCallInput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.TopologyApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 import org.apache.ignite.rest.client.model.ClusterNode;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigShowCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigShowCall.java
index ebf2060d1c..c419e9ba12 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigShowCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigShowCall.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.cli.call.configuration;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.ClusterConfigurationApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigUpdateCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigUpdateCall.java
index 8e1f8ddd6d..791f7e988f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigUpdateCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/ClusterConfigUpdateCall.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.cli.call.configuration;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.ClusterConfigurationApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigShowCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigShowCall.java
index 456b323a1b..0fc6a6acd4 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigShowCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigShowCall.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.cli.call.configuration;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.NodeConfigurationApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigUpdateCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigUpdateCall.java
index c5dda6f002..c641eb665a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigUpdateCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/configuration/NodeConfigUpdateCall.java
@@ -18,10 +18,10 @@
 package org.apache.ignite.internal.cli.call.configuration;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.NodeConfigurationApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
index f545f2f08d..2cd815fff9 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/connect/ConnectCall.java
@@ -23,7 +23,6 @@ import java.net.MalformedURLException;
 import java.util.Objects;
 import org.apache.ignite.internal.cli.config.CliConfigKeys;
 import org.apache.ignite.internal.cli.config.StateConfigProvider;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.JdbcUrl;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
@@ -32,6 +31,7 @@ import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
 import org.apache.ignite.internal.cli.core.repl.Session;
 import org.apache.ignite.internal.cli.core.repl.SessionInfo;
 import org.apache.ignite.internal.cli.core.repl.config.RootConfig;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.style.component.MessageUiComponent;
 import org.apache.ignite.internal.cli.core.style.element.UiElements;
 import org.apache.ignite.rest.client.api.NodeConfigurationApi;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSetListCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSetListCall.java
index d7bb97915b..65117353b0 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSetListCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSetListCall.java
@@ -19,12 +19,12 @@ package org.apache.ignite.internal.cli.call.node.metric;
 
 import jakarta.inject.Singleton;
 import java.util.List;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.call.UrlCallInput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.NodeMetricApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 import org.apache.ignite.rest.client.model.MetricSet;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceEnableCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceEnableCall.java
index 9f3576668e..dc02d734fd 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceEnableCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceEnableCall.java
@@ -18,11 +18,11 @@
 package org.apache.ignite.internal.cli.call.node.metric;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.NodeMetricApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceListCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceListCall.java
index da036db076..78dabd5b27 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceListCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/metric/NodeMetricSourceListCall.java
@@ -19,12 +19,12 @@ package org.apache.ignite.internal.cli.call.node.metric;
 
 import jakarta.inject.Singleton;
 import java.util.List;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.call.UrlCallInput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.NodeMetricApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 import org.apache.ignite.rest.client.model.MetricSource;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/status/NodeStatusCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/status/NodeStatusCall.java
index 36707a7e4c..a27e483d27 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/status/NodeStatusCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/status/NodeStatusCall.java
@@ -18,12 +18,12 @@
 package org.apache.ignite.internal.cli.call.node.status;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.call.UrlCallInput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.NodeManagementApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 import org.apache.ignite.rest.client.model.NodeState;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java
index ca1bbd28f3..60b36f01bb 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/node/version/NodeVersionCall.java
@@ -18,12 +18,12 @@
 package org.apache.ignite.internal.cli.call.node.version;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.call.UrlCallInput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.NodeManagementApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCall.java
index 7093accc12..8d8a94547a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCall.java
@@ -23,7 +23,6 @@ import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.concurrent.CompletableFuture;
 import okhttp3.Call;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.AsyncCall;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
@@ -31,6 +30,7 @@ import org.apache.ignite.internal.cli.core.call.ProgressTracker;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
 import org.apache.ignite.internal.cli.core.exception.UnitAlreadyExistsException;
 import org.apache.ignite.internal.cli.core.repl.registry.UnitsRegistry;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.style.component.MessageUiComponent;
 import org.apache.ignite.internal.cli.core.style.element.UiElements;
 import org.apache.ignite.rest.client.api.DeploymentApi;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCallFactory.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCallFactory.java
index e5637eeb54..d756140dd8 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCallFactory.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/DeployUnitCallFactory.java
@@ -18,9 +18,9 @@
 package org.apache.ignite.internal.cli.call.unit;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.ProgressTracker;
 import org.apache.ignite.internal.cli.core.repl.registry.UnitsRegistry;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 
 /** Factory for {@link DeployUnitCall}. */
 @Singleton
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java
index 8a51311660..d62baca364 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/ListUnitCall.java
@@ -20,12 +20,12 @@ package org.apache.ignite.internal.cli.call.unit;
 import jakarta.inject.Singleton;
 import java.util.List;
 import java.util.stream.Collectors;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.call.UrlCallInput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.DeploymentApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 import org.apache.ignite.rest.client.model.UnitStatus;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UndeployUnitCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UndeployUnitCall.java
index e6a532caf0..e3b536fa11 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UndeployUnitCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UndeployUnitCall.java
@@ -18,13 +18,13 @@
 package org.apache.ignite.internal.cli.call.unit;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
 import org.apache.ignite.internal.cli.core.exception.UnitNotFoundException;
 import org.apache.ignite.internal.cli.core.repl.registry.UnitsRegistry;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.style.component.MessageUiComponent;
 import org.apache.ignite.internal.cli.core.style.element.UiElements;
 import org.apache.ignite.rest.client.api.DeploymentApi;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusCall.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusCall.java
index 64cc308eca..be3297b5d0 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusCall.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/call/unit/UnitStatusCall.java
@@ -18,12 +18,12 @@
 package org.apache.ignite.internal.cli.call.unit;
 
 import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.call.Call;
 import org.apache.ignite.internal.cli.core.call.CallOutput;
 import org.apache.ignite.internal.cli.core.call.DefaultCallOutput;
 import org.apache.ignite.internal.cli.core.exception.IgniteCliApiException;
 import org.apache.ignite.internal.cli.core.exception.UnitNotFoundException;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.rest.client.api.DeploymentApi;
 import org.apache.ignite.rest.client.invoker.ApiException;
 import org.apache.ignite.rest.client.model.UnitStatus;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
index 160805fb81..96c41bc7f7 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/Options.java
@@ -53,7 +53,22 @@ public enum Options {
     PLAIN(Constants.PLAIN_OPTION, Constants.PLAIN_OPTION, Constants.PLAIN_OPTION_DESC),
     VERBOSE(Constants.VERBOSE_OPTION, Constants.VERBOSE_OPTION_SHORT, Constants.VERBOSE_OPTION_DESC),
     HELP(Constants.HELP_OPTION, Constants.HELP_OPTION_SHORT, Constants.HELP_OPTION_DESC),
-    VERSION(Constants.VERSION_OPTION, Constants.VERSION_OPTION, Constants.VERSION_OPTION_DESC);
+    VERSION(Constants.VERSION_OPTION, Constants.VERSION_OPTION, Constants.VERSION_OPTION_DESC),
+    AUTHENTICATION_ENABLED(
+            Constants.AUTHENTICATION_ENABLED_OPTION,
+            Constants.AUTHENTICATION_ENABLED_OPTION_SHORT,
+            Constants.AUTHENTICATION_ENABLED_OPTION_DESC
+    ),
+    BASIC_AUTHENTICATION_LOGIN(
+            Constants.BASIC_AUTHENTICATION_LOGIN_OPTION,
+            Constants.BASIC_AUTHENTICATION_LOGIN_OPTION_SHORT,
+            Constants.BASIC_AUTHENTICATION_LOGIN_OPTION_DESC
+    ),
+    BASIC_AUTHENTICATION_PASSWORD(
+            Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION,
+            Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION_SHORT,
+            Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION_DESC
+    );
 
     private final String fullName;
     private final String shortName;
@@ -212,6 +227,9 @@ public enum Options {
         /** Version option long name. */
         public static final String VERSION_OPTION = "--version";
 
+        /** Version option description. */
+        public static final String VERSION_OPTION_DESC = "Print version information and exit";
+
         /** Version option short name. */
         public static final String UNIT_VERSION_OPTION_SHORT = "-uv";
 
@@ -227,7 +245,22 @@ public enum Options {
         /** Unit path option description. */
         public static final String UNIT_PATH_OPTION_DESC = "Path to deployment unit file or directory";
 
-        /** Version option description. */
-        public static final String VERSION_OPTION_DESC = "Print version information and exit";
+        public static final String AUTHENTICATION_ENABLED_OPTION = "--auth-enabled";
+
+        public static final String AUTHENTICATION_ENABLED_OPTION_SHORT = "-ae";
+
+        public static final String AUTHENTICATION_ENABLED_OPTION_DESC = "Authentication enabled flag";
+
+        public static final String BASIC_AUTHENTICATION_LOGIN_OPTION = "--basic-auth-login";
+
+        public static final String BASIC_AUTHENTICATION_LOGIN_OPTION_SHORT = "-bl";
+
+        public static final String BASIC_AUTHENTICATION_LOGIN_OPTION_DESC = "Login which will be used for basic authentication";
+
+        public static final String BASIC_AUTHENTICATION_PASSWORD_OPTION = "--basic-auth-password";
+
+        public static final String BASIC_AUTHENTICATION_PASSWORD_OPTION_SHORT = "-bp";
+
+        public static final String BASIC_AUTHENTICATION_PASSWORD_OPTION_DESC = "Password which will be used for basic authentication";
     }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/AuthenticationOptions.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/AuthenticationOptions.java
new file mode 100644
index 0000000000..668473f50e
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/AuthenticationOptions.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.commands.cluster.init;
+
+import static org.apache.ignite.internal.cli.commands.Options.Constants.AUTHENTICATION_ENABLED_OPTION;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.AUTHENTICATION_ENABLED_OPTION_DESC;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.AUTHENTICATION_ENABLED_OPTION_SHORT;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_LOGIN_OPTION;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_LOGIN_OPTION_DESC;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_LOGIN_OPTION_SHORT;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION_DESC;
+import static org.apache.ignite.internal.cli.commands.Options.Constants.BASIC_AUTHENTICATION_PASSWORD_OPTION_SHORT;
+
+import picocli.CommandLine.Option;
+
+/**
+ * Mixin class for authentication options.
+ */
+public class AuthenticationOptions {
+
+    @Option(names = {AUTHENTICATION_ENABLED_OPTION, AUTHENTICATION_ENABLED_OPTION_SHORT},
+            description = AUTHENTICATION_ENABLED_OPTION_DESC)
+    private boolean enabled;
+
+    @Option(names = {BASIC_AUTHENTICATION_LOGIN_OPTION, BASIC_AUTHENTICATION_LOGIN_OPTION_SHORT},
+            description = BASIC_AUTHENTICATION_LOGIN_OPTION_DESC)
+    private String basicLogin;
+
+    @Option(names = {BASIC_AUTHENTICATION_PASSWORD_OPTION, BASIC_AUTHENTICATION_PASSWORD_OPTION_SHORT},
+            description = BASIC_AUTHENTICATION_PASSWORD_OPTION_DESC)
+    private String basicPassword;
+
+    public boolean enabled() {
+        return enabled;
+    }
+
+    public String basicLogin() {
+        return basicLogin;
+    }
+
+    public String basicPassword() {
+        return basicPassword;
+    }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
index 75fd02f512..cc340afb16 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/cluster/init/ClusterInitOptions.java
@@ -29,6 +29,7 @@ import static org.apache.ignite.internal.cli.commands.Options.Constants.META_STO
 
 import java.util.ArrayList;
 import java.util.List;
+import picocli.CommandLine.Mixin;
 import picocli.CommandLine.Option;
 
 /**
@@ -54,15 +55,22 @@ public class ClusterInitOptions {
     @Option(names = {CLUSTER_NAME_OPTION, CLUSTER_NAME_OPTION_SHORT}, required = true, description = CLUSTER_NAME_OPTION_DESC)
     private String clusterName;
 
-    public List<String> getMetaStorageNodes() {
+    @Mixin
+    private AuthenticationOptions authenticationOptions;
+
+    public List<String> metaStorageNodes() {
         return metaStorageNodes;
     }
 
-    public List<String> getCmgNodes() {
+    public List<String> cmgNodes() {
         return cmgNodes;
     }
 
-    public String getClusterName() {
+    public String clusterName() {
         return clusterName;
     }
+
+    public AuthenticationOptions authenticationOptions() {
+        return authenticationOptions;
+    }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
index 15383b5472..1880450027 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/CliConfigKeys.java
@@ -42,7 +42,13 @@ public enum CliConfigKeys {
     REST_KEY_STORE_PASSWORD(Constants.REST_KEY_STORE_PASSWORD),
 
     /** Default JDBC URL property name. */
-    JDBC_URL(Constants.JDBC_URL);
+    JDBC_URL(Constants.JDBC_URL),
+
+    /** Basic authentication login. */
+    BASIC_AUTHENTICATION_LOGIN(Constants.BASIC_AUTHENTICATION_LOGIN),
+
+    /** Basic authentication password. */
+    BASIC_AUTHENTICATION_PASSWORD(Constants.BASIC_AUTHENTICATION_PASSWORD);
 
     private final String value;
 
@@ -66,6 +72,10 @@ public enum CliConfigKeys {
 
         public static final String JDBC_URL = "ignite.jdbc-url";
 
+        public static final String BASIC_AUTHENTICATION_LOGIN = "ignite.auth.basic.login";
+
+        public static final String BASIC_AUTHENTICATION_PASSWORD = "ignite.auth.basic.password";
+
         /** Environment variable which points to the base directory for configuration files according to XDG spec. */
         private static final String XDG_CONFIG_HOME = "XDG_CONFIG_HOME";
 
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
index a509a75c96..29719649c5 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/config/ini/IniConfigManager.java
@@ -17,9 +17,15 @@
 
 package org.apache.ignite.internal.cli.config.ini;
 
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_LOGIN;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_PASSWORD;
 import static org.apache.ignite.internal.cli.config.CliConfigKeys.CLUSTER_URL;
 import static org.apache.ignite.internal.cli.config.CliConfigKeys.Constants.CURRENT_PROFILE;
 import static org.apache.ignite.internal.cli.config.CliConfigKeys.JDBC_URL;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PASSWORD;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PATH;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PASSWORD;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PATH;
 
 import java.io.File;
 import java.io.IOException;
@@ -119,6 +125,12 @@ public class IniConfigManager implements ConfigManager {
             IniSection defaultSection = ini.createSection(DEFAULT_PROFILE_NAME);
             defaultSection.setProperty(CLUSTER_URL.value(), "http://localhost:10300");
             defaultSection.setProperty(JDBC_URL.value(), "jdbc:ignite:thin://127.0.0.1:10800");
+            defaultSection.setProperty(REST_KEY_STORE_PATH.value(), "");
+            defaultSection.setProperty(REST_KEY_STORE_PASSWORD.value(), "");
+            defaultSection.setProperty(REST_TRUST_STORE_PATH.value(), "");
+            defaultSection.setProperty(REST_TRUST_STORE_PASSWORD.value(), "");
+            defaultSection.setProperty(BASIC_AUTHENTICATION_LOGIN.value(), "");
+            defaultSection.setProperty(BASIC_AUTHENTICATION_PASSWORD.value(), "");
             ini.store();
             return ini;
         } catch (IOException e) {
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientBuilder.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientBuilder.java
deleted file mode 100644
index 287a075424..0000000000
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientBuilder.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.cli.core;
-
-import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
-
-import java.io.File;
-import java.io.IOException;
-import java.security.KeyManagementException;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.CertificateException;
-import javax.net.ssl.KeyManager;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
-import okhttp3.OkHttpClient;
-import okhttp3.OkHttpClient.Builder;
-import okhttp3.internal.tls.OkHostnameVerifier;
-import org.apache.ignite.rest.client.invoker.ApiClient;
-
-class ApiClientBuilder {
-    private String basePath;
-    private String keyStorePath;
-    private String keyStorePassword;
-    private String trustStorePath;
-    private String trustStorePassword;
-
-    static ApiClientBuilder create() {
-        return new ApiClientBuilder();
-    }
-
-    ApiClientBuilder basePath(String basePath) {
-        this.basePath = basePath;
-        return this;
-    }
-
-    ApiClientBuilder keyStorePath(String keyStorePath) {
-        this.keyStorePath = keyStorePath;
-        return this;
-    }
-
-    ApiClientBuilder keyStorePassword(String keyStorePassword) {
-        this.keyStorePassword = keyStorePassword;
-        return this;
-    }
-
-    ApiClientBuilder trustStorePath(String trustStorePath) {
-        this.trustStorePath = trustStorePath;
-        return this;
-    }
-
-    ApiClientBuilder trustStorePassword(String trustStorePassword) {
-        this.trustStorePassword = trustStorePassword;
-        return this;
-    }
-
-    ApiClient build() throws CertificateException,
-            NoSuchAlgorithmException,
-            KeyStoreException,
-            IOException,
-            UnrecoverableKeyException,
-            KeyManagementException {
-
-        Builder builder = new Builder();
-
-        if (!nullOrBlank(keyStorePath) || !nullOrBlank(trustStorePath)) {
-            applySslSettings(builder);
-        }
-
-        OkHttpClient okHttpClient = builder.build();
-
-        return new ApiClient(okHttpClient)
-                .setBasePath(basePath);
-    }
-
-    private Builder applySslSettings(Builder builder) throws UnrecoverableKeyException,
-            CertificateException,
-            NoSuchAlgorithmException,
-            KeyStoreException,
-            IOException,
-            KeyManagementException {
-
-        TrustManagerFactory trustManagerFactory = trustManagerFactory();
-        KeyManagerFactory keyManagerFactory = keyManagerFactory();
-
-        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
-        KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
-
-        SSLContext sslContext = SSLContext.getInstance("TLS");
-        sslContext.init(keyManagers, trustManagers, new SecureRandom());
-        return builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
-                .hostnameVerifier(OkHostnameVerifier.INSTANCE);
-    }
-
-    private KeyManagerFactory keyManagerFactory()
-            throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
-        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
-
-        if (nullOrBlank(keyStorePath)) {
-            keyManagerFactory.init(null, null);
-        } else {
-            char[] password = keyStorePassword == null ? null : keyStorePassword.toCharArray();
-            KeyStore keyStore = KeyStore.getInstance(new File(keyStorePath), password);
-            keyManagerFactory.init(keyStore, keyStorePassword.toCharArray());
-        }
-
-        return keyManagerFactory;
-    }
-
-    private TrustManagerFactory trustManagerFactory()
-            throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
-        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
-
-        if (nullOrBlank(trustStorePath)) {
-            trustManagerFactory.init((KeyStore) null);
-        } else {
-            char[] password = trustStorePassword == null ? null : trustStorePassword.toCharArray();
-            KeyStore trustStore = KeyStore.getInstance(new File(trustStorePath), password);
-            trustManagerFactory.init(trustStore);
-        }
-
-        return trustManagerFactory;
-    }
-}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientFactory.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientFactory.java
deleted file mode 100644
index 1960f97ba8..0000000000
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/ApiClientFactory.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ignite.internal.cli.core;
-
-import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PASSWORD;
-import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PATH;
-import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PASSWORD;
-import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PATH;
-
-import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.config.ConfigManager;
-import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
-import org.apache.ignite.internal.cli.core.exception.IgniteCliException;
-import org.apache.ignite.internal.cli.logger.CliLoggers;
-import org.apache.ignite.rest.client.invoker.ApiClient;
-
-/**
- * This class holds the map from URLs to {@link ApiClient}.
- */
-@Singleton
-public class ApiClientFactory {
-
-    private final ConfigManagerProvider configManagerProvider;
-
-    public ApiClientFactory(ConfigManagerProvider configManagerProvider) {
-        this.configManagerProvider = configManagerProvider;
-    }
-
-    /**
-     * Gets {@link ApiClient} for the base path.
-     *
-     * @param path Base path.
-     * @return created API client.
-     */
-    public ApiClient getClient(String path) {
-        ApiClient apiClient = createClient(path);
-        CliLoggers.addApiClient(path, apiClient);
-        return apiClient;
-    }
-
-    private ApiClient createClient(String path) {
-        try {
-            ConfigManager configManager = configManagerProvider.get();
-            return ApiClientBuilder.create()
-                    .basePath(path)
-                    .keyStorePath(configManager.getCurrentProperty(REST_KEY_STORE_PATH.value()))
-                    .keyStorePassword(configManager.getCurrentProperty(REST_KEY_STORE_PASSWORD.value()))
-                    .trustStorePath(configManager.getCurrentProperty(REST_TRUST_STORE_PATH.value()))
-                    .trustStorePassword(configManager.getCurrentProperty(REST_TRUST_STORE_PASSWORD.value()))
-                    .build();
-        } catch (Exception e) {
-            throw new IgniteCliException("Couldn't build REST client", e);
-        }
-    }
-}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
index a3f99a0098..6ae12c042c 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/exception/handler/IgniteCliApiExceptionHandler.java
@@ -69,7 +69,16 @@ public class IgniteCliApiExceptionHandler implements ExceptionHandler<IgniteCliA
             } else if (apiCause != null) {
                 errorComponentBuilder.header(apiCause.getMessage());
             } else {
-                tryToExtractProblem(errorComponentBuilder, cause);
+                Problem problem = extractProblem(cause);
+                if (problem.getStatus() == 401) {
+                    errorComponentBuilder
+                            .header("Authentication error")
+                            .details("Could not connect to node with URL %s. "
+                                    + "Check authentication configuration", UiElements.url(e.getUrl()))
+                            .verbose(e.getMessage());
+                } else {
+                    renderProblem(errorComponentBuilder, problem);
+                }
             }
         } else {
             errorComponentBuilder.header(e.getCause() != e ? e.getCause().getMessage() : e.getMessage());
@@ -84,22 +93,25 @@ public class IgniteCliApiExceptionHandler implements ExceptionHandler<IgniteCliA
         return 1;
     }
 
-    private static void tryToExtractProblem(ErrorComponentBuilder errorComponentBuilder, ApiException cause) {
+    private static Problem extractProblem(ApiException cause) {
         try {
-            Problem problem = objectMapper.readValue(cause.getResponseBody(), Problem.class);
-            List<InvalidParam> invalidParams = problem.getInvalidParams();
-            if (invalidParams != null && !invalidParams.isEmpty()) {
-                errorComponentBuilder.details(extractInvalidParams(invalidParams));
-            }
-            errorComponentBuilder
-                    .header(problem.getDetail() != null ? problem.getDetail() : problem.getTitle())
-                    .errorCode(problem.getCode())
-                    .traceId(problem.getTraceId());
+            return objectMapper.readValue(cause.getResponseBody(), Problem.class);
         } catch (JsonProcessingException ex) {
             throw new RuntimeException(ex);
         }
     }
 
+    private static void renderProblem(ErrorComponentBuilder errorComponentBuilder, Problem problem) {
+        List<InvalidParam> invalidParams = problem.getInvalidParams();
+        if (invalidParams != null && !invalidParams.isEmpty()) {
+            errorComponentBuilder.details(extractInvalidParams(invalidParams));
+        }
+        errorComponentBuilder
+                .header(problem.getDetail() != null ? problem.getDetail() : problem.getTitle())
+                .errorCode(problem.getCode())
+                .traceId(problem.getTraceId());
+    }
+
     @NotNull
     private static String extractInvalidParams(List<InvalidParam> invalidParams) {
         return invalidParams.stream()
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/JdbcUrlRegistryImpl.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/JdbcUrlRegistryImpl.java
index 772b4e29d0..21a6b15345 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/JdbcUrlRegistryImpl.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/registry/impl/JdbcUrlRegistryImpl.java
@@ -29,13 +29,13 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
-import org.apache.ignite.internal.cli.core.ApiClientFactory;
 import org.apache.ignite.internal.cli.core.JdbcUrl;
 import org.apache.ignite.internal.cli.core.repl.AsyncSessionEventListener;
 import org.apache.ignite.internal.cli.core.repl.SessionInfo;
 import org.apache.ignite.internal.cli.core.repl.config.RootConfig;
 import org.apache.ignite.internal.cli.core.repl.registry.JdbcUrlRegistry;
 import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
+import org.apache.ignite.internal.cli.core.rest.ApiClientFactory;
 import org.apache.ignite.internal.cli.logger.CliLoggers;
 import org.apache.ignite.internal.logger.IgniteLogger;
 import org.apache.ignite.internal.thread.NamedThreadFactory;
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientFactory.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientFactory.java
new file mode 100644
index 0000000000..0eaff4c84d
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientFactory.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.rest;
+
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_LOGIN;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.BASIC_AUTHENTICATION_PASSWORD;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PASSWORD;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_KEY_STORE_PATH;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PASSWORD;
+import static org.apache.ignite.internal.cli.config.CliConfigKeys.REST_TRUST_STORE_PATH;
+import static org.apache.ignite.internal.util.StringUtils.nullOrBlank;
+
+import jakarta.inject.Singleton;
+import java.io.File;
+import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import okhttp3.Interceptor;
+import okhttp3.OkHttpClient;
+import okhttp3.OkHttpClient.Builder;
+import okhttp3.internal.tls.OkHostnameVerifier;
+import org.apache.ignite.internal.cli.config.ConfigManager;
+import org.apache.ignite.internal.cli.config.ConfigManagerProvider;
+import org.apache.ignite.internal.cli.core.exception.IgniteCliException;
+import org.apache.ignite.internal.cli.logger.CliLoggers;
+import org.apache.ignite.rest.client.invoker.ApiClient;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Factory for {@link ApiClient}. This class holds the map from {@link ApiClientSettings} to {@link ApiClient}. If settings are changed, the
+ * factory will recreate a new client. Otherwise, returns a client from the cache.
+ */
+@Singleton
+public class ApiClientFactory {
+
+    private final Map<ApiClientSettings, ApiClient> clientMap = new ConcurrentHashMap<>();
+
+    private final ConfigManagerProvider configManagerProvider;
+
+    public ApiClientFactory(ConfigManagerProvider configManagerProvider) {
+        this.configManagerProvider = configManagerProvider;
+    }
+
+    /**
+     * Returns {@link ApiClient} for the base path.
+     *
+     * @param path Base path.
+     * @return created API client.
+     */
+    public ApiClient getClient(String path) {
+        ApiClient apiClient = clientMap.computeIfAbsent(settings(path), this::buildClient);
+        CliLoggers.addApiClient(path, apiClient);
+        return apiClient;
+    }
+
+    private ApiClientSettings settings(String path) {
+        ConfigManager configManager = configManagerProvider.get();
+        return ApiClientSettings.builder()
+                .basePath(path)
+                .keyStorePath(configManager.getCurrentProperty(REST_KEY_STORE_PATH.value()))
+                .keyStorePassword(configManager.getCurrentProperty(REST_KEY_STORE_PASSWORD.value()))
+                .trustStorePath(configManager.getCurrentProperty(REST_TRUST_STORE_PATH.value()))
+                .trustStorePassword(configManager.getCurrentProperty(REST_TRUST_STORE_PASSWORD.value()))
+                .basicAuthLogin(configManager.getCurrentProperty(BASIC_AUTHENTICATION_LOGIN.value()))
+                .basicAuthPassword(configManager.getCurrentProperty(BASIC_AUTHENTICATION_PASSWORD.value()))
+                .build();
+    }
+
+
+    private ApiClient buildClient(ApiClientSettings settings) {
+        try {
+            Builder builder = new Builder();
+
+            if (!nullOrBlank(settings.keyStorePath()) || !nullOrBlank(settings.keyStorePassword())) {
+                applySslSettings(builder, settings);
+            }
+
+            Interceptor authInterceptor = authInterceptor(settings);
+            if (authInterceptor != null) {
+                builder.addInterceptor(authInterceptor);
+            }
+
+            OkHttpClient okHttpClient = builder.build();
+
+            return new ApiClient(okHttpClient)
+                    .setBasePath(settings.basePath());
+
+        } catch (Exception e) {
+            throw new IgniteCliException("Couldn't build REST client", e);
+        }
+    }
+
+    private Builder applySslSettings(Builder builder, ApiClientSettings settings) throws UnrecoverableKeyException,
+            CertificateException,
+            NoSuchAlgorithmException,
+            KeyStoreException,
+            IOException,
+            KeyManagementException {
+
+        TrustManagerFactory trustManagerFactory = trustManagerFactory(settings);
+        KeyManagerFactory keyManagerFactory = keyManagerFactory(settings);
+
+        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
+        KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
+
+        SSLContext sslContext = SSLContext.getInstance("TLS");
+        sslContext.init(keyManagers, trustManagers, new SecureRandom());
+        return builder.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
+                .hostnameVerifier(OkHostnameVerifier.INSTANCE);
+    }
+
+    private KeyManagerFactory keyManagerFactory(ApiClientSettings settings)
+            throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, CertificateException, IOException {
+        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+
+        if (nullOrBlank(settings.keyStorePath())) {
+            keyManagerFactory.init(null, null);
+        } else {
+            char[] password = settings.keyStorePassword() == null ? null : settings.keyStorePassword().toCharArray();
+            KeyStore keyStore = KeyStore.getInstance(new File(settings.keyStorePath()), password);
+            keyManagerFactory.init(keyStore, settings.keyStorePassword().toCharArray());
+        }
+
+        return keyManagerFactory;
+    }
+
+    private TrustManagerFactory trustManagerFactory(ApiClientSettings settings)
+            throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException {
+        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+
+        if (nullOrBlank(settings.trustStorePath())) {
+            trustManagerFactory.init((KeyStore) null);
+        } else {
+            char[] password = settings.trustStorePassword() == null ? null : settings.trustStorePassword().toCharArray();
+            KeyStore trustStore = KeyStore.getInstance(new File(settings.trustStorePath()), password);
+            trustManagerFactory.init(trustStore);
+        }
+
+        return trustManagerFactory;
+    }
+
+    @Nullable
+    private Interceptor authInterceptor(ApiClientSettings settings) {
+        if (!nullOrBlank(settings.basicAuthLogin()) && !nullOrBlank(settings.basicAuthPassword())) {
+            return new BasicAuthenticationInterceptor(settings.basicAuthLogin(), settings.basicAuthPassword());
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettings.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettings.java
new file mode 100644
index 0000000000..091baf6e96
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettings.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.rest;
+
+import java.util.Objects;
+
+/** Api client settings. */
+public class ApiClientSettings {
+
+    private String basePath;
+
+    private String keyStorePath;
+
+    private String keyStorePassword;
+
+    private String trustStorePath;
+
+    private String trustStorePassword;
+
+    private String basicAuthLogin;
+
+    private String basicAuthPassword;
+
+    ApiClientSettings(String basePath, String keyStorePath, String keyStorePassword, String trustStorePath,
+            String trustStorePassword,
+            String basicAuthLogin, String basicAuthPassword) {
+        this.basePath = basePath;
+        this.keyStorePath = keyStorePath;
+        this.keyStorePassword = keyStorePassword;
+        this.trustStorePath = trustStorePath;
+        this.trustStorePassword = trustStorePassword;
+        this.basicAuthLogin = basicAuthLogin;
+        this.basicAuthPassword = basicAuthPassword;
+    }
+
+    public static ApiClientSettingsBuilder builder() {
+        return new ApiClientSettingsBuilder();
+    }
+
+    public String basePath() {
+        return basePath;
+    }
+
+    public String keyStorePath() {
+        return keyStorePath;
+    }
+
+    public String keyStorePassword() {
+        return keyStorePassword;
+    }
+
+    public String trustStorePath() {
+        return trustStorePath;
+    }
+
+    public String trustStorePassword() {
+        return trustStorePassword;
+    }
+
+    public String basicAuthLogin() {
+        return basicAuthLogin;
+    }
+
+    public String basicAuthPassword() {
+        return basicAuthPassword;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        ApiClientSettings that = (ApiClientSettings) o;
+        return Objects.equals(basePath, that.basePath) && Objects.equals(keyStorePath, that.keyStorePath)
+                && Objects.equals(keyStorePassword, that.keyStorePassword) && Objects.equals(trustStorePath,
+                that.trustStorePath) && Objects.equals(trustStorePassword, that.trustStorePassword) && Objects.equals(
+                basicAuthLogin, that.basicAuthLogin) && Objects.equals(basicAuthPassword, that.basicAuthPassword);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(basePath, keyStorePath, keyStorePassword, trustStorePath, trustStorePassword, basicAuthLogin,
+                basicAuthPassword);
+    }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettingsBuilder.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettingsBuilder.java
new file mode 100644
index 0000000000..6ba9b4b027
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/ApiClientSettingsBuilder.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.rest;
+
+/** Builder for {@link ApiClientSettings}. */
+public class ApiClientSettingsBuilder {
+    private String basePath;
+    private String keyStorePath;
+    private String keyStorePassword;
+    private String trustStorePath;
+    private String trustStorePassword;
+    private String basicAuthLogin;
+    private String basicAuthPassword;
+
+    public ApiClientSettingsBuilder basePath(String basePath) {
+        this.basePath = basePath;
+        return this;
+    }
+
+    public ApiClientSettingsBuilder keyStorePath(String keyStorePath) {
+        this.keyStorePath = keyStorePath;
+        return this;
+    }
+
+    public ApiClientSettingsBuilder keyStorePassword(String keyStorePassword) {
+        this.keyStorePassword = keyStorePassword;
+        return this;
+    }
+
+    public ApiClientSettingsBuilder trustStorePath(String trustStorePath) {
+        this.trustStorePath = trustStorePath;
+        return this;
+    }
+
+    public ApiClientSettingsBuilder trustStorePassword(String trustStorePassword) {
+        this.trustStorePassword = trustStorePassword;
+        return this;
+    }
+
+    public ApiClientSettingsBuilder basicAuthLogin(String basicAuthLogin) {
+        this.basicAuthLogin = basicAuthLogin;
+        return this;
+    }
+
+    public ApiClientSettingsBuilder basicAuthPassword(String basicAuthPassword) {
+        this.basicAuthPassword = basicAuthPassword;
+        return this;
+    }
+
+    public ApiClientSettings build() {
+        return new ApiClientSettings(basePath, keyStorePath, keyStorePassword, trustStorePath, trustStorePassword, basicAuthLogin,
+                basicAuthPassword);
+    }
+}
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/BasicAuthenticationInterceptor.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/BasicAuthenticationInterceptor.java
new file mode 100644
index 0000000000..bbf7af037f
--- /dev/null
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/rest/BasicAuthenticationInterceptor.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.core.rest;
+
+import java.io.IOException;
+import okhttp3.Credentials;
+import okhttp3.Interceptor;
+import okhttp3.Request;
+import okhttp3.Response;
+
+/**
+ * Basic authentication interceptor. Adds "Authorization" header with basic authentication.
+ */
+public class BasicAuthenticationInterceptor implements Interceptor {
+
+    private final String credentials;
+
+    public BasicAuthenticationInterceptor(String user, String password) {
+        this.credentials = Credentials.basic(user, password);
+    }
+
+    @Override
+    public Response intercept(Chain chain) throws IOException {
+        Request request = chain.request();
+        Request authenticatedRequest = request.newBuilder()
+                .header("Authorization", credentials).build();
+        return chain.proceed(authenticatedRequest);
+    }
+}
diff --git a/modules/cli/src/test/java/org/apache/ignite/internal/cli/IgniteCliInterfaceTest.java b/modules/cli/src/test/java/org/apache/ignite/internal/cli/IgniteCliInterfaceTest.java
index 75a406a25f..3093843e9b 100644
--- a/modules/cli/src/test/java/org/apache/ignite/internal/cli/IgniteCliInterfaceTest.java
+++ b/modules/cli/src/test/java/org/apache/ignite/internal/cli/IgniteCliInterfaceTest.java
@@ -271,6 +271,63 @@ public class IgniteCliInterfaceTest extends AbstractCliTest {
             assertThatStderrIsEmpty();
         }
 
+        @Test
+        @DisplayName("init --cluster-endpoint-url http://localhost:10300 --meta-storage-node node1ConsistentId --meta-storage-node node2ConsistentId "
+                + "--cmg-node node2ConsistentId --cmg-node node3ConsistentId --cluster-name cluster "
+                + "--auth-enabled --basic-auth-login admin --basic-auth-password password")
+        void initWithAuthenticationSuccess() {
+            var expectedSentContent = "{\n"
+                    + "  \"metaStorageNodes\": [\n"
+                    + "    \"node1ConsistentId\",\n"
+                    + "    \"node2ConsistentId\"\n"
+                    + "  ],\n"
+                    + "  \"cmgNodes\": [\n"
+                    + "    \"node2ConsistentId\",\n"
+                    + "    \"node3ConsistentId\"\n"
+                    + "  ],\n"
+                    + "  \"clusterName\": \"cluster\",\n"
+                    + "  \"authenticationConfig\": {\n"
+                    + "    \"enabled\": true,\n"
+                    + "    \"providers\": [\n"
+                    + "      {\n"
+                    + "        \"login\": \"admin\",\n"
+                    + "        \"password\": \"password\",\n"
+                    + "        \"name\": \"basic\",\n"
+                    + "        \"type\": \"BASIC\"\n"
+                    + "      }\n"
+                    + "    ]\n"
+                    + "  }\n"
+                    + "}";
+
+            clientAndServer
+                    .when(request()
+                            .withMethod("POST")
+                            .withPath("/management/v1/cluster/init")
+                            .withBody(json(expectedSentContent, ONLY_MATCHING_FIELDS))
+                            .withContentType(MediaType.APPLICATION_JSON_UTF_8)
+                    )
+                    .respond(response(null));
+
+
+            int exitCode = execute(
+                    "cluster", "init",
+                    "--cluster-endpoint-url", mockUrl,
+                    "--meta-storage-node", "node1ConsistentId",
+                    "--meta-storage-node", "node2ConsistentId",
+                    "--cmg-node", "node2ConsistentId",
+                    "--cmg-node", "node3ConsistentId",
+                    "--cluster-name", "cluster",
+                    "--auth-enabled",
+                    "--basic-auth-login", "admin",
+                    "--basic-auth-password", "password"
+            );
+
+            assertThatExitCodeMeansSuccess(exitCode);
+
+            assertOutputEqual("Cluster was initialized successfully");
+            assertThatStderrIsEmpty();
+        }
+
         @Test
         void initError() {
             clientAndServer