Commit 355a0ef
feat: add Custom Token Exchange support (RFC 8693)
Add comprehensive support for Custom Token Exchange via RFC 8693, enabling
token exchange using Auth0 Token Exchange Profiles. This implementation has
been validated against auth0-auth-js for security and behavioral alignment.
## Core Features
- RFC 8693 OAuth 2.0 Token Exchange implementation
- Token exchange via subject_token and subject_token_type
- Support for optional audience, scope, and requested_token_type
- Extra parameters support for custom profile/Action data
- HTTP Basic authentication (client_secret_basic)
- Configurable HTTP timeout (default 10 seconds)
## Security Improvements
- Strict subject token validation (fail-fast on whitespace/Bearer prefix)
- Case-insensitive reserved OAuth parameter denylist
- DoS protection with 20-item array limit for extra parameters
- Client credential requirement (confidential client only)
- List item string conversion for extra parameters
- Validated against auth0-auth-js implementation
## API Changes (Non-Breaking)
- Add ApiClient.get_token_by_exchange_profile() method
- Add GetTokenByExchangeProfileError exception class
- Add timeout parameter to ApiClientOptions (default: 10.0 seconds)
- Export GetTokenByExchangeProfileError in __init__.py
## Code Improvements
- Extracted _apply_extra() helper for parameter validation
- Case-insensitive reserved parameter checking
- Simplified verify_request token validation logic
- Collapsed subject_token validation to avoid redundant strip() calls
- Applied ValueError (vs Exception) for JSON parsing consistency
- Module-level constants for token exchange parameters
## Documentation
- Added Custom Token Exchange section to README
- Documented confidential client requirement with link
- Added security warnings for extra parameters
- Replaced hard-coded namespace list with guidance and link
- Fixed misleading audience parameter documentation
- Added note about token targeting without explicit audience
## Testing
- Added 88 comprehensive tests (86 existing + 2 new)
- 85% code coverage maintained
- Added freezegun for deterministic time testing
- Parameterized tests with descriptive ids for better CI output
- Pytest fixtures (api_client_confidential, mock_discovery, last_form)
- Validation short-circuit test ensures fail-fast behavior
- Test for MAX_ARRAY_VALUES_PER_KEY (DoS protection)
- Test for case-insensitive reserved parameter checking
- All tests passing with ruff linting checks
## Validation
Implementation validated line-by-line against auth0-auth-js:
- Subject token validation matches JS SDK behavior
- Array size limits align (MAX_ARRAY_VALUES_PER_KEY = 20)
- Reserved parameters match PARAM_DENYLIST (case-insensitive)
- Client authentication method (client_secret_basic)
- Error handling and response parsing
- Intentional improvements: fail-fast reserved params, expires_in return
Related: https://github.com/auth0/auth0-auth-js
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>1 parent a4c600c commit 355a0ef
9 files changed
Lines changed: 707 additions & 220 deletions
File tree
- src/auth0_api_python
- tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
113 | 113 | | |
114 | 114 | | |
115 | 115 | | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
116 | 181 | | |
117 | 182 | | |
118 | 183 | | |
| |||
126 | 191 | | |
127 | 192 | | |
128 | 193 | | |
129 | | - | |
| 194 | + | |
130 | 195 | | |
131 | 196 | | |
132 | 197 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
| 27 | + | |
27 | 28 | | |
28 | 29 | | |
29 | 30 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
13 | | - | |
| 14 | + | |
| 15 | + | |
14 | 16 | | |
0 commit comments