Authentication and Session Management¶
This guide explains how authentication works in nwp500-python and how to properly manage sessions across different clients.
Quick Start¶
The simplest way to get started:
import asyncio
from nwp500 import NavienAuthClient, NavienAPIClient
async def main():
# Create auth client and authenticate
async with NavienAuthClient("email@example.com", "password") as auth:
# Create API client using the auth session
api = NavienAPIClient(auth_client=auth)
# Use the API client
devices = await api.list_devices()
print(f"Found {len(devices)} devices")
asyncio.run(main())
How It Works¶
Authentication Flow¶
Create the auth client:
NavienAuthClient(email, password)- Stores credentials in memory - Does NOT authenticate yetEnter the context manager:
async with auth_client:- Creates an aiohttp session - Authenticates with Navien API (using stored credentials) - Tokens are obtained and stored - Ready to useCreate other clients:
NavienAPIClient(auth_client=auth)- Reuses the same session from auth client - No need for separate authentication - Can create multiple clients, all sharing the same sessionExit the context manager:
async withblock ends - Session is automatically closed - All tokens are discarded - Clients can no longer be used
Session Management¶
The auth client manages a single aiohttp session that is shared with all other clients for efficiency.
Session lifecycle:
auth = NavienAuthClient(email, password)
# Session doesn't exist yet!
async with auth:
# Session created here
api = NavienAPIClient(auth_client=auth)
mqtt = NavienMqttClient(auth_client=auth)
# Both api and mqtt share the same session
devices = await api.list_devices()
await mqtt.connect()
# Use clients...
# Session is closed here!
# api and mqtt can no longer be used
Token Management¶
Automatic Token Refresh¶
Tokens are automatically refreshed when they expire:
async with NavienAuthClient(email, password) as auth:
# Tokens obtained during __aenter__
print(auth.current_tokens.access_token)
# Tokens are automatically refreshed when making API calls
# No manual refresh needed in most cases
devices = await api.list_devices()
Checking Token Expiration¶
You can check if tokens are expired:
async with NavienAuthClient(email, password) as auth:
tokens = auth.current_tokens
# Check JWT token expiration
if tokens.is_expired:
print("JWT token has expired (already refreshed by library)")
# Check AWS credentials expiration
if tokens.are_aws_credentials_expired:
print("AWS credentials have expired")
Restoring Previous Sessions¶
If you saved tokens from a previous session, you can restore them without requiring another login:
import json
from nwp500 import NavienAuthClient, AuthTokens
# Save tokens from a previous session
async with NavienAuthClient(email, password) as auth:
tokens_data = auth.current_tokens.model_dump(mode="json")
with open("tokens.json", "w") as f:
json.dump(tokens_data, f)
# Later: Restore from saved tokens
with open("tokens.json") as f:
saved_tokens = AuthTokens.model_validate_json(f.read())
# Authenticate using saved tokens (skips login if still valid)
async with NavienAuthClient(email, password, stored_tokens=saved_tokens) as auth:
api = NavienAPIClient(auth_client=auth)
devices = await api.list_devices()
Token Storage Best Practices¶
Security Considerations:
Never hardcode credentials: Use environment variables or secure vaults
Tokens have expiration: Store with timestamp to check validity
Refresh tokens are sensitive: Protect like passwords
Use HTTPS: Always use secure connections
Rotate tokens regularly: Don’t reuse the same tokens indefinitely
Example: Secure storage with expiration check
import json
from datetime import datetime
from nwp500 import NavienAuthClient, AuthTokens
async def authenticate_with_cache(email: str, password: str, cache_file: str):
"""Authenticate, using cached tokens if still valid."""
# Try to load cached tokens
try:
with open(cache_file) as f:
data = json.load(f)
cached_tokens = AuthTokens.model_validate(data["tokens"])
cached_time = datetime.fromisoformat(data["cached_at"])
# Use cached tokens if less than 1 hour old
if (datetime.now() - cached_time).total_seconds() < 3600:
return NavienAuthClient(
email,
password,
stored_tokens=cached_tokens
)
except (FileNotFoundError, json.JSONDecodeError, ValueError):
pass
# Create new auth (triggers fresh login)
return NavienAuthClient(email, password)
Advanced: Custom Session¶
If you need to use a custom aiohttp session:
import aiohttp
from nwp500 import NavienAuthClient, NavienAPIClient
# Create custom session with specific configuration
connector = aiohttp.TCPConnector(limit_per_host=5)
custom_session = aiohttp.ClientSession(connector=connector)
try:
# Pass custom session to auth client
auth = NavienAuthClient(
email,
password,
session=custom_session
)
async with auth:
api = NavienAPIClient(auth_client=auth)
devices = await api.list_devices()
finally:
# Session management is YOUR responsibility
await custom_session.close()
Troubleshooting¶
“Session is closed” Error¶
Problem: You get “Session is closed” when trying to use clients
Cause: Exited the auth context manager before using clients
Solution: Keep using clients inside the async with block
# ❌ WRONG
async with NavienAuthClient(email, password) as auth:
api = NavienAPIClient(auth_client=auth)
# Session is closed here!
devices = await api.list_devices() # Error!
# ✅ CORRECT
async with NavienAuthClient(email, password) as auth:
api = NavienAPIClient(auth_client=auth)
devices = await api.list_devices() # Works!
“Authentication Failed” Error¶
Problem: Invalid credentials error during authentication
Cause: Wrong email or password
Solution: Check credentials and verify account exists
from nwp500 import InvalidCredentialsError
try:
async with NavienAuthClient(email, password) as auth:
...
except InvalidCredentialsError:
print("Email or password is incorrect")
Token Refresh Failures¶
Problem: Tokens can’t be refreshed
Cause: Refresh token expired or revoked
Solution: Perform a fresh login (don’t use stored_tokens)
# Don't use stored tokens if refresh is failing
auth = NavienAuthClient(email, password) # No stored_tokens
async with auth:
# Fresh login will be performed
...