Complete implementation planning for CLI app with MFA

- Created implementation plan with technical context
- Developed data models for User Session, Sync Job, and Authentication Token
- Defined API contracts for authentication, sync triggering, and status checking
- Created quickstart guide for CLI usage
- Updated agent context with new technology stack
- Verified constitution compliance for all design decisions
This commit is contained in:
2025-12-18 13:50:51 -08:00
parent ec20c21920
commit 28ab4f3416
7 changed files with 784 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
openapi: 3.0.0
info:
title: GarminSync CLI Authentication API
version: 1.0.0
description: API for CLI-based authentication with MFA support
paths:
/api/auth/cli/login:
post:
summary: Authenticate user via CLI with optional MFA
description: Authenticates a user with username/password and optional MFA code
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- username
- password
properties:
username:
type: string
description: User's email or username
password:
type: string
description: User's password
mfa_code:
type: string
description: MFA code if required
remember_me:
type: boolean
description: Whether to store tokens for future use
responses:
'200':
description: Authentication successful
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
session_id:
type: string
description: Unique session identifier
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
user:
type: object
properties:
id:
type: string
description: User identifier
email:
type: string
description: User's email
username:
type: string
description: User's username
'400':
description: Invalid credentials or missing required fields
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 for this account
'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 attempts
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: false
error:
type: string
description: Error message

View File

@@ -0,0 +1,185 @@
openapi: 3.0.0
info:
title: GarminSync CLI Sync Status API
version: 1.0.0
description: API for checking sync job status via CLI
paths:
/api/sync/cli/status:
get:
summary: Check status of sync operations
description: Retrieves the status of current and recent sync jobs
security:
- bearerAuth: []
parameters:
- name: job_id
in: query
required: false
description: Specific job ID to check (returns all recent if not provided)
schema:
type: string
responses:
'200':
description: Sync status information retrieved successfully
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
jobs:
type: array
items:
type: object
properties:
job_id:
type: string
description: Unique identifier for the sync job
status:
type: string
description: Current status of the sync job
enum: [pending, running, completed, failed, cancelled]
progress:
type: number
format: float
minimum: 0
maximum: 100
description: Percentage of completion (0-100)
sync_type:
type: string
enum: [activities, health, workouts]
description: Type of data being synced
created_at:
type: string
format: date-time
description: When the job was created
started_at:
type: string
format: date-time
description: When the job started executing
completed_at:
type: string
format: date-time
description: When the job completed (if completed)
total_items:
type: integer
description: Total number of items to sync
processed_items:
type: integer
description: Number of items processed so far
error_message:
type: string
description: Error details if job failed
'401':
description: Authentication required or invalid token
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: false
error:
type: string
description: Error message
/api/sync/cli/status/{job_id}:
get:
summary: Check status of a specific sync job
description: Retrieves the status of a specific sync job by ID
security:
- bearerAuth: []
parameters:
- name: job_id
in: path
required: true
description: Unique identifier of the sync job
schema:
type: string
responses:
'200':
description: Specific sync job status retrieved successfully
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
job:
type: object
properties:
job_id:
type: string
description: Unique identifier for the sync job
status:
type: string
description: Current status of the sync job
enum: [pending, running, completed, failed, cancelled]
progress:
type: number
format: float
minimum: 0
maximum: 100
description: Percentage of completion (0-100)
sync_type:
type: string
enum: [activities, health, workouts]
description: Type of data being synced
created_at:
type: string
format: date-time
description: When the job was created
started_at:
type: string
format: date-time
description: When the job started executing
completed_at:
type: string
format: date-time
description: When the job completed (if completed)
total_items:
type: integer
description: Total number of items to sync
processed_items:
type: integer
description: Number of items processed so far
error_message:
type: string
description: Error details if job failed
'401':
description: Authentication required or invalid token
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: false
error:
type: string
description: Error message
'404':
description: Sync job not found
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: false
error:
type: string
description: Error message
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT

View File

@@ -0,0 +1,107 @@
openapi: 3.0.0
info:
title: GarminSync CLI Sync Trigger API
version: 1.0.0
description: API for triggering sync operations via CLI
paths:
/api/sync/cli/trigger:
post:
summary: Trigger a sync operation via CLI
description: Initiates a data sync operation for the authenticated user
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
sync_type:
type: string
enum: [activities, health, workouts]
description: Type of data to sync
date_range:
type: object
properties:
start_date:
type: string
format: date
description: Start date for sync (YYYY-MM-DD)
end_date:
type: string
format: date
description: End date for sync (YYYY-MM-DD)
force_full_sync:
type: boolean
description: Whether to perform a full sync instead of incremental
responses:
'200':
description: Sync operation successfully initiated
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: true
job_id:
type: string
description: Unique identifier for the sync job
status:
type: string
description: Current status of the sync job
enum: [pending, running]
message:
type: string
description: Status message
'400':
description: Invalid request parameters
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: false
error:
type: string
description: Error message
'401':
description: Authentication required or invalid token
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: false
error:
type: string
description: Error message
'409':
description: Sync already in progress for this user
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
example: false
error:
type: string
description: Error message
active_job_id:
type: string
description: ID of the currently active job
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT

View File

@@ -0,0 +1,71 @@
# Data Model: CLI App for API Interaction with MFA
## Key Entities
### User Session
Represents an authenticated user session with associated tokens and permissions
**Attributes:**
- `session_id`: Unique identifier for the session
- `user_id`: Identifier for the authenticated user
- `access_token`: JWT or API token for authenticated requests
- `refresh_token`: Token used to refresh the access token
- `expires_at`: Timestamp when the access token expires
- `mfa_enabled`: Boolean indicating if MFA is required for this session
- `created_at`: Timestamp when the session was created
- `last_used_at`: Timestamp of last activity with this session
### Sync Job
Represents an initiated sync operation with status, progress, and metadata
**Attributes:**
- `job_id`: Unique identifier for the sync job
- `user_id`: Identifier of the user who initiated the job
- `status`: Current status (pending, running, completed, failed, cancelled)
- `progress`: Percentage of completion (0-100)
- `start_time`: Timestamp when the sync started
- `end_time`: Timestamp when the sync completed (if completed)
- `sync_type`: Type of sync (activities, health, workouts, etc.)
- `error_message`: Error details if the job failed
- `total_items`: Total number of items to sync
- `processed_items`: Number of items processed so far
### Authentication Token
Secure credential used to access the API on behalf of the user
**Attributes:**
- `token_id`: Unique identifier for the token
- `user_id`: Identifier of the user this token represents
- `access_token`: The actual access token value
- `token_type`: Type of token (Bearer, etc.)
- `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
## Relationships
1. **User Session** 1 → * **Sync Job**: A user session can have multiple sync jobs
2. **Authentication Token** 1 → 1 **User Session**: Each session is associated with one primary auth token
## Validation Rules
From Functional Requirements:
- **FR-005**: Authentication tokens must be stored securely with appropriate file permissions
- **FR-008**: Expired tokens must be refreshed automatically when possible
- **FR-007**: All operations must provide clear feedback on success or failure
## State Transitions
### Sync Job Status Transitions
- `pending``running` (when sync starts)
- `running``completed` (when sync finishes successfully)
- `running``failed` (when sync encounters an error)
- `pending``cancelled` (when user cancels before start)
- `running``cancelled` (when user cancels during execution)
### Session Expiration
- Active session remains valid until `expires_at` timestamp
- Session marked as invalid after expiration
- Automatic refresh attempted if refresh token exists

View File

@@ -0,0 +1,105 @@
# Implementation Plan: CLI App for API Interaction with MFA
**Branch**: `006-cli-auth-sync-mfa` | **Date**: Thursday, December 18, 2025 | **Spec**: /home/sstent/Projects/FitTrack/GarminSync/specs/006-cli-auth-sync-mfa/spec.md
**Input**: User description: "create a simple python CLI app for interacting with our API to authenticate the user, trigger a sync, and see the sync status. Allow for the use of MFA on the acct."
## Summary
This feature implements a text-based command-line interface that allows users to authenticate with the API (with MFA support), trigger sync operations, and check sync status. The implementation follows the project's constitution standards, using Python 3.13 with appropriate libraries for CLI interface, authentication, and API communication.
## Technical Context
**Language/Version**: Python 3.13 (Constitution requirement: Python Modernization principle)
**Primary Dependencies**: Click/Typer for CLI (Constitution requirement: cli_interface standard), httpx for API calls, pydantic for data validation, garth/garminconnect for Garmin authentication
**Storage**: Local file storage for authentication tokens and configuration (JSON/YAML format)
**Testing**: pytest (Constitution requirement: TDD principle) for unit and integration tests
**Target Platform**: Linux, macOS, Windows command-line environments
**Project Type**: Single project CLI application
**Performance Goals**: Authentication in under 30 seconds (per spec SC-001), status checks in under 5 seconds (per spec SC-003)
**Constraints**: Secure handling of credentials and tokens, support for MFA flows, offline token storage
**Scale/Scope**: Individual user tool, single-user operation, designed for personal use scenarios
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- **Python Modernization**: Compliant (Python 3.13, type hints, dataclasses for models)
- **Virtual Environment Isolation**: Compliant (uses .venv, dependencies will be pinned)
- **Test-Driven Development**: Compliant (pytest for testing, will follow TDD approach)
- **Containerization Standards**: Not applicable (CLI tool, not a containerized service)
- **Project Structure Standards**: Compliant (follows src/ structure with models, services, etc.)
- **Service-Specific Standards**:
- **cli_interface**: Compliant (using Click as required by constitution, YAML config, multiple output formats)
- **API Standards**: Compliant (will interact with existing API endpoints following REST patterns)
- **Code Quality Standards**: Compliant (will use Black, Flake8, Mypy, Isort as required)
- **Dependency Management**: Compliant (will use requirements.txt with pinned dependencies)
All constitution gates pass. No violations detected post-design.
## Project Structure
### Documentation (this feature)
```
specs/006-cli-auth-sync-mfa/
├── 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)
```
cli/
├── src/
│ ├── __init__.py
│ ├── main.py # CLI entry point with commands for auth, sync, status
│ ├── auth/
│ │ ├── __init__.py
│ │ ├── auth_manager.py # Handles authentication flows with MFA support
│ │ └── token_manager.py # Manages local token storage and refresh
│ ├── api/
│ │ ├── __init__.py
│ │ ├── client.py # API client for communicating with backend
│ │ └── endpoints.py # API endpoint definitions
│ ├── models/
│ │ ├── __init__.py
│ │ ├── session.py # User session data model
│ │ ├── sync_job.py # Sync job data model
│ │ └── token.py # Authentication token model
│ ├── commands/
│ │ ├── __init__.py
│ │ ├── auth_cmd.py # Authentication command implementation
│ │ ├── sync_cmd.py # Sync triggering command implementation
│ │ └── status_cmd.py # Status checking command implementation
│ └── utils/
│ ├── __init__.py
│ ├── config.py # Configuration handling
│ ├── output.py # Output formatting (JSON, table, CSV)
│ └── validators.py # Input validation utilities
├── tests/
│ ├── unit/
│ │ ├── test_auth_manager.py
│ │ ├── test_token_manager.py
│ │ ├── test_api_client.py
│ │ └── test_commands.py
│ ├── integration/
│ │ ├── test_auth_flow.py
│ │ └── test_sync_operations.py
│ └── contract/
│ └── test_api_contracts.py
└── requirements.txt
```
**Structure Decision**: Created a dedicated CLI directory with a modular structure following the constitution's source structure requirements. The CLI module will follow the cli_interface standards from the constitution, using Click/Typer for the interface and supporting configuration and output formats as required.
## Complexity Tracking
*Fill ONLY if Constitution Check has violations that must be justified*
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| (None) | (None) | (None) |

View File

@@ -0,0 +1,171 @@
# Quickstart Guide: CLI App for API Interaction with MFA
## Overview
This guide provides essential steps for setting up and using the GarminSync CLI application to authenticate with MFA support, trigger sync operations, and check sync status.
## Prerequisites
Before you begin, ensure you have the following installed:
* **Python 3.13+**: The CLI application is built with Python 3.13 as required by project standards.
* [Install Python](https://www.python.org/downloads/)
* **Git**: For cloning the repository (if installing from source).
## Installation
### Option 1: Install from Package (Recommended)
```bash
pip install garmin-sync-cli
```
### Option 2: Install from Source
1. Clone the repository:
```bash
git clone <repository-url>
cd <repository-name>
```
2. Navigate to the CLI directory:
```bash
cd cli
```
3. Install the CLI application in a virtual environment:
```bash
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -r requirements.txt
pip install -e .
```
## Basic Usage
### 1. Authentication
To authenticate with your account (without MFA):
```bash
garmin-sync auth --username your_email@example.com --password your_password
```
To authenticate with your account (with MFA):
```bash
garmin-sync auth --username your_email@example.com --password your_password --mfa-code 123456
```
For interactive authentication (recommended for MFA):
```bash
garmin-sync auth --interactive
```
This will prompt you for your credentials and MFA code if required.
### 2. Trigger a Sync
After authenticating, trigger a sync operation:
```bash
garmin-sync sync trigger --type activities --start-date 2024-01-01 --end-date 2024-01-31
```
Available sync types: `activities`, `health`, `workouts`
For a full sync (not incremental):
```bash
garmin-sync sync trigger --type activities --force-full
```
### 3. Check Sync Status
Check the status of all recent sync jobs:
```bash
garmin-sync sync status
```
Check the status of a specific sync job:
```bash
garmin-sync sync status --job-id abc123def456
```
## Configuration
The CLI application stores authentication tokens and configuration in your home directory:
* `~/.garmin-sync/config.yaml`: Configuration settings
* `~/.garmin-sync/tokens.json`: Encrypted authentication tokens
### Custom Configuration
You can create a custom configuration file at `~/.garmin-sync/config.yaml`:
```yaml
api_base_url: https://api.yourdomain.com
default_timeout: 30
output_format: table # Options: table, json, csv
remember_login: true
```
## Output Formats
The CLI supports multiple output formats:
* **Table** (default): Human-readable tabular format
* **JSON**: Structured JSON output for scripting
* **CSV**: Comma-separated values for data analysis
Specify output format with the `--format` option:
```bash
garmin-sync sync status --format json
```
## Help
Get help with any command:
```bash
garmin-sync --help
garmin-sync auth --help
garmin-sync sync --help
```
## Examples
### Authenticate and Sync Activities
```bash
# Authenticate interactively
garmin-sync auth --interactive
# Trigger a sync for recent activities
garmin-sync sync trigger --type activities --start-date $(date -d "7 days ago" +%Y-%m-%d)
# Check the status of the sync
garmin-sync sync status
```
### Check All Sync Jobs in JSON Format
```bash
garmin-sync sync status --format json
```
## Troubleshooting
### Authentication Issues
* Ensure your credentials are correct
* If using MFA, make sure your code is current
* Clear stored tokens if needed: `garmin-sync auth logout`
### Sync Issues
* Check your internet connection
* Verify API endpoint accessibility
* Check if your account has proper permissions

View File

@@ -0,0 +1,33 @@
# Research Findings for CLI App with MFA Support
## Phase 0: Outline & Research
### Decision
Selected Click as the CLI framework for the Python application based on the project constitution's requirement for CLI interfaces (must be Click or Typer). Decided to create a new CLI module that interfaces with the existing API structure.
### Rationale
- The constitution specifically requires either Click or Typer for CLI interfaces
- Click provides excellent support for multi-level commands which is needed for auth/sync/status operations
- Click has good support for handling MFA flows through prompts
- Consistent with the existing backend architecture of the project
### Technology Choices Made
- **CLI Framework**: Click (as required by constitution)
- **API Client**: httpx (modern, async-capable, excellent for API interactions)
- **Configuration**: YAML (as required by constitution)
- **Output Formats**: JSON, table, CSV (as required by constitution)
- **Token Storage**: Local JSON file with appropriate security measures
- **Authentication Flow**: OAuth2/MFA integration with existing backend API
### Alternatives Considered
- **Typer vs Click**: Typer offers better type hints integration, but Click was selected to match constitution requirements
- **argparse**: Simpler but lacks the advanced features needed for this CLI application
- **Direct HTTP requests**: httpx was chosen over basic requests library for better async support and API interactions
### Integration Approach
The CLI app will integrate with the existing backend API, reusing authentication mechanisms and sync endpoints. This approach leverages existing functionality while providing a new interface for users who prefer command-line operations.
### Security Considerations
- Local token storage will use appropriate file permissions (600)
- MFA handling will follow platform-appropriate secure input methods
- Token refresh mechanisms will be implemented to handle expiration