Overview
This endpoint is called by OAuth providers after the user authorizes access. It exchanges the authorization code for access and refresh tokens, stores the connection, and triggers an initial data sync.
This endpoint is designed to be called by OAuth providers, not directly by your application. Users are redirected here automatically after authorizing on the provider’s site.
Path Parameters
Provider identifier (e.g., garmin, polar, strava, oura)
Query Parameters
Authorization code from the OAuth provider. This is a one-time code that will be exchanged for access tokens.
State parameter for CSRF protection. Must match the state generated in the authorize request.
Error code if user denied access or authorization failed
Human-readable description of the error
Response
This endpoint returns a redirect response (HTTP 303 See Other), not JSON:
Success Redirect
If a custom redirect_uri was provided during authorization:
HTTP/1.1 303 See Other
Location: https://your-app.com/success?connected=true
If no custom redirect URI was provided:
HTTP/1.1 303 See Other
Location: /api/v1/oauth/success?provider=garmin&user_id=123e4567-e89b-12d3-a456-426614174000
Error Redirect
If authorization fails:
HTTP/1.1 303 See Other
Location: /api/v1/oauth/error?message=access_denied:+User+denied+authorization
Success Response Schema
When redirected to /api/v1/oauth/success, the response is:
Always true for successful connections
Success message (e.g., “Successfully connected to garmin”)
UUID of the connected user
Example Success Response
{
"success": true,
"message": "Successfully connected to garmin",
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"provider": "garmin"
}
Error Response Schema
When redirected to /api/v1/oauth/error, the response is:
Example Error Response
{
"success": false,
"message": "access_denied: User denied authorization"
}
Callback Flow
Validate parameters
Endpoint checks for error parameter or missing code/state
Verify state
State parameter is validated against stored value in Redis to prevent CSRF attacks
Exchange code
Authorization code is exchanged for access and refresh tokens via provider’s token endpoint
Store connection
User connection is created/updated in database with encrypted tokens
Trigger sync
Background Celery task is queued to sync user’s health data
Redirect user
User is redirected to success page or custom redirect URI
Background Processing
After successful callback:
Standard Data Sync
A sync_vendor_data Celery task is automatically queued to fetch the user’s latest health data:
sync_vendor_data.delay(
user_id=str(user_id),
start_date=None, # Uses provider's default lookback period
end_date=None,
providers=[provider]
)
Garmin Backfill
For Garmin connections, an additional 30-day backfill task is triggered:
start_garmin_full_backfill.delay(str(user_id))
This fetches historical data for all supported Garmin data types.
Common Error Scenarios
User Denied Access
Provider redirects with error parameter:
/api/v1/oauth/garmin/callback?error=access_denied&error_description=User+denied+authorization
Invalid State (CSRF)
If state doesn’t match or has expired, the callback fails with a 400 error.
Missing Parameters
If code or state is missing:
/api/v1/oauth/error?message=Missing+OAuth+parameters
Token Exchange Failure
If the provider’s token endpoint returns an error, it will be logged and an error page shown.
Implementation Example
While this endpoint is called by providers, here’s how you’d set up the full OAuth flow:
import httpx
import webbrowser
# Step 1: Get authorization URL
auth_response = httpx.get(
"https://api.openwearables.com/api/v1/oauth/garmin/authorize",
params={"user_id": "123e4567-e89b-12d3-a456-426614174000"},
headers={"Authorization": "Bearer YOUR_API_KEY"}
)
auth_data = auth_response.json()
authorization_url = auth_data["authorization_url"]
# Step 2: Open browser for user to authorize
webbrowser.open(authorization_url)
# Step 3: Provider calls callback endpoint automatically
# Step 4: User is redirected to success page
print("User will be redirected after authorization")
Security Considerations
- State Validation: The state parameter prevents CSRF attacks by ensuring the callback matches an initiated authorization flow
- Token Storage: Access and refresh tokens are encrypted before storage in the database
- Token Expiration: Token expiration times are tracked and tokens are automatically refreshed when needed
- HTTPS Only: OAuth flows must use HTTPS in production to prevent token interception
Notes
- The callback URL must be registered with each OAuth provider in their developer console
- State tokens expire after 10 minutes if not used
- Some providers (like Garmin) may take several seconds to process the token exchange
- The data sync happens asynchronously in the background and may take several minutes depending on data volume