How TeslaMate Handles API Authentication and Automatic Token Refresh
TeslaMate’s API module manages Tesla authentication through a GenServer that orchestrates OAuth token refresh, stores encrypted credentials, and schedules automatic renewal at 75% of token expiry to ensure uninterrupted vehicle data logging.
The teslamate-org/teslamate repository implements a resilient authentication layer in Elixir that abstracts Tesla’s OAuth 2.0 flow behind a supervised process. The TeslaMate.Api module serves as the central authority for all API interactions, handling everything from initial sign-in to transparent token rotation without user intervention.
Authentication Flow and Token Acquisition
The authentication process begins in lib/teslamate/api.ex through the sign_in/2 function, which accepts either fresh credentials or existing token structs. When provided with a %TeslaMate.Auth.Tokens{} struct containing decrypted access and refresh tokens, the system initiates the refresh workflow immediately.
The GenServer delegates the actual OAuth refresh request to TeslaApi.Auth.refresh/1 in lib/tesla_api/auth/refresh.ex. This function constructs a POST payload with grant_type=refresh_token and sends it to Tesla’s token endpoint via the HTTP client defined in lib/tesla_api/auth.ex. Upon receiving a successful 200 response, the function parses the JSON body and returns a populated %TeslaApi.Auth{} struct containing the new access_token, refresh_token, and expires_in values.
Once refreshed, the credentials persist through two mechanisms. The insert_auth/2 function stores the struct in an ETS table for fast runtime access, while a call to TeslaMate.Auth encrypts and writes the tokens to the PostgreSQL database via the schema defined in lib/teslamate/auth/tokens.ex. After successful persistence, the vehicle supervisor restarts to ensure all live processes receive the fresh credentials.
Automatic Token Refresh Scheduling
TeslaMate eliminates manual token management through proactive scheduling logic implemented in the TeslaMate.Api GenServer.
Startup Initialization
During init/1, the server queries the database for existing tokens using the dependency-injected auth module. If tokens exist, refresh_tokens/1 executes immediately to ensure the system starts with valid credentials rather than potentially expired ones.
Refresh Timer Calculation
The schedule_refresh/2 function calculates the refresh interval as 75% of the token’s expires_in value, converting this to milliseconds and registering a :refresh_auth message via Process.send_after/3. This aggressive scheduling provides a 25% buffer before expiry, accounting for network latency or temporary API unavailability.
Handling Refresh Events
When the :refresh_auth message fires, handle_info/2 triggers another call to Auth.refresh/1. On success, the new tokens replace the old ones in both ETS and the database, and a new timer schedules the next refresh. If the refresh fails—due to network issues or revoked tokens—the system logs the error and retries after five minutes, preventing tight error loops while maintaining recovery attempts.
Fuse Protection Mechanism
The module implements a fuse pattern in handle_result/3 to catch authorization failures during regular API operations. When any request returns an unauthorized error, the fuse "melts" and the next request triggers an immediate :refresh_auth message before failing. This circuit-breaker approach prevents endless retry loops against Tesla’s API while ensuring stale tokens get refreshed automatically when possible.
Secure Token Storage Architecture
Token security relies on the TeslaMate.Auth.Tokens Ecto schema, which stores both access and refresh tokens as Encrypted.Binary types. This custom Ecto type utilizes the Vault abstraction (typically lib/teslamate/vault.ex) to encrypt tokens at the application layer before they reach the database. Consequently, raw OAuth tokens never appear in plaintext in PostgreSQL logs, backups, or database dumps, satisfying security requirements for credential storage.
Code Examples
The following examples demonstrate interaction with the authentication system:
# Sign in with email and password (triggers full OAuth flow)
{:ok, _pid} = TeslaMate.Api.start_link(name: :teslamate)
:ok = TeslaMate.Api.sign_in(:teslamate, {"user@example.com", "secure_password"})
# Resume session with existing tokens after server restart
tokens = %TeslaMate.Auth.Tokens{access: "existing_access_token", refresh: "existing_refresh_token"}
:ok = TeslaMate.Api.sign_in(:teslamate, tokens)
# Automatic refresh occurs transparently
# After ~45 minutes (75% of a 60-minute expiry), the GenServer:
# 1. Receives :refresh_auth message
# 2. Calls TeslaApi.Auth.refresh/1
# 3. Updates ETS cache and encrypted database storage
# 4. Reschedules next refresh
Summary
- Centralized GenServer:
lib/teslamate/api.exmanages all authentication state as a single process, providing functions likelist_vehicles/1andget_vehicle/2that always use valid tokens. - Dual Storage Strategy: Tokens live in ETS for millisecond-level access during API calls and encrypted PostgreSQL for persistence across restarts.
- Proactive Refreshing: The system calculates refresh timers at 75% of token lifetime, with automatic retry logic and fuse protection against auth failures.
- Secure by Default: All tokens encrypt at rest using Elixir’s Vault abstraction, ensuring credential confidentiality even with database access.
Frequently Asked Questions
How does TeslaMate handle expired access tokens during active API calls?
When an API call returns an unauthorized error, the handle_result/3 function in lib/teslamate/api.ex melts the authentication fuse. This triggers an immediate :refresh_auth message to refresh the token before the next request attempt, preventing cascading failures while ensuring operations resume once fresh credentials are obtained.
What happens to vehicle data collection when tokens need refreshing?
Vehicle processes remain operational during token refresh because the TeslaMate.Api GenServer maintains the credentials separately from the vehicle supervisors. When new tokens arrive, the vehicle supervisor restarts to propagate fresh auth state, but this occurs seamlessly without data loss due to the buffered architecture of the logging pipeline.
Where are Tesla API tokens stored and how are they protected?
Tokens persist in the PostgreSQL database via the tokens table managed by lib/teslamate/auth/tokens.ex. The schema uses Encrypted.Binary types that apply application-layer encryption through the Vault module, ensuring that database dumps, logs, and backups contain only ciphertext rather than usable OAuth tokens.
Can TeslaMate recover from a failed token refresh?
Yes, the system implements exponential backoff logic in handle_info/2. If Auth.refresh/1 fails due to network issues or Tesla API downtime, TeslaMate logs the error and automatically retries the refresh after five minutes, continuing this cycle until successful re-authentication occurs or an administrator intervenes with new credentials.
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 →