feat: Custom Token Exchange support#141
Conversation
| """ | ||
| return await self.client.complete_interactive_login(callback_url, store_options=store_options) | ||
|
|
||
| async def custom_token_exchange( |
There was a problem hiding this comment.
This should go at the last.
| await auth_client.handle_backchannel_logout(invalid_token) | ||
|
|
||
|
|
||
| class TestCustomTokenExchange: |
There was a problem hiding this comment.
These tests should go at the last. We should avoid adding new tests in middle of file until it's relevant to the existing class or function.
| with patch.object(auth_client.client, 'custom_token_exchange', new_callable=AsyncMock) as mock_exchange: | ||
| mock_exchange.side_effect = CustomTokenExchangeError( | ||
| CustomTokenExchangeErrorCode.INVALID_TOKEN_FORMAT, | ||
| "subject_token_type must be a valid URI", |
There was a problem hiding this comment.
This simulated error message describes URI validation that the exchange path doesn't actually do. The underlying custom_token_exchange only rejects an empty or whitespace-only token and a "Bearer " prefix, there's no valid-URI check on subject_token_type anywhere. A maintainer reading this test would come away thinking URI validation exists when it doesn't.
Shall we switch the message to match a real trigger (like the empty-token case) so the test doesn't imply behavior we don't have? Worth noting the spec does ask for client-side URI validation before the network call, but since this SDK just wraps the base one, the actual check belongs in auth0-server-python rather than being added here.
| ) | ||
|
|
||
| @pytest.mark.asyncio | ||
| async def test_login_with_custom_token_exchange_passes_store_options_for_session_write( |
There was a problem hiding this comment.
These login-variant tests cover the happy path and store_options forwarding, but not two things the docs lean on: forwarding actor_token/actor_token_type through this method (the custom_token_exchange test above checks that, the login one doesn't), and the missing-response case that the README and examples call out as raising a ValueError.
Could we add a couple of small delegation tests here to lock both down?
| - `TOKEN_EXCHANGE_FAILED`: general token exchange failure (e.g., Auth0 returned an OAuth error) | ||
| - `INVALID_RESPONSE`: Auth0 returned a non-JSON response | ||
|
|
||
| `INVALID_TOKEN_FORMAT` is raised client-side before any network call — malformed tokens never reach Auth0. |
There was a problem hiding this comment.
This says malformed tokens never reach Auth0, but the client-side check only catches an empty or whitespace-only token and a "Bearer " prefix. A subject_token_type that isn't a valid URI, or any other malformed-but-nonempty token, still goes to Auth0. Since this SDK just wraps auth0-server-python and that layer doesn't do URI validation either, the guarantee here reads stronger than what the code actually does.
Shall we soften this to say only empty, whitespace, or "Bearer "-prefixed tokens are caught before the call? And if we do want real URI validation, that check belongs in auth0-server-python so every wrapper inherits it.
|
|
||
| `INVALID_TOKEN_FORMAT` is raised client-side before any network call — malformed tokens never reach Auth0. | ||
|
|
||
| ## 5. Token Type URIs |
There was a problem hiding this comment.
This token-type list, and the error-code list just above it, restate what's already in the auth0-server-python doc we link at the bottom. Since this SDK just wraps that one, these will drift the moment the base SDK changes a code or its URI guidance.
Could we trim both to a short pointer to the base SDK doc and the official Auth0 docs instead of re-listing them here? One thing to double check while we're at it: the reserved-namespace line names okta and http variants that aren't in the base doc or the spec, so that dashboard-config detail is probably safer left to the official docs.
| ``` | ||
|
|
||
| > **NOTE**: When an `actor_token` is present, Auth0 does not issue a refresh token (`offline_access` is dropped). The acting party is fixed at exchange time and is not re-emitted on a later token refresh. | ||
|
|
There was a problem hiding this comment.
We can add Organization Support Section here
Changes
Features
AuthClient):custom_token_exchangeandlogin_with_custom_token_exchange. Callcustom_token_exchangeto exchange a token for Auth0 tokens without touching the caller's session(service-to-service delegation, downstream API calls with a different audience/scope). Call
login_with_custom_token_exchangeto exchange a token and establish a full Auth0 session, same as completing/auth/callback.actor_token/actor_token_typeto represent a party acting on behalf of the subject; Auth0 surfaces this as theactclaim on the response(
custom_token_exchange) or persisted on the session user (login_with_custom_token_exchange).organizationparameter to scope the exchange to a specific org.CustomTokenExchangeErroris raised on exchange failure or invalid subject/actor token parameters, and is mapped to an HTTP400JSON response automatically onceAPI Changes
AuthClient.custom_token_exchange,AuthClient.login_with_custom_token_exchange.auth0_fastapi.errors:CustomTokenExchangeError,CustomTokenExchangeErrorCode(re-exported fromauth0-server-python).