You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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 sub to a random UUID, rather than the value supplied in the username parameter to the login form
drops any parameters passed via the claims parameter to the login form
drops any parameters passed via the tokenCallbacks[].requestMappings[].claims config 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:
After testing with some other OAuth 2.0 providers, I've found a mistake in my PKCE implementation: the code_challenge includes Base64 padding. After removing the extra = from the end of the code_challenge, PKCE now succeeds on the 1st attempt only. So that's good. 😄
My suggestion here would be that the /authorize endpoint should try to decode Base64, and refuse the request if:
the Base64-encoded value contains padding
it was encoded with "regular" Base64 rather than URL-safe Base64
the value is not the correct length for the given code_challenge_method
It working on the 2nd try only seemed strange to me, so I updated my script to hard code code_challenge to a fixed value (without padding), while still generating a random code_verifier – which should always fail PKCE checks.
However, mock-oauth2-server seems to accept the token request on the second attempt only. That's not good – and if it was in a real OAuth 2.0 provider, that'd be a major security hole.
I don't know enough about this code base to tell whether this caused by something mock-oauth2-server is doing, or there is a bug in the Nimbus SDK (which would be a security hole if used in a real OAuth 2.0 server).
micolous
changed the title
Auth Code flow with PKCE only works correctly on the 2nd attempt (not the 1st, 3rd, 4th, etc.)
Auth Code flow with incorrect PKCE code_challenge works correctly on the 2nd attempt (not the 1st, 3rd, 4th, etc.)
Mar 20, 2025
micolous
changed the title
Auth Code flow with incorrect PKCE code_challenge works correctly on the 2nd attempt (not the 1st, 3rd, 4th, etc.)
Auth Code flow with incorrect PKCE code_challenge works correctly on the 2nd attempt
Mar 20, 2025
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 likemock-oauth2-server
accepts incorrectcode_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:The 2nd attempt works correctly, with all claims set as configured.
On the 3rd and following attempts,
mock-oauth2-server
returns a valid but incorrectaccess_token
andid_token
, where it:sub
to a random UUID, rather than the value supplied in theusername
parameter to the login formclaims
parameter to the login formtokenCallbacks[].requestMappings[].claims
config optionI 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 auseEffect
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 onlinux/aarch64
, with this config: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.
The text was updated successfully, but these errors were encountered: