Authentication¶
Metricis supports multiple authentication methods for different use cases.
Portal Authentication (JWT)¶
Researchers and administrators use JWT-based authentication.
Login¶
Request:
POST /api/auth/login
Content-Type: application/x-www-form-urlencoded
username=admin@metricis.app&password=your-password
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer",
"expires_in": 86400
}
Using the Token¶
Include token in Authorization header:
Token Expiration¶
Tokens expire after 24 hours by default. Configure in server settings:
Refresh Token (Coming Soon)¶
Patient Portal Authentication (Magic Link)¶
Patients and caregivers use passwordless magic link authentication.
Request Magic Link¶
Request:
Response:
Email contains link:
Verify Magic Link Token¶
When user clicks link, frontend verifies token:
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
// Store token
localStorage.setItem('auth_token', token);
// Fetch user info
const user = await api.getCurrentUser();
API Key Authentication (Coming Soon)¶
For programmatic access and integrations:
Session Authentication¶
Assessment sessions use session IDs for data submission:
Start Session:
POST /api/session/start
Content-Type: application/json
{
"participant_id": "P001",
"battery_id": "battery_1"
}
Response:
Use Session ID:
POST /api/submit
X-Session-ID: sess_abc123
Content-Type: application/json
{
"session_id": "sess_abc123",
"task_summaries": {...},
"trials": [...]
}
Security Best Practices¶
Storing Tokens¶
Browser:
// Store in localStorage (acceptable for public clients)
localStorage.setItem('auth_token', token);
// Or use sessionStorage (cleared on tab close)
sessionStorage.setItem('auth_token', token);
Server-side:
# Store in secure, httpOnly cookies
response.set_cookie(
key="auth_token",
value=token,
httponly=True,
secure=True, # HTTPS only
samesite="lax"
)
Token Validation¶
Server validates JWT tokens on each request:
from jose import JWTError, jwt
from app.config import get_settings
settings = get_settings()
def verify_token(token: str) -> dict:
try:
payload = jwt.decode(
token,
settings.JWT_SECRET_KEY.get_secret_value(),
algorithms=[settings.JWT_ALGORITHM]
)
return payload
except JWTError:
raise HTTPException(status_code=401, detail="Invalid token")
Password Security¶
Passwords are hashed using bcrypt:
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Hash password
hashed = pwd_context.hash("user-password")
# Verify password
is_valid = pwd_context.verify("user-password", hashed)
Role-Based Access Control¶
Users have roles that determine permissions:
class UserRole(str, Enum):
ADMIN = "admin" # Full system access
RESEARCHER = "researcher" # Study management
COORDINATOR = "coordinator" # Participant management
VIEWER = "viewer" # Read-only access
Permission checks:
from fastapi import Depends, HTTPException
from app.utils.auth import get_current_user, require_role
@router.post("/studies")
async def create_study(
study: StudyCreate,
current_user: User = Depends(require_role(UserRole.ADMIN))
):
# Only admins can create studies
...
OAuth2 Integration (Coming Soon)¶
Support for institutional SSO:
- SAML 2.0
- OpenID Connect
- Azure AD / Entra ID
- Google Workspace
Two-Factor Authentication (Coming Soon)¶
Optional 2FA for enhanced security:
- TOTP (Time-based One-Time Password)
- SMS verification
- Email verification
Example: Full Auth Flow¶
Portal Login¶
// Login
const response = await fetch('http://localhost:8000/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
username: 'admin@metricis.app',
password: 'admin123',
}),
});
const { access_token } = await response.json();
localStorage.setItem('auth_token', access_token);
// Use token for requests
const studies = await fetch('http://localhost:8000/api/studies', {
headers: { 'Authorization': `Bearer ${access_token}` },
});
Patient Portal Magic Link¶
// Request magic link
await fetch('http://localhost:8000/api/auth/magic-link', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: 'participant@example.com' }),
});
// User clicks link in email: https://app.metricis.app/auth?token=...
// Frontend extracts and stores token
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
localStorage.setItem('auth_token', token);
// Redirect to home page
window.location.href = '/';
Troubleshooting¶
Token Expired¶
Error:
Solution: Re-authenticate to get new token
Invalid Token¶
Error:
Solution: Verify token format and check for corruption
Rate Limit Exceeded¶
Error:
Solution: Wait 1 minute before retrying
Next Steps¶
- REST API Overview - API basics
- Endpoints - Complete endpoint reference
- Server Architecture - Technical details