From cbef0c15614e88dc8ee14c918affac3e725cbc80 Mon Sep 17 00:00:00 2001 From: Phil Skentelbery Date: Sat, 22 Nov 2025 18:50:23 -0700 Subject: [PATCH] fix: Add grant_type to IndieAuth token exchange (v0.9.3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The token exchange request was missing the required grant_type parameter per OAuth 2.0 RFC 6749. IndieAuth providers that properly validate this were rejecting the request with a 422 error. - Add grant_type=authorization_code to token exchange data - Add ADR-022 documenting the spec compliance requirement 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CHANGELOG.md | 8 ++ ...022-indieauth-token-exchange-compliance.md | 84 +++++++++++++++++++ docs/reports/2025-11-22-grant-type-fix.md | 68 +++++++++++++++ starpunk/__init__.py | 4 +- starpunk/auth.py | 1 + 5 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 docs/decisions/ADR-022-indieauth-token-exchange-compliance.md create mode 100644 docs/reports/2025-11-22-grant-type-fix.md diff --git a/CHANGELOG.md b/CHANGELOG.md index a78de38..beda683 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.9.3] - 2025-11-22 + +### Fixed +- **IndieAuth token exchange missing grant_type**: Added required `grant_type=authorization_code` parameter to token exchange request + - OAuth 2.0 spec requires this parameter for authorization code flow + - Some IndieAuth providers reject token exchange without this parameter + - Fixes authentication failures with spec-compliant IndieAuth providers + ## [0.9.2] - 2025-11-22 ### Fixed diff --git a/docs/decisions/ADR-022-indieauth-token-exchange-compliance.md b/docs/decisions/ADR-022-indieauth-token-exchange-compliance.md new file mode 100644 index 0000000..b15395a --- /dev/null +++ b/docs/decisions/ADR-022-indieauth-token-exchange-compliance.md @@ -0,0 +1,84 @@ +# ADR-022: IndieAuth Token Exchange Compliance + +## Status +Accepted + +## Context +StarPunk's IndieAuth implementation is failing to authenticate with certain providers (specifically gondulf.thesatelliteoflove.com) during the token exchange phase. The provider is rejecting our token exchange requests with a "missing grant_type" error. + +Our current implementation sends: +- `code` +- `client_id` +- `redirect_uri` +- `code_verifier` (for PKCE) + +But does NOT include `grant_type=authorization_code`. + +## Decision +StarPunk MUST include `grant_type=authorization_code` in all token exchange requests to be compliant with both OAuth 2.0 RFC 6749 and IndieAuth specifications. + +## Rationale + +### OAuth 2.0 RFC 6749 Compliance +RFC 6749 Section 4.1.3 explicitly states that `grant_type` is a REQUIRED parameter with the value MUST be set to "authorization_code" for the authorization code grant flow. + +### IndieAuth Specification +While the IndieAuth specification (W3C TR) doesn't use explicit RFC 2119 language (MUST/REQUIRED) for the grant_type parameter, it: +1. Lists `grant_type=authorization_code` as part of the token request parameters in Section 6.3.1 +2. Shows it in all examples (Example 12) +3. States that IndieAuth "builds upon the OAuth 2.0 [RFC6749] Framework" + +Since IndieAuth builds on OAuth 2.0, and OAuth 2.0 requires this parameter, IndieAuth implementations should include it. + +### Provider Compliance +The provider (gondulf.thesatelliteoflove.com) is **correctly following the specifications** by requiring the `grant_type` parameter. + +## Consequences + +### Positive +- Full compliance with OAuth 2.0 RFC 6749 +- Compatibility with all spec-compliant IndieAuth providers +- Clear, standard-compliant token exchange requests + +### Negative +- Requires immediate code change to add the missing parameter +- May reveal other non-compliant providers that don't check for this parameter + +## Implementation Requirements + +The token exchange request MUST include these parameters: +``` +grant_type=authorization_code # REQUIRED by OAuth 2.0 +code={authorization_code} # REQUIRED +client_id={client_url} # REQUIRED +redirect_uri={redirect_url} # REQUIRED if used in initial request +me={user_profile_url} # REQUIRED by IndieAuth (extension to OAuth) +``` + +### Note on PKCE +The `code_verifier` parameter currently being sent is NOT part of the IndieAuth specification. IndieAuth does not mention PKCE (RFC 7636) support. However: +- Including it shouldn't break compliant providers (they should ignore unknown parameters) +- It provides additional security for public clients +- Consider making PKCE optional or detecting provider support + +## Alternatives Considered + +### Alternative 1: Argue for Optional grant_type +**Rejected**: While IndieAuth could theoretically make grant_type optional since there's only one grant type, this would break compatibility with OAuth 2.0 compliant libraries and providers. + +### Alternative 2: Provider-specific workarounds +**Rejected**: Creating provider-specific code paths would violate the principle of standards compliance and create maintenance burden. + +## Recommendation + +**Immediate Action Required**: +1. Add `grant_type=authorization_code` to all token exchange requests +2. Maintain the existing parameters +3. Consider making PKCE optional or auto-detecting provider support + +**StarPunk is at fault** - the implementation is missing a required OAuth 2.0 parameter that IndieAuth inherits. + +## References +- [OAuth 2.0 RFC 6749 Section 4.1.3](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.3) +- [IndieAuth W3C TR Section 6.3.1](https://www.w3.org/TR/indieauth/#token-request) +- [PKCE RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636) (not part of IndieAuth spec) \ No newline at end of file diff --git a/docs/reports/2025-11-22-grant-type-fix.md b/docs/reports/2025-11-22-grant-type-fix.md new file mode 100644 index 0000000..b48cbf0 --- /dev/null +++ b/docs/reports/2025-11-22-grant-type-fix.md @@ -0,0 +1,68 @@ +# IndieAuth Token Exchange grant_type Fix + +**Date**: 2025-11-22 +**Version**: 0.9.3 +**Type**: Bug Fix + +## Summary + +Added the required `grant_type=authorization_code` parameter to the IndieAuth token exchange request. + +## Problem + +The token exchange request in `starpunk/auth.py` was missing the `grant_type` parameter. Per OAuth 2.0 spec (RFC 6749 Section 4.1.3), the token exchange request MUST include: + +``` +grant_type=authorization_code +``` + +Some IndieAuth providers that strictly validate OAuth 2.0 compliance would reject the token exchange request without this parameter. + +## Solution + +Added `"grant_type": "authorization_code"` to the `token_exchange_data` dictionary in the `handle_callback` function. + +### Before + +```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, +} +``` + +### After + +```python +token_exchange_data = { + "grant_type": "authorization_code", + "code": code, + "client_id": current_app.config["SITE_URL"], + "redirect_uri": f"{current_app.config['SITE_URL']}auth/callback", + "code_verifier": code_verifier, +} +``` + +## Files Modified + +1. **`starpunk/auth.py`** (line 412) + - Added `"grant_type": "authorization_code"` to token_exchange_data + +2. **`starpunk/__init__.py`** (line 156) + - Version bumped from 0.9.2 to 0.9.3 + +3. **`CHANGELOG.md`** + - Added 0.9.3 release notes + +## Testing + +- Module imports successfully +- Pre-existing test failures are unrelated (OAuth metadata and h-app tests for removed functionality) +- No new test failures introduced + +## References + +- RFC 6749 Section 4.1.3: Access Token Request +- IndieAuth specification diff --git a/starpunk/__init__.py b/starpunk/__init__.py index a29c3d4..68610ea 100644 --- a/starpunk/__init__.py +++ b/starpunk/__init__.py @@ -153,5 +153,5 @@ def create_app(config=None): # Package version (Semantic Versioning 2.0.0) # See docs/standards/versioning-strategy.md for details -__version__ = "0.9.2" -__version_info__ = (0, 9, 2) +__version__ = "0.9.3" +__version_info__ = (0, 9, 3) diff --git a/starpunk/auth.py b/starpunk/auth.py index 876b9c6..9f70640 100644 --- a/starpunk/auth.py +++ b/starpunk/auth.py @@ -409,6 +409,7 @@ def handle_callback(code: str, state: str, iss: Optional[str] = None) -> Optiona # Prepare token exchange request with PKCE verifier token_exchange_data = { + "grant_type": "authorization_code", "code": code, "client_id": current_app.config["SITE_URL"], "redirect_uri": f"{current_app.config['SITE_URL']}auth/callback",