How TeslaMate Handles User Sessions and Authentication in Its Web Interface
TeslaMate uses a signed HTTP cookie session managed by Phoenix LiveView combined with encrypted OAuth tokens stored in the database to maintain secure user access and authenticate API calls to Tesla.
TeslaMate's web interface is built on the Phoenix framework and follows a conventional session-plus-token strategy to balance security with usability. The application separates transient browser session state from sensitive authentication credentials, ensuring that user sessions and authentication remain persistent across page loads while keeping OAuth tokens encrypted at rest. This architecture enables seamless interaction with the Tesla API without exposing tokens to the client-side browser.
HTTP Session Configuration
The foundation of TeslaMate's session handling lies in lib/teslamate_web/endpoint.ex, where the application configures a signed cookie store for all browser connections. The @session_options module attribute defines a strict same-site cookie policy to prevent cross-site request forgery.
# lib/teslamate_web/endpoint.ex
@session_options [
store: :cookie,
key: "_teslamate_key",
signing_salt: "yt5O3CAQ",
same_site: "Strict"
]
plug Plug.Session, @session_options
This configuration creates a signed cookie named _teslamate_key that persists the user's session ID and locale preferences. The same_site: "Strict" setting ensures the cookie is never sent in cross-origin requests, mitigating CSRF attacks.
Router Pipeline and Session Loading
Every HTTP request passes through the :browser pipeline defined in lib/teslamate_web/router.ex, which loads the session data and makes it available to LiveViews. The pipeline combines standard Phoenix plugs with custom locale handling.
# lib/teslamate_web/router.ex
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session # <-- loads the cookie into conn.session
plug :fetch_live_flash
plug Cldr.Plug.AcceptLanguage, cldr_backend: TeslaMateWeb.Cldr, no_match_log_level: :debug
plug Cldr.Plug.PutLocale, apps: [:cldr, :gettext], from: [:query, :session, :accept_language], gettext: TeslaMateWeb.Gettext, cldr: TeslaMateWeb.Cldr
plug TeslaMateWeb.Plugs.PutSession # stores locale values in the session
plug :protect_from_forgery
plug :put_secure_browser_headers
plug :fetch_settings # puts global settings into conn.assigns and session
end
The fetch_session plug decodes the signed cookie, while TeslaMateWeb.Plugs.PutSession persists CLDR and Gettext locale selections back into the session for subsequent requests. The custom fetch_settings plug additionally loads global configuration from the database into both connection assigns and the session, ensuring settings remain available across LiveView reconnections.
OAuth Token Storage and Encryption
Unlike the transient session cookie, Tesla OAuth tokens receive persistent encrypted storage in the database. The TeslaMate.Auth context in lib/teslamate/auth.ex handles all create, read, update, and delete operations for authentication tokens.
# lib/teslamate/auth.ex
def save(%{token: access, refresh_token: refresh}) do
attrs = %{access: access, refresh: refresh}
maybe_created_or_updated =
case get_tokens() do
nil -> create_tokens(attrs)
tokens -> update_tokens(tokens, attrs)
end
with {:ok, _tokens} <- maybe_created_or_updated, do: :ok
end
The actual token schema in lib/teslamate/auth/tokens.ex utilizes the Vault library to encrypt the access and refresh tokens at rest. This ensures that even with database access, an attacker cannot use the stored credentials without the encryption key.
Sign-In LiveView Process
User authentication occurs through TeslaMateWeb.SignInLive.Index, a LiveView module that renders the sign-in form and handles the OAuth flow. On mount, the view initializes a changeset for token fields and checks for environment-based configuration.
# lib/teslamate_web/live/signin_live/index.ex
def mount(_params, _session, socket) do
assigns = %{
api: get_api(socket),
page_title: gettext("Sign in"),
error: nil,
task: nil,
changeset: Auth.change_tokens(),
token: System.get_env("TOKEN", ""),
provider: System.get_env("TESLA_AUTH_HOST", "https://auth.tesla.com")
}
{:ok, assign(socket, assigns)}
end
When a user submits credentials, the handle_event("sign_in") function spawns a background Task that calls the Tesla API. Upon successful authentication, the returned token pair passes to Auth.save/1, which encrypts and persists them to the database. The user then redirects to the main car view while the browser retains the original session cookie, preserving locale and UI state.
API Request Authentication
All outbound requests to Tesla's API include the stored access token via middleware. The TeslaApi.Middleware.TokenAuth module in lib/tesla_api/middleware/token_auth.ex intercepts every request and injects the bearer token into the authorization header.
# lib/tesla_api/middleware/token_auth.ex
def call(%Tesla.Env{} = env, next, _opts) do
env =
case env.opts[:access_token] do
nil -> env
token -> Tesla.put_header(env, "Authorization", "Bearer " <> token)
end
Tesla.run(env, next)
end
This middleware retrieves the decrypted access token from the database through the API module, ensuring that no token data ever reaches the client-side JavaScript or browser storage.
End-to-End Session Lifecycle
The complete session and authentication flow follows these steps:
- Initial Request: The browser hits
TeslaMateWeb.Endpoint, which creates the signed_teslamate_keycookie. - Pipeline Processing:
Plug.Sessionloads the cookie intoconn.session, andPutSessionpersists locale data. - Authentication: The user visits
/sign_inand submits credentials throughSignInLive.Index. - Token Persistence: On successful API validation,
Auth.save/1encrypts and stores tokens via theTokensschema. - API Usage: Subsequent background syncs use
TokenAuthmiddleware to attach the bearer token to Tesla API requests. - Session Continuity: Page reloads or LiveView reconnections restore state from the signed cookie without requiring re-authentication.
Summary
- Signed cookies handle browser session state via
Plug.Sessionconfigured inendpoint.exwith strict same-site policies. - Encrypted database storage protects OAuth tokens using the Vault library in the
Tokensschema, accessed through theAuthcontext. - Phoenix LiveView manages the sign-in flow through
SignInLive.Index, spawning background tasks for API calls to prevent UI blocking. - Middleware injection automatically adds bearer tokens to outbound requests via
TeslaApi.Middleware.TokenAuth, keeping credentials server-side only. - Locale persistence survives page reloads through the custom
PutSessionplug, storing CLDR and Gettext settings in the same signed cookie.
Frequently Asked Questions
How does TeslaMate secure OAuth tokens from unauthorized access?
TeslaMate stores OAuth tokens in a PostgreSQL database using the Vault library for encryption at rest. The TeslaMate.Auth.Tokens schema encrypts both the access and refresh tokens before persistence, ensuring that database access alone does not compromise account credentials.
What maintains the user session during page reloads or browser restarts?
A signed HTTP cookie named _teslamate_key maintains the session, configured in lib/teslamate_web/endpoint.ex with same_site: "Strict" protection. This cookie persists the session ID and locale preferences, allowing LiveViews to restore state without requiring repeated authentication.
Does the Tesla API token ever reach the user's browser?
No, the Tesla API access token never leaves the server. The TeslaApi.Middleware.TokenAuth module injects the bearer token into API requests on the backend only. The browser receives only the signed session cookie, which contains no OAuth credentials.
How does the application handle the initial sign-in process?
The TeslaMateWeb.SignInLive.Index LiveView renders a form backed by Auth.change_tokens/0. Upon submission, it spawns a background Task to call the Tesla API, then persists the returned tokens using Auth.save/1 before redirecting the user to the dashboard.
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 →