diff --git a/docs/configfile.rst b/docs/configfile.rst
index 49689c3..b34a304 100644
--- a/docs/configfile.rst
+++ b/docs/configfile.rst
@@ -39,13 +39,15 @@ The following configuration options are available for defining node entries:
       - http_local (available in EOS 4.14.5 or later)
       - http
       - https
+      - https_certs
 
 :port: Configures the port to use for the eAPI connection.  A default
     port is used if this parameter is absent, based on the transport setting
     using the following values:
 
       - transport: http, default port: 80
-      - transport: https, deafult port: 443
+      - transport: https, default port: 443
+      - transport: https_certs, default port: 443
       - transport: http_local, default port: 8080
       - transport: socket, default port: n/a
 
@@ -62,6 +64,7 @@ Transport   eapi.conf Required Script run from Authentication Required
 =========== ================== =============== ========================
 http        Yes                On/Off-switch   Yes
 https       Yes                On/Off-switch   Yes
+https_certs Yes                On/Off-switch   Yes (Auth done via certs, not un/pw)
 http_local  Yes                On-switch only  No
 socket      No                 On-switch only  No
 =========== ================== =============== ========================
@@ -169,6 +172,29 @@ As the table above indicates, a pyeapi configuration file is required in
   username: admin
   password: admin
 
+Using HTTPS with Certificates
+=============================
+
+The https_certs transport options allows users to do authentication for pyeapi
+with certificates instead of username/password. This requires functional
+certificate chains are setup, copied to the proper location and trusted by
+EOS beforehand. The ca_file parameter is optional. If provided the switches
+certificate will also be validated against the provided CA cert. If no CA cert
+is provided then no server side validation will be done.
+
+.. code-block:: console
+
+  [connection:veos01]
+  transport: https_certs
+  cert_file: /path/to/certificate/file
+  key_file: /path/to/private/key/file
+  ca_file: /path/to/CA/certificate/file
+
+  [connection:veos02]
+  transport: https_certs
+  cert_file: /path/to/certificate/file
+  key_file: /path/to/private/key/file
+
 *******************
 The DEFAULT Section
 *******************
diff --git a/docs/quickstart.rst b/docs/quickstart.rst
index 8934cd6..333bd54 100644
--- a/docs/quickstart.rst
+++ b/docs/quickstart.rst
@@ -19,6 +19,7 @@ include:
 
   - HTTP
   - HTTPS
+  - HTTPS Certificates
   - HTTP Local
   - Unix Socket
 
diff --git a/pyeapi/client.py b/pyeapi/client.py
index 52e80d6..8a0947e 100644
--- a/pyeapi/client.py
+++ b/pyeapi/client.py
@@ -106,6 +106,7 @@ except ImportError:
 from pyeapi.utils import load_module, make_iterable, debug
 
 from pyeapi.eapilib import HttpEapiConnection, HttpsEapiConnection
+from pyeapi.eapilib import HttpsEapiCertConnection
 from pyeapi.eapilib import SocketEapiConnection, HttpLocalEapiConnection
 from pyeapi.eapilib import CommandError
 
@@ -115,7 +116,8 @@ TRANSPORTS = {
     'socket': SocketEapiConnection,
     'http_local': HttpLocalEapiConnection,
     'http': HttpEapiConnection,
-    'https': HttpsEapiConnection
+    'https': HttpsEapiConnection,
+    'https_certs': HttpsEapiCertConnection
 }
 
 DEFAULT_TRANSPORT = 'https'
@@ -327,6 +329,7 @@ def load_config(filename):
     """
     return config.load(filename)
 
+
 def config_for(name):
     """ Function to get settings for named config
 
@@ -344,6 +347,7 @@ def config_for(name):
     """
     return config.get_connection(name)
 
+
 def hosts_for_tag(tag):
     """ Returns the hosts assocated with the specified tag
 
@@ -361,6 +365,7 @@ def hosts_for_tag(tag):
     """
     return config.tags.get(tag)
 
+
 def make_connection(transport, **kwargs):
     """ Creates a connection instance based on the transport
 
@@ -386,7 +391,8 @@ def make_connection(transport, **kwargs):
 
 
 def connect(transport=None, host='localhost', username='admin',
-            password='', port=None, timeout=60, return_node=False, **kwargs):
+            password='', port=None, key_file=None, cert_file=None,
+            ca_file=None, timeout=60, return_node=False, **kwargs):
     """ Creates a connection using the supplied settings
 
     This function will create a connection to an Arista EOS node using
@@ -405,6 +411,10 @@ def connect(transport=None, host='localhost', username='admin',
         port (int): The TCP port of the endpoint for the eAPI connection.  If
             this keyword is not specified, the default value is automatically
             determined by the transport type. (http=80, https=443)
+        key_file (str): Path to private key file for ssl validation
+        cert_file (str): Path to PEM formatted cert file for ssl validation
+        ca_file (str): Path to CA PEM formatted cert file for ssl validation
+        timeout (int): timeout
         return_node (bool): Returns a Node object if True, otherwise
             returns an EapiConnection object.
 
@@ -415,10 +425,13 @@ def connect(transport=None, host='localhost', username='admin',
     """
     transport = transport or DEFAULT_TRANSPORT
     connection = make_connection(transport, host=host, username=username,
-                                 password=password, port=port, timeout=timeout)
+                                 password=password, key_file=key_file,
+                                 cert_file=cert_file, ca_file=ca_file,
+                                 port=port, timeout=timeout)
     if return_node:
         return Node(connection, transport=transport, host=host,
-                    username=username, password=password, port=port, **kwargs)
+                    username=username, password=password, key_file=key_file,
+                    cert_file=cert_file, ca_file=ca_file, port=port, **kwargs)
     return connection
 
 
diff --git a/pyeapi/eapilib.py b/pyeapi/eapilib.py
index c6ce8f3..3e0f679 100644
--- a/pyeapi/eapilib.py
+++ b/pyeapi/eapilib.py
@@ -58,6 +58,7 @@ _LOGGER = logging.getLogger(__name__)
 DEFAULT_HTTP_PORT = 80
 DEFAULT_HTTPS_PORT = 443
 DEFAULT_HTTP_LOCAL_PORT = 8080
+DEFAULT_HTTPS_LOCAL_PORT = 8443
 DEFAULT_HTTP_PATH = '/command-api'
 DEFAULT_UNIX_SOCKET = '/var/run/command-api.sock'
 
@@ -207,6 +208,55 @@ class HttpsConnection(HTTPSConnection):
         return 'https://%s:%s/%s' % (self.host, self.port, self.path)
 
 
+class HTTPSCertConnection(HTTPSConnection):
+    """ Class to make a HTTPS connection, with support
+        for full client-based SSL Authentication.
+    """
+
+    def __init__(self, path, host, port, key_file, cert_file, ca_file,
+                 timeout=None):
+        HTTPSConnection.__init__(self, host, key_file=key_file,
+                                 cert_file=cert_file)
+        self.key_file = key_file
+        self.cert_file = cert_file
+        self.ca_file = ca_file
+        self.timeout = timeout
+        self.path = path
+        self.port = port
+
+    def __str__(self):
+        return 'https://%s:%s/%s - %s,%s' % (self.host, self.port, self.path,
+                                             self.key_file, self.cert_file)
+
+    def __repr__(self):
+        return 'https://%s:%s/%s - %s,%s' % (self.host, self.port, self.path,
+                                             self.key_file, self.cert_file)
+
+    def connect(self):
+        """ Connect to a host on a given (SSL) port.
+            If ca_file is pointing somewhere, use it
+            to check Server Certificate.
+
+            Redefined/copied and extended from httplib.py:1105 (Python 2.6.x).
+            This is needed to pass cert_reqs=ssl.CERT_REQUIRED as parameter
+            to ssl.wrap_socket(), which forces SSL to check server certificate
+            against our client certificate.
+        """
+        sock = socket.create_connection((self.host, self.port), self.timeout)
+        if self._tunnel_host:
+            self.sock = sock
+            self._tunnel()
+        # If there's no CA File, don't force Server Certificate Check
+        if self.ca_file:
+            self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
+                                        ca_certs=self.ca_file,
+                                        cert_reqs=ssl.CERT_REQUIRED)
+        else:
+            self.sock = ssl.wrap_socket(sock, self.key_file,
+                                        self.cert_file,
+                                        cert_reqs=ssl.CERT_NONE)
+
+
 class EapiConnection(object):
     """Creates a connection to eAPI for sending and receiving eAPI requests
 
@@ -551,7 +601,7 @@ class HttpsEapiConnection(EapiConnection):
     def disable_certificate_verification(self):
         # SSL/TLS certificate verification is enabled by default in latest
         # Python releases and causes self-signed certificates generated
-        # on EOS to fail validation (unless explicitely imported).
+        # on EOS to fail validation (unless explicitly imported).
         # Disable the SSL/TLS certificate verification for now.
         # Use the approach in PEP476 to disable certificate validation.
         # TODO:
@@ -560,3 +610,20 @@ class HttpsEapiConnection(EapiConnection):
         # temporary until a proper fix is implemented.
         if hasattr(ssl, '_create_unverified_context'):
             return ssl._create_unverified_context()
+
+
+class HttpsEapiCertConnection(EapiConnection):
+    def __init__(self, host, port=None, path=None, key_file=None,
+                 cert_file=None, ca_file=None, timeout=60, **kwargs):
+        if key_file is None or cert_file is None:
+            raise ValueError("For https_cert connections both a key_file and "
+                             "cert_file are required. A ca_file is also "
+                             "recommended")
+        super(HttpsEapiCertConnection, self).__init__()
+        port = port or DEFAULT_HTTPS_PORT
+        path = path or DEFAULT_HTTP_PATH
+
+        self.transport = HTTPSCertConnection(path, host, int(port),
+                                             key_file=key_file,
+                                             cert_file=cert_file,
+                                             ca_file=ca_file, timeout=timeout)

API doesn't support authentication other than username/password
Please provide support for some method other than username/password in a text file to access the API. SSH support exists for SSH keys, maybe allow the same key to be used to generate client x.509 certificates.
diff --git a/test/unit/test_client.py b/test/unit/test_client.py
index 1cf3977..b5c6ecc 100644
--- a/test/unit/test_client.py
+++ b/test/unit/test_client.py
@@ -310,7 +310,8 @@ class TestClient(unittest.TestCase):
     def test_connect_types(self, connection):
         transports = list(pyeapi.client.TRANSPORTS.keys())
         kwargs = dict(host='localhost', username='admin', password='',
-                      port=None, timeout=60)
+                      port=None, key_file=None, cert_file=None,
+                      ca_file=None, timeout=60)
 
         for transport in transports:
             pyeapi.client.connect(transport)
@@ -408,7 +409,8 @@ class TestClient(unittest.TestCase):
         with patch.dict(pyeapi.client.TRANSPORTS, {'https': transport}):
             pyeapi.client.connect()
             kwargs = dict(host='localhost', username='admin', password='',
-                          port=None, timeout=60)
+                          port=None, key_file=None, cert_file=None,
+                          ca_file=None, timeout=60)
             transport.assert_called_once_with(**kwargs)
 
     def test_connect_return_node(self):
@@ -420,7 +422,8 @@ class TestClient(unittest.TestCase):
                                          password='password', port=None,
                                          timeout=60, return_node=True)
             kwargs = dict(host='192.168.1.16', username='eapi',
-                          password='password', port=None, timeout=60)
+                          password='password', port=None, key_file=None,
+                          cert_file=None, ca_file=None, timeout=60)
             transport.assert_called_once_with(**kwargs)
             self.assertIsNone(node._enablepwd)
 
@@ -434,7 +437,8 @@ class TestClient(unittest.TestCase):
                                          timeout=60, enablepwd='enablepwd',
                                          return_node=True)
             kwargs = dict(host='192.168.1.16', username='eapi',
-                          password='password', port=None, timeout=60)
+                          password='password', port=None, key_file=None,
+                          cert_file=None, ca_file=None, timeout=60)
             transport.assert_called_once_with(**kwargs)
             self.assertEqual(node._enablepwd, 'enablepwd')
 
@@ -445,7 +449,8 @@ class TestClient(unittest.TestCase):
             pyeapi.client.load_config(filename=conf)
             node = pyeapi.client.connect_to('test1')
             kwargs = dict(host='192.168.1.16', username='eapi',
-                          password='password', port=None, timeout=60)
+                          password='password', port=None, key_file=None,
+                          cert_file=None, ca_file=None, timeout=60)
             transport.assert_called_once_with(**kwargs)
             self.assertEqual(node._enablepwd, 'enablepwd')
 

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest mock coveralls twine check-manifest pep8 pyflakes coverage sphinx sphinxcontrib-napoleon flake8 flake8-print flake8-debugger pep8-naming
3.9
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.16
babel==2.17.0
backports.tarfile==1.2.0
build==1.2.2.post1
certifi==2025.1.31
cffi==1.17.1
charset-normalizer==3.4.1
check-manifest==0.50
coverage==7.8.0
coveralls==4.0.1
cryptography==44.0.2
docopt==0.6.2
docutils==0.21.2
exceptiongroup==1.2.2
flake8==7.2.0
flake8-debugger==4.1.2
flake8-print==5.0.0
id==1.5.0
idna==3.10
imagesize==1.4.1
importlib_metadata==8.6.1
iniconfig==2.1.0
jaraco.classes==3.4.0
jaraco.context==6.0.1
jaraco.functools==4.1.0
jeepney==0.9.0
Jinja2==3.1.6
keyring==25.6.0
markdown-it-py==3.0.0
MarkupSafe==3.0.2
mccabe==0.7.0
mdurl==0.1.2
mock==5.2.0
more-itertools==10.6.0
netaddr==1.3.0
nh3==0.2.21
packaging==24.2
pep8==1.7.1
pep8-naming==0.14.1
pluggy==1.5.0
pockets==0.9.1
pycodestyle==2.13.0
pycparser==2.22
-e git+https://github.com/arista-eosplus/pyeapi.git@96a74faef1fe3bd79c4e900aed29c9956a0587d6#egg=pyeapi
pyflakes==3.3.2
Pygments==2.19.1
pyproject_hooks==1.2.0
pytest==8.3.5
readme_renderer==44.0
requests==2.32.3
requests-toolbelt==1.0.0
rfc3986==2.0.0
rich==14.0.0
SecretStorage==3.3.3
six==1.17.0
snowballstemmer==2.2.0
Sphinx==7.4.7
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-napoleon==0.7
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
tomli==2.2.1
twine==6.1.0
typing_extensions==4.13.0
urllib3==2.3.0
zipp==3.21.0

name: pyeapi
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.16
      - babel==2.17.0
      - backports-tarfile==1.2.0
      - build==1.2.2.post1
      - certifi==2025.1.31
      - cffi==1.17.1
      - charset-normalizer==3.4.1
      - check-manifest==0.50
      - coverage==7.8.0
      - coveralls==4.0.1
      - cryptography==44.0.2
      - docopt==0.6.2
      - docutils==0.21.2
      - exceptiongroup==1.2.2
      - flake8==7.2.0
      - flake8-debugger==4.1.2
      - flake8-print==5.0.0
      - id==1.5.0
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - jaraco-classes==3.4.0
      - jaraco-context==6.0.1
      - jaraco-functools==4.1.0
      - jeepney==0.9.0
      - jinja2==3.1.6
      - keyring==25.6.0
      - markdown-it-py==3.0.0
      - markupsafe==3.0.2
      - mccabe==0.7.0
      - mdurl==0.1.2
      - mock==5.2.0
      - more-itertools==10.6.0
      - netaddr==1.3.0
      - nh3==0.2.21
      - packaging==24.2
      - pep8==1.7.1
      - pep8-naming==0.14.1
      - pluggy==1.5.0
      - pockets==0.9.1
      - pycodestyle==2.13.0
      - pycparser==2.22
      - pyflakes==3.3.2
      - pygments==2.19.1
      - pyproject-hooks==1.2.0
      - pytest==8.3.5
      - readme-renderer==44.0
      - requests==2.32.3
      - requests-toolbelt==1.0.0
      - rfc3986==2.0.0
      - rich==14.0.0
      - secretstorage==3.3.3
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==7.4.7
      - sphinxcontrib-applehelp==2.0.0
      - sphinxcontrib-devhelp==2.0.0
      - sphinxcontrib-htmlhelp==2.1.0
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-napoleon==0.7
      - sphinxcontrib-qthelp==2.0.0
      - sphinxcontrib-serializinghtml==2.0.0
      - tomli==2.2.1
      - twine==6.1.0
      - typing-extensions==4.13.0
      - urllib3==2.3.0
      - zipp==3.21.0
prefix: /opt/conda/envs/pyeapi

test/unit/test_client.py::TestClient::test_connect_default_type test/unit/test_client.py::TestClient::test_connect_return_node test/unit/test_client.py::TestClient::test_connect_return_node_enablepwd test/unit/test_client.py::TestClient::test_connect_to_with_config test/unit/test_client.py::TestClient::test_connect_types
test/unit/test_client.py::TestNode::test_api_autoloader test/unit/test_client.py::TestNode::test_config_with_multiple_commands test/unit/test_client.py::TestNode::test_config_with_multiple_multilines test/unit/test_client.py::TestNode::test_config_with_single_command test/unit/test_client.py::TestNode::test_config_with_single_multiline test/unit/test_client.py::TestNode::test_enable_authentication test/unit/test_client.py::TestNode::test_enable_with_config_statement test/unit/test_client.py::TestNode::test_enable_with_multiple_commands test/unit/test_client.py::TestNode::test_enable_with_multiple_unicode_commands test/unit/test_client.py::TestNode::test_enable_with_single_command test/unit/test_client.py::TestNode::test_enable_with_single_extended_command test/unit/test_client.py::TestNode::test_enable_with_single_unicode_command test/unit/test_client.py::TestNode::test_get_config test/unit/test_client.py::TestNode::test_get_config_as_string test/unit/test_client.py::TestNode::test_get_config_raises_type_error test/unit/test_client.py::TestNode::test_get_version_properties_match_version_number_no_match_model test/unit/test_client.py::TestNode::test_get_version_properties_no_match_version_number_match_model test/unit/test_client.py::TestNode::test_no_enable_with_single_command test/unit/test_client.py::TestNode::test_version_properties_populate test/unit/test_client.py::TestClient::test_config_always_has_default_connection test/unit/test_client.py::TestClient::test_config_for_replaces_host_w_name test/unit/test_client.py::TestClient::test_connections_property test/unit/test_client.py::TestClient::test_hosts_for_tag_returns_names test/unit/test_client.py::TestClient::test_hosts_for_tag_returns_none test/unit/test_client.py::TestClient::test_load_config test/unit/test_client.py::TestClient::test_load_config_empty_conf test/unit/test_client.py::TestClient::test_load_config_env_path test/unit/test_client.py::TestClient::test_load_config_for_connection_with_env test/unit/test_client.py::TestClient::test_load_config_for_connection_with_filename test/unit/test_client.py::TestClient::test_load_config_yaml test/unit/test_client.py::TestClient::test_make_connection_raises_typeerror test/unit/test_client.py::TestClient::test_missing_connection_raises_attribute_error test/unit/test_client.py::TestClient::test_node_hasattr_connection test/unit/test_client.py::TestClient::test_node_repr_returns test/unit/test_client.py::TestClient::test_node_returns_cached_model test/unit/test_client.py::TestClient::test_node_returns_cached_startup_config test/unit/test_client.py::TestClient::test_node_returns_cached_version test/unit/test_client.py::TestClient::test_node_returns_cached_version_number test/unit/test_client.py::TestClient::test_node_returns_model test/unit/test_client.py::TestClient::test_node_returns_running_config test/unit/test_client.py::TestClient::test_node_returns_startup_config test/unit/test_client.py::TestClient::test_node_returns_version test/unit/test_client.py::TestClient::test_node_returns_version_number test/unit/test_client.py::TestClient::test_node_str_returns
pyeapi/eapilib.py docs/quickstart.rst pyeapi/client.py docs/configfile.rst
pyeapi/eapilib.py docs/quickstart.rst pyeapi/client.py docs/configfile.rst