Files
archived-hipudding-teslamate/lib/tesla_api/auth.ex
jlestel c8fed077bb Fix 401 on direct Fleet API calls (#4095)
* feat: endpoints by env

* fix: typo

* fix: useless env

* fix: format

* fix: distinct auth domain and url

* format

* fix: force issuer url if needed

* feat: new streaming based on vin

* fix refresh

* revert

* up

* feat: no need for access token / refresh token if the TOKEN env var is present

* feat: update login if token env var exists

* feat: add ENV var to allow insecure wss

* fix: remove TESLA_CN

* fix(naming): TESLA_API_URL to TESLA_API_DOMAIN

* feat: add an env var to allo invalid certs on WSS

* doc: add API domains env vars description

* fix: typo

* feat: add env var to change log level

* fix: APP_LOG_LEVEL

* feat: add TOKEN documention and wording

* fix: refacto insecure param

* feat: naming and doc

* fix: missing env var usage

* fix: rebound variable issuer_url

* fix: compilation warning on the issuer_url variable

* fix: format code

* fix: issuer_url assignments

* feat: customize polling intervals

* typo

* update doc

* typo

* add online interval

* add POLLING_ONLINE_INTERVAL

* format

* add minimum interval

* Add minimum on fetch parameter

* typo

* format

* respect immediate fetch, add charging interval

* add log level

* Respect log_level

* format

* use LOG_LEVEL env var to facilitate debug

* revert on log_level

* format

* typo

* format again

* revert on default error intervals

* Add a note in MD about polling settings

* not let the user set intervals via env variables shorter than our defaults

* Fix 401 on direct Fleet API

* try to fix in prod context

* log test

* up

* up

* format

* revert on log

* Update refresh.ex

---------

Co-authored-by: Julien <julien@citio.digital>
2024-07-27 14:06:57 +02:00

99 lines
2.8 KiB
Elixir

defmodule TeslaApi.Auth do
use Tesla
alias TeslaApi.Error
@web_client_id "ownerapi"
@redirect_uri "https://auth.tesla.com/void/callback"
def web_client_id, do: @web_client_id
def redirect_uri, do: @redirect_uri
@default_headers [
{"user-agent", "TeslaMate/#{Mix.Project.config()[:version]}"},
{"Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"},
{"Accept-Language", "en-US,de-DE;q=0.5"}
]
adapter Tesla.Adapter.Finch, name: TeslaMate.HTTP, receive_timeout: 60_000
plug TeslaApi.Middleware.FollowRedirects, except: [@redirect_uri]
plug Tesla.Middleware.BaseUrl, System.get_env("TESLA_AUTH_HOST", "https://auth.tesla.com")
plug Tesla.Middleware.Headers, @default_headers
plug Tesla.Middleware.JSON
plug Tesla.Middleware.Logger, debug: true, log_level: &log_level/1
defstruct [:token, :type, :expires_in, :refresh_token, :created_at]
defdelegate refresh(auth), to: __MODULE__.Refresh
def issuer_url(%__MODULE__{token: access_token}) do
case derive_issuer_url_from_oat(access_token) do
{:ok, issuer_url} ->
issuer_url
:error ->
case decode_jwt_payload(access_token) do
{:ok, %{"iss" => issuer_url}} -> issuer_url
_ -> "https://auth.tesla.com/oauth2/v3"
end
end
end
def region(%__MODULE__{} = auth) do
tld =
auth
|> issuer_url()
|> URI.parse()
|> Map.fetch!(:host)
|> String.split(".")
|> List.last()
case tld do
"cn" -> :chinese
"com" -> :global
_other -> :other
end
end
defp derive_issuer_url_from_oat("qts-" <> _),
do:
{:ok,
System.get_env("TESLA_AUTH_HOST", "https://auth.tesla.com") <>
System.get_env("TESLA_AUTH_PATH", "/oauth2/v3")}
defp derive_issuer_url_from_oat("eu-" <> _),
do:
{:ok,
System.get_env("TESLA_AUTH_HOST", "https://auth.tesla.com") <>
System.get_env("TESLA_AUTH_PATH", "/oauth2/v3")}
defp derive_issuer_url_from_oat("cn-" <> _),
do:
{:ok,
System.get_env("TESLA_AUTH_HOST", "https://auth.tesla.com") <>
System.get_env("TESLA_AUTH_PATH", "/oauth2/v3")}
defp derive_issuer_url_from_oat(_), do: :error
defp decode_jwt_payload(jwt) do
with [_algo, payload, _signature] <- String.split(jwt, "."),
{:ok, payload} <- Base.decode64(payload, padding: false),
{:ok, payload} <- Jason.decode(payload) do
{:ok, payload}
else
l when is_list(l) ->
Error.into({:error, :invalid_jwt}, :invalid_access_token)
{:error, reason} ->
Error.into({:error, reason}, :invalid_access_token)
_error ->
Error.into({:error, "Invalid access token"}, :invalid_access_token)
end
end
defp log_level(%Tesla.Env{} = env) when env.status >= 400, do: :error
defp log_level(%Tesla.Env{}), do: :info
end