# How TeslaMate Handles API Authentication and Automatic Token Refresh

> TeslaMate's API module securely handles Tesla authentication and automatically refreshes OAuth tokens before they expire, ensuring continuous vehicle data logging.

- Repository: [TeslaMate/teslamate](https://github.com/teslamate-org/teslamate)
- Tags: internals
- Published: 2026-06-16

---

**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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/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:

```elixir

# 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.ex`](https://github.com/teslamate-org/teslamate/blob/main/lib/teslamate/api.ex) manages all authentication state as a single process, providing functions like `list_vehicles/1` and `get_vehicle/2` that 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`](https://github.com/teslamate-org/teslamate/blob/main/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`](https://github.com/teslamate-org/teslamate/blob/main/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.