fix: Use authorization endpoint for IndieAuth code verification (v0.9.4)
IndieAuth authentication-only flows should redeem the code at the authorization endpoint, not the token endpoint. The token endpoint is only for authorization flows that need access tokens. - Remove grant_type parameter (only needed for token flows) - Change endpoint from /token to /authorize - Update debug logging to reflect code verification flow 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
# ADR-022: IndieAuth Authentication Endpoint Correction
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
StarPunk is encountering authentication failures with certain IndieAuth providers (specifically gondulf.thesatelliteoflove.com). After investigation, we discovered that StarPunk is incorrectly using the **token endpoint** for authentication-only flows, when it should be using the **authorization endpoint**.
|
||||
|
||||
### The Problem
|
||||
|
||||
When attempting to authenticate with gondulf.thesatelliteoflove.com, the provider returns:
|
||||
```json
|
||||
{
|
||||
"error": "invalid_grant",
|
||||
"error_description": "Authorization code must be redeemed at the authorization endpoint"
|
||||
}
|
||||
```
|
||||
|
||||
StarPunk is currently sending authentication code redemption requests to `/token` when it should be sending them to the authorization endpoint for authentication-only flows.
|
||||
|
||||
### IndieAuth Specification Analysis
|
||||
|
||||
According to the W3C IndieAuth specification (https://www.w3.org/TR/indieauth/):
|
||||
|
||||
1. **Authentication-only flows** (Section 5.4):
|
||||
- Used when the client only needs to verify user identity
|
||||
- Code redemption happens at the **authorization endpoint**
|
||||
- No `grant_type` parameter is used
|
||||
- Response contains only `{"me": "user-url"}`
|
||||
|
||||
2. **Authorization flows** (Section 6.3):
|
||||
- Used when the client needs an access token for API access
|
||||
- Code redemption happens at the **token endpoint**
|
||||
- Requires `grant_type=authorization_code` parameter
|
||||
- Response contains access token and user identity
|
||||
|
||||
### Current StarPunk Implementation
|
||||
|
||||
StarPunk's current code in `/home/phil/Projects/starpunk/starpunk/auth.py` (lines 410-419):
|
||||
|
||||
```python
|
||||
token_exchange_data = {
|
||||
"grant_type": "authorization_code", # WRONG for authentication-only
|
||||
"code": code,
|
||||
"client_id": current_app.config["SITE_URL"],
|
||||
"redirect_uri": f"{current_app.config['SITE_URL']}auth/callback",
|
||||
"code_verifier": code_verifier, # PKCE verification
|
||||
}
|
||||
|
||||
token_url = f"{current_app.config['INDIELOGIN_URL']}/token" # WRONG endpoint
|
||||
```
|
||||
|
||||
This implementation has two errors:
|
||||
1. Uses `/token` endpoint instead of authorization endpoint
|
||||
2. Includes `grant_type` parameter which should not be present for authentication-only flows
|
||||
|
||||
## Decision
|
||||
|
||||
StarPunk must correct its IndieAuth authentication implementation to comply with the specification:
|
||||
|
||||
1. **Use the authorization endpoint** for code redemption in authentication-only flows
|
||||
2. **Remove the `grant_type` parameter** from authentication requests
|
||||
3. **Keep PKCE parameters** (`code_verifier`) as they are still required
|
||||
|
||||
## Rationale
|
||||
|
||||
### Why This Matters
|
||||
|
||||
1. **Standards Compliance**: The IndieAuth specification clearly distinguishes between authentication and authorization flows
|
||||
2. **Provider Compatibility**: Some providers (like gondulf) strictly enforce the specification
|
||||
3. **Correct Semantics**: StarPunk only needs to verify admin identity, not obtain an access token
|
||||
|
||||
### Authentication vs Authorization
|
||||
|
||||
StarPunk's admin login is an **authentication-only** use case:
|
||||
- We only need to verify the admin's identity (`me` URL)
|
||||
- We don't need an access token to access external resources
|
||||
- We create our own session after successful authentication
|
||||
|
||||
This is fundamentally different from Micropub client authorization where:
|
||||
- External clients need access tokens
|
||||
- Tokens are used to authorize API access
|
||||
- The token endpoint is the correct choice
|
||||
|
||||
## Implementation
|
||||
|
||||
### Required Changes
|
||||
|
||||
In `/home/phil/Projects/starpunk/starpunk/auth.py`, the `handle_callback` function must be updated:
|
||||
|
||||
```python
|
||||
def handle_callback(code: str, state: str, iss: Optional[str] = None) -> Optional[str]:
|
||||
# ... existing state verification code ...
|
||||
|
||||
# Prepare authentication request (NOT token exchange)
|
||||
auth_data = {
|
||||
# NO grant_type parameter for authentication-only flows
|
||||
"code": code,
|
||||
"client_id": current_app.config["SITE_URL"],
|
||||
"redirect_uri": f"{current_app.config['SITE_URL']}auth/callback",
|
||||
"code_verifier": code_verifier, # PKCE verification still required
|
||||
}
|
||||
|
||||
# Use authorization endpoint (NOT token endpoint)
|
||||
# The same endpoint used for the initial authorization request
|
||||
auth_url = f"{current_app.config['INDIELOGIN_URL']}/auth" # or /authorize
|
||||
|
||||
# Exchange code for identity (authentication-only)
|
||||
response = httpx.post(
|
||||
auth_url,
|
||||
data=auth_data,
|
||||
timeout=10.0,
|
||||
)
|
||||
|
||||
# Response will be: {"me": "https://user.example.com"}
|
||||
# NOT an access token response
|
||||
```
|
||||
|
||||
### Endpoint Discovery Consideration
|
||||
|
||||
IndieAuth providers may use different paths for their authorization endpoint:
|
||||
- IndieLogin.com uses `/auth`
|
||||
- Some providers use `/authorize`
|
||||
- The gondulf provider appears to use its root domain as the authorization endpoint
|
||||
|
||||
The correct approach is to:
|
||||
1. Discover the authorization endpoint from the provider's metadata
|
||||
2. Use the same endpoint for both authorization initiation and code redemption
|
||||
3. Store the discovered endpoint during the initial authorization request
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
- **Specification Compliance**: Correctly implements IndieAuth authentication flow
|
||||
- **Provider Compatibility**: Works with strict IndieAuth implementations
|
||||
- **Semantic Correctness**: Uses the right flow for the use case
|
||||
|
||||
### Negative
|
||||
- **Breaking Change**: May affect compatibility with providers that accept both endpoints
|
||||
- **Testing Required**: Need to verify with multiple IndieAuth providers
|
||||
|
||||
### Migration Impact
|
||||
- Existing sessions remain valid (no database changes)
|
||||
- Only affects new login attempts
|
||||
- Should be transparent to users
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
Test with multiple IndieAuth providers:
|
||||
1. **IndieLogin.com** - Current provider (should continue working)
|
||||
2. **gondulf.thesatelliteoflove.com** - Strict implementation
|
||||
3. **tokens.indieauth.com** - Token-only endpoint (should fail for auth)
|
||||
4. **Self-hosted implementations** - Various compliance levels
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Support Both Endpoints
|
||||
Attempt token endpoint first, fall back to authorization endpoint on failure.
|
||||
- **Pros**: Maximum compatibility
|
||||
- **Cons**: Not specification-compliant, adds complexity
|
||||
- **Verdict**: Rejected - violates standards
|
||||
|
||||
### Alternative 2: Make Endpoint Configurable
|
||||
Allow admin to configure which endpoint to use.
|
||||
- **Pros**: Flexible for different providers
|
||||
- **Cons**: Confusing for users, not needed if we follow spec
|
||||
- **Verdict**: Rejected - specification is clear
|
||||
|
||||
### Alternative 3: Always Use Token Endpoint
|
||||
Continue current implementation, document incompatibility.
|
||||
- **Pros**: No code changes needed
|
||||
- **Cons**: Violates specification, limits provider choice
|
||||
- **Verdict**: Rejected - incorrect implementation
|
||||
|
||||
## References
|
||||
|
||||
- [IndieAuth Specification Section 5.4](https://www.w3.org/TR/indieauth/#authentication-response): Authorization Code Verification for authentication flows
|
||||
- [IndieAuth Specification Section 6.3](https://www.w3.org/TR/indieauth/#token-response): Token Endpoint for authorization flows
|
||||
- [IndieAuth Authentication vs Authorization](https://indieweb.org/IndieAuth#Authentication_vs_Authorization): Community documentation
|
||||
- [ADR-021: IndieAuth Provider Strategy](/home/phil/Projects/starpunk/docs/decisions/ADR-021-indieauth-provider-strategy.md): Related architectural decision
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.0
|
||||
**Created**: 2025-11-22
|
||||
**Author**: StarPunk Architecture Team
|
||||
**Status**: Accepted
|
||||
93
docs/reports/2025-11-22-authorization-endpoint-fix.md
Normal file
93
docs/reports/2025-11-22-authorization-endpoint-fix.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# IndieAuth Authentication Endpoint Correction
|
||||
|
||||
**Date**: 2025-11-22
|
||||
**Version**: 0.9.4
|
||||
**Type**: Bug Fix
|
||||
|
||||
## Summary
|
||||
|
||||
Corrected the IndieAuth code redemption endpoint from `/token` to `/authorize` for authentication-only flows, and removed the unnecessary `grant_type` parameter.
|
||||
|
||||
## Problem
|
||||
|
||||
StarPunk was using the wrong endpoint for IndieAuth authentication. Per the IndieAuth specification:
|
||||
|
||||
- **Authentication-only flows** (identity verification): Use the **authorization endpoint** (`/authorize`)
|
||||
- **Authorization flows** (getting access tokens): Use the **token endpoint** (`/token`)
|
||||
|
||||
StarPunk only needs identity verification (to check if the user is the admin), so it should POST to the authorization endpoint, not the token endpoint.
|
||||
|
||||
Additionally, the `grant_type` parameter is only required for token endpoint requests (OAuth 2.0 access token requests), not for authentication-only code redemption at the authorization endpoint.
|
||||
|
||||
### IndieAuth Spec Reference
|
||||
|
||||
From the IndieAuth specification:
|
||||
> If the client only needs to know the user who logged in, the client will exchange the authorization code at the authorization endpoint. If the client needs an access token, the client will exchange the authorization code at the token endpoint.
|
||||
|
||||
## Solution
|
||||
|
||||
1. Changed the endpoint from `/token` to `/authorize`
|
||||
2. Removed the `grant_type` parameter (not needed for authentication-only)
|
||||
3. Updated debug logging to reflect "code verification" instead of "token exchange"
|
||||
|
||||
### Before
|
||||
|
||||
```python
|
||||
token_exchange_data = {
|
||||
"grant_type": "authorization_code", # Not needed for authentication-only
|
||||
"code": code,
|
||||
"client_id": current_app.config["SITE_URL"],
|
||||
"redirect_uri": f"{current_app.config['SITE_URL']}auth/callback",
|
||||
"code_verifier": code_verifier,
|
||||
}
|
||||
|
||||
token_url = f"{current_app.config['INDIELOGIN_URL']}/token" # Wrong endpoint
|
||||
```
|
||||
|
||||
### After
|
||||
|
||||
```python
|
||||
token_exchange_data = {
|
||||
"code": code,
|
||||
"client_id": current_app.config["SITE_URL"],
|
||||
"redirect_uri": f"{current_app.config['SITE_URL']}auth/callback",
|
||||
"code_verifier": code_verifier,
|
||||
}
|
||||
|
||||
# Use authorization endpoint for authentication-only flow (identity verification)
|
||||
token_url = f"{current_app.config['INDIELOGIN_URL']}/authorize"
|
||||
```
|
||||
|
||||
## Files Modified
|
||||
|
||||
1. **`starpunk/auth.py`**
|
||||
- Line 410-423: Removed `grant_type`, changed endpoint to `/authorize`, added explanatory comments
|
||||
- Line 434: Updated log message from "token exchange request" to "code verification request to authorization endpoint"
|
||||
- Line 445: Updated comment to clarify authentication-only flow
|
||||
- Line 455: Updated log message from "token exchange response" to "code verification response"
|
||||
|
||||
2. **`starpunk/__init__.py`**
|
||||
- Version bumped from 0.9.3 to 0.9.4
|
||||
|
||||
3. **`CHANGELOG.md`**
|
||||
- Added 0.9.4 release notes
|
||||
|
||||
## Testing
|
||||
|
||||
- All tests pass at the same rate as before (no new failures introduced)
|
||||
- 28 pre-existing test failures remain (related to OAuth metadata and h-app tests for removed functionality from v0.8.0)
|
||||
- 486 tests pass
|
||||
|
||||
## Technical Context
|
||||
|
||||
The v0.9.3 fix that added `grant_type` was based on an incorrect assumption that IndieLogin.com uses the token endpoint for all code redemption. However:
|
||||
|
||||
1. IndieLogin.com follows the IndieAuth spec which distinguishes between authentication and authorization
|
||||
2. For authentication-only (which is all StarPunk needs), the authorization endpoint is correct
|
||||
3. The token endpoint is only for obtaining access tokens (which StarPunk doesn't need)
|
||||
|
||||
## References
|
||||
|
||||
- [IndieAuth Specification - Authentication](https://indieauth.spec.indieweb.org/#authentication)
|
||||
- [IndieAuth Specification - Authorization Endpoint](https://indieauth.spec.indieweb.org/#authorization-endpoint)
|
||||
- ADR-022: IndieAuth Authentication Endpoint Correction (if created)
|
||||
Reference in New Issue
Block a user