1 """Support for Alexa skill auth."""
4 from asyncio
import timeout
5 from datetime
import datetime, timedelta
6 from http
import HTTPStatus
19 from .const
import STORAGE_ACCESS_TOKEN, STORAGE_REFRESH_TOKEN
20 from .diagnostics
import async_redact_lwa_params
22 _LOGGER = logging.getLogger(__name__)
24 LWA_TOKEN_URI =
"https://api.amazon.com/auth/o2/token"
25 LWA_HEADERS = {
"Content-Type":
"application/x-www-form-urlencoded;charset=UTF-8"}
27 PREEMPTIVE_REFRESH_TTL_IN_SECONDS = 300
28 STORAGE_KEY =
"alexa_auth"
30 STORAGE_EXPIRE_TIME =
"expire_time"
34 """Handle authentication to send events to Alexa."""
36 def __init__(self, hass: HomeAssistant, client_id: str, client_secret: str) ->
None:
37 """Initialize the Auth class."""
43 self.
_prefs_prefs: dict[str, Any] |
None =
None
44 self._store: Store =
Store(hass, STORAGE_VERSION, STORAGE_KEY)
49 """Do authentication with an AcceptGrant code."""
53 lwa_params: dict[str, str] = {
54 "grant_type":
"authorization_code",
55 "code": accept_grant_code,
60 "Calling LWA to get the access token (first time), with: %s",
68 """Invalidate access token."""
69 assert self.
_prefs_prefs
is not None
70 self.
_prefs_prefs[STORAGE_ACCESS_TOKEN] =
None
73 """Perform access token or token refresh request."""
75 if self.
_prefs_prefs
is None:
78 assert self.
_prefs_prefs
is not None
80 _LOGGER.debug(
"Token still valid, using it")
81 token: str = self.
_prefs_prefs[STORAGE_ACCESS_TOKEN]
84 if self.
_prefs_prefs[STORAGE_REFRESH_TOKEN]
is None:
85 _LOGGER.debug(
"Token invalid and no refresh token available")
88 lwa_params: dict[str, str] = {
89 "grant_type":
"refresh_token",
90 "refresh_token": self.
_prefs_prefs[STORAGE_REFRESH_TOKEN],
95 _LOGGER.debug(
"Calling LWA to refresh the access token")
100 """Check if a token is already loaded and if it is still valid."""
101 assert self.
_prefs_prefs
is not None
102 if not self.
_prefs_prefs[STORAGE_ACCESS_TOKEN]:
105 expire_time: datetime |
None = dt_util.parse_datetime(
106 self.
_prefs_prefs[STORAGE_EXPIRE_TIME]
108 assert expire_time
is not None
109 preemptive_expire_time = expire_time -
timedelta(
110 seconds=PREEMPTIVE_REFRESH_TTL_IN_SECONDS
113 return dt_util.utcnow() < preemptive_expire_time
117 session = aiohttp_client.async_get_clientsession(self.
hasshass)
118 async
with timeout(10):
119 response = await session.post(
123 allow_redirects=
True,
126 except (TimeoutError, aiohttp.ClientError):
127 _LOGGER.error(
"Timeout calling LWA to get auth token")
130 _LOGGER.debug(
"LWA response header: %s", response.headers)
131 _LOGGER.debug(
"LWA response status: %s", response.status)
133 if response.status != HTTPStatus.OK:
134 _LOGGER.error(
"Error calling LWA to get auth token")
137 response_json = await response.json()
140 access_token: str = response_json[
"access_token"]
141 refresh_token: str = response_json[
"refresh_token"]
142 expires_in: int = response_json[
"expires_in"]
143 expire_time = dt_util.utcnow() +
timedelta(seconds=expires_in)
146 access_token, refresh_token, expire_time.isoformat()
152 """Load preferences with stored tokens."""
155 if self.
_prefs_prefs
is None:
157 STORAGE_ACCESS_TOKEN:
None,
158 STORAGE_REFRESH_TOKEN:
None,
159 STORAGE_EXPIRE_TIME:
None,
163 self, access_token: str, refresh_token: str, expire_time: str
165 """Update user preferences."""
166 if self.
_prefs_prefs
is None:
168 assert self.
_prefs_prefs
is not None
170 if access_token
is not None:
171 self.
_prefs_prefs[STORAGE_ACCESS_TOKEN] = access_token
172 if refresh_token
is not None:
173 self.
_prefs_prefs[STORAGE_REFRESH_TOKEN] = refresh_token
174 if expire_time
is not None:
175 self.
_prefs_prefs[STORAGE_EXPIRE_TIME] = expire_time
None _async_update_preferences(self, str access_token, str refresh_token, str expire_time)
None async_invalidate_access_token(self)
None async_load_preferences(self)
str|None _async_request_new_token(self, dict[str, str] lwa_params)
str|None async_do_auth(self, str accept_grant_code)
bool is_token_valid(self)
str|None async_get_access_token(self)
None __init__(self, HomeAssistant hass, str client_id, str client_secret)
dict[str, str] async_redact_lwa_params(dict[str, str] lwa_params)
None async_load(HomeAssistant hass)
None async_save(self, _T data)