feat: Implement reauthentication flow

This commit is contained in:
Julian Pawlowski 2025-11-06 16:59:41 +00:00
parent 543b1114ed
commit 433558f60b
7 changed files with 141 additions and 62 deletions

View file

@ -88,15 +88,75 @@ class TibberPricesFlowHandler(ConfigFlow, domain=DOMAIN):
"""Create an options flow for this configentry."""
return TibberPricesOptionsFlowHandler()
@staticmethod
def async_get_reauth_flow(entry: ConfigEntry) -> ConfigFlow:
"""Return the reauth flow handler for this integration."""
return TibberPricesReauthFlowHandler(entry)
def is_matching(self, other_flow: dict) -> bool:
"""Return True if match_dict matches this flow."""
return bool(other_flow.get("domain") == DOMAIN)
async def async_step_reauth(self, entry_data: dict[str, Any]) -> ConfigFlowResult: # noqa: ARG002
"""Handle reauth flow when access token becomes invalid."""
self._reauth_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 | None = None) -> ConfigFlowResult:
"""Confirm reauth dialog - prompt for new access token."""
_errors = {}
if user_input is not None:
try:
viewer = await self._get_viewer_details(access_token=user_input[CONF_ACCESS_TOKEN])
except TibberPricesApiClientAuthenticationError as exception:
LOGGER.warning(exception)
_errors["base"] = "auth"
except TibberPricesApiClientCommunicationError as exception:
LOGGER.error(exception)
_errors["base"] = "connection"
except TibberPricesApiClientError as exception:
LOGGER.exception(exception)
_errors["base"] = "unknown"
else:
# Validate that the new token has access to all configured homes
if self._reauth_entry:
# Get all configured home IDs (main entry + subentries)
configured_home_ids = self._get_all_configured_home_ids(self._reauth_entry)
# Get accessible home IDs from the new token
accessible_homes = viewer.get("homes", [])
accessible_home_ids = {home["id"] for home in accessible_homes}
# Check if all configured homes are accessible with the new token
missing_home_ids = configured_home_ids - accessible_home_ids
if missing_home_ids:
# New token doesn't have access to all configured homes
LOGGER.error(
"New access token missing access to configured homes: %s",
", ".join(missing_home_ids),
)
_errors["base"] = "missing_homes"
else:
# Update the config entry with the new access token
self.hass.config_entries.async_update_entry(
self._reauth_entry,
data={
**self._reauth_entry.data,
CONF_ACCESS_TOKEN: user_input[CONF_ACCESS_TOKEN],
},
)
await self.hass.config_entries.async_reload(self._reauth_entry.entry_id)
return self.async_abort(reason="reauth_successful")
return self.async_show_form(
step_id="reauth_confirm",
data_schema=vol.Schema(
{
vol.Required(CONF_ACCESS_TOKEN): TextSelector(
TextSelectorConfig(type=TextSelectorType.TEXT),
),
}
),
errors=_errors,
)
async def async_step_user(
self,
user_input: dict | None = None,
@ -211,6 +271,21 @@ class TibberPricesFlowHandler(ConfigFlow, domain=DOMAIN):
),
)
def _get_all_configured_home_ids(self, main_entry: ConfigEntry) -> set[str]:
"""Get all configured home IDs from main entry and all subentries."""
home_ids = set()
# Add home_id from main entry if it exists
if main_entry.data.get("home_id"):
home_ids.add(main_entry.data["home_id"])
# Add home_ids from all subentries
for entry in self.hass.config_entries.async_entries(DOMAIN):
if entry.data.get("home_id") and entry != main_entry:
home_ids.add(entry.data["home_id"])
return home_ids
@staticmethod
def _get_home_title(home: dict) -> str:
"""Generate a user-friendly title for a home."""
@ -240,52 +315,6 @@ class TibberPricesFlowHandler(ConfigFlow, domain=DOMAIN):
return result["viewer"]
class TibberPricesReauthFlowHandler(ConfigFlow):
"""Handle a reauthentication flow for tibber_prices."""
def __init__(self, entry: ConfigEntry) -> None:
"""Initialize the reauth flow handler."""
self._entry = entry
self._errors: dict[str, str] = {}
async def async_step_user(self, user_input: dict | None = None) -> ConfigFlowResult:
"""Prompt for a new access token."""
if user_input is not None:
try:
await TibberPricesApiClient(
access_token=user_input[CONF_ACCESS_TOKEN],
session=async_create_clientsession(self.hass),
).async_get_viewer_details()
except TibberPricesApiClientAuthenticationError as exception:
LOGGER.warning(exception)
self._errors["base"] = "auth"
except TibberPricesApiClientCommunicationError as exception:
LOGGER.error(exception)
self._errors["base"] = "connection"
except TibberPricesApiClientError as exception:
LOGGER.exception(exception)
self._errors["base"] = "unknown"
else:
self.hass.config_entries.async_update_entry(
self._entry,
data={
**self._entry.data,
CONF_ACCESS_TOKEN: user_input[CONF_ACCESS_TOKEN],
},
)
return self.async_abort(reason="reauth_successful")
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_ACCESS_TOKEN): TextSelector(TextSelectorConfig(type=TextSelectorType.TEXT)),
}
),
errors=self._errors,
)
class TibberPricesSubentryFlowHandler(ConfigSubentryFlow):
"""Handle subentry flows for tibber_prices."""

View file

@ -6,7 +6,7 @@
],
"config_flow": true,
"documentation": "https://github.com/jpawlowski/hass.tibber_prices",
"integration_type": "hub",
"integration_type": "service",
"iot_class": "cloud_polling",
"issue_tracker": "https://github.com/jpawlowski/hass.tibber_prices/issues",
"requirements": [

View file

@ -23,6 +23,14 @@
},
"title": "Wähle ein Zuhause",
"submit": "Zuhause auswählen"
},
"reauth_confirm": {
"title": "Tibber-Integration erneut authentifizieren",
"description": "Der Zugriffstoken für Tibber ist nicht mehr gültig. Bitte gib einen neuen API-Zugriffstoken ein, um diese Integration weiter zu nutzen.\n\nUm einen neuen API-Zugriffstoken zu generieren, besuche https://developer.tibber.com.",
"data": {
"access_token": "API-Zugriffstoken"
},
"submit": "Token aktualisieren"
}
},
"error": {
@ -30,12 +38,14 @@
"connection": "Verbindung zu Tibber nicht möglich. Bitte überprüfe deine Internetverbindung.",
"unknown": "Ein unerwarteter Fehler ist aufgetreten. Bitte überprüfe die Logs für Details.",
"cannot_connect": "Verbindung fehlgeschlagen",
"invalid_access_token": "Ungültiges Zugriffstoken"
"invalid_access_token": "Ungültiges Zugriffstoken",
"missing_homes": "Der neue Zugriffstoken hat keinen Zugriff auf alle konfigurierten Zuhause. Bitte verwende einen Zugriffstoken, der Zugriff auf die gleichen Tibber-Zuhause hat."
},
"abort": {
"already_configured": "Integration ist bereits konfiguriert",
"entry_not_found": "Tibber Konfigurationseintrag nicht gefunden.",
"setup_complete": "Einrichtung abgeschlossen! Du kannst zusätzliche Optionen für Tibber Preise in den Integrationsoptionen ändern, nachdem du diesen Dialog geschlossen hast."
"setup_complete": "Einrichtung abgeschlossen! Du kannst zusätzliche Optionen für Tibber Preise in den Integrationsoptionen ändern, nachdem du diesen Dialog geschlossen hast.",
"reauth_successful": "Erneute Authentifizierung erfolgreich. Die Integration wurde mit dem neuen Zugriffstoken aktualisiert."
}
},
"config_subentries": {

View file

@ -23,6 +23,14 @@
},
"title": "Pick a home",
"submit": "Select Home"
},
"reauth_confirm": {
"title": "Reauthenticate Tibber Integration",
"description": "The access token for Tibber is no longer valid. Please enter a new API access token to continue using this integration.\n\nTo generate a new API access token, visit https://developer.tibber.com.",
"data": {
"access_token": "API access token"
},
"submit": "Update Token"
}
},
"error": {
@ -30,12 +38,14 @@
"connection": "Unable to connect to Tibber. Please check your internet connection.",
"unknown": "Unexpected error",
"cannot_connect": "Failed to connect",
"invalid_access_token": "Invalid access token"
"invalid_access_token": "Invalid access token",
"missing_homes": "The new access token does not have access to all configured homes. Please use an access token that has access to the same Tibber homes."
},
"abort": {
"already_configured": "Integration is already configured",
"entry_not_found": "Tibber configuration entry not found.",
"setup_complete": "Setup complete! You can change additional options for Tibber Prices in the integration's options after closing this dialog."
"setup_complete": "Setup complete! You can change additional options for Tibber Prices in the integration's options after closing this dialog.",
"reauth_successful": "Reauthentication successful. The integration has been updated with the new access token."
}
},
"config_subentries": {

View file

@ -23,6 +23,14 @@
},
"title": "Velg et hjem",
"submit": "Velg Hjem"
},
"reauth_confirm": {
"title": "Autentiser Tibber-integrasjonen på nytt",
"description": "Tilgangstokenet for Tibber er ikke lenger gyldig. Vennligst skriv inn et nytt API-tilgangstoken for å fortsette å bruke denne integrasjonen.\n\nFor å generere et nytt API-tilgangstoken, besøk https://developer.tibber.com.",
"data": {
"access_token": "API-tilgangstoken"
},
"submit": "Oppdater Token"
}
},
"error": {
@ -30,12 +38,14 @@
"connection": "Kan ikke koble til Tibber. Vennligst sjekk internettforbindelsen din.",
"unknown": "Uventet feil",
"cannot_connect": "Tilkobling mislyktes",
"invalid_access_token": "Ugyldig tilgangstoken"
"invalid_access_token": "Ugyldig tilgangstoken",
"missing_homes": "Det nye tilgangstokenet har ikke tilgang til alle konfigurerte hjem. Vennligst bruk et tilgangstoken som har tilgang til de samme Tibber-hjemmene."
},
"abort": {
"already_configured": "Integrasjonen er allerede konfigurert",
"entry_not_found": "Tibber-konfigurasjonsoppføring ikke funnet.",
"setup_complete": "Oppsett fullført! Du kan endre flere alternativer for Tibber Priser i integrasjonens innstillinger etter å ha lukket denne dialogen."
"setup_complete": "Oppsett fullført! Du kan endre flere alternativer for Tibber Priser i integrasjonens innstillinger etter å ha lukket denne dialogen.",
"reauth_successful": "Autentisering på nytt vellykket. Integrasjonen har blitt oppdatert med det nye tilgangstokenet."
}
},
"config_subentries": {

View file

@ -23,6 +23,14 @@
},
"title": "Kies een woning",
"submit": "Selecteer Woning"
},
"reauth_confirm": {
"title": "Tibber-integratie opnieuw authenticeren",
"description": "Het toegangstoken voor Tibber is niet langer geldig. Voer een nieuw API-toegangstoken in om deze integratie te blijven gebruiken.\n\nOm een nieuw API-toegangstoken te genereren, bezoek https://developer.tibber.com.",
"data": {
"access_token": "API-toegangstoken"
},
"submit": "Token Bijwerken"
}
},
"error": {
@ -30,12 +38,14 @@
"connection": "Kan geen verbinding maken met Tibber. Controleer je internetverbinding.",
"unknown": "Onverwachte fout",
"cannot_connect": "Verbinding mislukt",
"invalid_access_token": "Ongeldig toegangstoken"
"invalid_access_token": "Ongeldig toegangstoken",
"missing_homes": "Het nieuwe toegangstoken heeft geen toegang tot alle geconfigureerde woningen. Gebruik een toegangstoken dat toegang heeft tot dezelfde Tibber-woningen."
},
"abort": {
"already_configured": "Integratie is al geconfigureerd",
"entry_not_found": "Tibber configuratie-invoer niet gevonden.",
"setup_complete": "Installatie voltooid! Je kunt extra opties voor Tibber Prijzen wijzigen in de integratie-opties na het sluiten van dit venster."
"setup_complete": "Installatie voltooid! Je kunt extra opties voor Tibber Prijzen wijzigen in de integratie-opties na het sluiten van dit venster.",
"reauth_successful": "Herauthenticatie succesvol. De integratie is bijgewerkt met het nieuwe toegangstoken."
}
},
"config_subentries": {

View file

@ -23,6 +23,14 @@
},
"title": "Välj ett hem",
"submit": "Välj Hem"
},
"reauth_confirm": {
"title": "Autentisera Tibber-integrationen igen",
"description": "Åtkomsttoken för Tibber är inte längre giltigt. Vänligen ange en ny API-åtkomsttoken för att fortsätta använda denna integration.\n\nFör att generera en ny API-åtkomsttoken, besök https://developer.tibber.com.",
"data": {
"access_token": "API-åtkomsttoken"
},
"submit": "Uppdatera Token"
}
},
"error": {
@ -30,12 +38,14 @@
"connection": "Kan inte ansluta till Tibber. Kontrollera din internetanslutning.",
"unknown": "Oväntat fel",
"cannot_connect": "Anslutning misslyckades",
"invalid_access_token": "Ogiltigt åtkomsttoken"
"invalid_access_token": "Ogiltigt åtkomsttoken",
"missing_homes": "Den nya åtkomsttoken har inte tillgång till alla konfigurerade hem. Använd en åtkomsttoken som har tillgång till samma Tibber-hem."
},
"abort": {
"already_configured": "Integrationen är redan konfigurerad",
"entry_not_found": "Tibber-konfigurationspost hittades inte.",
"setup_complete": "Installation klar! Du kan ändra fler alternativ för Tibber Priser i integrationens alternativ efter att ha stängt denna dialog."
"setup_complete": "Installation klar! Du kan ändra fler alternativ för Tibber Priser i integrationens alternativ efter att ha stängt denna dialog.",
"reauth_successful": "Återautentisering lyckades. Integrationen har uppdaterats med den nya åtkomsttoken."
}
},
"config_subentries": {