# How TeslaMate Handles API Authentication: Token Storage, Encryption, and Auto-Refresh

> TeslaMate secures API authentication with encrypted token storage, middleware injection, and auto-refresh. Learn how your Tesla data stays safe and accessible.

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

---

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

```elixir

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

```elixir

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

```elixir

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