Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Component to allow users to login and get tokens.
2 
3 # POST /auth/token
4 
5 This is an OAuth2 endpoint for granting tokens. We currently support the grant
6 types "authorization_code" and "refresh_token". Because we follow the OAuth2
7 spec, data should be send in formatted as x-www-form-urlencoded. Examples will
8 be in JSON as it's more readable.
9 
10 ## Grant type authorization_code
11 
12 Exchange the authorization code retrieved from the login flow for tokens.
13 
14 {
15  "client_id": "https://hassbian.local:8123/",
16  "grant_type": "authorization_code",
17  "code": "411ee2f916e648d691e937ae9344681e"
18 }
19 
20 Return value will be the access and refresh tokens. The access token will have
21 a limited expiration. New access tokens can be requested using the refresh
22 token. The value ha_auth_provider will contain the auth provider type that was
23 used to authorize the refresh token.
24 
25 {
26  "access_token": "ABCDEFGH",
27  "expires_in": 1800,
28  "refresh_token": "IJKLMNOPQRST",
29  "token_type": "Bearer",
30  "ha_auth_provider": "homeassistant"
31 }
32 
33 ## Grant type refresh_token
34 
35 Request a new access token using a refresh token.
36 
37 {
38  "client_id": "https://hassbian.local:8123/",
39  "grant_type": "refresh_token",
40  "refresh_token": "IJKLMNOPQRST"
41 }
42 
43 Return value will be a new access token. The access token will have
44 a limited expiration.
45 
46 {
47  "access_token": "ABCDEFGH",
48  "expires_in": 1800,
49  "token_type": "Bearer"
50 }
51 
52 ## Revoking a refresh token
53 
54 It is also possible to revoke a refresh token and all access tokens that have
55 ever been granted by that refresh token. Response code will ALWAYS be 200.
56 
57 {
58  "token": "IJKLMNOPQRST",
59  "action": "revoke"
60 }
61 
62 # Websocket API
63 
64 ## Get current user
65 
66 Send websocket command `auth/current_user` will return current user of the
67 active websocket connection.
68 
69 {
70  "id": 10,
71  "type": "auth/current_user",
72 }
73 
74 The result payload likes
75 
76 {
77  "id": 10,
78  "type": "result",
79  "success": true,
80  "result": {
81  "id": "USER_ID",
82  "name": "John Doe",
83  "is_owner": true,
84  "credentials": [{
85  "auth_provider_type": "homeassistant",
86  "auth_provider_id": null
87  }],
88  "mfa_modules": [{
89  "id": "totp",
90  "name": "TOTP",
91  "enabled": true
92  }]
93  }
94 }
95 
96 ## Create a long-lived access token
97 
98 Send websocket command `auth/long_lived_access_token` will create
99 a long-lived access token for current user. Access token will not be saved in
100 Home Assistant. User need to record the token in secure place.
101 
102 {
103  "id": 11,
104  "type": "auth/long_lived_access_token",
105  "client_name": "GPS Logger",
106  "lifespan": 365
107 }
108 
109 Result will be a long-lived access token:
110 
111 {
112  "id": 11,
113  "type": "result",
114  "success": true,
115  "result": "ABCDEFGH"
116 }
117 
118 
119 # POST /auth/external/callback
120 
121 This is an endpoint for OAuth2 Authorization callbacks used by integrations
122 that link accounts with other cloud providers using LocalOAuth2Implementation
123 as part of a config flow.
124 """
125 
126 from __future__ import annotations
127 
128 import asyncio
129 from collections.abc import Callable
130 from datetime import datetime, timedelta
131 from http import HTTPStatus
132 from logging import getLogger
133 from typing import Any, cast
134 import uuid
135 
136 from aiohttp import web
137 from multidict import MultiDictProxy
138 import voluptuous as vol
139 
140 from homeassistant.auth import InvalidAuthError
141 from homeassistant.auth.models import (
142  TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN,
143  Credentials,
144  RefreshToken,
145  User,
146 )
147 from homeassistant.components import websocket_api
148 from homeassistant.components.http import KEY_HASS
150  async_sign_path,
151  async_user_not_allowed_do_auth,
152 )
153 from homeassistant.components.http.ban import log_invalid_auth
154 from homeassistant.components.http.data_validator import RequestDataValidator
155 from homeassistant.components.http.view import HomeAssistantView
156 from homeassistant.core import HomeAssistant, callback
157 from homeassistant.helpers import config_validation as cv
158 from homeassistant.helpers.config_entry_oauth2_flow import OAuth2AuthorizeCallbackView
159 from homeassistant.helpers.typing import ConfigType
160 from homeassistant.loader import bind_hass
161 from homeassistant.util import dt as dt_util
162 from homeassistant.util.hass_dict import HassKey
163 
164 from . import indieauth, login_flow, mfa_setup_flow
165 
166 DOMAIN = "auth"
167 
168 type StoreResultType = Callable[[str, Credentials], str]
169 type RetrieveResultType = Callable[[str, str], Credentials | None]
170 DATA_STORE: HassKey[StoreResultType] = HassKey(DOMAIN)
171 CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
172 
173 DELETE_CURRENT_TOKEN_DELAY = 2
174 
175 
176 @bind_hass
178  hass: HomeAssistant, client_id: str, credential: Credentials
179 ) -> str:
180  """Create an authorization code to fetch tokens."""
181  return hass.data[DATA_STORE](client_id, credential)
182 
183 
184 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
185  """Component to allow users to login."""
186  store_result, retrieve_result = _create_auth_code_store()
187 
188  hass.data[DATA_STORE] = store_result
189 
190  hass.http.register_view(TokenView(retrieve_result))
191  hass.http.register_view(RevokeTokenView())
192  hass.http.register_view(LinkUserView(retrieve_result))
193  hass.http.register_view(OAuth2AuthorizeCallbackView())
194 
195  websocket_api.async_register_command(hass, websocket_current_user)
196  websocket_api.async_register_command(hass, websocket_create_long_lived_access_token)
197  websocket_api.async_register_command(hass, websocket_refresh_tokens)
198  websocket_api.async_register_command(hass, websocket_delete_refresh_token)
199  websocket_api.async_register_command(hass, websocket_delete_all_refresh_tokens)
200  websocket_api.async_register_command(hass, websocket_sign_path)
201  websocket_api.async_register_command(hass, websocket_refresh_token_set_expiry)
202 
203  login_flow.async_setup(hass, store_result)
204  mfa_setup_flow.async_setup(hass)
205 
206  return True
207 
208 
209 class RevokeTokenView(HomeAssistantView):
210  """View to revoke tokens."""
211 
212  url = "/auth/revoke"
213  name = "api:auth:revocation"
214  requires_auth = False
215  cors_allowed = True
216 
217  async def post(self, request: web.Request) -> web.Response:
218  """Revoke a token."""
219  hass = request.app[KEY_HASS]
220  data = cast(MultiDictProxy[str], await request.post())
221 
222  # OAuth 2.0 Token Revocation [RFC7009]
223  # 2.2 The authorization server responds with HTTP status code 200
224  # if the token has been revoked successfully or if the client
225  # submitted an invalid token.
226  if (token := data.get("token")) is None:
227  return web.Response(status=HTTPStatus.OK)
228 
229  refresh_token = hass.auth.async_get_refresh_token_by_token(token)
230 
231  if refresh_token is None:
232  return web.Response(status=HTTPStatus.OK)
233 
234  hass.auth.async_remove_refresh_token(refresh_token)
235  return web.Response(status=HTTPStatus.OK)
236 
237 
238 class TokenView(HomeAssistantView):
239  """View to issue tokens."""
240 
241  url = "/auth/token"
242  name = "api:auth:token"
243  requires_auth = False
244  cors_allowed = True
245 
246  def __init__(self, retrieve_auth: RetrieveResultType) -> None:
247  """Initialize the token view."""
248  self._retrieve_auth_retrieve_auth = retrieve_auth
249 
250  @log_invalid_auth
251  async def post(self, request: web.Request) -> web.Response:
252  """Grant a token."""
253  hass = request.app[KEY_HASS]
254  data = cast(MultiDictProxy[str], await request.post())
255 
256  grant_type = data.get("grant_type")
257 
258  # IndieAuth 6.3.5
259  # The revocation endpoint is the same as the token endpoint.
260  # The revocation request includes an additional parameter,
261  # action=revoke.
262  if data.get("action") == "revoke":
263  # action=revoke is deprecated. Use /auth/revoke instead.
264  # Keep here for backwards compat
265  return await RevokeTokenView.post(self, request) # type: ignore[arg-type]
266 
267  if grant_type == "authorization_code":
268  return await self._async_handle_auth_code_async_handle_auth_code(hass, data, request)
269 
270  if grant_type == "refresh_token":
271  return await self._async_handle_refresh_token_async_handle_refresh_token(hass, data, request)
272 
273  return self.json(
274  {"error": "unsupported_grant_type"}, status_code=HTTPStatus.BAD_REQUEST
275  )
276 
278  self,
279  hass: HomeAssistant,
280  data: MultiDictProxy[str],
281  request: web.Request,
282  ) -> web.Response:
283  """Handle authorization code request."""
284  client_id = data.get("client_id")
285  if client_id is None or not indieauth.verify_client_id(client_id):
286  return self.json(
287  {"error": "invalid_request", "error_description": "Invalid client id"},
288  status_code=HTTPStatus.BAD_REQUEST,
289  )
290 
291  if (code := data.get("code")) is None:
292  return self.json(
293  {"error": "invalid_request", "error_description": "Invalid code"},
294  status_code=HTTPStatus.BAD_REQUEST,
295  )
296 
297  credential = self._retrieve_auth_retrieve_auth(client_id, code)
298 
299  if credential is None or not isinstance(credential, Credentials):
300  return self.json(
301  {"error": "invalid_request", "error_description": "Invalid code"},
302  status_code=HTTPStatus.BAD_REQUEST,
303  )
304 
305  user = await hass.auth.async_get_or_create_user(credential)
306 
307  if user_access_error := async_user_not_allowed_do_auth(hass, user):
308  return self.json(
309  {
310  "error": "access_denied",
311  "error_description": user_access_error,
312  },
313  status_code=HTTPStatus.FORBIDDEN,
314  )
315 
316  refresh_token = await hass.auth.async_create_refresh_token(
317  user, client_id, credential=credential
318  )
319  try:
320  access_token = hass.auth.async_create_access_token(
321  refresh_token, request.remote
322  )
323  except InvalidAuthError as exc:
324  return self.json(
325  {"error": "access_denied", "error_description": str(exc)},
326  status_code=HTTPStatus.FORBIDDEN,
327  )
328 
329  return self.json(
330  {
331  "access_token": access_token,
332  "token_type": "Bearer",
333  "refresh_token": refresh_token.token,
334  "expires_in": int(
335  refresh_token.access_token_expiration.total_seconds()
336  ),
337  "ha_auth_provider": credential.auth_provider_type,
338  },
339  headers={
340  "Cache-Control": "no-store",
341  "Pragma": "no-cache",
342  },
343  )
344 
346  self,
347  hass: HomeAssistant,
348  data: MultiDictProxy[str],
349  request: web.Request,
350  ) -> web.Response:
351  """Handle refresh token request."""
352  client_id = data.get("client_id")
353  if client_id is not None and not indieauth.verify_client_id(client_id):
354  return self.json(
355  {"error": "invalid_request", "error_description": "Invalid client id"},
356  status_code=HTTPStatus.BAD_REQUEST,
357  )
358 
359  if (token := data.get("refresh_token")) is None:
360  return self.json(
361  {"error": "invalid_request"}, status_code=HTTPStatus.BAD_REQUEST
362  )
363 
364  refresh_token = hass.auth.async_get_refresh_token_by_token(token)
365 
366  if refresh_token is None:
367  return self.json(
368  {"error": "invalid_grant"}, status_code=HTTPStatus.BAD_REQUEST
369  )
370 
371  if refresh_token.client_id != client_id:
372  return self.json(
373  {"error": "invalid_request"}, status_code=HTTPStatus.BAD_REQUEST
374  )
375 
376  if user_access_error := async_user_not_allowed_do_auth(
377  hass, refresh_token.user
378  ):
379  return self.json(
380  {
381  "error": "access_denied",
382  "error_description": user_access_error,
383  },
384  status_code=HTTPStatus.FORBIDDEN,
385  )
386 
387  try:
388  access_token = hass.auth.async_create_access_token(
389  refresh_token, request.remote
390  )
391  except InvalidAuthError as exc:
392  return self.json(
393  {"error": "access_denied", "error_description": str(exc)},
394  status_code=HTTPStatus.FORBIDDEN,
395  )
396 
397  return self.json(
398  {
399  "access_token": access_token,
400  "token_type": "Bearer",
401  "expires_in": int(
402  refresh_token.access_token_expiration.total_seconds()
403  ),
404  },
405  headers={
406  "Cache-Control": "no-store",
407  "Pragma": "no-cache",
408  },
409  )
410 
411 
412 class LinkUserView(HomeAssistantView):
413  """View to link existing users to new credentials."""
414 
415  url = "/auth/link_user"
416  name = "api:auth:link_user"
417 
418  def __init__(self, retrieve_credentials: RetrieveResultType) -> None:
419  """Initialize the link user view."""
420  self._retrieve_credentials_retrieve_credentials = retrieve_credentials
421 
422  @RequestDataValidator(vol.Schema({"code": str, "client_id": str}))
423  async def post(self, request: web.Request, data: dict[str, Any]) -> web.Response:
424  """Link a user."""
425  hass = request.app[KEY_HASS]
426  user: User = request["hass_user"]
427 
428  credentials = self._retrieve_credentials_retrieve_credentials(data["client_id"], data["code"])
429 
430  if credentials is None:
431  return self.json_message("Invalid code", status_code=HTTPStatus.BAD_REQUEST)
432 
433  linked_user = await hass.auth.async_get_user_by_credentials(credentials)
434  if linked_user != user and linked_user is not None:
435  return self.json_message(
436  "Credential already linked", status_code=HTTPStatus.BAD_REQUEST
437  )
438 
439  # No-op if credential is already linked to the user it will be linked to
440  if linked_user != user:
441  await hass.auth.async_link_user(user, credentials)
442  return self.json_message("User linked")
443 
444 
445 @callback
446 def _create_auth_code_store() -> tuple[StoreResultType, RetrieveResultType]:
447  """Create an in memory store."""
448  temp_results: dict[tuple[str, str], tuple[datetime, Credentials]] = {}
449 
450  @callback
451  def store_result(client_id: str, result: Credentials) -> str:
452  """Store flow result and return a code to retrieve it."""
453  if not isinstance(result, Credentials):
454  raise TypeError("result has to be a Credentials instance")
455 
456  code = uuid.uuid4().hex
457  temp_results[(client_id, code)] = (
458  dt_util.utcnow(),
459  result,
460  )
461  return code
462 
463  @callback
464  def retrieve_result(client_id: str, code: str) -> Credentials | None:
465  """Retrieve flow result."""
466  key = (client_id, code)
467 
468  if key not in temp_results:
469  return None
470 
471  created, result = temp_results.pop(key)
472 
473  # OAuth 4.2.1
474  # The authorization code MUST expire shortly after it is issued to
475  # mitigate the risk of leaks. A maximum authorization code lifetime of
476  # 10 minutes is RECOMMENDED.
477  if dt_util.utcnow() - created < timedelta(minutes=10):
478  return result
479 
480  return None
481 
482  return store_result, retrieve_result
483 
484 
485 @websocket_api.websocket_command({vol.Required("type"): "auth/current_user"})
486 @websocket_api.ws_require_user()
487 @websocket_api.async_response
489  hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
490 ) -> None:
491  """Return the current user."""
492  user = connection.user
493  enabled_modules = await hass.auth.async_get_enabled_mfa(user)
494 
495  connection.send_message(
496  websocket_api.result_message(
497  msg["id"],
498  {
499  "id": user.id,
500  "name": user.name,
501  "is_owner": user.is_owner,
502  "is_admin": user.is_admin,
503  "credentials": [
504  {
505  "auth_provider_type": c.auth_provider_type,
506  "auth_provider_id": c.auth_provider_id,
507  }
508  for c in user.credentials
509  ],
510  "mfa_modules": [
511  {
512  "id": module.id,
513  "name": module.name,
514  "enabled": module.id in enabled_modules,
515  }
516  for module in hass.auth.auth_mfa_modules
517  ],
518  },
519  )
520  )
521 
522 
523 @websocket_api.websocket_command( { vol.Required("type"): "auth/long_lived_access_token",
524  vol.Required("lifespan"): int, # days
525  vol.Required("client_name"): str,
526  vol.Optional("client_icon"): str,
527  }
528 )
529 @websocket_api.ws_require_user()
530 @websocket_api.async_response
532  hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
533 ) -> None:
534  """Create or a long-lived access token."""
535  refresh_token = await hass.auth.async_create_refresh_token(
536  connection.user,
537  client_name=msg["client_name"],
538  client_icon=msg.get("client_icon"),
539  token_type=TOKEN_TYPE_LONG_LIVED_ACCESS_TOKEN,
540  access_token_expiration=timedelta(days=msg["lifespan"]),
541  )
542 
543  try:
544  access_token = hass.auth.async_create_access_token(refresh_token)
545  except InvalidAuthError as exc:
546  connection.send_error(msg["id"], websocket_api.ERR_UNAUTHORIZED, str(exc))
547  return
548 
549  connection.send_result(msg["id"], access_token)
550 
551 
552 @websocket_api.websocket_command({vol.Required("type"): "auth/refresh_tokens"})
553 @websocket_api.ws_require_user()
554 @callback
556  hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
557 ) -> None:
558  """Return metadata of users refresh tokens."""
559  current_id = connection.refresh_token_id
560 
561  tokens: list[dict[str, Any]] = []
562  for refresh in connection.user.refresh_tokens.values():
563  if refresh.credential:
564  auth_provider_type = refresh.credential.auth_provider_type
565  else:
566  auth_provider_type = None
567 
568  expire_at = None
569  if refresh.expire_at:
570  expire_at = dt_util.utc_from_timestamp(refresh.expire_at)
571 
572  tokens.append(
573  {
574  "auth_provider_type": auth_provider_type,
575  "client_icon": refresh.client_icon,
576  "client_id": refresh.client_id,
577  "client_name": refresh.client_name,
578  "created_at": refresh.created_at,
579  "expire_at": expire_at,
580  "id": refresh.id,
581  "is_current": refresh.id == current_id,
582  "last_used_at": refresh.last_used_at,
583  "last_used_ip": refresh.last_used_ip,
584  "type": refresh.token_type,
585  }
586  )
587 
588  connection.send_result(msg["id"], tokens)
589 
590 
591 @callback
592 @websocket_api.websocket_command( { vol.Required("type"): "auth/delete_refresh_token",
593  vol.Required("refresh_token_id"): str,
594  }
595 )
596 @websocket_api.ws_require_user()
598  hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
599 ) -> None:
600  """Handle a delete refresh token request."""
601  refresh_token = connection.user.refresh_tokens.get(msg["refresh_token_id"])
602 
603  if refresh_token is None:
604  connection.send_error(msg["id"], "invalid_token_id", "Received invalid token")
605  return
606 
607  hass.auth.async_remove_refresh_token(refresh_token)
608 
609  connection.send_result(msg["id"], {})
610 
611 
612 @callback
613 @websocket_api.websocket_command( { vol.Required("type"): "auth/delete_all_refresh_tokens",
614  vol.Optional("token_type"): cv.string,
615  vol.Optional("delete_current_token", default=True): bool,
616  }
617 )
618 @websocket_api.ws_require_user()
620  hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
621 ) -> None:
622  """Handle delete all refresh tokens request."""
623  current_refresh_token: RefreshToken
624  remove_failed = False
625  token_type = msg.get("token_type")
626  delete_current_token = msg.get("delete_current_token")
627  limit_token_types = token_type is not None
628 
629  for token in list(connection.user.refresh_tokens.values()):
630  if token.id == connection.refresh_token_id:
631  # Skip the current refresh token as it has revoke_callback,
632  # which cancels/closes the connection.
633  # It will be removed after sending the result.
634  current_refresh_token = token
635  continue
636  if limit_token_types and token_type != token.token_type:
637  continue
638  try:
639  hass.auth.async_remove_refresh_token(token)
640  except Exception:
641  getLogger(__name__).exception("Error during refresh token removal")
642  remove_failed = True
643 
644  if remove_failed:
645  connection.send_error(
646  msg["id"], "token_removing_error", "During removal, an error was raised."
647  )
648  else:
649  connection.send_result(msg["id"], {})
650 
651  async def _delete_current_token_soon() -> None:
652  """Delete the current token after a delay.
653 
654  We do not want to delete the current token immediately as it will
655  close the connection.
656 
657  This is implemented as a tracked task to ensure the token
658  is still deleted if Home Assistant is shut down during
659  the delay.
660 
661  It should not be refactored to use a call_later as that
662  would not be tracked and the token would not be deleted
663  if Home Assistant was shut down during the delay.
664  """
665  try:
666  await asyncio.sleep(DELETE_CURRENT_TOKEN_DELAY)
667  finally:
668  # If the task is cancelled because we are shutting down, delete
669  # the token right away.
670  hass.auth.async_remove_refresh_token(current_refresh_token)
671 
672  if delete_current_token and (
673  not limit_token_types or current_refresh_token.token_type == token_type
674  ):
675  # Deleting the token will close the connection so we need
676  # to do it with a delay in a tracked task to ensure it still
677  # happens if Home Assistant is shutting down.
678  hass.async_create_task(_delete_current_token_soon())
679 
680 
681 @websocket_api.websocket_command( { vol.Required("type"): "auth/sign_path",
682  vol.Required("path"): str,
683  vol.Optional("expires", default=30): int,
684  }
685 )
686 @websocket_api.ws_require_user()
687 @callback
689  hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
690 ) -> None:
691  """Handle a sign path request."""
692  connection.send_message(
693  websocket_api.result_message(
694  msg["id"],
695  {
697  hass,
698  msg["path"],
699  timedelta(seconds=msg["expires"]),
700  )
701  },
702  )
703  )
704 
705 
706 @callback
707 @websocket_api.websocket_command( { vol.Required("type"): "auth/refresh_token_set_expiry",
708  vol.Required("refresh_token_id"): str,
709  vol.Required("enable_expiry"): bool,
710  }
711 )
712 @websocket_api.ws_require_user()
714  hass: HomeAssistant, connection: websocket_api.ActiveConnection, msg: dict[str, Any]
715 ) -> None:
716  """Handle a set expiry of a refresh token request."""
717  refresh_token = connection.user.refresh_tokens.get(msg["refresh_token_id"])
718 
719  if refresh_token is None:
720  connection.send_error(msg["id"], "invalid_token_id", "Received invalid token")
721  return
722 
723  hass.auth.async_set_expiry(refresh_token, enable_expiry=msg["enable_expiry"])
724  connection.send_result(msg["id"], {})
725 
None __init__(self, RetrieveResultType retrieve_credentials)
Definition: __init__.py:418
web.Response post(self, web.Request request, dict[str, Any] data)
Definition: __init__.py:423
web.Response post(self, web.Request request)
Definition: __init__.py:217
web.Response _async_handle_refresh_token(self, HomeAssistant hass, MultiDictProxy[str] data, web.Request request)
Definition: __init__.py:350
web.Response _async_handle_auth_code(self, HomeAssistant hass, MultiDictProxy[str] data, web.Request request)
Definition: __init__.py:282
None __init__(self, RetrieveResultType retrieve_auth)
Definition: __init__.py:246
web.Response post(self, web.Request request)
Definition: __init__.py:251
None websocket_current_user(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)
Definition: __init__.py:490
str create_auth_code(HomeAssistant hass, str client_id, Credentials credential)
Definition: __init__.py:179
tuple[StoreResultType, RetrieveResultType] _create_auth_code_store()
Definition: __init__.py:446
None websocket_delete_refresh_token(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)
Definition: __init__.py:603
None websocket_refresh_tokens(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)
Definition: __init__.py:559
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:184
None websocket_sign_path(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)
Definition: __init__.py:698
None websocket_create_long_lived_access_token(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)
Definition: __init__.py:535
None websocket_delete_all_refresh_tokens(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)
Definition: __init__.py:627
None websocket_refresh_token_set_expiry(HomeAssistant hass, websocket_api.ActiveConnection connection, dict[str, Any] msg)
Definition: __init__.py:725
str|None async_user_not_allowed_do_auth(HomeAssistant hass, User user, Request|None request=None)
Definition: auth.py:91
str async_sign_path(HomeAssistant hass, str path, timedelta expiration, *str|None refresh_token_id=None, bool use_content_user=False)
Definition: auth.py:52