JWT (JSON Web Token) authentication provides secure, time-limited access to the Open Wearables API. This method is designed for interactive applications where users log in with credentials.
Overview
JWT tokens are short-lived credentials issued after successful login. They include:
Access Token: Short-lived token (default: 60 minutes) for API requests
Refresh Token: Long-lived token for obtaining new access tokens
Token Type: Always bearer
Expiration: Time until access token expires (in seconds)
Token Structure
Access tokens are JWT-encoded strings containing:
{
"exp" : 1709820600 , // Expiration timestamp (Unix epoch)
"sub" : "550e8400-e29b-41d4-a716-446655440000" // Subject (developer ID)
}
Tokens are signed using HS256 algorithm with a server-side secret. Never attempt to decode or forge tokens client-side.
Authentication Flow
Login with credentials
Send email and password to /api/v1/auth/login to receive access and refresh tokens.
Include token in requests
Add the access token to the Authorization header as Bearer {token} for all API requests.
Refresh when expired
When the access token expires (401 error), use the refresh token to get a new access token without re-authenticating.
Logout when done
Call /api/v1/auth/logout and clear stored tokens on the client side.
API Endpoints
Login
This endpoint uses OAuth2 password flow, which requires application/x-www-form-urlencoded content type and uses the username field for the email address.
Developer email address (OAuth2 spec uses “username” field)
Developer account password
Request Example:
curl -X POST https://api.openwearables.com/api/v1/auth/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "[email protected] " \
-d "password=your-secure-password"
Response (200 OK):
{
"access_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDk4MjA2MDAsInN1YiI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMCJ9.Xm1j2k3n4l5m6n7o8p9q0r1s2t3u4v5w6x7y8z9a0b" ,
"token_type" : "bearer" ,
"refresh_token" : "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6" ,
"expires_in" : 3600
}
JWT access token to use in Authorization header. Valid for the duration specified in expires_in.
Always "bearer". Use as Authorization: Bearer {access_token}.
Long-lived token to obtain new access tokens. Store securely.
Number of seconds until the access token expires (default: 3600 = 1 hour).
Error Response (401 Unauthorized):
{
"detail" : "Incorrect email or password"
}
Get Current Developer
Request Example:
curl https://api.openwearables.com/api/v1/auth/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Response (200 OK):
{
"id" : "550e8400-e29b-41d4-a716-446655440000" ,
"email" : "[email protected] " ,
"first_name" : "Jane" ,
"last_name" : "Developer" ,
"created_at" : "2026-01-15T08:30:00Z" ,
"updated_at" : "2026-03-07T10:30:00Z"
}
Unique identifier for the developer account.
Developer’s email address.
created_at
string (datetime)
required
ISO 8601 timestamp when the account was created.
updated_at
string (datetime)
required
ISO 8601 timestamp of the last account update.
Update Current Developer
Update the developer’s first name (max 100 characters).
Update the developer’s last name (max 100 characters).
Update the developer’s email address.
Request Example:
curl -X PATCH https://api.openwearables.com/api/v1/auth/me \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "Content-Type: application/json" \
-d '{"first_name": "Jane", "last_name": "Smith"}'
Response (200 OK):
{
"id" : "550e8400-e29b-41d4-a716-446655440000" ,
"email" : "[email protected] " ,
"first_name" : "Jane" ,
"last_name" : "Smith" ,
"created_at" : "2026-01-15T08:30:00Z" ,
"updated_at" : "2026-03-07T11:45:00Z"
}
Logout
Currently, token invalidation is handled client-side. Ensure you clear both access and refresh tokens from storage after logout.
Request Example:
curl -X POST https://api.openwearables.com/api/v1/auth/logout \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Response (200 OK):
{
"message" : "Successfully logged out"
}
Token Refresh
Coming Soon: The refresh token endpoint is planned but not yet implemented. Currently, users must re-authenticate when tokens expire.
The refresh endpoint will allow exchanging a refresh token for a new access token:
curl -X POST https://api.openwearables.com/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "your-refresh-token"}'
Using JWT Tokens
Include the access token in the Authorization header using the Bearer scheme:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Python with requests
JavaScript with fetch
import requests
class OpenWearablesClient :
def __init__ ( self , access_token : str ):
self .access_token = access_token
self .base_url = "https://api.openwearables.com/api/v1"
def _headers ( self ) -> dict :
return { "Authorization" : f "Bearer { self .access_token } " }
def get_current_developer ( self ) -> dict :
response = requests.get(
f " { self .base_url } /auth/me" ,
headers = self ._headers()
)
response.raise_for_status()
return response.json()
def list_api_keys ( self ) -> list[ dict ]:
response = requests.get(
f " { self .base_url } /api-keys" ,
headers = self ._headers()
)
response.raise_for_status()
return response.json()
# Usage
client = OpenWearablesClient( access_token = "your-jwt-token" )
developer = client.get_current_developer()
api_keys = client.list_api_keys()
Handling Token Expiration
Implement automatic token refresh to maintain seamless user sessions:
import time
import requests
from typing import Optional
class TokenManager :
"""Manage JWT tokens with automatic refresh."""
def __init__ ( self , email : str , password : str ):
self .email = email
self .password = password
self .base_url = "https://api.openwearables.com/api/v1"
self .access_token: Optional[ str ] = None
self .refresh_token: Optional[ str ] = None
self .expires_at: Optional[ float ] = None
def login ( self ) -> None :
"""Perform initial login."""
response = requests.post(
f " { self .base_url } /auth/login" ,
data = {
"username" : self .email,
"password" : self .password
}
)
response.raise_for_status()
data = response.json()
self .access_token = data[ "access_token" ]
self .refresh_token = data[ "refresh_token" ]
self .expires_at = time.time() + data[ "expires_in" ]
def is_token_valid ( self ) -> bool :
"""Check if current token is still valid (with 5 min buffer)."""
if not self .access_token or not self .expires_at:
return False
return time.time() < ( self .expires_at - 300 ) # 5 min buffer
def refresh_access_token ( self ) -> None :
"""Refresh the access token using refresh token."""
# Note: This endpoint is not yet implemented
# When available, it will look like this:
response = requests.post(
f " { self .base_url } /auth/refresh" ,
json = { "refresh_token" : self .refresh_token}
)
response.raise_for_status()
data = response.json()
self .access_token = data[ "access_token" ]
self .expires_at = time.time() + data[ "expires_in" ]
def get_valid_token ( self ) -> str :
"""Get a valid access token, refreshing if necessary."""
if not self .is_token_valid():
if self .refresh_token:
try :
self .refresh_access_token()
except Exception :
# Refresh failed, re-login
self .login()
else :
self .login()
return self .access_token
def logout ( self ) -> None :
"""Logout and clear tokens."""
if self .access_token:
try :
requests.post(
f " { self .base_url } /auth/logout" ,
headers = { "Authorization" : f "Bearer { self .access_token } " }
)
except Exception :
pass # Logout failed, but clear tokens anyway
self .access_token = None
self .refresh_token = None
self .expires_at = None
# Usage
token_manager = TokenManager(
email = "[email protected] " ,
password = "your-password"
)
# Get a valid token (automatically refreshes if needed)
access_token = token_manager.get_valid_token()
# Use the token
response = requests.get(
"https://api.openwearables.com/api/v1/auth/me" ,
headers = { "Authorization" : f "Bearer { access_token } " }
)
# Logout when done
token_manager.logout()
Best Practices
Web Applications:
Use httpOnly cookies for refresh tokens (prevents XSS attacks)
Store access tokens in memory or sessionStorage
Never store tokens in localStorage if XSS is a concern
Mobile Applications:
Use secure storage (Keychain on iOS, Keystore on Android)
Never store tokens in plain text
Server Applications:
Use environment variables
Prefer API keys over JWT for server-to-server
Handle expiration gracefully
Implement automatic token refresh
Add a 5-minute buffer before actual expiration
Handle 401 errors by attempting refresh
Re-authenticate if refresh fails
Show user-friendly “session expired” messages
Call the logout endpoint
Clear all stored tokens (access + refresh)
Redirect to login page
Cancel any pending requests
Never send tokens over HTTP
Validate SSL certificates
Use certificate pinning in mobile apps
Protect against token theft
Implement CSRF protection for web apps
Use short expiration times (15-60 minutes)
Monitor for suspicious activity
Rotate refresh tokens periodically
Error Responses
Status Code Error Description 401Unauthorized Invalid credentials or expired token 403Forbidden Valid token but insufficient permissions 422Validation Error Invalid request format (check content-type) 429Rate Limited Too many login attempts 500Server Error Internal server error
Common Error Responses:
// Invalid credentials
{
"detail" : "Incorrect email or password"
}
// Expired token
{
"detail" : "Could not validate credentials"
}
// Missing token
{
"detail" : "Not authenticated"
}
Complete Example: React Authentication
import { useState , useEffect } from 'react' ;
interface TokenData {
access_token : string ;
refresh_token : string ;
expires_in : number ;
}
interface Developer {
id : string ;
email : string ;
first_name : string | null ;
last_name : string | null ;
}
export function useAuth () {
const [ accessToken , setAccessToken ] = useState < string | null >( null );
const [ developer , setDeveloper ] = useState < Developer | null >( null );
const [ loading , setLoading ] = useState ( true );
const baseUrl = 'https://api.openwearables.com/api/v1' ;
// Check for existing token on mount
useEffect (() => {
const token = sessionStorage . getItem ( 'access_token' );
if ( token ) {
setAccessToken ( token );
fetchDeveloper ( token );
} else {
setLoading ( false );
}
}, []);
const fetchDeveloper = async ( token : string ) => {
try {
const response = await fetch ( ` ${ baseUrl } /auth/me` , {
headers: { 'Authorization' : `Bearer ${ token } ` }
});
if ( response . ok ) {
const dev = await response . json ();
setDeveloper ( dev );
} else {
// Token invalid, clear it
logout ();
}
} catch ( error ) {
console . error ( 'Failed to fetch developer:' , error );
} finally {
setLoading ( false );
}
};
const login = async ( email : string , password : string ) : Promise < boolean > => {
try {
const response = await fetch ( ` ${ baseUrl } /auth/login` , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/x-www-form-urlencoded' },
body: new URLSearchParams ({ username: email , password })
});
if ( ! response . ok ) {
const error = await response . json ();
throw new Error ( error . detail || 'Login failed' );
}
const tokenData : TokenData = await response . json ();
// Store tokens
sessionStorage . setItem ( 'access_token' , tokenData . access_token );
sessionStorage . setItem ( 'refresh_token' , tokenData . refresh_token );
setAccessToken ( tokenData . access_token );
await fetchDeveloper ( tokenData . access_token );
return true ;
} catch ( error ) {
console . error ( 'Login error:' , error );
return false ;
}
};
const logout = async () => {
if ( accessToken ) {
try {
await fetch ( ` ${ baseUrl } /auth/logout` , {
method: 'POST' ,
headers: { 'Authorization' : `Bearer ${ accessToken } ` }
});
} catch ( error ) {
console . error ( 'Logout error:' , error );
}
}
// Clear everything
sessionStorage . removeItem ( 'access_token' );
sessionStorage . removeItem ( 'refresh_token' );
setAccessToken ( null );
setDeveloper ( null );
};
return {
accessToken ,
developer ,
loading ,
isAuthenticated: !! developer ,
login ,
logout
};
}
API Key Authentication Learn about long-lived credentials for server integration
Authentication Overview Compare authentication methods and security best practices