Skip to content

Commit 4b1a69c

Browse files
committed
refactor: send TOKEN as Bearer header via FleetAuth middleware
Replace TOKEN query-param injection (?token=...) with a dedicated FleetAuth middleware that sends the token as an Authorization: Bearer header on all API and auth requests, including the streaming subscribe message. Legacy ?token= format remains supported for backward compatibility. Update docs accordingly.
1 parent 979f70b commit 4b1a69c

8 files changed

Lines changed: 60 additions & 18 deletions

File tree

lib/tesla_api.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ defmodule TeslaApi do
1212
plug Tesla.Middleware.Headers, [{"user-agent", "TeslaMate/#{@version}"}]
1313
plug Tesla.Middleware.JSON
1414
plug TeslaApi.Middleware.TokenAuth
15+
plug TeslaApi.Middleware.FleetAuth
1516

1617
plug Tesla.Middleware.Logger,
1718
debug: true,

lib/tesla_api/auth.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ defmodule TeslaApi.Auth do
2121
plug Tesla.Middleware.BaseUrl, System.get_env("TESLA_AUTH_HOST", "https://auth.tesla.com")
2222
plug Tesla.Middleware.Headers, @default_headers
2323
plug Tesla.Middleware.JSON
24+
plug TeslaApi.Middleware.FleetAuth
2425
plug Tesla.Middleware.Logger, debug: true, log_level: &log_level/1
2526

2627
defstruct [:token, :type, :expires_in, :refresh_token, :created_at]

lib/tesla_api/auth/refresh.ex

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ defmodule TeslaApi.Auth.Refresh do
2020
refresh_token: auth.refresh_token
2121
}
2222

23-
case post(
24-
"#{issuer_url}/token" <> System.get_env("TOKEN", ""),
25-
data
26-
) do
23+
case post("#{issuer_url}/token", data) do
2724
{:ok, %Tesla.Env{status: 200, body: body}} ->
2825
auth = %Auth{
2926
token: body["access_token"],
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
defmodule TeslaApi.Middleware.FleetAuth do
2+
@moduledoc """
3+
Fleet mode authentication.
4+
5+
When the `TOKEN` env var is set, every request is routed through a proxy that
6+
handles the real Tesla authentication. TeslaMate only needs to authenticate to
7+
that proxy, with a single bearer token, so we override the `Authorization`
8+
header with `Bearer <TOKEN>` (replacing the per-account access token set by
9+
`TeslaApi.Middleware.TokenAuth`).
10+
11+
Passing the token as a header instead of a query/path component keeps it out of
12+
the request URLs that `Tesla.Middleware.Logger` writes on every call.
13+
14+
When `TOKEN` is unset (owner mode), this middleware is a no-op.
15+
"""
16+
17+
@behaviour Tesla.Middleware
18+
19+
@impl Tesla.Middleware
20+
def call(%Tesla.Env{} = env, next, _opts) do
21+
env =
22+
case token() do
23+
nil -> env
24+
token -> Tesla.put_header(env, "Authorization", "Bearer " <> token)
25+
end
26+
27+
Tesla.run(env, next)
28+
end
29+
30+
@doc """
31+
The fleet proxy token from the `TOKEN` env var, or `nil` in owner mode.
32+
33+
For backward compatibility the legacy `?token=...` (or `token=...`) form is
34+
accepted and normalized to the raw token value.
35+
"""
36+
def token do
37+
case System.get_env("TOKEN", "") |> String.trim() do
38+
"" -> nil
39+
raw -> normalize(raw)
40+
end
41+
end
42+
43+
defp normalize("?token=" <> token), do: token
44+
defp normalize("token=" <> token), do: token
45+
defp normalize(token), do: token
46+
end

lib/tesla_api/stream.ex

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ defmodule TeslaApi.Stream do
33

44
require Logger
55
alias TeslaApi.Auth
6+
alias TeslaApi.Middleware.FleetAuth
67
alias __MODULE__.Data
78

89
defmodule State do
@@ -33,14 +34,10 @@ defmodule TeslaApi.Stream do
3334
endpoint_url =
3435
case Auth.region(state.auth) do
3536
:chinese ->
36-
System.get_env("TESLA_WSS_HOST", "wss://streaming.vn.cloud.tesla.cn") <>
37-
"/streaming/" <>
38-
System.get_env("TOKEN", "")
37+
System.get_env("TESLA_WSS_HOST", "wss://streaming.vn.cloud.tesla.cn") <> "/streaming/"
3938

4039
_global ->
41-
System.get_env("TESLA_WSS_HOST", "wss://streaming.vn.teslamotors.com") <>
42-
"/streaming/" <>
43-
System.get_env("TOKEN", "")
40+
System.get_env("TESLA_WSS_HOST", "wss://streaming.vn.teslamotors.com") <> "/streaming/"
4441
end
4542

4643
WebSockex.start_link(endpoint_url, __MODULE__, state,
@@ -71,7 +68,7 @@ defmodule TeslaApi.Stream do
7168
end
7269

7370
@impl true
74-
def handle_info(:subscribe, %State{auth: %Auth{token: token}, vehicle_id: vid} = state) do
71+
def handle_info(:subscribe, %State{auth: %Auth{token: access_token}, vehicle_id: vid} = state) do
7572
Logger.debug("Subscribing …")
7673

7774
cancel_timer(state.timer)
@@ -80,7 +77,7 @@ defmodule TeslaApi.Stream do
8077

8178
connect_message = %{
8279
msg_type: "data:subscribe_oauth",
83-
token: token,
80+
token: FleetAuth.token() || access_token,
8481
value: Enum.join(@columns, ","),
8582
tag: "#{vid}"
8683
}

lib/tesla_api/vehicle.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ defmodule TeslaApi.Vehicle do
2929
_global -> System.get_env("TESLA_API_HOST", "https://owner-api.teslamotors.com")
3030
end
3131

32-
TeslaApi.get(endpoint_url <> "/api/1/products" <> System.get_env("TOKEN", ""),
32+
TeslaApi.get(endpoint_url <> "/api/1/products",
3333
opts: [access_token: auth.token]
3434
)
3535
|> handle_response(transform: &list_result/1)
@@ -42,7 +42,7 @@ defmodule TeslaApi.Vehicle do
4242
_global -> System.get_env("TESLA_API_HOST", "https://owner-api.teslamotors.com")
4343
end
4444

45-
TeslaApi.get(endpoint_url <> "/api/1/vehicles/#{id}" <> System.get_env("TOKEN", ""),
45+
TeslaApi.get(endpoint_url <> "/api/1/vehicles/#{id}",
4646
opts: [access_token: auth.token]
4747
)
4848
|> handle_response(transform: &result/1)
@@ -56,7 +56,7 @@ defmodule TeslaApi.Vehicle do
5656
end
5757

5858
TeslaApi.get(
59-
endpoint_url <> "/api/1/vehicles/#{id}/vehicle_data" <> System.get_env("TOKEN", ""),
59+
endpoint_url <> "/api/1/vehicles/#{id}/vehicle_data",
6060
query: [
6161
endpoints:
6262
"charge_state;climate_state;closures_state;drive_state;gui_settings;location_data;vehicle_config;vehicle_state;vehicle_data_combo"

website/docs/configuration/api.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ You must use the `URL` and the `TOKEN` given by the third party API provider.
5151

5252
```yml
5353
# API Fleet
54-
- TOKEN=?token=xxxx-xxxx-xxxx-xxxx
54+
- TOKEN=xxxx-xxxx-xxxx-xxxx
5555
- TESLA_API_HOST=https://api.myteslamate.com
5656
- TESLA_AUTH_HOST=https://api.myteslamate.com
5757
- TESLA_AUTH_PATH=/api/oauth2/v3
@@ -82,7 +82,7 @@ MyTeslaMate also provides streaming by [reproducing the old streaming from the d
8282
1. Use this `TOKEN` instead of _`xxxx-xxxx-xxxx-xxxx`_ and add the following environment variables:
8383

8484
```yml
85-
- TOKEN=?token=xxxx-xxxx-xxxx-xxxx
85+
- TOKEN=xxxx-xxxx-xxxx-xxxx
8686
- TESLA_API_HOST=https://api.teslemetry.com
8787
- TESLA_AUTH_HOST=https://api.teslemetry.com
8888
- TESLA_AUTH_PATH=/api/oauth2/v3

website/docs/configuration/environment_variables.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ TeslaMate accepts the following environment variables for runtime configuration:
4848
| **TESLA_WSS_HOST** | Hostname of the Tesla streaming | wss://streaming.vn.teslamotors.com (or for chinese: wss://streaming.vn.cloud.tesla.cn) |
4949
| **TESLA_WSS_TLS_ACCEPT_INVALID_CERTS** | Accepts invalid certificates on TESLA_WSS_HOST if `true` | |
5050
| **TESLA_WSS_USE_VIN** | Use the `vin` field instead of `vid` to connect stream if `true` | |
51-
| **TOKEN** | Token given by a third party Tesla API provider. This `TOKEN` is added to each API request and the format must be `?token=xxxx-xxxx-xxxx` | |
51+
| **TOKEN** | Token given by a third party Tesla API provider. It is sent as an `Authorization: Bearer` header on each API request (and in the streaming subscribe message), so provide only the raw token, e.g. `xxxx-xxxx-xxxx`. For backward compatibility the legacy `?token=xxxx-xxxx-xxxx` format is still accepted. | |
5252
| **POLLING_ASLEEP_INTERVAL** | Interval between API fetch when the vehicle is asleep (in seconds). **Important: Do not alter this setting unless you are certain of the implications.** | 30 |
5353
| **POLLING_CHARGING_INTERVAL** | Minimum interval between API fetch when the vehicle is charging (in seconds). **Important: Do not alter this setting unless you are certain of the implications.** | 5 |
5454
| **POLLING_DRIVING_INTERVAL** | Interval between API fetch when the vehicle is driving (in seconds). **Important: Do not alter this setting unless you are certain of the implications.** | 2.5 |

0 commit comments

Comments
 (0)