mirror of
https://github.com/sstent/FitTrack_GarminSync.git
synced 2026-01-25 08:35:23 +00:00
Complete implementation planning for MFA authentication with garth
- Created detailed implementation plan with technical context - Developed data models for GarthToken, MFAChallenge, and UserSession entities - Defined API contracts for MFA authentication flow - Created quickstart guide for implementation - Updated agent context with new technology stack - Verified constitution compliance for all design decisions
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
openapi: 3.0.0
|
||||
info:
|
||||
title: GarminSync Authentication API with MFA
|
||||
version: 1.0.0
|
||||
description: API for authenticating users with MFA support using garth
|
||||
paths:
|
||||
/api/garmin/login:
|
||||
post:
|
||||
summary: Authenticate user with optional MFA
|
||||
description: Authenticate a user with username and password, with support for MFA challenges
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
required:
|
||||
- username
|
||||
- password
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
description: User's Garmin Connect username or email
|
||||
password:
|
||||
type: string
|
||||
description: User's Garmin Connect password
|
||||
mfa_code:
|
||||
type: string
|
||||
description: MFA code if authentication requires it
|
||||
responses:
|
||||
'200':
|
||||
description: Authentication successful
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: true
|
||||
session_id:
|
||||
type: string
|
||||
description: Unique identifier for the authenticated session
|
||||
access_token:
|
||||
type: string
|
||||
description: Access token for API calls
|
||||
token_type:
|
||||
type: string
|
||||
description: Type of token (e.g., Bearer)
|
||||
expires_in:
|
||||
type: integer
|
||||
description: Time until token expiration in seconds
|
||||
mfa_required:
|
||||
type: boolean
|
||||
description: Whether MFA is required for this account
|
||||
mfa_challenge_id:
|
||||
type: string
|
||||
description: ID for MFA challenge if MFA is required (available in initial response)
|
||||
user:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: User identifier
|
||||
email:
|
||||
type: string
|
||||
description: User's email address
|
||||
'400':
|
||||
description: Invalid credentials or MFA code
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
error:
|
||||
type: string
|
||||
description: Error message
|
||||
mfa_required:
|
||||
type: boolean
|
||||
description: Whether MFA is required
|
||||
mfa_challenge_id:
|
||||
type: string
|
||||
description: ID for the MFA challenge
|
||||
mfa_type:
|
||||
type: string
|
||||
description: Type of MFA challenge (push, sms, email)
|
||||
'401':
|
||||
description: Authentication failed
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
error:
|
||||
type: string
|
||||
description: Error message
|
||||
'429':
|
||||
description: Too many failed authentication attempts
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
example: false
|
||||
error:
|
||||
type: string
|
||||
description: Error message
|
||||
retry_after:
|
||||
type: integer
|
||||
description: Seconds to wait before retrying
|
||||
97
specs/007-update-the-authentication/data-model.md
Normal file
97
specs/007-update-the-authentication/data-model.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Data Model: Update Authentication Flow for MFA with garth
|
||||
|
||||
## Key Entities
|
||||
|
||||
### GarthToken
|
||||
Secure authentication tokens (OAuth1 and OAuth2) used for Garmin Connect API access
|
||||
|
||||
**Attributes:**
|
||||
- `id`: Unique identifier for the token record
|
||||
- `user_id`: Identifier of the user this token represents
|
||||
- `access_token`: The OAuth2 access token value
|
||||
- `refresh_token`: The OAuth2 refresh token value
|
||||
- `oauth1_token`: The OAuth1 token data (dictionary format as used by garth)
|
||||
- `token_type`: Type of token (usually "Bearer")
|
||||
- `expires_in`: Time until expiration in seconds
|
||||
- `scope`: Permissions associated with this token
|
||||
- `created_at`: Timestamp when token was created
|
||||
- `last_used_at`: Timestamp of last usage
|
||||
- `mfa_verified`: Boolean indicating if MFA was completed for this token session
|
||||
- `is_active`: Boolean indicating if token is currently valid/useable
|
||||
- `device_uuid`: UUID of the device where authentication originated (for security)
|
||||
|
||||
**Relationships:**
|
||||
- Belongs to one User (via user_id foreign key)
|
||||
|
||||
### MFAChallenge
|
||||
Represents a multi-factor authentication request with type, expiration time, and validation status
|
||||
|
||||
**Attributes:**
|
||||
- `id`: Unique identifier for the MFA challenge
|
||||
- `user_id`: Identifier of the user requesting authentication
|
||||
- `session_id`: Session identifier linking to the authentication attempt
|
||||
- `challenge_type`: Type of MFA challenge (push, sms, email)
|
||||
- `challenge_reference`: Reference identifier from Garmin's MFA service
|
||||
- `sent_to`: Destination where MFA was sent (phone number, email address)
|
||||
- `status`: Current status (pending, completed, expired, failed)
|
||||
- `attempts_count`: Number of attempts made to validate this challenge
|
||||
- `expires_at`: Timestamp when the challenge expires
|
||||
- `completed_at`: Timestamp when the challenge was completed (if applicable)
|
||||
- `created_at`: Timestamp when challenge was initiated
|
||||
|
||||
### UserSession
|
||||
Represents an authenticated user session with associated garth tokens and permissions
|
||||
|
||||
**Attributes:**
|
||||
- `id`: Unique session identifier
|
||||
- `user_id`: Identifier of the authenticated user
|
||||
- `session_token`: Secure session token for internal use
|
||||
- `garth_token_id`: Reference to the associated GarthToken
|
||||
- `is_mfa_complete`: Boolean indicating if MFA has been completed for this session
|
||||
- `mfa_completed_at`: Timestamp when MFA was completed (if applicable)
|
||||
- `created_at`: Timestamp when the session was created
|
||||
- `last_activity_at`: Timestamp of last activity with this session
|
||||
- `expires_at`: Timestamp when the session expires
|
||||
- `ip_address`: IP address from which the session was created
|
||||
- `user_agent`: User agent string of the client that created the session
|
||||
|
||||
**Relationships:**
|
||||
- Belongs to one User (via user_id foreign key)
|
||||
- References one GarthToken (via garth_token_id foreign key)
|
||||
|
||||
## Relationships
|
||||
|
||||
1. **User** 1 → * **GarthToken**: A user can have multiple tokens over time (rotation, refresh, etc.)
|
||||
2. **User** 1 → * **MFAChallenge**: A user can have multiple MFA challenges (one per auth attempt)
|
||||
3. **User** 1 → * **UserSession**: A user can have multiple concurrent sessions
|
||||
4. **UserSession** 1 → 1 **GarthToken**: Each session is associated with one active token
|
||||
|
||||
## Validation Rules
|
||||
|
||||
From Functional Requirements:
|
||||
- **FR-005**: garth tokens must be stored securely with appropriate encryption in CentralDB
|
||||
- **FR-006**: Tokens must be validated for existence and validity before reuse
|
||||
- **FR-007**: Expired tokens must be refreshed automatically when possible, or fail gracefully
|
||||
- **FR-008**: MFA challenges must have clear status and expiration times to prevent abuse
|
||||
- **FR-009**: MFA challenge types must be properly distinguished and handled
|
||||
|
||||
## State Transitions
|
||||
|
||||
### MFAChallenge Status Transitions
|
||||
- `pending` → `completed` (when correct MFA code is provided)
|
||||
- `pending` → `expired` (when challenge exceeds expiration time)
|
||||
- `pending` → `failed` (when too many incorrect attempts are made)
|
||||
- `completed` → `expired` (after successful authentication has been used sufficiently)
|
||||
|
||||
### UserSession States
|
||||
- Active session remains valid until `expires_at` timestamp
|
||||
- Session can be invalidated early by user logout or security monitoring
|
||||
- Session tokens should be invalidated when associated garth tokens are refreshed
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- GarthToken records should be encrypted at rest in the database
|
||||
- MFAChallenge attempts should be rate-limited to prevent brute force attacks
|
||||
- Session tokens should use cryptographically secure random generation
|
||||
- Token refresh mechanisms should follow OAuth2 best practices
|
||||
- Device identification should be used to detect suspicious authentication patterns
|
||||
112
specs/007-update-the-authentication/plan.md
Normal file
112
specs/007-update-the-authentication/plan.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# Implementation Plan: Update Authentication Flow for MFA with garth
|
||||
|
||||
**Branch**: `007-auth-flow-mfa` | **Date**: Thursday, December 19, 2025 | **Spec**: /home/sstent/Projects/FitTrack/GarminSync/specs/007-update-the-authentication/spec.md
|
||||
**Input**: Feature specification from `/specs/007-update-the-authentication/spec.md`
|
||||
|
||||
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
|
||||
|
||||
## Summary
|
||||
|
||||
This feature updates the authentication flow to properly handle Multi-Factor Authentication (MFA) using the garth library. The implementation will detect when MFA is required during Garmin Connect authentication, handle MFA challenge/response flow, securely manage garth tokens in CentralDB, and improve error messaging for MFA-related scenarios. This enables users with MFA-enabled Garmin Connect accounts to authenticate successfully through the CLI.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: Python 3.13 (Constitution requirement: Python Modernization principle)
|
||||
**Primary Dependencies**: garth (for Garmin authentication), garminconnect (for API calls), fastapi (for API), sqlalchemy (for CentralDB access), click (for CLI), httpx (for HTTP client)
|
||||
**Storage**: CentralDB (PostgreSQL or SQLite) for garth token storage via SQLAlchemy ORM
|
||||
**Testing**: pytest (Constitution requirement: TDD principle) for unit, integration, and API tests
|
||||
**Target Platform**: Linux, macOS, Windows server environments (for backend service) and command-line environments (for CLI)
|
||||
**Project Type**: Single service with CLI interface
|
||||
**Performance Goals**: MFA authentication completes within 2 minutes (per spec SC-002), 95% token reuse rate (per spec SC-004)
|
||||
**Constraints**: Secure handling of OAuth tokens, proper MFA challenge handling, resilience to network interruptions
|
||||
**Scale/Scope**: Individual user tool with centralized token storage, designed for personal/gym use cases
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-checked after Phase 1 design.*
|
||||
|
||||
- **Python Modernization**: Compliant (Python 3.13 with type hints)
|
||||
- **Virtual Environment Isolation**: Compliant (uses .venv, dependencies pinned)
|
||||
- **Test-Driven Development**: Compliant (pytest for testing as required)
|
||||
- **Containerization Standards**: Compliant (Docker Compose V3, multi-stage builds)
|
||||
- **Project Structure Standards**: Compliant (follows src/api, src/models, src/services structure)
|
||||
- **Service-Specific Standards**:
|
||||
- **garminsync_service**: Compliant (asynchronous processing, OAuth flows for Garmin Connect)
|
||||
- **cli_interface**: Compliant (using Click as required, YAML config, multiple output formats)
|
||||
- **centraldb_service**: Compliant (PostgreSQL/SQLite, SQLAlchemy 2.0+, FastAPI)
|
||||
- **API Standards**: Compliant (FastAPI, auto-generated docs, structured errors, API specifications published to APISpecs directory with GarminSync prefix)
|
||||
- **Code Quality Standards**: Compliant (Black, Flake8, Mypy, Isort as required)
|
||||
- **Dependency Management**: Compliant (requirements.txt with pinned dependencies)
|
||||
|
||||
All constitution gates pass. No violations detected post-design.
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```
|
||||
specs/007-update-the-authentication/
|
||||
├── plan.md # This file (/speckit.plan command output)
|
||||
├── research.md # Phase 0 output (/speckit.plan command)
|
||||
├── data-model.md # Phase 1 output (/speckit.plan command)
|
||||
├── quickstart.md # Phase 1 output (/speckit.plan command)
|
||||
├── contracts/ # Phase 1 output (/speckit.plan command)
|
||||
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```
|
||||
src/
|
||||
├── models/
|
||||
│ ├── __init__.py
|
||||
│ ├── auth.py # Authentication-related data models (GarthToken, MFACallenge, UserSession)
|
||||
│ ├── base.py # Base model with common functionality
|
||||
│ └── sync_job.py # Sync job model
|
||||
├── services/
|
||||
│ ├── __init__.py
|
||||
│ ├── auth_service.py # Authentication service handling garth integration and MFA flow
|
||||
│ └── sync_service.py # Sync service
|
||||
├── api/
|
||||
│ ├── __init__.py
|
||||
│ ├── deps.py # API dependency injection
|
||||
│ ├── auth.py # Authentication endpoints with MFA handling
|
||||
│ └── sync.py # Sync endpoints
|
||||
├── db/
|
||||
│ ├── __init__.py
|
||||
│ ├── base.py # Database base
|
||||
│ ├── models.py # Database models
|
||||
│ └── session.py # Database session management
|
||||
└── main.py # FastAPI application entry point
|
||||
|
||||
cli/
|
||||
├── src/
|
||||
│ ├── __init__.py
|
||||
│ ├── main.py # CLI entry point
|
||||
│ ├── api_client.py # CLI API client for communication with backend
|
||||
│ └── commands/
|
||||
│ ├── __init__.py
|
||||
│ ├── auth_cmd.py # Authentication CLI commands with MFA support
|
||||
│ └── sync_cmd.py # Sync CLI commands
|
||||
└── requirements.txt
|
||||
|
||||
tests/
|
||||
├── unit/
|
||||
│ ├── test_auth_service.py
|
||||
│ └── test_auth_api.py
|
||||
├── integration/
|
||||
│ ├── test_auth_flow.py
|
||||
│ └── test_mfa_flow.py
|
||||
└── api/
|
||||
└── test_auth_endpoints.py
|
||||
```
|
||||
|
||||
**Structure Decision**: Following the existing project structure with enhancements to accommodate the MFA flow requirements. The auth_service will handle the garth integration and MFA challenge/response flow, with secure token storage in CentralDB.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
*Fill ONLY if Constitution Check has violations that must be justified*
|
||||
|
||||
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
||||
|-----------|------------|-------------------------------------|
|
||||
| (None) | (None) | (None) |
|
||||
180
specs/007-update-the-authentication/quickstart.md
Normal file
180
specs/007-update-the-authentication/quickstart.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# Quickstart Guide: Updated Authentication Flow for MFA with garth
|
||||
|
||||
## Overview
|
||||
|
||||
This guide provides essential information for developers implementing the updated MFA authentication flow using garth. The updated flow properly handles multi-factor authentication challenges when users have MFA enabled on their Garmin Connect accounts.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The authentication flow follows this sequence:
|
||||
1. CLI sends username/password to backend service
|
||||
2. Backend service uses garth to initiate authentication with Garmin
|
||||
3. If MFA is required, backend responds with MFA challenge details
|
||||
4. CLI prompts user for MFA code
|
||||
5. CLI sends MFA code to backend
|
||||
6. Backend completes authentication with garth/Garmin and returns tokens
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### 1. Backend Service Updates
|
||||
|
||||
The backend service needs to be updated to handle the multi-step MFA flow:
|
||||
|
||||
```python
|
||||
# In your authentication endpoint
|
||||
async def garmin_login():
|
||||
# Step 1: Attempt initial authentication
|
||||
result = await garth_client.login(username, password)
|
||||
|
||||
# Check if MFA is required
|
||||
if garth_client.mfa_required:
|
||||
# Return MFA challenge details
|
||||
return {
|
||||
"success": False,
|
||||
"mfa_required": True,
|
||||
"mfa_challenge_id": generate_challenge_id(),
|
||||
"mfa_type": determine_mfa_type(username)
|
||||
}
|
||||
|
||||
# If no MFA required, complete authentication
|
||||
if result:
|
||||
token = await store_tokens(result)
|
||||
return {
|
||||
"success": True,
|
||||
"session_id": generate_session_id(),
|
||||
"access_token": token.access_token,
|
||||
"expires_in": token.expires_in
|
||||
}
|
||||
```
|
||||
|
||||
### 2. MFA Challenge Completion Endpoint
|
||||
|
||||
Create an endpoint for completing the authentication after MFA code is provided:
|
||||
|
||||
```python
|
||||
@app.post("/api/garmin/login/mfa-complete")
|
||||
async def complete_mfa_login(mfa_data: MFACompletionRequest):
|
||||
# Complete authentication with MFA code using garth
|
||||
result = await garth_client.enter_two_step(mfa_data.mfa_code, mfa_data.challenge_id)
|
||||
|
||||
if result:
|
||||
token = await store_tokens(result)
|
||||
return {
|
||||
"success": True,
|
||||
"session_id": generate_session_id(),
|
||||
"access_token": token.access_token,
|
||||
"expires_in": token.expires_in
|
||||
}
|
||||
else:
|
||||
return {"success": False, "error": "Invalid MFA code"}
|
||||
```
|
||||
|
||||
### 3. CLI Updates
|
||||
|
||||
Update the CLI to handle the two-step flow:
|
||||
|
||||
```python
|
||||
# First call - initial authentication
|
||||
response = await api_client.post("/api/garmin/login", json=payload)
|
||||
|
||||
if response.get("mfa_required"):
|
||||
# Get MFA code from user
|
||||
mfa_code = input(f"Enter {response.get('mfa_type')} code: ")
|
||||
|
||||
# Complete authentication with MFA code
|
||||
completion_payload = {
|
||||
"mfa_code": mfa_code,
|
||||
"challenge_id": response.get("mfa_challenge_id")
|
||||
}
|
||||
response = await api_client.post("/api/garmin/login/mfa-complete", json=completion_payload)
|
||||
|
||||
# Handle successful authentication
|
||||
if response.get("success"):
|
||||
# Store tokens locally
|
||||
token_manager.save_tokens(response)
|
||||
```
|
||||
|
||||
### 4. Token Management
|
||||
|
||||
Implement secure storage and retrieval of garth tokens in CentralDB:
|
||||
|
||||
```python
|
||||
class TokenRepository:
|
||||
async def save_garth_tokens(self, user_id: str, tokens: dict):
|
||||
"""Save garth tokens (OAuth1, OAuth2) to CentralDB"""
|
||||
# Encrypt tokens before storing
|
||||
encrypted_tokens = encrypt(tokens)
|
||||
|
||||
# Store in database
|
||||
await db.execute(
|
||||
insert(GarthToken).values(
|
||||
user_id=user_id,
|
||||
encrypted_tokens=encrypted_tokens,
|
||||
created_at=datetime.utcnow(),
|
||||
expires_at=self.calculate_expiry(tokens.get('expires_in'))
|
||||
)
|
||||
)
|
||||
|
||||
async def load_garth_tokens(self, user_id: str):
|
||||
"""Load and decrypt garth tokens from CentralDB"""
|
||||
result = await db.execute(
|
||||
select(GarthToken).where(GarthToken.user_id == user_id)
|
||||
)
|
||||
token_record = result.fetchone()
|
||||
|
||||
if token_record and self.is_token_valid(token_record):
|
||||
return decrypt(token_record.encrypted_tokens)
|
||||
return None
|
||||
```
|
||||
|
||||
## Testing MFA Flow
|
||||
|
||||
### Unit Tests
|
||||
|
||||
Test the individual components of the MFA flow:
|
||||
|
||||
```python
|
||||
# Test authentication without MFA
|
||||
def test_auth_without_mfa():
|
||||
# Mock garth to return successful authentication without MFA
|
||||
with patch('garth.login', return_value=MOCK_SUCCESS_RESPONSE):
|
||||
response = await auth_endpoint({"username": "test", "password": "pass"})
|
||||
assert response["success"] == True
|
||||
|
||||
# Test MFA challenge initiation
|
||||
def test_auth_with_mfa_required():
|
||||
# Mock garth to indicate MFA is required
|
||||
with patch('garth.login') as mock_login:
|
||||
mock_login.side_effect = MFARequiredException()
|
||||
response = await auth_endpoint({"username": "test", "password": "pass"})
|
||||
assert response["mfa_required"] == True
|
||||
assert "mfa_challenge_id" in response
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
Test the complete MFA flow:
|
||||
|
||||
```python
|
||||
@pytest.mark.asyncio
|
||||
async def test_complete_mfa_flow():
|
||||
# Test the full flow: initiate auth -> receive MFA challenge -> complete with code -> get tokens
|
||||
# 1. Initiate authentication (should return MFA required)
|
||||
init_resp = await client.post("/api/garmin/login", json={"username": "mfa_user", "password": "pass"})
|
||||
assert init_resp.json()["mfa_required"] == True
|
||||
|
||||
# 2. Complete MFA (in mock environment, use known code)
|
||||
mfa_resp = await client.post("/api/garmin/login/mfa-complete",
|
||||
json={"mfa_code": "123456", "challenge_id": init_resp.json()["challenge_id"]})
|
||||
assert mfa_resp.json()["success"] == True
|
||||
assert "access_token" in mfa_resp.json()
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Store garth tokens encrypted in CentralDB
|
||||
- Implement rate limiting for authentication attempts to prevent brute force
|
||||
- Use secure session tokens with appropriate expiration times
|
||||
- Log authentication attempts for security monitoring
|
||||
- Implement secure handling of MFA codes (don't log them)
|
||||
- Validate MFA codes have not expired before accepting them
|
||||
33
specs/007-update-the-authentication/research.md
Normal file
33
specs/007-update-the-authentication/research.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Research Findings: Update Authentication Flow for MFA with garth
|
||||
|
||||
## Phase 0: Outline & Research
|
||||
|
||||
### Decision
|
||||
Update the GarminSync authentication flow to properly handle Multi-Factor Authentication (MFA) by integrating with garth's built-in MFA capabilities. The implementation will follow garth's authentication patterns as observed in the reference implementation while ensuring compatibility with the existing GarminSync service architecture.
|
||||
|
||||
### Rationale
|
||||
- The current implementation doesn't properly handle Garmin's MFA flow, causing authentication to fail for users with MFA enabled
|
||||
- garth library has built-in support for Garmin's MFA challenges, but needs to be properly integrated into the API endpoints
|
||||
- The existing architecture separates CLI from authentication logic (CLI ↔ Backend Service ↔ garth ↔ Garmin), which needs to properly relay MFA challenges back to the user
|
||||
- garth token management needs to be securely stored in CentralDB instead of local files for service-based architecture
|
||||
|
||||
### Technology Choices Made
|
||||
- **garth Library**: Continue using garth as it's specifically designed for Garmin authentication and handles MFA flows properly
|
||||
- **Token Storage**: Use CentralDB for storing garth tokens with proper security (encrypted storage)
|
||||
- **MFA Flow**: Implement multi-step authentication where initial request triggers MFA, then a second request completes with MFA code
|
||||
- **API Design**: Create proper API endpoints to handle the multi-step MFA authentication flow
|
||||
- **CLI Integration**: Update CLI to handle multi-step authentication flow with appropriate user prompts
|
||||
|
||||
### Key Implementation Insights
|
||||
1. garth's `login()` function can handle MFA internally when run in an interactive context, but in a service context, MFA challenges need to be handled differently
|
||||
2. The service needs to implement a two-phase authentication approach: first request to initiate, second request with MFA code to complete
|
||||
3. garth tokens (OAuth1 and OAuth2) need to be securely extracted and stored after successful authentication
|
||||
4. Token refresh mechanisms should be implemented to handle token expiration automatically
|
||||
|
||||
### Alternatives Considered
|
||||
- **Direct garth integration in CLI**: Would simplify MFA flow but would break the service-oriented architecture
|
||||
- **Custom MFA handling**: Would require more complex implementation than leveraging garth's built-in support
|
||||
- **Storing tokens in filesystem**: Less secure than CentralDB storage which allows for proper access controls
|
||||
|
||||
### Integration Approach
|
||||
The solution will modify the existing authentication endpoints to support a multi-step MFA flow while maintaining compatibility with the existing API contract. The backend service will use garth to initiate authentication, detect MFA requirements, and allow completion of authentication when provided with the MFA code.
|
||||
Reference in New Issue
Block a user