Documentation Index
Fetch the complete documentation index at: https://docs.mirobody.ai/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Health device providers use OAuth for secure user authorization. Mirobody Health supports both OAuth 1.0 and OAuth 2.0 protocols.
OAuth 1.0
Used by: Garmin, Fitbit (legacy)
OAuth 2.0
Used by: Whoop, Apple Health, most modern APIs
OAuth 2.0 Implementation
Most modern health APIs use OAuth 2.0. Here’s how to implement it:
1. Initiate Authorization
async def link(self, user_id: str, return_url: str = None) -> Dict:
"""Initiate OAuth 2.0 authorization"""
# Generate state parameter for CSRF protection
state = self._generate_state()
await self._store_state(user_id, state, return_url)
# Build authorization URL
auth_params = {
"client_id": self.client_id,
"response_type": "code",
"redirect_uri": self.redirect_url,
"scope": self.scopes,
"state": state
}
auth_url = f"{self.auth_url}?{urlencode(auth_params)}"
return {
"link_web_url": auth_url,
"state": state
}
2. Handle Callback
async def callback(self, user_id: str, code: str, state: str, **kwargs) -> Dict:
"""Handle OAuth callback and exchange code for tokens"""
# Verify state parameter
stored_state = await self._get_stored_state(user_id)
if state != stored_state:
raise ValueError("Invalid state parameter")
# Exchange authorization code for access token
token_data = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": self.redirect_url,
"client_id": self.client_id,
"client_secret": self.client_secret
}
async with aiohttp.ClientSession() as session:
async with session.post(self.token_url, data=token_data) as resp:
tokens = await resp.json()
# Store tokens securely
await self._store_tokens(user_id, tokens)
return {"success": True, "message": "Provider linked successfully"}
3. Token Refresh
async def _refresh_token(self, user_id: str) -> Dict:
"""Refresh expired access token"""
tokens = await self._get_tokens(user_id)
refresh_token = tokens.get("refresh_token")
if not refresh_token:
raise ValueError("No refresh token available")
token_data = {
"grant_type": "refresh_token",
"refresh_token": refresh_token,
"client_id": self.client_id,
"client_secret": self.client_secret
}
async with aiohttp.ClientSession() as session:
async with session.post(self.token_url, data=token_data) as resp:
new_tokens = await resp.json()
# Update stored tokens
await self._store_tokens(user_id, new_tokens)
return new_tokens
OAuth 1.0 Implementation
Some providers (like Garmin) use OAuth 1.0:
1. Request Token
async def link(self, user_id: str, return_url: str = None) -> Dict:
"""Initiate OAuth 1.0 flow"""
from requests_oauthlib import OAuth1Session
# Store return URL
if return_url:
await self._store_return_url(user_id, return_url)
# Create OAuth1 session
oauth = OAuth1Session(
client_key=self.client_id,
client_secret=self.client_secret,
callback_uri=self.redirect_url
)
# Fetch request token
request_token = oauth.fetch_request_token(self.request_token_url)
# Store request token temporarily
await self._store_request_token(user_id, request_token)
# Build authorization URL
auth_url = oauth.authorization_url(self.auth_url)
return {"link_web_url": auth_url}
2. Access Token Exchange
async def callback(self, user_id: str, oauth_token: str, oauth_verifier: str, **kwargs) -> Dict:
"""Exchange verifier for access token"""
from requests_oauthlib import OAuth1Session
# Retrieve request token
request_token = await self._get_request_token(user_id)
# Create OAuth1 session with request token
oauth = OAuth1Session(
client_key=self.client_id,
client_secret=self.client_secret,
resource_owner_key=request_token["oauth_token"],
resource_owner_secret=request_token["oauth_token_secret"],
verifier=oauth_verifier
)
# Fetch access token
access_token = oauth.fetch_access_token(self.access_token_url)
# Store access token
await self._store_tokens(user_id, access_token)
return {"success": True}
Security Best Practices
- Encrypt tokens before database storage
- Use secure key management (e.g., environment variables)
- Never log tokens or credentials
- Implement token rotation where supported
For OAuth 2.0:
- Generate cryptographically random state
- Store state temporarily (10-15 minutes TTL)
- Verify state in callback
- Prevent CSRF attacks
- Handle expired tokens gracefully
- Retry failed requests with exponential backoff
- Log errors without exposing sensitive data
- Provide clear error messages to users
Complete Reference
For detailed implementation with all required methods and patterns, see:
Full Integration Guide
Complete provider integration guide in repository
Garmin Example
OAuth 1.0 implementation example
Whoop Example
OAuth 2.0 implementation example
Provider Testing
Testing strategies