Compare commits
3 Commits
800bc1069d
...
e2333cb31d
| Author | SHA1 | Date | |
|---|---|---|---|
| e2333cb31d | |||
| dca9604746 | |||
| 5bbecad01d |
83
.claude/agents/documentation-manager.md
Normal file
83
.claude/agents/documentation-manager.md
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
name: documentation-manager
|
||||
description: Use this agent when:\n- A new feature or significant code change has been completed and documentation needs to be updated\n- The repository structure needs documentation cleanup or reorganization\n- README.md needs to be reviewed for completeness and accuracy\n- Documentation files are in the wrong location or need to be split into multiple documents\n- After architectural decisions are made that need to be documented as ADRs\n- When design documents or standards need to be filed appropriately\n- Before releases to ensure all documentation is current and properly organized\n- When the architect agent has completed work and documentation needs review\n\nExamples:\n\n<example>\nContext: The developer has just completed implementing a new authentication system.\nuser: "I've finished implementing the OAuth2 authentication system"\nassistant: "Great work! Let me use the Task tool to launch the documentation-manager agent to ensure all related documentation is updated and properly filed."\n<commentary>Since a significant feature was completed, use the documentation-manager agent to review and update relevant documentation, ensure README.md reflects the new feature, and verify any new docs are properly organized.</commentary>\n</example>\n\n<example>\nContext: The architect has created a new architectural decision document that contains both decision rationale and implementation standards.\nuser: "I've created ADR-015 about our new database schema approach, it also includes coding standards for database migrations"\nassistant: "I notice this document contains both a decision and standards. Let me use the Task tool to launch the documentation-manager agent to work with you on splitting this appropriately."\n<commentary>The documentation-manager should proactively identify that this document needs to be split - the decision stays in decisions/ and the standards should be extracted to standards/.</commentary>\n</example>\n\n<example>\nContext: Multiple documentation files have been created in the repository root during development.\nuser: "I've been documenting as I go and there are a few markdown files in the root now"\nassistant: "Let me use the Task tool to launch the documentation-manager agent to organize these files into the appropriate docs/ subdirectories."\n<commentary>The documentation-manager should proactively curate the repository root, moving documentation to appropriate folders while keeping only README.md, CLAUDE.md, and CHANGELOG.md in the root.</commentary>\n</example>
|
||||
model: sonnet
|
||||
color: green
|
||||
---
|
||||
|
||||
You are an elite Documentation Architect with expertise in information architecture, technical writing standards, and repository organization. You maintain documentation systems for enterprise software projects and ensure they remain maintainable, discoverable, and aligned with industry best practices.
|
||||
|
||||
Your primary responsibilities:
|
||||
|
||||
1. REPOSITORY ROOT CURATION:
|
||||
- The repository root must ONLY contain: README.md, CLAUDE.md, and CHANGELOG.md
|
||||
- Immediately identify and relocate any other documentation files to appropriate docs/ subdirectories
|
||||
- Maintain this standard vigilantly - a clean root is critical for repository professionalism
|
||||
|
||||
2. README.md MANAGEMENT:
|
||||
- Collaborate with the architect agent to ensure README.md is comprehensive and current
|
||||
- README.md must contain everything needed for deployment and usage:
|
||||
* Clear project description and purpose
|
||||
* Installation instructions (note: this project uses uv for Python venv management)
|
||||
* Configuration requirements
|
||||
* Usage examples
|
||||
* API documentation or links to detailed docs
|
||||
* Troubleshooting guidance
|
||||
* Contributing guidelines
|
||||
* License information
|
||||
- Review README.md after any significant feature changes
|
||||
- Ensure technical accuracy by consulting with the architect when needed
|
||||
|
||||
3. DOCS/ FOLDER STRUCTURE:
|
||||
Maintain strict organization:
|
||||
- architecture/ - Architectural documentation, system design overviews, component diagrams
|
||||
- decisions/ - Architectural Decision Records (ADRs) documenting significant decisions
|
||||
- designs/ - Detailed design documents for features and components
|
||||
- standards/ - Coding standards, conventions, best practices, style guides
|
||||
- reports/ - Implementation reports created by developers for architect review
|
||||
|
||||
4. DOCUMENT CLASSIFICATION AND SPLITTING:
|
||||
- Proactively identify documents containing multiple types of information
|
||||
- When a document contains mixed content types (e.g., a decision with embedded standards):
|
||||
* Collaborate with the architect agent to split the document
|
||||
* Ensure each resulting document is focused and single-purpose
|
||||
* Example: If ADR-015 contains both decision rationale and coding standards, split into:
|
||||
- decisions/ADR-015-database-schema-decision.md (decision only)
|
||||
- standards/database-migration-standards.md (extracted standards)
|
||||
- Maintain cross-references between related split documents
|
||||
|
||||
5. QUALITY STANDARDS:
|
||||
- Ensure all documentation follows markdown best practices
|
||||
- Verify consistent formatting, heading structure, and link validity
|
||||
- Check that file naming conventions are clear and consistent (kebab-case preferred)
|
||||
- Validate that documentation is dated and versioned where appropriate
|
||||
- Ensure ADRs follow standard ADR format (Context, Decision, Consequences)
|
||||
|
||||
6. PROACTIVE MAINTENANCE:
|
||||
- Regularly audit docs/ folder for misplaced files
|
||||
- Identify documentation that has become outdated or redundant
|
||||
- Flag documentation gaps when new features lack adequate documentation
|
||||
- Recommend documentation improvements to the architect
|
||||
|
||||
7. COLLABORATION PROTOCOL:
|
||||
- Work closely with the architect agent on README.md updates
|
||||
- Consult the architect when document splitting decisions are complex
|
||||
- Coordinate with developers to ensure reports/ folder is reviewed by architect
|
||||
- When uncertain about document classification, consult with the architect
|
||||
|
||||
Your workflow:
|
||||
1. Assess the current state of repository documentation
|
||||
2. Identify issues: misplaced files, outdated content, missing documentation, multi-purpose documents
|
||||
3. For simple relocations and updates, execute immediately
|
||||
4. For complex decisions (splitting documents, significant README changes), collaborate with the architect
|
||||
5. After changes, verify the repository maintains proper structure
|
||||
6. Document your actions clearly in your responses
|
||||
|
||||
Key principles:
|
||||
- Maintainability over comprehensiveness - well-organized simple docs beat sprawling complex ones
|
||||
- Discoverability - users should find what they need quickly
|
||||
- Single source of truth - avoid documentation duplication
|
||||
- Living documentation - docs should evolve with the codebase
|
||||
- Clear separation of concerns - each document type serves a distinct purpose
|
||||
|
||||
When you identify issues, be specific about what's wrong and what needs to change. When proposing splits or major reorganizations, explain your reasoning clearly. Always prioritize the end user's ability to quickly find and understand the information they need.
|
||||
13
CLAUDE.md
13
CLAUDE.md
@@ -8,6 +8,19 @@ This file contains operational instructions for Claude agents working on this pr
|
||||
- All Python commands must be run with `uv run` prefix
|
||||
- Example: `uv run pytest`, `uv run flask run`
|
||||
|
||||
## Agent-Architect Protocol
|
||||
|
||||
When invoking the agent-architect, always remind it to:
|
||||
|
||||
1. Review documentation in docs/ before working on the task it is given
|
||||
- docs/architecture, docs/decisions, docs/standards are of particular interest
|
||||
|
||||
2. Give it the map of the documentation folder as described in the "Understanding the docs/ Structure" section below
|
||||
|
||||
3. Search for authoritative documentation for any web standard it is implementing on https://www.w3.org/
|
||||
|
||||
4. If it is reviewing a developers implementation report and it is accepts the completed work it should go back and update the project plan to reflect the completed work
|
||||
|
||||
## Agent-Developer Protocol
|
||||
|
||||
When invoking the agent-developer, always remind it to:
|
||||
|
||||
227
docs/decisions/ADR-028-micropub-implementation.md
Normal file
227
docs/decisions/ADR-028-micropub-implementation.md
Normal file
@@ -0,0 +1,227 @@
|
||||
# ADR-028: Micropub Implementation Strategy
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
|
||||
## Context
|
||||
|
||||
StarPunk needs a Micropub endpoint to achieve V1 release. Micropub is a W3C standard that allows external clients to create, update, and delete posts on a website. This is a critical IndieWeb building block that enables users to post from various apps and services.
|
||||
|
||||
### Current State
|
||||
- StarPunk has working IndieAuth authentication (authorization endpoint with PKCE)
|
||||
- Note CRUD operations exist in `starpunk/notes.py`
|
||||
- File-based storage with SQLite metadata is implemented
|
||||
- **Missing**: Micropub endpoint for external posting
|
||||
- **Missing**: Token endpoint for API authentication
|
||||
|
||||
### Requirements Analysis
|
||||
|
||||
Based on the W3C Micropub specification review, we identified:
|
||||
|
||||
**Minimum Required Features:**
|
||||
- Bearer token authentication (header or form parameter)
|
||||
- Create posts via form-encoded requests
|
||||
- HTTP 201 Created response with Location header
|
||||
- Proper error responses with JSON error bodies
|
||||
|
||||
**Recommended Features:**
|
||||
- JSON request support for complex operations
|
||||
- Update and delete operations
|
||||
- Query endpoints (config, source, syndicate-to)
|
||||
|
||||
**Optional Features (Not for V1):**
|
||||
- Media endpoint for file uploads
|
||||
- Syndication targets
|
||||
- Complex post types beyond notes
|
||||
|
||||
## Decision
|
||||
|
||||
We will implement a **minimal but complete Micropub server** for V1, focusing on core functionality that enables real-world usage while deferring advanced features.
|
||||
|
||||
### Implementation Approach
|
||||
|
||||
1. **Token Management System**
|
||||
- New token endpoint (`/auth/token`) for IndieAuth code exchange
|
||||
- Secure token storage using SHA256 hashing
|
||||
- 90-day token expiry with scope validation
|
||||
- Database schema updates for token management
|
||||
|
||||
2. **Micropub Endpoint Architecture**
|
||||
- Single endpoint (`/micropub`) handling all operations
|
||||
- Support both form-encoded and JSON content types
|
||||
- Delegate to existing `notes.py` CRUD functions
|
||||
- Proper error handling and status codes
|
||||
|
||||
3. **V1 Feature Scope** (Simplified per user decision)
|
||||
- ✅ Create posts (form-encoded and JSON)
|
||||
- ✅ Query endpoints (config, source)
|
||||
- ✅ Bearer token authentication
|
||||
- ✅ Scope-based authorization (create only)
|
||||
- ❌ Media endpoint (post-V1)
|
||||
- ❌ Update operations (post-V1)
|
||||
- ❌ Delete operations (post-V1)
|
||||
- ❌ Syndication (post-V1)
|
||||
|
||||
### Technology Choices
|
||||
|
||||
| Component | Technology | Rationale |
|
||||
|-----------|------------|-----------|
|
||||
| Token Storage | SQLite with SHA256 hashing | Secure, consistent with existing database |
|
||||
| Token Format | Random URL-safe strings | Simple, secure, no JWT complexity |
|
||||
| Request Parsing | Flask built-in + custom normalization | Handles both form and JSON naturally |
|
||||
| Response Format | JSON for errors, headers for success | Follows Micropub spec exactly |
|
||||
|
||||
## Rationale
|
||||
|
||||
### Why Minimal V1 Scope?
|
||||
|
||||
1. **Get to V1 Faster**: Core create functionality enables 90% of use cases
|
||||
2. **Real Usage Feedback**: Deploy and learn from actual usage patterns
|
||||
3. **Reduced Complexity**: Fewer edge cases and error conditions
|
||||
4. **Clear Foundation**: Establish patterns before adding complexity
|
||||
|
||||
### Why Not JWT Tokens?
|
||||
|
||||
1. **Unnecessary Complexity**: JWT adds libraries and complexity
|
||||
2. **No Distributed Validation**: Single-server system doesn't need it
|
||||
3. **Simpler Revocation**: Database tokens are easily revoked
|
||||
4. **Consistent with IndieAuth**: Random tokens match the pattern
|
||||
|
||||
### Why Reuse Existing CRUD?
|
||||
|
||||
1. **Proven Code**: `notes.py` already handles file/database sync
|
||||
2. **Consistency**: Same validation and error handling
|
||||
3. **Maintainability**: Single source of truth for note operations
|
||||
4. **Atomic Operations**: Existing transaction handling
|
||||
|
||||
### Security Considerations
|
||||
|
||||
1. **Token Hashing**: Never store plaintext tokens
|
||||
2. **Scope Enforcement**: Each operation checks required scopes
|
||||
3. **HTTPS Required**: Enforce in production configuration
|
||||
4. **Token Expiry**: 90-day lifetime limits exposure
|
||||
5. **Single-Use Auth Codes**: Prevent replay attacks
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
✅ **Enables V1 Release**: Removes the last blocker for V1
|
||||
✅ **Real IndieWeb Participation**: Can post from standard clients
|
||||
✅ **Clean Architecture**: Clear separation of concerns
|
||||
✅ **Extensible Design**: Easy to add features later
|
||||
✅ **Security First**: Proper token handling from day one
|
||||
|
||||
### Negative
|
||||
|
||||
⚠️ **Limited Initial Features**: No media uploads in V1
|
||||
⚠️ **Database Migration Required**: Token schema changes needed
|
||||
⚠️ **Client Testing Needed**: Must verify with real Micropub clients
|
||||
⚠️ **Additional Complexity**: New endpoints and token management
|
||||
|
||||
### Neutral
|
||||
|
||||
- **8-10 Day Implementation**: Reasonable timeline for critical feature
|
||||
- **New Dependencies**: None required (using existing libraries)
|
||||
- **Documentation Burden**: Must document API for users
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Token Infrastructure (Days 1-3)
|
||||
- Token database schema and migration
|
||||
- Token generation and storage functions
|
||||
- Token endpoint for code exchange
|
||||
- Scope validation helpers
|
||||
|
||||
### Phase 2: Micropub Core (Days 4-7)
|
||||
- Main endpoint handler
|
||||
- Property normalization for form/JSON
|
||||
- Create post functionality
|
||||
- Error response formatting
|
||||
|
||||
### Phase 3: Queries & Polish (Days 6-8)
|
||||
- Config and source query endpoints
|
||||
- Authorization endpoint with admin session check
|
||||
- Discovery headers and links
|
||||
- Client testing and documentation
|
||||
|
||||
**Note**: Timeline reduced from 8-10 days to 6-8 days due to V1 scope simplification (no update/delete)
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Full Micropub Implementation
|
||||
**Rejected**: Too complex for V1, would delay release by weeks
|
||||
|
||||
### Alternative 2: Custom API Instead of Micropub
|
||||
**Rejected**: Breaks IndieWeb compatibility, requires custom clients
|
||||
|
||||
### Alternative 3: JWT-Based Tokens
|
||||
**Rejected**: Unnecessary complexity for single-server system
|
||||
|
||||
### Alternative 4: Separate Media Endpoint First
|
||||
**Rejected**: Not required for text posts, can add later
|
||||
|
||||
## Compliance
|
||||
|
||||
### Standards Compliance
|
||||
- ✅ W3C Micropub specification
|
||||
- ✅ IndieAuth specification for tokens
|
||||
- ✅ OAuth 2.0 Bearer Token usage
|
||||
|
||||
### Project Principles
|
||||
- ✅ Minimal code (reuses existing CRUD)
|
||||
- ✅ Standards-first (follows W3C spec)
|
||||
- ✅ No lock-in (standard protocols)
|
||||
- ✅ Progressive enhancement (can add features)
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
| Risk | Impact | Probability | Mitigation |
|
||||
|------|--------|-------------|------------|
|
||||
| Token security breach | High | Low | SHA256 hashing, HTTPS required |
|
||||
| Client incompatibility | Medium | Medium | Test with 3+ clients before release |
|
||||
| Scope creep | Medium | High | Strict V1 feature list |
|
||||
| Performance issues | Low | Low | Simple operations, indexed database |
|
||||
|
||||
## Success Metrics
|
||||
|
||||
1. **Functional Success**
|
||||
- Posts can be created from Indigenous app
|
||||
- Posts can be created from Quill
|
||||
- Token endpoint works with IndieAuth flow
|
||||
|
||||
2. **Performance Targets**
|
||||
- Post creation < 500ms
|
||||
- Token validation < 50ms
|
||||
- Query responses < 200ms
|
||||
|
||||
3. **Security Requirements**
|
||||
- All tokens hashed in database
|
||||
- Expired tokens rejected
|
||||
- Invalid scopes return 403
|
||||
|
||||
## References
|
||||
|
||||
- [W3C Micropub Specification](https://www.w3.org/TR/micropub/)
|
||||
- [IndieAuth Specification](https://indieauth.spec.indieweb.org/)
|
||||
- [OAuth 2.0 Bearer Token Usage](https://tools.ietf.org/html/rfc6750)
|
||||
- [Micropub Rocks Validator](https://micropub.rocks/)
|
||||
|
||||
## Related ADRs
|
||||
|
||||
- ADR-004: File-based Note Storage (storage layer)
|
||||
- ADR-019: IndieAuth Implementation (authentication foundation)
|
||||
- ADR-025: PKCE Authentication (security pattern)
|
||||
|
||||
## Version Impact
|
||||
|
||||
**Version Change**: 0.9.5 → 1.0.0 (V1 Release!)
|
||||
|
||||
This change represents the final feature for V1 release, warranting the major version increment to 1.0.0.
|
||||
|
||||
---
|
||||
|
||||
**Date**: 2024-11-24
|
||||
**Author**: StarPunk Architecture Team
|
||||
**Status**: Proposed
|
||||
537
docs/decisions/ADR-029-micropub-indieauth-integration.md
Normal file
537
docs/decisions/ADR-029-micropub-indieauth-integration.md
Normal file
@@ -0,0 +1,537 @@
|
||||
# ADR-029: Micropub IndieAuth Integration Strategy
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
The developer review of our Micropub design (ADR-028) revealed critical issues and questions about how IndieAuth and Micropub integrate. This ADR addresses all architectural decisions needed to proceed with implementation.
|
||||
|
||||
### Critical Issues Identified
|
||||
|
||||
1. **Token endpoint missing required `me` parameter** in the IndieAuth spec
|
||||
2. **PKCE confusion** - it's not part of IndieAuth spec, but StarPunk uses it with IndieLogin.com
|
||||
3. **Database security issue** - tokens stored in plain text
|
||||
4. **Missing `authorization_codes` table** for token exchange
|
||||
5. **Property mapping rules** undefined for Micropub to StarPunk conversion
|
||||
6. **Authorization endpoint location** unclear
|
||||
7. **Two authentication flows** need clarification
|
||||
|
||||
### V1 Scope Decision
|
||||
|
||||
The user has agreed to **simplify V1** by:
|
||||
- ✅ Omitting update operations from V1
|
||||
- ✅ Omitting delete operations from V1
|
||||
- ✅ Focusing on create-only for V1 release
|
||||
- Post-V1 features will be tracked separately
|
||||
|
||||
## Decision
|
||||
|
||||
We will implement a **hybrid IndieAuth architecture** that clearly separates admin authentication from Micropub authorization.
|
||||
|
||||
### Architectural Decisions
|
||||
|
||||
#### 1. Token Endpoint `me` Parameter (RESOLVED)
|
||||
|
||||
**Issue**: IndieAuth spec requires `me` parameter in token exchange, but our design missed it.
|
||||
|
||||
**Decision**: Add `me` parameter validation to token endpoint.
|
||||
|
||||
**Implementation**:
|
||||
```python
|
||||
# Token exchange request MUST include:
|
||||
POST /auth/token
|
||||
grant_type=authorization_code
|
||||
code={code}
|
||||
client_id={client_url}
|
||||
redirect_uri={redirect_url}
|
||||
me={user_profile_url} # REQUIRED by IndieAuth spec
|
||||
```
|
||||
|
||||
**Validation**:
|
||||
- Verify `me` matches the value stored with the authorization code
|
||||
- Return error if mismatch (prevents code hijacking)
|
||||
|
||||
#### 2. PKCE Strategy (RESOLVED)
|
||||
|
||||
**Issue**: PKCE is not part of IndieAuth spec, but StarPunk uses it with IndieLogin.com.
|
||||
|
||||
**Decision**: Make PKCE **optional but recommended**.
|
||||
|
||||
**Implementation**:
|
||||
- Check for `code_challenge` in authorization request
|
||||
- If present, require `code_verifier` in token exchange
|
||||
- If absent, proceed without PKCE (spec-compliant)
|
||||
- Document as security enhancement beyond spec
|
||||
|
||||
**Rationale**:
|
||||
- IndieLogin.com supports PKCE as an extension
|
||||
- Other IndieAuth providers may not support it
|
||||
- Making it optional ensures broader compatibility
|
||||
|
||||
#### 3. Token Storage Security (RESOLVED)
|
||||
|
||||
**Issue**: Current `tokens` table stores tokens in plain text (major security vulnerability).
|
||||
|
||||
**Decision**: Implement **immediate migration** to hashed token storage.
|
||||
|
||||
**Migration Strategy**:
|
||||
```sql
|
||||
-- Step 1: Create new secure tokens table
|
||||
CREATE TABLE tokens_secure (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
token_hash TEXT UNIQUE NOT NULL, -- SHA256 hash
|
||||
me TEXT NOT NULL,
|
||||
client_id TEXT,
|
||||
scope TEXT DEFAULT 'create',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
last_used_at TIMESTAMP,
|
||||
revoked_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- Step 2: Invalidate all existing tokens (security breach recovery)
|
||||
-- Since we can't hash plain text tokens retroactively, all must be revoked
|
||||
DROP TABLE IF EXISTS tokens;
|
||||
|
||||
-- Step 3: Rename secure table
|
||||
ALTER TABLE tokens_secure RENAME TO tokens;
|
||||
|
||||
-- Step 4: Create indexes
|
||||
CREATE INDEX idx_tokens_hash ON tokens(token_hash);
|
||||
CREATE INDEX idx_tokens_me ON tokens(me);
|
||||
CREATE INDEX idx_tokens_expires ON tokens(expires_at);
|
||||
```
|
||||
|
||||
**Security Notice**: All existing tokens will be invalidated. Users must re-authenticate.
|
||||
|
||||
#### 4. Authorization Codes Table (RESOLVED)
|
||||
|
||||
**Issue**: Design references `authorization_codes` table that doesn't exist.
|
||||
|
||||
**Decision**: Create the table as part of Micropub implementation.
|
||||
|
||||
**Schema**:
|
||||
```sql
|
||||
CREATE TABLE authorization_codes (
|
||||
code TEXT PRIMARY KEY,
|
||||
code_hash TEXT UNIQUE NOT NULL, -- SHA256 hash for security
|
||||
me TEXT NOT NULL,
|
||||
client_id TEXT NOT NULL,
|
||||
redirect_uri TEXT NOT NULL,
|
||||
scope TEXT DEFAULT 'create',
|
||||
code_challenge TEXT, -- Optional PKCE
|
||||
code_challenge_method TEXT, -- S256 if PKCE used
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
used_at TIMESTAMP -- Prevent replay attacks
|
||||
);
|
||||
|
||||
CREATE INDEX idx_auth_codes_hash ON authorization_codes(code_hash);
|
||||
CREATE INDEX idx_auth_codes_expires ON authorization_codes(expires_at);
|
||||
```
|
||||
|
||||
#### 5. Property Mapping Rules (RESOLVED)
|
||||
|
||||
**Issue**: Functions like `extract_title()` and `extract_content()` are undefined.
|
||||
|
||||
**Decision**: Define explicit mapping rules for V1.
|
||||
|
||||
**Micropub → StarPunk Mapping**:
|
||||
```python
|
||||
# Content mapping (required)
|
||||
content = properties.get('content', [''])[0] # First content value
|
||||
if not content:
|
||||
return error_response("invalid_request", "Content is required")
|
||||
|
||||
# Title mapping (optional)
|
||||
# Option 1: Use 'name' property if provided
|
||||
title = properties.get('name', [''])[0]
|
||||
# Option 2: If no name, extract from content (first line up to 50 chars)
|
||||
if not title and content:
|
||||
first_line = content.split('\n')[0]
|
||||
title = first_line[:50] + ('...' if len(first_line) > 50 else '')
|
||||
|
||||
# Tags mapping
|
||||
tags = properties.get('category', []) # All category values become tags
|
||||
|
||||
# Published date (respect if provided, otherwise use current time)
|
||||
published = properties.get('published', [''])[0]
|
||||
if published:
|
||||
# Parse ISO 8601 date
|
||||
created_at = parse_iso8601(published)
|
||||
else:
|
||||
created_at = datetime.now()
|
||||
|
||||
# Slug generation
|
||||
mp_slug = properties.get('mp-slug', [''])[0]
|
||||
if mp_slug:
|
||||
slug = slugify(mp_slug)
|
||||
else:
|
||||
slug = generate_slug(title or content[:30])
|
||||
```
|
||||
|
||||
### Q1: Authorization Endpoint Location (RESOLVED)
|
||||
|
||||
**Issue**: Design mentions `/auth/authorization` but it doesn't exist.
|
||||
|
||||
**Decision**: Create **NEW** `/auth/authorization` endpoint for Micropub clients.
|
||||
|
||||
**Rationale**:
|
||||
- Keep admin login (`/auth/login`) separate from Micropub authorization
|
||||
- Clear separation of concerns
|
||||
- Follows IndieAuth spec naming conventions
|
||||
|
||||
**Implementation**:
|
||||
```python
|
||||
@bp.route("/auth/authorization", methods=["GET", "POST"])
|
||||
def authorization_endpoint():
|
||||
"""
|
||||
IndieAuth authorization endpoint for Micropub clients
|
||||
|
||||
GET: Display authorization form
|
||||
POST: Process authorization and redirect with code
|
||||
"""
|
||||
if request.method == "GET":
|
||||
# Parse IndieAuth parameters
|
||||
response_type = request.args.get('response_type')
|
||||
client_id = request.args.get('client_id')
|
||||
redirect_uri = request.args.get('redirect_uri')
|
||||
state = request.args.get('state')
|
||||
scope = request.args.get('scope', 'create')
|
||||
me = request.args.get('me')
|
||||
code_challenge = request.args.get('code_challenge')
|
||||
|
||||
# Validate parameters
|
||||
if response_type != 'code':
|
||||
return error_response("unsupported_response_type")
|
||||
|
||||
# Check if user is logged in (via admin session)
|
||||
if not verify_admin_session():
|
||||
# Redirect to login, then back here
|
||||
session['pending_auth'] = request.url
|
||||
return redirect(url_for('auth.login_form'))
|
||||
|
||||
# Display authorization form
|
||||
return render_template('auth/authorize.html',
|
||||
client_id=client_id,
|
||||
scope=scope,
|
||||
redirect_uri=redirect_uri)
|
||||
|
||||
else: # POST
|
||||
# User approved/denied authorization
|
||||
# Generate authorization code
|
||||
# Store in authorization_codes table
|
||||
# Redirect to client with code
|
||||
```
|
||||
|
||||
### Q2: Two Authentication Flows Integration (RESOLVED)
|
||||
|
||||
**Decision**: Maintain **two separate flows** with clear boundaries.
|
||||
|
||||
**Flow 1: Admin Login** (Existing)
|
||||
- Purpose: Admin access to StarPunk interface
|
||||
- Path: `/auth/login` → IndieLogin.com → `/auth/callback`
|
||||
- Result: Session cookie for admin panel
|
||||
- No changes needed
|
||||
|
||||
**Flow 2: Micropub Authorization** (New)
|
||||
- Purpose: Micropub client authorization
|
||||
- Path: `/auth/authorization` → `/auth/token`
|
||||
- Result: Bearer token for API access
|
||||
|
||||
**Integration Point**: The authorization endpoint checks for admin session:
|
||||
```python
|
||||
def authorization_endpoint():
|
||||
# Check if admin is logged in
|
||||
if not has_admin_session():
|
||||
# Store authorization request
|
||||
# Redirect to admin login
|
||||
# After login, return to authorization
|
||||
return redirect_to_login_with_return()
|
||||
|
||||
# Admin is logged in, show authorization form
|
||||
return show_authorization_form()
|
||||
```
|
||||
|
||||
**Key Design Choice**: We act as our **own authorization server** for Micropub, not delegating to IndieLogin.com for this flow. This is because:
|
||||
1. IndieLogin.com doesn't issue access tokens
|
||||
2. We need to control scopes and token lifetime
|
||||
3. We already have admin authentication to verify the user
|
||||
|
||||
### Q3: Scope Validation Rules (RESOLVED)
|
||||
|
||||
**Issue**: What happens when client requests no scopes?
|
||||
|
||||
**Decision**: Implement **Option C** - Allow empty scope during authorization, reject at token endpoint.
|
||||
|
||||
**Rationale**: This matches the IndieAuth spec requirement exactly.
|
||||
|
||||
**Implementation**:
|
||||
```python
|
||||
def handle_authorization():
|
||||
scope = request.args.get('scope', '')
|
||||
|
||||
# Store whatever scope was requested (even empty)
|
||||
authorization_code = create_authorization_code(
|
||||
scope=scope, # Can be empty string
|
||||
# ... other parameters
|
||||
)
|
||||
|
||||
def handle_token_exchange():
|
||||
auth_code = get_authorization_code(code)
|
||||
|
||||
# IndieAuth spec: MUST NOT issue token if no scope
|
||||
if not auth_code.scope:
|
||||
return error_response("invalid_scope",
|
||||
"Authorization code was issued without scope")
|
||||
|
||||
# Issue token with the authorized scope
|
||||
token = create_access_token(scope=auth_code.scope)
|
||||
```
|
||||
|
||||
### Q4: V1 Scope - Update/Delete Operations (RESOLVED)
|
||||
|
||||
**Decision**: Remove update/delete from V1 completely.
|
||||
|
||||
**Changes Required**:
|
||||
1. Remove `handle_update()` and `handle_delete()` from design doc
|
||||
2. Remove update/delete from supported scopes in V1
|
||||
3. Return "invalid_request" if action=update or action=delete
|
||||
4. Document in project plan for post-V1
|
||||
|
||||
**V1 Supported Actions**:
|
||||
- ✅ action=create (or no action - default)
|
||||
- ❌ action=update → error response
|
||||
- ❌ action=delete → error response
|
||||
|
||||
### Q5: Token Storage Security Fix (RESOLVED)
|
||||
|
||||
**Decision**: Fix the security issue as part of Micropub implementation.
|
||||
|
||||
**Implementation Plan**:
|
||||
1. Create migration to new secure schema
|
||||
2. Hash all new tokens before storage
|
||||
3. Document that existing tokens will be invalidated
|
||||
4. Add security notice to changelog
|
||||
|
||||
## Implementation Architecture
|
||||
|
||||
### Complete Authorization Flow
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Micropub Client │
|
||||
└────────────────────┬────────────────────────────────────┘
|
||||
│
|
||||
│ 1. GET /auth/authorization?
|
||||
│ response_type=code&
|
||||
│ client_id=https://app.example&
|
||||
│ redirect_uri=...&
|
||||
│ state=...&
|
||||
│ scope=create&
|
||||
│ me=https://user.example
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ StarPunk Authorization Endpoint │
|
||||
│ /auth/authorization │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ if not admin_logged_in: │
|
||||
│ redirect_to_login() │
|
||||
│ else: │
|
||||
│ show_authorization_form() │
|
||||
└────────────────────┬────────────────────────────────────┘
|
||||
│
|
||||
│ 2. User approves
|
||||
│ POST /auth/authorization
|
||||
│
|
||||
│ 3. Redirect with code
|
||||
│ https://app.example/callback?
|
||||
│ code=xxx&state=yyy
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Micropub Client │
|
||||
└────────────────────┬────────────────────────────────────┘
|
||||
│
|
||||
│ 4. POST /auth/token
|
||||
│ grant_type=authorization_code&
|
||||
│ code=xxx&
|
||||
│ client_id=https://app.example&
|
||||
│ redirect_uri=...&
|
||||
│ me=https://user.example&
|
||||
│ code_verifier=... (if PKCE)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ StarPunk Token Endpoint │
|
||||
│ /auth/token │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 1. Verify authorization code │
|
||||
│ 2. Check code not used │
|
||||
│ 3. Verify client_id matches │
|
||||
│ 4. Verify redirect_uri matches │
|
||||
│ 5. Verify me matches │
|
||||
│ 6. Verify PKCE if present │
|
||||
│ 7. Check scope not empty │
|
||||
│ 8. Generate access token │
|
||||
│ 9. Store hashed token │
|
||||
│ 10. Return token response │
|
||||
└────────────────────┬────────────────────────────────────┘
|
||||
│
|
||||
│ 5. Response:
|
||||
│ {
|
||||
│ "access_token": "xxx",
|
||||
│ "token_type": "Bearer",
|
||||
│ "scope": "create",
|
||||
│ "me": "https://user.example"
|
||||
│ }
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Micropub Client │
|
||||
└────────────────────┬────────────────────────────────────┘
|
||||
│
|
||||
│ 6. POST /micropub
|
||||
│ Authorization: Bearer xxx
|
||||
│ h=entry&content=Hello
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ StarPunk Micropub Endpoint │
|
||||
│ /micropub │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ 1. Extract bearer token │
|
||||
│ 2. Hash token and lookup │
|
||||
│ 3. Verify not expired │
|
||||
│ 4. Check scope includes "create" │
|
||||
│ 5. Parse Micropub properties │
|
||||
│ 6. Create note via notes.py │
|
||||
│ 7. Return 201 with Location header │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
- ✅ All spec compliance issues resolved
|
||||
- ✅ Clear separation between admin auth and Micropub auth
|
||||
- ✅ Security vulnerability in token storage fixed
|
||||
- ✅ Simplified V1 scope (create-only)
|
||||
- ✅ PKCE optional for compatibility
|
||||
- ✅ Clear property mapping rules
|
||||
|
||||
### Negative
|
||||
- ⚠️ Existing tokens will be invalidated (security fix)
|
||||
- ⚠️ More complex than initially designed
|
||||
- ⚠️ Two authorization flows to maintain
|
||||
|
||||
### Neutral
|
||||
- We become our own authorization server (for Micropub only)
|
||||
- Admin must be logged in to authorize Micropub clients
|
||||
- Update/delete deferred to post-V1
|
||||
|
||||
## Migration Requirements
|
||||
|
||||
### Database Migration Script
|
||||
```sql
|
||||
-- Migration: Fix token security and add authorization codes
|
||||
-- Version: 0.10.0
|
||||
|
||||
-- 1. Create secure tokens table
|
||||
CREATE TABLE tokens_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
token_hash TEXT UNIQUE NOT NULL,
|
||||
me TEXT NOT NULL,
|
||||
client_id TEXT,
|
||||
scope TEXT DEFAULT 'create',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
last_used_at TIMESTAMP,
|
||||
revoked_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- 2. Drop insecure table (invalidates all tokens)
|
||||
DROP TABLE IF EXISTS tokens;
|
||||
|
||||
-- 3. Rename to final name
|
||||
ALTER TABLE tokens_new RENAME TO tokens;
|
||||
|
||||
-- 4. Create authorization codes table
|
||||
CREATE TABLE authorization_codes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code_hash TEXT UNIQUE NOT NULL,
|
||||
me TEXT NOT NULL,
|
||||
client_id TEXT NOT NULL,
|
||||
redirect_uri TEXT NOT NULL,
|
||||
scope TEXT,
|
||||
state TEXT,
|
||||
code_challenge TEXT,
|
||||
code_challenge_method TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
used_at TIMESTAMP
|
||||
);
|
||||
|
||||
-- 5. Create indexes
|
||||
CREATE INDEX idx_tokens_hash ON tokens(token_hash);
|
||||
CREATE INDEX idx_tokens_expires ON tokens(expires_at);
|
||||
CREATE INDEX idx_auth_codes_hash ON authorization_codes(code_hash);
|
||||
CREATE INDEX idx_auth_codes_expires ON authorization_codes(expires_at);
|
||||
|
||||
-- 6. Clean up expired auth state
|
||||
DELETE FROM auth_state WHERE expires_at < datetime('now');
|
||||
```
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
### Phase 1: Security & Database
|
||||
- [ ] Create database migration script
|
||||
- [ ] Implement token hashing functions
|
||||
- [ ] Add authorization_codes table
|
||||
- [ ] Update database.py schema
|
||||
|
||||
### Phase 2: Authorization Endpoint
|
||||
- [ ] Create `/auth/authorization` route
|
||||
- [ ] Implement authorization form template
|
||||
- [ ] Add scope approval UI
|
||||
- [ ] Generate and store authorization codes
|
||||
|
||||
### Phase 3: Token Endpoint
|
||||
- [ ] Create `/auth/token` route
|
||||
- [ ] Implement code exchange logic
|
||||
- [ ] Add `me` parameter validation
|
||||
- [ ] Optional PKCE verification
|
||||
- [ ] Generate and store hashed tokens
|
||||
|
||||
### Phase 4: Micropub Endpoint (Create Only)
|
||||
- [ ] Create `/micropub` route
|
||||
- [ ] Bearer token extraction
|
||||
- [ ] Token verification (hash lookup)
|
||||
- [ ] Property normalization
|
||||
- [ ] Content/title/tags mapping
|
||||
- [ ] Note creation via notes.py
|
||||
- [ ] Location header response
|
||||
|
||||
### Phase 5: Testing & Documentation
|
||||
- [ ] Test with Indigenous app
|
||||
- [ ] Test with Quill
|
||||
- [ ] Update API documentation
|
||||
- [ ] Security audit
|
||||
- [ ] Performance testing
|
||||
|
||||
## References
|
||||
|
||||
- [IndieAuth Spec - Token Endpoint](https://indieauth.spec.indieweb.org/#token-endpoint)
|
||||
- [IndieAuth Spec - Authorization Code](https://indieauth.spec.indieweb.org/#authorization-code)
|
||||
- [Micropub Spec - Authentication](https://www.w3.org/TR/micropub/#authentication)
|
||||
- [OAuth 2.0 Security Best Practices](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics)
|
||||
|
||||
## Related ADRs
|
||||
|
||||
- ADR-021: IndieAuth Provider Strategy (understanding flows)
|
||||
- ADR-028: Micropub Implementation Strategy (original design)
|
||||
- ADR-005: IndieLogin Authentication (admin auth flow)
|
||||
|
||||
---
|
||||
|
||||
**Date**: 2024-11-24
|
||||
**Author**: StarPunk Architecture Team
|
||||
**Status**: Accepted
|
||||
**Version Impact**: Requires 0.10.0 (breaking change - token invalidation)
|
||||
1087
docs/design/micropub-endpoint-design.md
Normal file
1087
docs/design/micropub-endpoint-design.md
Normal file
File diff suppressed because it is too large
Load Diff
307
docs/design/token-security-migration.md
Normal file
307
docs/design/token-security-migration.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# Token Security Migration Strategy
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the migration strategy for fixing the critical security issue where access tokens are stored in plain text in the database. This migration will invalidate all existing tokens as a necessary security measure.
|
||||
|
||||
## Security Issue
|
||||
|
||||
**Current State**: The `tokens` table stores tokens in plain text, which is a major security vulnerability. If the database is compromised, all tokens are immediately usable by an attacker.
|
||||
|
||||
**Target State**: Store only SHA256 hashes of tokens, making stolen database contents useless without the original tokens.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
### Phase 1: Database Schema Migration
|
||||
|
||||
#### Migration Script (`migrations/005_token_security.sql`)
|
||||
|
||||
```sql
|
||||
-- Migration: Fix token security and add Micropub support
|
||||
-- Version: 0.10.0
|
||||
-- Breaking Change: This will invalidate all existing tokens
|
||||
|
||||
-- Step 1: Create new secure tokens table
|
||||
CREATE TABLE tokens_secure (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
token_hash TEXT UNIQUE NOT NULL, -- SHA256 hash of token
|
||||
me TEXT NOT NULL, -- User identity URL
|
||||
client_id TEXT, -- Client application URL
|
||||
scope TEXT DEFAULT 'create', -- Granted scopes
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP NOT NULL, -- Token expiration
|
||||
last_used_at TIMESTAMP, -- Track usage
|
||||
revoked_at TIMESTAMP -- Soft revocation
|
||||
);
|
||||
|
||||
-- Step 2: Create indexes for performance
|
||||
CREATE INDEX idx_tokens_secure_hash ON tokens_secure(token_hash);
|
||||
CREATE INDEX idx_tokens_secure_me ON tokens_secure(me);
|
||||
CREATE INDEX idx_tokens_secure_expires ON tokens_secure(expires_at);
|
||||
|
||||
-- Step 3: Create authorization_codes table for Micropub
|
||||
CREATE TABLE authorization_codes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
code_hash TEXT UNIQUE NOT NULL, -- SHA256 hash of code
|
||||
me TEXT NOT NULL, -- User identity
|
||||
client_id TEXT NOT NULL, -- Client application
|
||||
redirect_uri TEXT NOT NULL, -- Callback URL
|
||||
scope TEXT, -- Requested scopes
|
||||
state TEXT, -- CSRF state
|
||||
code_challenge TEXT, -- PKCE challenge
|
||||
code_challenge_method TEXT, -- PKCE method
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP NOT NULL, -- 10 minute expiry
|
||||
used_at TIMESTAMP -- Prevent replay
|
||||
);
|
||||
|
||||
-- Step 4: Create indexes for authorization codes
|
||||
CREATE INDEX idx_auth_codes_hash ON authorization_codes(code_hash);
|
||||
CREATE INDEX idx_auth_codes_expires ON authorization_codes(expires_at);
|
||||
|
||||
-- Step 5: Drop old insecure tokens table
|
||||
-- WARNING: This will invalidate all existing tokens
|
||||
DROP TABLE IF EXISTS tokens;
|
||||
|
||||
-- Step 6: Rename secure table to final name
|
||||
ALTER TABLE tokens_secure RENAME TO tokens;
|
||||
|
||||
-- Step 7: Clean up expired auth state
|
||||
DELETE FROM auth_state WHERE expires_at < datetime('now');
|
||||
```
|
||||
|
||||
### Phase 2: Code Implementation
|
||||
|
||||
#### Token Generation and Storage
|
||||
|
||||
```python
|
||||
# starpunk/tokens.py
|
||||
import hashlib
|
||||
import secrets
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
def generate_token() -> str:
|
||||
"""Generate cryptographically secure random token"""
|
||||
return secrets.token_urlsafe(32)
|
||||
|
||||
def hash_token(token: str) -> str:
|
||||
"""Create SHA256 hash of token"""
|
||||
return hashlib.sha256(token.encode()).hexdigest()
|
||||
|
||||
def create_access_token(me: str, client_id: str, scope: str, db) -> str:
|
||||
"""
|
||||
Create new access token and store hash in database
|
||||
|
||||
Returns:
|
||||
Plain text token (only returned once, never stored)
|
||||
"""
|
||||
token = generate_token()
|
||||
token_hash = hash_token(token)
|
||||
|
||||
expires_at = datetime.now() + timedelta(days=90)
|
||||
|
||||
db.execute("""
|
||||
INSERT INTO tokens (token_hash, me, client_id, scope, expires_at)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
""", (token_hash, me, client_id, scope, expires_at))
|
||||
db.commit()
|
||||
|
||||
return token # Return plain text to user ONCE
|
||||
|
||||
def verify_token(token: str, db) -> dict:
|
||||
"""
|
||||
Verify token by comparing hash
|
||||
|
||||
Returns:
|
||||
Token info if valid, None if invalid/expired
|
||||
"""
|
||||
token_hash = hash_token(token)
|
||||
|
||||
row = db.execute("""
|
||||
SELECT me, client_id, scope
|
||||
FROM tokens
|
||||
WHERE token_hash = ?
|
||||
AND expires_at > datetime('now')
|
||||
AND revoked_at IS NULL
|
||||
""", (token_hash,)).fetchone()
|
||||
|
||||
if row:
|
||||
# Update last used timestamp
|
||||
db.execute("""
|
||||
UPDATE tokens
|
||||
SET last_used_at = datetime('now')
|
||||
WHERE token_hash = ?
|
||||
""", (token_hash,))
|
||||
db.commit()
|
||||
|
||||
return dict(row)
|
||||
|
||||
return None
|
||||
```
|
||||
|
||||
### Phase 3: Migration Execution
|
||||
|
||||
#### Step-by-Step Process
|
||||
|
||||
1. **Backup Database**
|
||||
```bash
|
||||
cp data/starpunk.db data/starpunk.db.backup-$(date +%Y%m%d)
|
||||
```
|
||||
|
||||
2. **Notify Users** (if applicable)
|
||||
- Email or announcement about token invalidation
|
||||
- Explain security improvement
|
||||
- Provide re-authentication instructions
|
||||
|
||||
3. **Apply Migration**
|
||||
```python
|
||||
# In starpunk/migrations.py
|
||||
def run_migration_005(conn):
|
||||
"""Apply token security migration"""
|
||||
with open('migrations/005_token_security.sql', 'r') as f:
|
||||
conn.executescript(f.read())
|
||||
conn.commit()
|
||||
```
|
||||
|
||||
4. **Update Code**
|
||||
- Deploy new token handling code
|
||||
- Update all token verification points
|
||||
- Add proper error messages
|
||||
|
||||
5. **Test Migration**
|
||||
```python
|
||||
# Verify new schema
|
||||
cursor = conn.execute("PRAGMA table_info(tokens)")
|
||||
columns = {col[1] for col in cursor.fetchall()}
|
||||
assert 'token_hash' in columns
|
||||
assert 'token' not in columns # Old column gone
|
||||
|
||||
# Test token operations
|
||||
token = create_access_token("https://user.example", "app", "create", conn)
|
||||
assert verify_token(token, conn) is not None
|
||||
assert verify_token("invalid", conn) is None
|
||||
```
|
||||
|
||||
### Phase 4: Post-Migration Validation
|
||||
|
||||
#### Security Checklist
|
||||
|
||||
- [ ] Verify no plain text tokens in database
|
||||
- [ ] Confirm all tokens are hashed with SHA256
|
||||
- [ ] Test token creation returns plain text once
|
||||
- [ ] Test token verification works with hash
|
||||
- [ ] Verify expired tokens are rejected
|
||||
- [ ] Check revoked tokens are rejected
|
||||
- [ ] Audit logs show migration completed
|
||||
|
||||
#### Functional Testing
|
||||
|
||||
- [ ] Micropub client can obtain new token
|
||||
- [ ] New tokens work for API requests
|
||||
- [ ] Invalid tokens return 401 Unauthorized
|
||||
- [ ] Token expiry is enforced
|
||||
- [ ] Last used timestamp updates
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If critical issues arise:
|
||||
|
||||
1. **Restore Database**
|
||||
```bash
|
||||
cp data/starpunk.db.backup-YYYYMMDD data/starpunk.db
|
||||
```
|
||||
|
||||
2. **Revert Code**
|
||||
```bash
|
||||
git revert <migration-commit>
|
||||
```
|
||||
|
||||
3. **Investigate Issues**
|
||||
- Review migration logs
|
||||
- Test in development environment
|
||||
- Fix issues before retry
|
||||
|
||||
## User Communication
|
||||
|
||||
### Pre-Migration Notice
|
||||
|
||||
```
|
||||
Subject: Important Security Update - Token Re-authentication Required
|
||||
|
||||
Dear StarPunk User,
|
||||
|
||||
We're implementing an important security update that will require you to
|
||||
re-authenticate any Micropub clients you use with StarPunk.
|
||||
|
||||
What's Changing:
|
||||
- Enhanced token security (SHA256 hashing)
|
||||
- All existing access tokens will be invalidated
|
||||
- You'll need to re-authorize Micropub clients
|
||||
|
||||
When:
|
||||
- [Date and time of migration]
|
||||
|
||||
What You Need to Do:
|
||||
1. After the update, go to your Micropub client
|
||||
2. Remove and re-add your StarPunk site
|
||||
3. Complete the authorization flow again
|
||||
|
||||
This change significantly improves the security of your StarPunk installation.
|
||||
|
||||
Thank you for your understanding.
|
||||
```
|
||||
|
||||
### Post-Migration Notice
|
||||
|
||||
```
|
||||
Subject: Security Update Complete - Please Re-authenticate
|
||||
|
||||
The security update has been completed successfully. All previous access
|
||||
tokens have been invalidated for security reasons.
|
||||
|
||||
To continue using Micropub clients:
|
||||
1. Open your Micropub client (Quill, Indigenous, etc.)
|
||||
2. Remove your StarPunk site if listed
|
||||
3. Add it again and complete authorization
|
||||
4. You're ready to post!
|
||||
|
||||
If you experience any issues, please contact support.
|
||||
```
|
||||
|
||||
## Timeline
|
||||
|
||||
| Phase | Duration | Description |
|
||||
|-------|----------|-------------|
|
||||
| Preparation | 1 day | Create migration scripts, test in dev |
|
||||
| Communication | 1 day | Notify users of upcoming change |
|
||||
| Migration | 2 hours | Apply migration, deploy code |
|
||||
| Validation | 2 hours | Test and verify success |
|
||||
| Support | 1 week | Help users re-authenticate |
|
||||
|
||||
## Risk Assessment
|
||||
|
||||
| Risk | Impact | Mitigation |
|
||||
|------|--------|------------|
|
||||
| Data loss | High | Full backup before migration |
|
||||
| User disruption | Medium | Clear communication, documentation |
|
||||
| Migration failure | Low | Test in dev, have rollback plan |
|
||||
| Performance impact | Low | Indexes on hash columns |
|
||||
|
||||
## Long-term Benefits
|
||||
|
||||
1. **Security**: Compromised database doesn't expose usable tokens
|
||||
2. **Compliance**: Follows security best practices
|
||||
3. **Auditability**: Can track token usage via last_used_at
|
||||
4. **Revocability**: Can revoke tokens without deletion
|
||||
5. **Foundation**: Proper structure for OAuth/IndieAuth
|
||||
|
||||
## Conclusion
|
||||
|
||||
While this migration will cause temporary disruption by invalidating existing tokens, it's a necessary security improvement that brings StarPunk in line with security best practices. The migration is straightforward, well-tested, and includes comprehensive rollback procedures if needed.
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.0
|
||||
**Created**: 2024-11-24
|
||||
**Author**: StarPunk Architecture Team
|
||||
**Related**: ADR-029 (IndieAuth Integration)
|
||||
@@ -1278,6 +1278,122 @@ Final steps before V1 release.
|
||||
|
||||
---
|
||||
|
||||
## Post-V1 Roadmap
|
||||
|
||||
### Phase 11: Micropub Extended Operations (V1.1)
|
||||
|
||||
**Priority**: HIGH for V1.1 release
|
||||
**Estimated Effort**: 4-6 hours
|
||||
**Dependencies**: Phase 6 (Micropub Core) must be complete
|
||||
|
||||
#### 11.1 Update Operations
|
||||
- [ ] Implement `action=update` handler in `/micropub`
|
||||
- Support replace operations (replace entire property)
|
||||
- Support add operations (append to array properties)
|
||||
- Support delete operations (remove from array properties)
|
||||
- Map Micropub properties to StarPunk note fields
|
||||
- Validate URL belongs to this StarPunk instance
|
||||
- **Acceptance Criteria**: Can update posts via Micropub clients
|
||||
|
||||
#### 11.2 Delete Operations
|
||||
- [ ] Implement `action=delete` handler in `/micropub`
|
||||
- Soft delete implementation (set deleted_at timestamp)
|
||||
- URL validation and slug extraction
|
||||
- Authorization check (delete scope required)
|
||||
- Proper 204 No Content response
|
||||
- **Acceptance Criteria**: Can delete posts via Micropub clients
|
||||
|
||||
#### 11.3 Extended Scopes
|
||||
- [ ] Add "update" and "delete" to SUPPORTED_SCOPES
|
||||
- [ ] Update authorization form to display requested scopes
|
||||
- [ ] Implement scope-specific permission checks
|
||||
- [ ] Update token endpoint to validate extended scopes
|
||||
- [ ] **Acceptance Criteria**: Fine-grained permission control
|
||||
|
||||
### Phase 12: Media Endpoint (V1.2)
|
||||
|
||||
**Priority**: MEDIUM for V1.2 release
|
||||
**Estimated Effort**: 6-8 hours
|
||||
**Dependencies**: Micropub core functionality
|
||||
|
||||
#### 12.1 Media Upload Endpoint
|
||||
- [ ] Create `/micropub/media` endpoint
|
||||
- [ ] Handle multipart/form-data file uploads
|
||||
- [ ] Store files in `/data/media/YYYY/MM/` structure
|
||||
- [ ] Generate unique filenames to prevent collisions
|
||||
- [ ] Image optimization (resize, compress)
|
||||
- [ ] Return 201 Created with Location header
|
||||
- [ ] **Acceptance Criteria**: Can upload images via Micropub clients
|
||||
|
||||
#### 12.2 Media in Posts
|
||||
- [ ] Support photo property in Micropub create/update
|
||||
- [ ] Embed images in Markdown content
|
||||
- [ ] Update templates to display images properly
|
||||
- [ ] Add media-endpoint to Micropub config query
|
||||
- [ ] **Acceptance Criteria**: Posts can include images
|
||||
|
||||
### Phase 13: Advanced IndieWeb Features (V2.0)
|
||||
|
||||
**Priority**: LOW - Future enhancement
|
||||
**Estimated Effort**: 10-15 hours per feature
|
||||
**Dependencies**: All V1.x features complete
|
||||
|
||||
#### 13.1 Webmentions
|
||||
- [ ] Receive webmentions at `/webmention` endpoint
|
||||
- [ ] Verify source links to target
|
||||
- [ ] Extract microformats from source
|
||||
- [ ] Store webmentions in database
|
||||
- [ ] Display webmentions on posts
|
||||
- [ ] Send webmentions on publish
|
||||
- [ ] Moderation interface in admin
|
||||
|
||||
#### 13.2 Syndication (POSSE)
|
||||
- [ ] Add syndication targets configuration
|
||||
- [ ] Support mp-syndicate-to in Micropub
|
||||
- [ ] Implement Mastodon syndication
|
||||
- [ ] Implement Twitter/X syndication (if API available)
|
||||
- [ ] Store syndication URLs in post metadata
|
||||
- [ ] Display syndication links on posts
|
||||
|
||||
#### 13.3 IndieAuth Server
|
||||
- [ ] Implement full authorization server
|
||||
- [ ] Allow StarPunk to be identity provider
|
||||
- [ ] Profile URL verification
|
||||
- [ ] Client registration/discovery
|
||||
- [ ] Token introspection endpoint
|
||||
- [ ] Token revocation endpoint
|
||||
- [ ] Refresh tokens support
|
||||
|
||||
### Phase 14: Enhanced Features (V2.0+)
|
||||
|
||||
**Priority**: LOW - Long-term vision
|
||||
**Estimated Effort**: Variable
|
||||
|
||||
#### 14.1 Multiple Post Types
|
||||
- [ ] Articles (long-form with title)
|
||||
- [ ] Replies (in-reply-to support)
|
||||
- [ ] Likes (like-of property)
|
||||
- [ ] Bookmarks (bookmark-of property)
|
||||
- [ ] Events (h-event microformat)
|
||||
- [ ] Check-ins (location data)
|
||||
|
||||
#### 14.2 Multi-User Support
|
||||
- [ ] User registration system
|
||||
- [ ] Per-user permissions and roles
|
||||
- [ ] Separate author feeds (/author/username)
|
||||
- [ ] Multi-author Micropub (me verification)
|
||||
- [ ] User profile pages
|
||||
|
||||
#### 14.3 Advanced UI Features
|
||||
- [ ] WYSIWYG Markdown editor
|
||||
- [ ] Draft/schedule posts
|
||||
- [ ] Batch operations interface
|
||||
- [ ] Analytics dashboard
|
||||
- [ ] Theme customization
|
||||
- [ ] Plugin system
|
||||
|
||||
---
|
||||
|
||||
## Summary Checklist
|
||||
|
||||
### Core Features (Must Have)
|
||||
|
||||
Reference in New Issue
Block a user