Skip to content

Commit 95cb1e7

Browse files
authored
Merge pull request #237 from fersingb/move_oauth2_to_hilo
Move oauth2 flow components from pyhilo to hilo.
2 parents 3e3dbcb + c9bfff1 commit 95cb1e7

File tree

4 files changed

+59
-36
lines changed

4 files changed

+59
-36
lines changed

pyhilo/api.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from aiohttp import ClientSession
1313
from aiohttp.client_exceptions import ClientResponseError
1414
import backoff
15-
from homeassistant.helpers import config_entry_oauth2_flow
1615

1716
from pyhilo.const import (
1817
ANDROID_CLIENT_ENDPOINT,
@@ -67,7 +66,7 @@ def __init__(
6766
self,
6867
*,
6968
session: ClientSession,
70-
oauth_session: config_entry_oauth2_flow.OAuth2Session,
69+
oauth_session,
7170
request_retries: int = REQUEST_RETRY,
7271
log_traces: bool = False,
7372
) -> None:
@@ -98,7 +97,7 @@ async def async_create(
9897
cls,
9998
*,
10099
session: ClientSession,
101-
oauth_session: config_entry_oauth2_flow.OAuth2Session,
100+
oauth_session,
102101
request_retries: int = REQUEST_RETRY,
103102
log_traces: bool = False,
104103
) -> API:

pyhilo/const.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
from typing import Final
44

55
import aiohttp
6-
import homeassistant.core
76

87
LOG: Final = logging.getLogger(__package__)
98
DEFAULT_STATE_FILE: Final = "hilo_state.yaml"
@@ -46,7 +45,7 @@
4645

4746

4847
# Request constants
49-
DEFAULT_USER_AGENT: Final = f"PyHilo/{PYHILO_VERSION} HomeAssistant/{homeassistant.core.__version__} aiohttp/{aiohttp.__version__} Python/{platform.python_version()}"
48+
DEFAULT_USER_AGENT: Final = f"PyHilo/{PYHILO_VERSION} aiohttp/{aiohttp.__version__} Python/{platform.python_version()}"
5049

5150

5251
# NOTE(dvd): Not sure how to get new ones so I'm using the ones from my emulator

pyhilo/oauth2.py

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Custom OAuth2 implementation."""
2+
23
import base64
34
import hashlib
45
import os
@@ -8,14 +9,8 @@
89
from homeassistant.core import HomeAssistant
910
from homeassistant.helpers.config_entry_oauth2_flow import LocalOAuth2Implementation
1011

11-
from pyhilo.const import (
12-
AUTH_AUTHORIZE,
13-
AUTH_CHALLENGE_METHOD,
14-
AUTH_CLIENT_ID,
15-
AUTH_SCOPE,
16-
AUTH_TOKEN,
17-
DOMAIN,
18-
)
12+
from pyhilo.const import AUTH_AUTHORIZE, AUTH_CLIENT_ID, AUTH_TOKEN, DOMAIN
13+
from pyhilo.oauth2helper import OAuth2Helper
1914

2015

2116
class AuthCodeWithPKCEImplementation(LocalOAuth2Implementation): # type: ignore[misc]
@@ -34,8 +29,8 @@ def __init__(
3429
AUTH_AUTHORIZE,
3530
AUTH_TOKEN,
3631
)
37-
self._code_verifier = self._get_code_verifier()
38-
self._code_challenge = self._get_code_challange(self._code_verifier)
32+
33+
self.oauth_helper = OAuth2Helper()
3934

4035
# ... Override AbstractOAuth2Implementation details
4136
@property
@@ -46,32 +41,15 @@ def name(self) -> str:
4641
@property
4742
def extra_authorize_data(self) -> dict:
4843
"""Extra data that needs to be appended to the authorize url."""
49-
return {
50-
"scope": AUTH_SCOPE,
51-
"code_challenge": self._code_challenge,
52-
"code_challenge_method": AUTH_CHALLENGE_METHOD,
53-
}
44+
return self.oauth_helper.get_authorize_parameters()
5445

5546
async def async_resolve_external_data(self, external_data: Any) -> dict:
5647
"""Resolve the authorization code to tokens."""
5748
return cast(
5849
dict,
5950
await self._token_request(
60-
{
61-
"grant_type": "authorization_code",
62-
"code": external_data["code"],
63-
"redirect_uri": external_data["state"]["redirect_uri"],
64-
"code_verifier": self._code_verifier,
65-
},
51+
self.oauth_helper.get_token_request_parameters(
52+
external_data["code"], external_data["state"]["redirect_uri"]
53+
)
6654
),
6755
)
68-
69-
# Ref : https://blog.sanghviharshit.com/reverse-engineering-private-api-oauth-code-flow-with-pkce/
70-
def _get_code_verifier(self) -> str:
71-
code = base64.urlsafe_b64encode(os.urandom(40)).decode("utf-8")
72-
return re.sub("[^a-zA-Z0-9]+", "", code)
73-
74-
def _get_code_challange(self, verifier: str) -> str:
75-
sha_verifier = hashlib.sha256(verifier.encode("utf-8")).digest()
76-
code = base64.urlsafe_b64encode(sha_verifier).decode("utf-8")
77-
return code.replace("=", "")

pyhilo/oauth2helper.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import base64
2+
import hashlib
3+
import os
4+
import re
5+
from typing import Any, cast
6+
7+
from pyhilo.const import (
8+
AUTH_AUTHORIZE,
9+
AUTH_CHALLENGE_METHOD,
10+
AUTH_CLIENT_ID,
11+
AUTH_SCOPE,
12+
AUTH_TOKEN,
13+
)
14+
15+
class OAuth2Helper:
16+
"""Custom OAuth2 implementation."""
17+
18+
def __init__(self) -> None:
19+
self._code_verifier = self._get_code_verifier()
20+
self._code_challenge = self._get_code_challenge(self._code_verifier)
21+
22+
# Ref : https://blog.sanghviharshit.com/reverse-engineering-private-api-oauth-code-flow-with-pkce/
23+
def _get_code_verifier(self) -> str:
24+
code = base64.urlsafe_b64encode(os.urandom(40)).decode("utf-8")
25+
return re.sub("[^a-zA-Z0-9]+", "", code)
26+
27+
def _get_code_challenge(self, verifier: str) -> str:
28+
sha_verifier = hashlib.sha256(verifier.encode("utf-8")).digest()
29+
code = base64.urlsafe_b64encode(sha_verifier).decode("utf-8")
30+
return code.replace("=", "")
31+
32+
def get_authorize_parameters(self):
33+
return {
34+
"scope": AUTH_SCOPE,
35+
"code_challenge": self._code_challenge,
36+
"code_challenge_method": AUTH_CHALLENGE_METHOD,
37+
"response_type": "code",
38+
"client_id": AUTH_CLIENT_ID,
39+
}
40+
41+
def get_token_request_parameters(self, code, redirect_uri):
42+
return {
43+
"grant_type": "authorization_code",
44+
"code": code,
45+
"redirect_uri": redirect_uri,
46+
"code_verifier": self._code_verifier,
47+
}

0 commit comments

Comments
 (0)