feat(phase-4a): complete Phase 3 implementation and gap analysis

Merges Phase 4a work including:

Implementation:
- Metadata discovery endpoint (/api/.well-known/oauth-authorization-server)
- h-app microformat parser service
- Enhanced authorization endpoint with client info display
- Configuration management system
- Dependency injection framework

Documentation:
- Comprehensive gap analysis for v1.0.0 compliance
- Phase 4a clarifications on development approach
- Phase 4-5 critical components breakdown

Testing:
- Unit tests for h-app parser (308 lines, comprehensive coverage)
- Unit tests for metadata endpoint (134 lines)
- Unit tests for configuration system (18 lines)
- Integration test updates

All tests passing with high coverage. Ready for Phase 4b security hardening.
This commit is contained in:
2025-11-20 17:16:11 -07:00
parent 5888e45b8c
commit 115e733604
18 changed files with 5815 additions and 4 deletions

View File

@@ -7,8 +7,9 @@ from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates
from gondulf.database.connection import Database
from gondulf.dependencies import get_database, get_verification_service
from gondulf.dependencies import get_database, get_happ_parser, get_verification_service
from gondulf.services.domain_verification import DomainVerificationService
from gondulf.services.happ_parser import HAppParser
from gondulf.utils.validation import (
extract_domain_from_url,
normalize_client_id,
@@ -32,7 +33,8 @@ async def authorize_get(
code_challenge_method: str | None = None,
scope: str | None = None,
me: str | None = None,
database: Database = Depends(get_database)
database: Database = Depends(get_database),
happ_parser: HAppParser = Depends(get_happ_parser)
) -> HTMLResponse:
"""
Handle authorization request (GET).
@@ -162,6 +164,15 @@ async def authorize_get(
# For Phase 2, we'll show consent form immediately (domain verification happens separately)
# In Phase 3, we'll check database for verified domains
# Fetch client metadata (h-app microformat)
client_metadata = None
try:
client_metadata = await happ_parser.fetch_and_parse(normalized_client_id)
logger.info(f"Fetched client metadata for {normalized_client_id}: {client_metadata.name}")
except Exception as e:
logger.warning(f"Failed to fetch client metadata for {normalized_client_id}: {e}")
# Continue without metadata - will show client_id instead
# Show consent form
return templates.TemplateResponse(
"authorize.html",
@@ -173,7 +184,8 @@ async def authorize_get(
"code_challenge": code_challenge,
"code_challenge_method": code_challenge_method,
"scope": scope or "",
"me": me
"me": me,
"client_metadata": client_metadata
}
)