I’ve finally found the solution to this. The problem was that the header of the JWT assertion needs to contain a “kid” field, which needs to match the kid of the public key which is uploaded to the server.
For some reason, this isn’t mentioned in the documentation, or maybe I just missed it.
In fact, the OKTA server does provide helpful error messages, but for some reason they don’t show up in the generated exception in Spring Boot.
I tried a few different things, but the one that worked was to copy the generated JWT into a curl command and call the the API from that.
Here’s an example:
@echo off
REM Set your variables
set CLIENT_ID=0oalji…
set AUTH_CODE=NXNGkZzy1xCg9dVGA58qr9INOpQAuNohKrp6acFO8vQ&
set JWT_ASSERTION=eyJ4NXQi…
set REDIRECT_URI=https://localhost:9444/callback
REM Make the cURL request
curl -X POST https://dev-60265957.okta.com/oauth2/default/v1/token ^
-H “Content-Type: application/x-www-form-urlencoded” ^
-d “grant_type=authorization_code” ^
-d “code=%AUTH_CODE%” ^
-d “redirect_uri=%REDIRECT_URI%” ^
-d “client_id=%CLIENT_ID%” ^
-d “client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer” ^
-d “client_assertion=%JWT_ASSERTION%”
REM Pause to keep the window open and see the output
pause