Exchange Chinese SSO tokens

This commit is contained in:
Adrian Kumpf
2021-10-31 16:56:36 +01:00
parent 1425f82b49
commit 9c9564ecc7
4 changed files with 108 additions and 34 deletions
+50
View File
@@ -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
+15 -3
View File
@@ -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
+34
View File
@@ -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
+9 -31
View File
@@ -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