diff --git a/homeassistant/components/vicare/__init__.py b/homeassistant/components/vicare/__init__.py
index 7a297ca8113e7004162cbed7dbdc83d0111e12ad..76de3a8a7ace6d6d608567779abfab9c69cc3aa3 100644
--- a/homeassistant/components/vicare/__init__.py
+++ b/homeassistant/components/vicare/__init__.py
@@ -10,10 +10,15 @@ from typing import Any
 
 from PyViCare.PyViCare import PyViCare
 from PyViCare.PyViCareDevice import Device
+from PyViCare.PyViCareUtils import (
+    PyViCareInvalidConfigurationError,
+    PyViCareInvalidCredentialsError,
+)
 
 from homeassistant.config_entries import ConfigEntry
 from homeassistant.const import CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME
 from homeassistant.core import HomeAssistant
+from homeassistant.exceptions import ConfigEntryAuthFailed
 from homeassistant.helpers.storage import STORAGE_DIR
 
 from .const import (
@@ -53,7 +58,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
     hass.data[DOMAIN] = {}
     hass.data[DOMAIN][entry.entry_id] = {}
 
-    await hass.async_add_executor_job(setup_vicare_api, hass, entry)
+    try:
+        await hass.async_add_executor_job(setup_vicare_api, hass, entry)
+    except (PyViCareInvalidConfigurationError, PyViCareInvalidCredentialsError) as err:
+        raise ConfigEntryAuthFailed("Authentication failed") from err
 
     await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
 
diff --git a/homeassistant/components/vicare/config_flow.py b/homeassistant/components/vicare/config_flow.py
index 5b2d3afa427f3ef40986270c7f5a80c1fd3d454f..87bfcf7b146d900b7238eda20fa2461b2206ca85 100644
--- a/homeassistant/components/vicare/config_flow.py
+++ b/homeassistant/components/vicare/config_flow.py
@@ -1,6 +1,7 @@
 """Config flow for ViCare integration."""
 from __future__ import annotations
 
+from collections.abc import Mapping
 import logging
 from typing import Any
 
@@ -28,11 +29,28 @@ from .const import (
 
 _LOGGER = logging.getLogger(__name__)
 
+REAUTH_SCHEMA = vol.Schema(
+    {
+        vol.Required(CONF_PASSWORD): cv.string,
+        vol.Required(CONF_CLIENT_ID): cv.string,
+    }
+)
+
+USER_SCHEMA = REAUTH_SCHEMA.extend(
+    {
+        vol.Required(CONF_USERNAME): cv.string,
+        vol.Required(CONF_HEATING_TYPE, default=DEFAULT_HEATING_TYPE.value): vol.In(
+            [e.value for e in HeatingType]
+        ),
+    }
+)
+
 
 class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
     """Handle a config flow for ViCare."""
 
     VERSION = 1
+    entry: config_entries.ConfigEntry | None
 
     async def async_step_user(
         self, user_input: dict[str, Any] | None = None
@@ -41,14 +59,6 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
         if self._async_current_entries():
             return self.async_abort(reason="single_instance_allowed")
 
-        data_schema = {
-            vol.Required(CONF_USERNAME): cv.string,
-            vol.Required(CONF_PASSWORD): cv.string,
-            vol.Required(CONF_CLIENT_ID): cv.string,
-            vol.Required(CONF_HEATING_TYPE, default=DEFAULT_HEATING_TYPE.value): vol.In(
-                [e.value for e in HeatingType]
-            ),
-        }
         errors: dict[str, str] = {}
 
         if user_input is not None:
@@ -63,7 +73,45 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
 
         return self.async_show_form(
             step_id="user",
-            data_schema=vol.Schema(data_schema),
+            data_schema=USER_SCHEMA,
+            errors=errors,
+        )
+
+    async def async_step_reauth(self, entry_data: Mapping[str, Any]) -> FlowResult:
+        """Handle re-authentication with ViCare."""
+        self.entry = self.hass.config_entries.async_get_entry(self.context["entry_id"])
+        return await self.async_step_reauth_confirm()
+
+    async def async_step_reauth_confirm(
+        self, user_input: dict[str, Any] | None = None
+    ) -> FlowResult:
+        """Confirm re-authentication with ViCare."""
+        errors: dict[str, str] = {}
+        assert self.entry is not None
+
+        if user_input:
+            data = {
+                **self.entry.data,
+                **user_input,
+            }
+
+            try:
+                await self.hass.async_add_executor_job(vicare_login, self.hass, data)
+            except (PyViCareInvalidConfigurationError, PyViCareInvalidCredentialsError):
+                errors["base"] = "invalid_auth"
+            else:
+                self.hass.config_entries.async_update_entry(
+                    self.entry,
+                    data=data,
+                )
+                await self.hass.config_entries.async_reload(self.entry.entry_id)
+                return self.async_abort(reason="reauth_successful")
+
+        return self.async_show_form(
+            step_id="reauth_confirm",
+            data_schema=self.add_suggested_values_to_schema(
+                REAUTH_SCHEMA, self.entry.data
+            ),
             errors=errors,
         )
 
diff --git a/homeassistant/components/vicare/strings.json b/homeassistant/components/vicare/strings.json
index 056a4df7920e7c27104e4fb88f43c14628f92155..2dc1eecd1e4aea342d5e4abd44b6dfe5209ce126 100644
--- a/homeassistant/components/vicare/strings.json
+++ b/homeassistant/components/vicare/strings.json
@@ -10,6 +10,13 @@
           "client_id": "Client ID",
           "heating_type": "Heating type"
         }
+      },
+      "reauth_confirm": {
+        "description": "Please verify credentials.",
+        "data": {
+          "password": "[%key:common::config_flow::data::password%]",
+          "client_id": "[%key:component::vicare::config::step::user::data::client_id%]"
+        }
       }
     },
     "error": {
@@ -17,6 +24,7 @@
     },
     "abort": {
       "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",
+      "reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
       "unknown": "[%key:common::config_flow::error::unknown%]"
     }
   },
diff --git a/tests/components/vicare/test_config_flow.py b/tests/components/vicare/test_config_flow.py
index 7f70c13f0b01e32b7c44e62138a086bfadc1dfcf..283f06b754d22ddc325e91aa3a632a7e31601f66 100644
--- a/tests/components/vicare/test_config_flow.py
+++ b/tests/components/vicare/test_config_flow.py
@@ -10,7 +10,7 @@ from syrupy.assertion import SnapshotAssertion
 
 from homeassistant.components import dhcp
 from homeassistant.components.vicare.const import DOMAIN
-from homeassistant.config_entries import SOURCE_DHCP, SOURCE_USER
+from homeassistant.config_entries import SOURCE_DHCP, SOURCE_REAUTH, SOURCE_USER
 from homeassistant.const import CONF_CLIENT_ID, CONF_PASSWORD, CONF_USERNAME
 from homeassistant.core import HomeAssistant
 from homeassistant.data_entry_flow import FlowResultType
@@ -93,6 +93,61 @@ async def test_user_create_entry(
     mock_setup_entry.assert_called_once()
 
 
+async def test_step_reauth(hass: HomeAssistant, mock_setup_entry: AsyncMock) -> None:
+    """Test reauth flow."""
+    new_password = "ABCD"
+    new_client_id = "EFGH"
+    config_entry = MockConfigEntry(
+        domain=DOMAIN,
+        data=VALID_CONFIG,
+    )
+    config_entry.add_to_hass(hass)
+
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN,
+        context={"source": SOURCE_REAUTH, "entry_id": config_entry.entry_id},
+        data=VALID_CONFIG,
+    )
+    assert result["type"] == FlowResultType.FORM
+    assert result["step_id"] == "reauth_confirm"
+
+    # test PyViCareInvalidConfigurationError
+    with patch(
+        f"{MODULE}.config_flow.vicare_login",
+        side_effect=PyViCareInvalidConfigurationError(
+            {"error": "foo", "error_description": "bar"}
+        ),
+    ):
+        result = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            user_input={CONF_PASSWORD: new_password, CONF_CLIENT_ID: new_client_id},
+        )
+        assert result["type"] == FlowResultType.FORM
+        assert result["step_id"] == "reauth_confirm"
+        assert result["errors"] == {"base": "invalid_auth"}
+
+    # test success
+    with patch(
+        f"{MODULE}.config_flow.vicare_login",
+        return_value=None,
+    ):
+        result = await hass.config_entries.flow.async_configure(
+            result["flow_id"],
+            user_input={CONF_PASSWORD: new_password, CONF_CLIENT_ID: new_client_id},
+        )
+        assert result["type"] == FlowResultType.ABORT
+        assert result["reason"] == "reauth_successful"
+
+        assert len(hass.config_entries.async_entries()) == 1
+        assert (
+            hass.config_entries.async_entries()[0].data[CONF_PASSWORD] == new_password
+        )
+        assert (
+            hass.config_entries.async_entries()[0].data[CONF_CLIENT_ID] == new_client_id
+        )
+        await hass.async_block_till_done()
+
+
 async def test_form_dhcp(
     hass: HomeAssistant, mock_setup_entry: AsyncMock, snapshot: SnapshotAssertion
 ) -> None: