Skip to content

Commit

Permalink
Implement Reauth
Browse files Browse the repository at this point in the history
  • Loading branch information
ReneNulschDE committed Mar 13, 2021
1 parent e1f6f79 commit aefd458
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 27 deletions.
17 changes: 16 additions & 1 deletion custom_components/mbapi2020/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@

from datetime import datetime

from homeassistant.config_entries import ConfigEntry
from homeassistant.config_entries import ConfigEntry, SOURCE_REAUTH
from homeassistant.const import (
CONF_USERNAME,
EVENT_HOMEASSISTANT_STOP,
LENGTH_KILOMETERS,
LENGTH_MILES,
Expand Down Expand Up @@ -91,6 +92,20 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry):
region=region
)


token_info = await mercedes.client.oauth.async_get_cached_token()

if token_info is None:
LOGGER.error("Authentication failed. Please reauthenticate.")
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_REAUTH},
data=config_entry,
)
)
return False

masterdata = await mercedes.client.api.get_user_info()
mercedes.client._write_debug_json_output(masterdata, "md")
for car in masterdata:
Expand Down
47 changes: 39 additions & 8 deletions custom_components/mbapi2020/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
import voluptuous as vol

from homeassistant import config_entries
from homeassistant.const import CONF_HOST, CONF_OFFSET, CONF_PASSWORD, CONF_USERNAME
from homeassistant.config_entries import SOURCE_REAUTH
from homeassistant.const import (
CONF_PASSWORD,
CONF_USERNAME,
CONF_SOURCE
)
from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client
import homeassistant.helpers.config_validation as cv

from .const import ( # pylint:disable=unused-import
CONF_ALLOWED_REGIONS,
Expand All @@ -20,7 +24,6 @@
CONF_PIN,
CONF_REGION,
DOMAIN,
DEFAULT_CACHE_PATH,
DEFAULT_LOCALE,
DEFAULT_COUNTRY_CODE,
VERIFY_SSL
Expand Down Expand Up @@ -48,28 +51,37 @@ class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):

def __init__(self):
"""Initialize component."""
self._existing_entry = None
self.data = None
self.reauth_mode = False


async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}

if user_input is not None:

await self.async_set_unique_id(f"{user_input[CONF_USERNAME]}")

if not self.reauth_mode:
self._abort_if_unique_id_configured()

session = aiohttp_client.async_get_clientsession(self.hass, VERIFY_SSL)
nonce = str(uuid.uuid4())
user_input["nonce"] = nonce
user_input["nonce"] = nonce

client = Client(session=session, hass=self.hass, region=user_input[CONF_REGION])
try:
result = await client.oauth.request_pin(user_input[CONF_USERNAME], nonce)
except MbapiError as error:
errors = error
errors = error

if not errors:
self.data = user_input
return await self.async_step_pin()
else:
_LOGGER.error(f"Request Pin Error: {errors}")
_LOGGER.error("Request Pin Error: %s", errors)

return self.async_show_form(
step_id="user", data_schema=SCHEMA_STEP_USER, errors= "Error unknow" #errors
Expand All @@ -88,18 +100,37 @@ async def async_step_pin(self, user_input=None):

client = Client(session=session, hass=self.hass, region=self.data[CONF_REGION])
try:
result = await client.oauth.request_access_token(self.data[CONF_USERNAME], pin, nonce)
result = await client.oauth.request_access_token(
self.data[CONF_USERNAME], pin, nonce)
except MbapiError as error:
_LOGGER.error(f"Request Token Error: {errors}")
_LOGGER.error("Request Token Error: %s", errors)
errors = error

if not errors:
_LOGGER.debug("token received")
self.data["token"] = result

if self.reauth_mode:
self.hass.async_create_task(
self.hass.config_entries.async_reload(self._existing_entry.entry_id)
)
return self.async_abort(reason="reauth_successful")

return self.async_create_entry(title=DOMAIN, data=self.data)

return self.async_show_form(step_id="pin", data_schema=SCHEMA_STEP_PIN, errors=errors)


async def async_step_reauth(self, user_input=None):
"""Get new tokens for a config entry that can't authenticate."""

self.reauth_mode = True
self._existing_entry = user_input

return self.async_show_form(
step_id="user", data_schema=SCHEMA_STEP_USER, errors= "Error unknow" #errors
)

@staticmethod
@callback
def async_get_options_flow(config_entry):
Expand Down
1 change: 1 addition & 0 deletions custom_components/mbapi2020/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@

VERIFY_SSL = True

LOGIN_APP_ID_EU = "01398c1c-dc45-4b42-882b-9f5ba9f175f1"
LOGIN_BASE_URI = "https://id.mercedes-benz.com"
LOGIN_BASE_URI_NA = "https://id.mercedes-benz.com"
LOGIN_BASE_URI_PA = "https://id.mercedes-benz.com"
Expand Down
2 changes: 1 addition & 1 deletion custom_components/mbapi2020/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"codeowners": [
"@ReneNulschDE"
],
"version": "0.5.1"
"version": "0.5.2"
}
31 changes: 16 additions & 15 deletions custom_components/mbapi2020/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import uuid
from typing import Optional

import asyncio

from aiohttp import ClientSession, ClientTimeout
from aiohttp.client_exceptions import ClientError

Expand All @@ -15,6 +13,7 @@
REGION_EUROPE,
REGION_NORAM,
REGION_APAC,
LOGIN_APP_ID_EU,
LOGIN_BASE_URI,
LOGIN_BASE_URI_NA,
LOGIN_BASE_URI_PA,
Expand Down Expand Up @@ -85,9 +84,10 @@ async def async_refresh_access_token(self, refresh_token: str):

token_info = await self._async_request(method="post", url=url, data=data, headers=headers)

token_info = self._add_custom_values_to_token_info(token_info)
self._save_token_info(token_info)
self.token = token_info
if token_info is not None:
token_info = self._add_custom_values_to_token_info(token_info)
self._save_token_info(token_info)
self.token = token_info

return token_info

Expand All @@ -109,12 +109,13 @@ async def request_access_token(self, email: str, pin: str, nonce: str):

token_info = await self._async_request("post", url, data=data, headers=headers)

token_info = self._add_custom_values_to_token_info(token_info)
self._save_token_info(token_info)
self.token = token_info

return token_info
if token_info is not None:
token_info = self._add_custom_values_to_token_info(token_info)
self._save_token_info(token_info)
self.token = token_info
return token_info

return None

async def async_get_cached_token(self):
""" Gets a cached auth token
Expand All @@ -138,8 +139,11 @@ async def async_get_cached_token(self):
return token_info

def is_token_expired(self, token_info):
now = int(time.time())
return token_info["expires_at"] - now < 60
if token_info is not None:
now = int(time.time())
return token_info["expires_at"] - now < 60

return True

def _save_token_info(self, token_info):
_LOGGER.debug("start: _save_token_info to %s", self.cache_path)
Expand Down Expand Up @@ -233,11 +237,8 @@ async def _async_request(self, method: str, url: str, data: str = "", **kwargs)
return await resp.json(content_type=None)
except ClientError as err:
_LOGGER.error(f"Error requesting data from {url}: {err}")
raise RequestError(f"Error requesting data from {url}: {err}")
except Exception as e:
_LOGGER.error(f"Error requesting data from {url}: {e}")
finally:
if not use_running_session:
await session.close()


57 changes: 57 additions & 0 deletions custom_components/mbapi2020/strings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"config": {
"abort": {
"already_configured": "Component is configured already.",
"reauth_successful": "Reauth successful! Component reload in progress."
},
"error": {
"cannot_connect": "cannot_connect",
"invalid_auth": "invalid_auth",
"unknown": "unknown"
},
"step": {
"user": {
"description": "Enter your Mercedes ME login account (email adress)",
"title": "Set up the Mercedes ME 2020 connection",
"data": {
"username": "MB username - email address",
"region": "Region"
}
},
"pin": {
"description": "You have received a PIN code via email. Please enter the PIN code (six numbers)",
"title": "Connection setup - PIN",
"data": {
"password": "PIN"
}
}
}
},
"options": {
"step": {
"init": {
"data": {
"country_code": "Country Code",
"locale": "Locale",
"excluded_cars": "VINs excluded (comma-sep)",
"pin": "Security PIN (to be created in mobile app) - Enter 0 to delete the value from the configuration",
"cap_check_disabled": "Disable capabilities check",
"save_files": "DEBUG ONLY: Enable save server messages to the messages folder"
},
"description": "Configure your options. Some changes require a restart of Home Assistant. You need to restart HA after PIN change.",
"title": "Mercedes ME 2020 Options"
}
},
"abort": {
"already_configured": "Component is configured already.",
"reauth_successful": "Reauth successful! Component reload in progress."
}
},
"system_health": {
"info": {
"api_endpoint_reachable": "MB API endpoint reachable",
"websocket_connection_state": "MB Websocket connection state"
}
},
"title": "MercedesME 2020"
}
7 changes: 6 additions & 1 deletion custom_components/mbapi2020/translations/en.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"config": {
"abort": {
"already_configured": "Component is configured already."
"already_configured": "Component is configured already.",
"reauth_successful": "Reauth successful! Component reload in progress."
},
"error": {
"cannot_connect": "cannot_connect",
Expand Down Expand Up @@ -40,6 +41,10 @@
"description": "Configure your options. Some changes require a restart of Home Assistant. You need to restart HA after PIN change.",
"title": "Mercedes ME 2020 Options"
}
},
"abort": {
"already_configured": "Component is configured already.",
"reauth_successful": "Reauth successful! Component reload in progress."
}
},
"system_health": {
Expand Down
7 changes: 6 additions & 1 deletion custom_components/mbapi2020/translations/it.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"config": {
"abort": {
"already_configured": "Componente già configurato."
"already_configured": "Componente già configurato.",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
},
"error": {
"cannot_connect": "Impossibile connettersi al server.",
Expand Down Expand Up @@ -38,6 +39,10 @@
"description": "Configura le opzioni. Alcuni cambiamenti richiedono il riavvio di Home Assistant.",
"title": "Mercedes ME 2020 - Opzioni"
}
},
"abort": {
"already_configured": "Componente già configurato.",
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
}
},
"system_health": {
Expand Down

0 comments on commit aefd458

Please sign in to comment.