mirror of
https://github.com/netfun2000/hipudding-teslamate.git
synced 2026-02-27 09:44:28 +08:00
Use SSO Access Tokens
This commit is contained in:
+15
-17
@@ -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, OwnerApi, Util}
|
||||
alias TeslaApi.Auth.{MFA, Util}
|
||||
|
||||
require Logger
|
||||
|
||||
@@ -56,9 +56,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, tokens} <-
|
||||
get_web_token(code, ctx.code_verifier, redirect_uri, ctx.state, base: ctx.base_url),
|
||||
{:ok, auth} <- OwnerApi.get_api_tokens(tokens) do
|
||||
{:ok, auth} <-
|
||||
get_web_token(code, ctx.code_verifier, redirect_uri, ctx.state, base: ctx.base_url) do
|
||||
{:ok, auth}
|
||||
end
|
||||
rescue
|
||||
@@ -193,9 +192,8 @@ defmodule TeslaApi.Auth.Login do
|
||||
with {:ok, env} <-
|
||||
MFA.verify_passcode(device_id, mfa_passcode, transaction_id, headers),
|
||||
{:ok, {redirect_uri, code}} <- Util.parse_location_header(env, ctx.state),
|
||||
{:ok, tokens} <-
|
||||
get_web_token(code, ctx.code_verifier, redirect_uri, ctx.state),
|
||||
{:ok, auth} <- OwnerApi.get_api_tokens(tokens) do
|
||||
{:ok, auth} <-
|
||||
get_web_token(code, ctx.code_verifier, redirect_uri, ctx.state) do
|
||||
{:ok, auth}
|
||||
end
|
||||
rescue
|
||||
@@ -234,16 +232,16 @@ defmodule TeslaApi.Auth.Login do
|
||||
}
|
||||
|
||||
case post("#{opts[:base]}/oauth2/v3/token", data) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: %{
|
||||
"access_token" => access_token,
|
||||
"refresh_token" => refresh_token,
|
||||
"state" => ^state
|
||||
}
|
||||
}} ->
|
||||
{:ok, %{access_token: access_token, refresh_token: refresh_token}}
|
||||
{:ok, %Tesla.Env{status: 200, body: %{"state" => ^state} = body}} ->
|
||||
auth = %TeslaApi.Auth{
|
||||
token: body["access_token"],
|
||||
type: body["token_type"],
|
||||
expires_in: body["expires_in"],
|
||||
refresh_token: body["refresh_token"],
|
||||
created_at: body["created_at"]
|
||||
}
|
||||
|
||||
{:ok, auth}
|
||||
|
||||
error ->
|
||||
Error.into(error, :web_token_error)
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
defmodule TeslaApi.Auth.OwnerApi do
|
||||
import TeslaApi.Auth, only: [post: 3]
|
||||
|
||||
alias TeslaApi.{Auth, Error}
|
||||
|
||||
@client_id "81527cff06843c8634fdc09e8ac0abefb46ac849f38fe1e431c2ef2106796384"
|
||||
@client_secret "c7257eb71a564034f9419ee651c7d0e5f7aa6bfbd18bafb5c5c033b093bb2fa3"
|
||||
|
||||
def get_api_tokens(%{access_token: access_token, refresh_token: refresh_token}) do
|
||||
data = %{
|
||||
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
|
||||
client_id: @client_id,
|
||||
client_secret: @client_secret
|
||||
}
|
||||
|
||||
headers = [{"Authorization", "Bearer #{access_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: refresh_token,
|
||||
created_at: body["created_at"]
|
||||
}
|
||||
|
||||
{:ok, auth}
|
||||
|
||||
error ->
|
||||
Error.into(error, :api_token_error)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -2,45 +2,62 @@ 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
|
||||
with {:ok, %{access_token: _} = tokens} <-
|
||||
refresh_oauth_access_token(auth.token, auth.refresh_token),
|
||||
{:ok, auth} <- OwnerApi.get_api_tokens(tokens) do
|
||||
{:ok, auth}
|
||||
else
|
||||
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
|
||||
|
||||
data = %{
|
||||
grant_type: "refresh_token",
|
||||
scope: "openid email offline_access",
|
||||
client_id: @web_client_id,
|
||||
refresh_token: auth.refresh_token
|
||||
}
|
||||
|
||||
case post("#{issuer_url}/token", data) 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: body["refresh_token"],
|
||||
created_at: body["created_at"]
|
||||
}
|
||||
|
||||
{:ok, auth}
|
||||
|
||||
error ->
|
||||
Error.into(error, :token_refresh)
|
||||
end
|
||||
end
|
||||
|
||||
defp refresh_oauth_access_token(access_token, refresh_token) do
|
||||
data = %{
|
||||
grant_type: "refresh_token",
|
||||
scope: "openid email offline_access",
|
||||
client_id: @web_client_id,
|
||||
refresh_token: refresh_token
|
||||
}
|
||||
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
|
||||
|
||||
base_url =
|
||||
case access_token do
|
||||
"cn-" <> _ -> "https://auth.tesla.cn"
|
||||
_qts -> nil
|
||||
end
|
||||
|
||||
case post("#{base_url}/oauth2/v3/token", data) do
|
||||
{:ok,
|
||||
%Tesla.Env{
|
||||
status: 200,
|
||||
body: %{"access_token" => access_token, "refresh_token" => refresh_token}
|
||||
}} ->
|
||||
{:ok, %{access_token: access_token, refresh_token: refresh_token}}
|
||||
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
|
||||
Error.into(error, :invalid_access_token)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -101,7 +101,7 @@ defmodule TeslaMate.Api do
|
||||
|
||||
with %Tokens{access: at, refresh: rt} when is_binary(at) and is_binary(rt) <-
|
||||
call(deps.auth, :get_tokens) do
|
||||
restored_tokens = %Auth{token: at, refresh_token: rt, expires_in: 1.12 * 60 * 60}
|
||||
restored_tokens = %Auth{token: at, refresh_token: rt, expires_in: 20 * 60}
|
||||
|
||||
case refresh_tokens(restored_tokens) do
|
||||
{:ok, refreshed_tokens} ->
|
||||
@@ -167,8 +167,8 @@ defmodule TeslaMate.Api do
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.warning("Token refresh failed: #{inspect(reason, pretty: true)}")
|
||||
Logger.warning("Retrying in 1 hour...")
|
||||
Process.send_after(self(), :refresh_auth, :timer.hours(1))
|
||||
Logger.warning("Retrying in 5 minutes...")
|
||||
Process.send_after(self(), :refresh_auth, :timer.minutes(5))
|
||||
end
|
||||
|
||||
{:error, reason} ->
|
||||
@@ -202,7 +202,7 @@ defmodule TeslaMate.Api do
|
||||
defp schedule_refresh(%Auth{} = auth) do
|
||||
ms =
|
||||
auth.expires_in
|
||||
|> Kernel.*(0.9)
|
||||
|> Kernel.*(0.5)
|
||||
|> round()
|
||||
|> :timer.seconds()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user