mirror of
https://github.com/teslamate-org/teslamate.git
synced 2026-01-24 21:06:08 +08:00
Exchange Chinese SSO tokens
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
defmodule TeslaApi.Auth do
|
||||
use Tesla
|
||||
|
||||
alias TeslaApi.Error
|
||||
|
||||
@web_client_id "ownerapi"
|
||||
@redirect_uri "https://auth.tesla.com/void/callback"
|
||||
|
||||
@@ -26,6 +28,54 @@ defmodule TeslaApi.Auth do
|
||||
defdelegate login(email, password), to: __MODULE__.Login
|
||||
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" => iss}} -> URI.parse(iss)
|
||||
_ -> "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, "https://auth.tesla.com/oauth2/v3"}
|
||||
defp derive_issuer_url_from_oat("eu-" <> _), do: {:ok, "https://auth.tesla.com/oauth2/v3"}
|
||||
defp derive_issuer_url_from_oat("cn-" <> _), do: {:ok, "https://auth.tesla.cn/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 ->
|
||||
Error.into(error, :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
|
||||
|
||||
@@ -2,7 +2,7 @@ defmodule TeslaApi.Auth.Login do
|
||||
import TeslaApi.Auth, only: [get: 2, post: 2, post: 3]
|
||||
|
||||
alias TeslaApi.Error
|
||||
alias TeslaApi.Auth.{MFA, Util}
|
||||
alias TeslaApi.Auth.{MFA, OwnerApi, Util}
|
||||
|
||||
require Logger
|
||||
|
||||
@@ -57,7 +57,8 @@ defmodule TeslaApi.Auth.Login do
|
||||
with {:ok, %Tesla.Env{} = env} <- submit_form(form, ctx),
|
||||
{:ok, {redirect_uri, code}} <- Util.parse_location_header(env, ctx.state),
|
||||
{:ok, auth} <-
|
||||
get_web_token(code, ctx.code_verifier, redirect_uri, ctx.state, base: ctx.base_url) do
|
||||
get_web_token(code, ctx.code_verifier, redirect_uri, ctx.state, base: ctx.base_url),
|
||||
{:ok, auth} <- maybe_exchange_sso_tokens(auth) do
|
||||
{:ok, auth}
|
||||
end
|
||||
rescue
|
||||
@@ -193,7 +194,8 @@ defmodule TeslaApi.Auth.Login do
|
||||
MFA.verify_passcode(device_id, mfa_passcode, transaction_id, headers),
|
||||
{:ok, {redirect_uri, code}} <- Util.parse_location_header(env, ctx.state),
|
||||
{:ok, auth} <-
|
||||
get_web_token(code, ctx.code_verifier, redirect_uri, ctx.state) do
|
||||
get_web_token(code, ctx.code_verifier, redirect_uri, ctx.state),
|
||||
{:ok, auth} <- maybe_exchange_sso_tokens(auth) do
|
||||
{:ok, auth}
|
||||
end
|
||||
rescue
|
||||
@@ -247,4 +249,14 @@ defmodule TeslaApi.Auth.Login do
|
||||
Error.into(error, :web_token_error)
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_exchange_sso_tokens(%TeslaApi.Auth{} = auth) do
|
||||
case TeslaApi.Auth.region(auth) do
|
||||
:chinese ->
|
||||
OwnerApi.exchange_sso_token(auth)
|
||||
|
||||
_other ->
|
||||
{:ok, auth}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
defmodule TeslaApi.Auth.OwnerApi do
|
||||
import TeslaApi.Auth, only: [post: 3]
|
||||
|
||||
alias TeslaApi.{Auth, Error}
|
||||
|
||||
@client_id "81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384"
|
||||
@client_secret "c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3"
|
||||
|
||||
def exchange_sso_token(%Auth{} = sso_auth) do
|
||||
data = %{
|
||||
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
client_id: @client_id,
|
||||
client_secret: @client_secret
|
||||
}
|
||||
|
||||
headers = [{"Authorization", "Bearer #{sso_auth.token}"}]
|
||||
|
||||
case post("https://owner-api.teslamotors.com/oauth/token", data, headers: headers) do
|
||||
{:ok, %Tesla.Env{status: 200, body: body}} ->
|
||||
auth = %Auth{
|
||||
token: body["access_token"],
|
||||
type: body["token_type"],
|
||||
expires_in: body["expires_in"],
|
||||
refresh_token: sso_auth.refresh_token,
|
||||
created_at: body["created_at"]
|
||||
}
|
||||
|
||||
{:ok, auth}
|
||||
|
||||
error ->
|
||||
Error.into(error, :api_token_error)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,21 +2,12 @@ defmodule TeslaApi.Auth.Refresh do
|
||||
import TeslaApi.Auth, only: [post: 2]
|
||||
|
||||
alias TeslaApi.{Auth, Error}
|
||||
alias TeslaApi.Auth.OwnerApi
|
||||
|
||||
@web_client_id TeslaApi.Auth.web_client_id()
|
||||
|
||||
def refresh(%Auth{} = auth) do
|
||||
issuer_url =
|
||||
case derive_issuer_url_from_oat(auth.token) do
|
||||
{:ok, issuer_url} ->
|
||||
issuer_url
|
||||
|
||||
:error ->
|
||||
case decode_jwt_payload(auth.token) do
|
||||
{:ok, %{"iss" => iss}} -> URI.parse(iss)
|
||||
_ -> "https://auth.tesla.com/oauth2/v3"
|
||||
end
|
||||
end
|
||||
issuer_url = Auth.issuer_url(auth)
|
||||
|
||||
data = %{
|
||||
grant_type: "refresh_token",
|
||||
@@ -35,29 +26,16 @@ defmodule TeslaApi.Auth.Refresh do
|
||||
created_at: body["created_at"]
|
||||
}
|
||||
|
||||
{:ok, auth}
|
||||
case Auth.region(auth) do
|
||||
:chinese ->
|
||||
OwnerApi.exchange_sso_token(auth)
|
||||
|
||||
_other ->
|
||||
{:ok, auth}
|
||||
end
|
||||
|
||||
error ->
|
||||
Error.into(error, :token_refresh)
|
||||
end
|
||||
end
|
||||
|
||||
defp derive_issuer_url_from_oat("qts-" <> _), do: {:ok, "https://auth.tesla.com/oauth2/v3"}
|
||||
defp derive_issuer_url_from_oat("eu-" <> _), do: {:ok, "https://auth.tesla.com/oauth2/v3"}
|
||||
defp derive_issuer_url_from_oat("cn-" <> _), do: {:ok, "https://auth.tesla.cn/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 ->
|
||||
Error.into(error, :invalid_access_token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user