-
Notifications
You must be signed in to change notification settings - Fork 68
Description
Edit (2025-03-21): I've discovered a mistake in my PKCE implementation: the code_challenge value included Base64 padding. Removing that padding fixes the issue, but it looks like mock-oauth2-server accepts incorrect code_challenge values on the 2nd attempt, and there are some usability improvements it could do.
Original report
I'm using mock-oauth2-server in its Docker container to test the server side of a Python application server which is a token-mediating backend + resource server hybrid for a browser-based single-page application.
When attempting to acquire a token with the Authorisation Code flow with PKCE (as a public client), mock-oauth2-server only works correctly on the 2nd attempt.
I've made a script which replicates a minimal subset of my server's test suite and auth logic to demonstrate the issue, which sends 5 identical token requests, but with their own cookie jars (to simulate multiple API calls): https://gist.github.com/micolous/b9c5bbd2964bd5488550a9bda12ece9c
Expected behaviour
The first (and some number of following) attempt(s) works, and all subsequent attempts (perhaps after a delay) fail.
Allowing more than one request to work correctly may be helpful (see additional notes).
Actual behaviour
On the 1st attempt, mock-oauth2-server returns:
{'error_description': 'invalid_pkce: code_verifier does not compute to code_challenge from request',
'error': 'invalid_grant'}The 2nd attempt works correctly, with all claims set as configured.
On the 3rd and following attempts, mock-oauth2-server returns a valid but incorrect access_token and id_token, where it:
- sets
subto a random UUID, rather than the value supplied in theusernameparameter to the login form - drops any parameters passed via the
claimsparameter to the login form - drops any parameters passed via the
tokenCallbacks[].requestMappings[].claimsconfig option
I suspect mock-oauth2-server is stashing some state from the /authorize request, but then it can't fetch it on the 1st attempt, and then deletes it after the 2nd attempt.
Workarounds
I've made my server side retry any token request twice, and fail if id_token is missing claims.
Additional notes
I never noticed this issue when testing with the SPA, because React's strict mode causes useEffect to run twice (and thus, any API call in a useEffect would also trigger twice). So the first call it made to complete the auth process would fail, but then the second request would be fine.
When I worked around this by adding a retry to the server side, React was still making two attempts, and that's where I found that requesting a token from mock-oauth2-server a 3rd time returns a token without any of the claims I expected.
Environment
mock-oauth2-server 2.1.10 using the Docker container on linux/aarch64, with this config:
{
"httpServer": {
"type": "NettyWrapper",
"ssl": {
"keyPassword": "",
"keystoreFile": "/run/secrets/server_p12",
"keystoreType": "PKCS12",
"keystorePassword": ""
}
}
}The keystore file is provided via Docker Secrets.
The example script will accept any certificate it is given. There is a commented out option to validate certificates properly.