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.Tokens schema with Encrypted.Binary fields to ensure encryption at rest.
  • The TeslaMate.Auth module provides save/1 and get_tokens/0 functions for persistence and retrieval of encrypted token pairs.
  • Runtime authentication uses the %TeslaApi.Auth{} struct, which includes regional endpoint detection via TeslaApi.Auth.region/1.
  • The TeslaApi.Middleware.TokenAuth middleware automatically injects the Authorization: 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →