How TeslaMate Handles API Authentication: Token Storage, Encryption, and Auto-Refresh
TeslaMate authenticates with Tesla’s API by storing encrypted access and refresh tokens in a PostgreSQL database, injecting the access token into every request via middleware, and automatically refreshing credentials when they expire.
The teslamate-org/teslamate repository implements a robust authentication pipeline that separates token persistence from API communication. By leveraging Elixir’s Ecto for database operations and custom encryption types, TeslaMate ensures secure credential storage while maintaining seamless integration with Tesla’s vehicle data APIs.
Token Storage and Encryption
TeslaMate persists sensitive credentials using a dedicated schema with automatic encryption at rest.
The Encrypted Tokens Schema
The tokens table is defined in lib/teslamate/auth/tokens.ex using the Ecto schema TeslaMate.Auth.Tokens. This schema utilizes the custom Encrypted.Binary type provided by TeslaMate.Vault to ensure both the access token and refresh token remain encrypted within the PostgreSQL database.
# lib/teslamate/auth/tokens.ex
schema "tokens" do
field :access, Encrypted.Binary
field :refresh, Encrypted.Binary
# ... timestamps
end
Persistence Operations
The TeslaMate.Auth module (lib/teslamate/auth.ex) provides the high-level interface for token management. The TeslaMate.Auth.save/1 function receives a token pair after user login, creating or updating a single database row with encrypted values. Retrieval is handled by TeslaMate.Auth.get_tokens/0, which returns the sole %TeslaMate.Auth.Tokens{} struct or nil if no tokens exist.
Building the Authentication Struct
When preparing to call Tesla’s API, TeslaMate converts stored database records into runtime authentication structures. The TeslaApi.Auth module (lib/tesla_api/auth.ex) defines the %TeslaApi.Auth{} struct that holds the current token (access token), refresh_token, and metadata such as expires_in.
The struct also embeds logic for regional API endpoint selection. The TeslaApi.Auth.region/1 function analyzes the issuer URL embedded within the JWT to determine whether to target the global Tesla API or the China-specific endpoint.
Middleware-Based Request Authorization
Rather than manually attaching headers to every request, TeslaMate uses Tesla HTTP client middleware for automatic header injection.
The TokenAuth Middleware
The TeslaApi.Middleware.TokenAuth module (lib/tesla_api/middleware/token_auth.ex) intercepts all outgoing API calls. It inspects env.opts[:access_token] and, when present, adds the required Authorization: Bearer <token> header to the request.
Vehicle-related modules such as TeslaApi.Vehicle pass the current access token via the opts parameter:
# The token is passed through the options
TeslaApi.Vehicle.list(opts: [access_token: auth.token])
This design ensures that every HTTP request to Tesla’s API carries valid authentication without duplicating header logic across wrapper functions.
Automatic Token Refresh
TeslaMate handles token expiration gracefully through a dedicated refresh workflow.
The Refresh Implementation
When an API request returns a 401 unauthorized response, the system can trigger TeslaApi.Auth.Refresh.refresh/1 (implemented in lib/tesla_api/auth/refresh.ex). This function constructs a POST request to https://auth.tesla.com/oauth2/v3/token (or the region-specific equivalent) with grant_type=refresh_token, the client ID, and the stored refresh token.
On success, the function returns a new %TeslaApi.Auth{} struct containing fresh access and refresh tokens. The caller then persists these updated credentials via TeslaMate.Auth.save/1, ensuring subsequent requests use valid authentication.
Complete Authentication Flow Example
The following Elixir code demonstrates the full lifecycle from storage to API call:
# 1. Retrieve and decrypt stored tokens
case TeslaMate.Auth.get_tokens() do
%TeslaMate.Auth.Tokens{access: access, refresh: refresh} ->
auth = %TeslaApi.Auth{
token: access,
refresh_token: refresh
}
# 2. Make an authenticated API request
case TeslaApi.Vehicle.list(opts: [access_token: auth.token]) do
{:ok, vehicles} ->
{:ok, vehicles}
{:error, :unauthorized} ->
# 3. Refresh tokens on 401 error
{:ok, new_auth} = TeslaApi.Auth.Refresh.refresh(auth)
:ok = TeslaMate.Auth.save(new_auth)
# Retry with new token
TeslaApi.Vehicle.list(opts: [access_token: new_auth.token])
end
nil ->
{:error, :no_tokens}
end
Summary
- TeslaMate stores API credentials in a PostgreSQL database using the
TeslaMate.Auth.Tokensschema withEncrypted.Binaryfields to ensure encryption at rest. - The
TeslaMate.Authmodule providessave/1andget_tokens/0functions for persistence and retrieval of encrypted token pairs. - Runtime authentication uses the
%TeslaApi.Auth{}struct, which includes regional endpoint detection viaTeslaApi.Auth.region/1. - The
TeslaApi.Middleware.TokenAuthmiddleware automatically injects theAuthorization: Bearer <token>header into all API requests based on options passed to the Tesla client. - Token expiration is handled automatically by
TeslaApi.Auth.Refresh.refresh/1, which exchanges refresh tokens for new access tokens via Tesla’s OAuth2 endpoint.
Frequently Asked Questions
How does TeslaMate encrypt API tokens?
TeslaMate encrypts tokens at rest using the Encrypted.Binary Ecto type backed by TeslaMate.Vault. This ensures that both the access token and refresh token remain encrypted in the PostgreSQL database, only decrypting when loaded into the application runtime by the TeslaMate.Auth.Tokens schema.
What happens when the Tesla API returns a 401 error?
When a request returns a 401 unauthorized response, TeslaMate triggers the refresh workflow defined in TeslaApi.Auth.Refresh. The refresh/1 function calls Tesla’s OAuth2 token endpoint to exchange the stored refresh token for a new access token, which is then persisted via TeslaMate.Auth.save/1 to prevent subsequent authentication failures.
Where is the Authorization header added to API requests?
The TeslaApi.Middleware.TokenAuth middleware (lib/tesla_api/middleware/token_auth.ex) automatically adds the Authorization: Bearer <token> header to every outgoing request. It reads the token from the access_token option passed in the request options, eliminating the need to manually set headers on every API call in the vehicle modules.
How does TeslaMate handle regional API differences?
The TeslaApi.Auth.region/1 function inspects the issuer URL embedded within the JWT token to determine the correct API region (global vs. China). This allows TeslaMate to route authentication requests to the appropriate Tesla OAuth2 endpoint and API base URL based on the user’s account region.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →