Skip to content

Commit

Permalink
Add open source support
Browse files Browse the repository at this point in the history
  • Loading branch information
Bre77 committed Jul 10, 2024
1 parent 3b1a304 commit e036a64
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 12 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ test.py
register.py
*.pyc
__pycache__
auth.json
auth.json
opensource.py
1 change: 1 addition & 0 deletions tesla_fleet_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .teslafleetapi import TeslaFleetApi
from .teslafleetoauth import TeslaFleetOAuth
from .teslafleetopensource import TeslaFleetOpenSource
from .teslemetry import Teslemetry
from .tessie import Tessie
from .charging import Charging
Expand Down
2 changes: 0 additions & 2 deletions tesla_fleet_api/teslafleetapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ def __init__(
if region not in SERVERS:
raise ValueError(f"Region must be one of {', '.join(SERVERS.keys())}")
self.server = SERVERS.get(region)
else:
raise ValueError("Either server or region must be provided.")

LOGGER.debug("Using server %s", self.server)

Expand Down
34 changes: 25 additions & 9 deletions tesla_fleet_api/teslafleetoauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,25 @@ class TeslaFleetOAuth(TeslaFleetApi):
"""Tesla Fleet OAuth API."""

expires: int
refresh_token: str
redirect_uri: str | None
_client_secret: str | None

def __init__(
self,
session: aiohttp.ClientSession,
client_id: str,
client_secret: str | None = None,
redirect_uri: str | None = None,
access_token: str | None = None,
refresh_token: str | None = None,
expires: int = 0,
region: str | None = None,
server: str | None = None,
):
self.client_id = client_id
self._client_secret = client_secret
self.redirect_uri = redirect_uri
self.access_token = access_token
self.refresh_token = refresh_token
self.expires = expires
Expand All @@ -34,25 +41,34 @@ def __init__(
server=server,
)

def get_login_url(
self, redirect_uri: str, scopes: list[Scope], state: str = "login"
) -> str:
def get_login_url(self, scopes: list[Scope], state: str = "login") -> str:
"""Get the login URL."""
return f"https://auth.tesla.com/oauth2/v3/authorize?response_type=code&client_id={self.client_id}&redirect_uri={redirect_uri}&scope={' '.join(scopes)}&state={state}"
if self.redirect_uri is None:
raise ValueError("Redirect URI is missing")
return f"https://auth.tesla.com/oauth2/v3/authorize?response_type=code&prompt=login&client_id={self.client_id}&redirect_uri={self.redirect_uri}&scope={' '.join(scopes)}&state={state}"

async def get_refresh_token(
self, client_secret: str, code: str, redirect_uri: str
) -> None:
async def get_refresh_token(self, code: str) -> None:
"""Get the refresh token."""

if self._client_secret is None:
raise ValueError("Client secret is missing")

if self.redirect_uri is None:
raise ValueError("Redirect URI is missing")

if self.server is None:
self.region = code.split("_")[0].lower()
self.server = SERVERS.get(self.region)

async with self.session.post(
"https://auth.tesla.com/oauth2/v3/token",
data={
"grant_type": "authorization_code",
"client_id": self.client_id,
"client_secret": client_secret,
"client_secret": self._client_secret,
"code": code,
"audience": self.server,
"redirect_uri": redirect_uri,
"redirect_uri": self.redirect_uri,
},
) as resp:
if resp.ok:
Expand Down
61 changes: 61 additions & 0 deletions tesla_fleet_api/teslafleetopensource.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import aiohttp
import time
import secrets
import hashlib
import base64

from .teslafleetoauth import TeslaFleetOAuth
from .const import Scope, SERVERS


class TeslaFleetOpenSource(TeslaFleetOAuth):
"""Tesla Fleet Open Source OAuth API."""

code_verifier: str
code_challenge: str

def __init__(
self,
session: aiohttp.ClientSession,
client_id: str,
redirect_uri: str,
):
self.code_verifier = secrets.token_urlsafe(32)

# Hash the code_verifier using SHA-256
hashed_verifier = hashlib.sha256(self.code_verifier.encode()).digest()
# Encode the hash using URL-safe Base64 encoding, without padding
self.code_challenge = (
base64.urlsafe_b64encode(hashed_verifier).decode().replace("=", "")
)

super().__init__(session, client_id, redirect_uri=redirect_uri)

def get_login_url(self, scopes: list[Scope], state: str = "login") -> str:
"""Get the login URL without a client secret."""

return (
super().get_login_url(scopes, state)
+ f"&code_challenge={self.code_challenge}"
)

async def get_refresh_token(self, code: str) -> None:
"""Get the refresh token."""
async with self.session.post(
"https://auth.tesla.com/oauth2/v3/token",
data={
"grant_type": "authorization_code",
"client_id": self.client_id,
"code": code,
"audience": self.server,
"redirect_uri": self.redirect_uri,
"code_verifier": self.code_verifier,
},
) as resp:
if resp.ok:
data = await resp.json()
self.refresh_token = data["refresh_token"]
self.access_token = data["access_token"]
self.expires = int(time.time()) + data["expires_in"]
region = code.split("_")[0].lower()
self.server = SERVERS.get(region)

0 comments on commit e036a64

Please sign in to comment.