CRITICAL SECURITY FIX: - Email code required EVERY login (authentication, not verification) - DNS TXT check cached separately (domain verification) - New auth_sessions table for per-login state - Codes hashed with SHA-256, constant-time comparison - Max 3 attempts, 10-minute session expiry - OAuth params stored server-side (security improvement) New files: - services/auth_session.py - migrations 004, 005 - ADR-010: domain verification vs user authentication 312 tests passing, 86.21% coverage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
6.3 KiB
Fix: Response Type Parameter Default Handling
Problem Statement
The current authorization endpoint incorrectly requires the response_type parameter for all requests. According to the W3C IndieAuth specification:
- Section 5.2: When
response_typeis omitted in an authentication request, the authorization endpoint MUST default toid - Section 6.2.1: The
response_type=codeis required for authorization (access token) requests
Currently, the endpoint returns an error when response_type is missing, instead of defaulting to id.
Design Overview
Modify the authorization endpoint to:
- Accept
response_typeas optional - Default to
idwhen omitted - Support both
id(authentication) andcode(authorization) flows - Return appropriate errors for invalid values
Implementation Changes
1. Response Type Validation Logic
Location: /src/gondulf/routers/authorization.py lines 111-119
Current implementation:
# Validate response_type
if response_type != "code":
error_params = {
"error": "unsupported_response_type",
"error_description": "Only response_type=code is supported",
"state": state or ""
}
redirect_url = f"{redirect_uri}?{urlencode(error_params)}"
return RedirectResponse(url=redirect_url, status_code=302)
New implementation:
# Validate response_type (defaults to 'id' per IndieAuth spec section 5.2)
if response_type is None:
response_type = "id" # Default per W3C spec
if response_type not in ["id", "code"]:
error_params = {
"error": "unsupported_response_type",
"error_description": f"response_type '{response_type}' not supported. Must be 'id' or 'code'",
"state": state or ""
}
redirect_url = f"{redirect_uri}?{urlencode(error_params)}"
return RedirectResponse(url=redirect_url, status_code=302)
2. Flow-Specific Validation
The authentication flow (id) and authorization flow (code) have different requirements:
Authentication Flow (response_type=id)
- PKCE is optional (not required)
- Scope is not applicable
- Returns only user profile URL
Authorization Flow (response_type=code)
- PKCE is required (current behavior)
- Scope is applicable
- Returns authorization code for token exchange
Modified PKCE validation (lines 121-139):
# Validate PKCE (required only for authorization flow)
if response_type == "code":
if not code_challenge:
error_params = {
"error": "invalid_request",
"error_description": "code_challenge is required for authorization requests (PKCE)",
"state": state or ""
}
redirect_url = f"{redirect_uri}?{urlencode(error_params)}"
return RedirectResponse(url=redirect_url, status_code=302)
# Validate code_challenge_method
if code_challenge_method != "S256":
error_params = {
"error": "invalid_request",
"error_description": "code_challenge_method must be S256",
"state": state or ""
}
redirect_url = f"{redirect_uri}?{urlencode(error_params)}"
return RedirectResponse(url=redirect_url, status_code=302)
3. Template Context Update
Pass the resolved response_type to the consent template (line 177-189):
return templates.TemplateResponse(
"authorize.html",
{
"request": request,
"client_id": normalized_client_id,
"redirect_uri": redirect_uri,
"response_type": response_type, # Add this - resolved value
"state": state or "",
"code_challenge": code_challenge or "", # Make optional
"code_challenge_method": code_challenge_method or "", # Make optional
"scope": scope or "",
"me": me,
"client_metadata": client_metadata
}
)
4. Consent Form Processing
The consent handler needs to differentiate between authentication and authorization flows:
Location: /src/gondulf/routers/authorization.py lines 193-245
Add response_type parameter to the form submission and handle accordingly:
- Add
response_typeas a form field (line ~196) - Process differently based on flow type
- For
idflow: Return simpler response without creating full authorization code - For
codeflow: Current behavior (create authorization code)
Test Requirements
New Test Cases
-
Test missing response_type defaults to 'id'
- Request without
response_typeparameter - Should NOT return error
- Should render consent page
- Form should have
response_type=id
- Request without
-
Test explicit response_type=id accepted
- Request with
response_type=id - Should render consent page
- PKCE parameters not required
- Request with
-
Test response_type=id without PKCE
- Request with
response_type=idand no PKCE - Should succeed (PKCE optional for authentication)
- Request with
-
Test response_type=code requires PKCE
- Request with
response_type=codewithout PKCE - Should redirect with error (current behavior)
- Request with
-
Test invalid response_type values
- Request with
response_type=tokenor other invalid values - Should redirect with error
- Request with
Modified Test Cases
Update existing test in test_authorization_flow.py:
- Line 115-126:
test_invalid_response_type_redirects_with_error- Keep testing invalid values like "token"
- Add new test for missing parameter (should NOT error)
Acceptance Criteria
- ✅ Missing
response_typedefaults toid(no error) - ✅
response_type=idis accepted and processed - ✅
response_type=codecontinues to work as before - ✅ Invalid response_type values return appropriate error
- ✅ PKCE is optional for
idflow - ✅ PKCE remains required for
codeflow - ✅ Error messages clearly indicate supported values
- ✅ All existing tests pass with modifications
- ✅ New tests cover all response_type scenarios
Security Considerations
- No security degradation: Authentication flow (
id) has fewer requirements by design - PKCE remains mandatory for authorization flow (
code) - Invalid values still produce errors
- State parameter continues to be preserved in all flows
Notes
This is a bug fix to bring the implementation into compliance with the W3C IndieAuth specification. The specification is explicit that response_type defaults to id when omitted, which enables simpler authentication-only flows.