smolnar82 opened a new pull request, #1334: URL: https://github.com/apache/knox/pull/1334
[KNOX-3399](https://issues.apache.org/jira/browse/KNOX-3399) - Fix and complete RFC 8693 token exchange ## What changes were proposed in this pull request? RFC 8693 token exchange did not work when a client sent the request the normal, standards-compliant way — a `POST` with an `application/x-www-form-urlencoded` body. The request was silently treated as a plain single-token request and the `actor_token` was ignored, so the on-behalf-of (OBO) flow never actually ran. This PR fixes that and brings the flow in line with the RFC: - **Fix:** the token-exchange request is now correctly recognized when `grant_type`, `subject_token`, and `actor_token` are sent in the request body (previously only the URL query string was inspected, which no real client uses). - **Spec alignment:** `actor_token` is now **optional** (per RFC 8693). With an actor token the exchange is a delegation/OBO exchange; without one, the subject token is simply exchanged for a new token representing the subject. - **Spec alignment:** `subject_token_type` (required) and `actor_token_type` (required only when an actor token is present) are now honored and validated. Only standard JWT token types are accepted; anything else is rejected with a clear error. - **Cleanup:** the token-exchange logic was moved out of `JWTFederationFilter` into a small, dedicated `TokenExchangeHandler` class, which the filter delegates to. This slims the filter down and makes the exchange logic easy to test in isolation. **Note for reviewers - behavior change:** token exchange now requires a JWT `subject_token` and a `subject_token_type`. Passcode-style subject tokens are no longer accepted through this grant (they continue to work via normal Bearer/passcode authentication). Callers must now include `subject_token_type` (and `actor_token_type` when sending an actor token). ## How was this patch tested? The original problem was reproduced and root-caused against a running gateway with a remote debugger (confirmed the request body parameters were not visible where the grant type was being read). 1. New unit tests: - `TokenExchangeHandlerTest`: covers all the request-validation rules and both the delegation (with actor) and plain (no actor) exchange paths. - `JWTFederationFilterTokenExchangeRoutingTest`: verifies the filter routes a body-based token-exchange request to the handler, and does not route other requests to it. - The full `gateway-provider-security-jwt` module test suite passes (272 tests, 0 failures) and Checkstyle is clean. 2. Manual testing: ``` export SUBJECT_TOKEN="eyJqa3UiOiJodHRwczovL2xvY2FsaG9zdDo4NDQzL2dhdGV3YXkvaG9tZXBhZ2Uva25veHRva2VuL2FwaS92Mi9qd2tzLmpzb24iLCJraWQiOiJHcE9sOFhmc05BT0Z6Uk9WaGItSE03dko1Q1JxUjFHTU03cFhIZFEyOVZRIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJhZG1pbiIsImprdSI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMvZ2F0ZXdheS9ob21lcGFnZS9rbm94dG9rZW4vYXBpL3YyL2p3a3MuanNvbiIsImtpZCI6IkdwT2w4WGZzTkFPRnpST1ZoYi1ITTd2SjVDUnFSMUdNTTdwWEhkUTI5VlEiLCJpc3MiOiJLTk9YU1NPIiwiZXhwIjoxNzg1NDIzNDcxLCJtYW5hZ2VkLnRva2VuIjoidHJ1ZSIsImtub3guaWQiOiIyMzU3NGEyZS01OGVlLTQzYTMtOTNkZi01NjhmYmU2OTk4ZTYifQ.QPVU56NA4O5APfpYZXsoqlDHgaM5arRng3Apk3kDu5xvJUK22aHSZFzEGIZbwUyWB4xJ8syfTB-K9s_9jFn7SzzCxNKjcjWrsDOic1GL-dTa2PcTFBGIVUOQNlCeFnrASyodR6HDE3b1-ITN8P7bLTqpys3JSRgDpgnebbjO4_tWcNXmGNJCaGQUX8ugijysNrcvxY9-3FABtRtx6dKqNqD_RwwI1MVCcjDJ8FUHFjchO1GzfRS9m_XwD5ruG1ShXumthSBJep0eaWuGb0tdKTepuhqzQuPekpuck4kbxk9N-817G0aPYC4cvR9UBwKcqBDPHhjcx7RmZhs5WD_tiQ" export ACTOR_TOKEN="eyJqa3UiOiJodHRwczovL2xvY2FsaG9zdDo4NDQzL2dhdGV3YXkvaG9tZXBhZ2Uva25veHRva2VuL2FwaS92Mi9qd2tzLmpzb24iLCJraWQiOiJHcE9sOFhmc05BT0Z6Uk9WaGItSE03dko1Q1JxUjFHTU03cFhIZFEyOVZRIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJzYW0iLCJqa3UiOiJodHRwczovL2xvY2FsaG9zdDo4NDQzL2dhdGV3YXkvaG9tZXBhZ2Uva25veHRva2VuL2FwaS92Mi9qd2tzLmpzb24iLCJraWQiOiJHcE9sOFhmc05BT0Z6Uk9WaGItSE03dko1Q1JxUjFHTU03cFhIZFEyOVZRIiwiaXNzIjoiS05PWFNTTyIsImV4cCI6MTc4NTQ5MTQxOSwibWFuYWdlZC50b2tlbiI6InRydWUiLCJrbm94LmlkIjoiY2U1NDJlYjctZjdhOS00NWM1LWEzODQtMWVmNTY4NmIwZjIxIn0.ARIJdwglq0-9vWZa38yk8722w_BX7aiFoS8X1NRSCbA92ETUD0OwiFCeweydKie6WTU__vhqZp8PDL0pWhj0W4K7GecoPyHkz-PgRdPEDXDMjLVTzbFit47ioADuK-icHE3LS6vRuWNde6F3ejaMqKs0F_qp5j0ps-tXwIvlOGy8WTp0tAM-mN-KJ4B83rBRtr669qk4GYLajbVACN5LQHzh5NCnuZR_f3rAbBvhqo6pcOmVBrfCOyQeChVs2k4BTnbZui7o85kHTROi3X_u9-jvxdoNR-xkOJamO4bXnHLzLanuegfSUWW96MAeakqR141LnHL4DcCBpbg5g_jEig" $ curl -k -X POST \ > "https://localhost:8443/gateway/tokenexchange/knoxtoken/api/v2/token" \ > -H "Content-Type: application/x-www-form-urlencoded" \ > -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \ > -d "subject_token=$SUBJECT_TOKEN" <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 400 invalid_request: the subject_token_type parameter is required</title> </head> <body><h2>HTTP ERROR 400 invalid_request: the subject_token_type parameter is required</h2> <table> <tr><th>URI:</th><td>/gateway/tokenexchange/knoxtoken/api/v2/token</td></tr> <tr><th>STATUS:</th><td>400</td></tr> <tr><th>MESSAGE:</th><td>invalid_request: the subject_token_type parameter is required</td></tr> <tr><th>SERVLET:</th><td>tokenexchange-knox-gateway-servlet</td></tr> </table> </body> </html> $ curl -k -X POST \ > "https://localhost:8443/gateway/tokenexchange/knoxtoken/api/v2/token" \ > -H "Content-Type: application/x-www-form-urlencoded" \ > -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \ > -d "subject_token=$SUBJECT_TOKEN" \ > -d "subject_token_type=urn:ietf:params:oauth:token-type:invalid" <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 400 unsupported_token_type: unsupported subject_token_type urn:ietf:params:oauth:token-type:invalid</title> </head> <body><h2>HTTP ERROR 400 unsupported_token_type: unsupported subject_token_type urn:ietf:params:oauth:token-type:invalid</h2> <table> <tr><th>URI:</th><td>/gateway/tokenexchange/knoxtoken/api/v2/token</td></tr> <tr><th>STATUS:</th><td>400</td></tr> <tr><th>MESSAGE:</th><td>unsupported_token_type: unsupported subject_token_type urn:ietf:params:oauth:token-type:invalid</td></tr> <tr><th>SERVLET:</th><td>tokenexchange-knox-gateway-servlet</td></tr> </table> </body> </html> $ curl -k -X POST \ > "https://localhost:8443/gateway/tokenexchange/knoxtoken/api/v2/token" \ > -H "Content-Type: application/x-www-form-urlencoded" \ > -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \ > -d "subject_token=$SUBJECT_TOKEN" \ > -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" {"access_token":"eyJqa3UiOiJodHRwczovL2xvY2FsaG9zdDo4NDQzL2dhdGV3YXkvdG9rZW5leGNoYW5nZS9rbm94dG9rZW4vYXBpL3YyL2p3a3MuanNvbiIsImtpZCI6IkdwT2w4WGZzTkFPRnpST1ZoYi1ITTd2SjVDUnFSMUdNTTdwWEhkUTI5VlEiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImprdSI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMvZ2F0ZXdheS90b2tlbmV4Y2hhbmdlL2tub3h0b2tlbi9hcGkvdjIvandrcy5qc29uIiwia2lkIjoiR3BPbDhYZnNOQU9GelJPVmhiLUhNN3ZKNUNScVIxR01NN3BYSGRRMjlWUSIsImlzcyI6IktOT1hTU08iLCJleHAiOjE3OTU3NzIxODEsIm1hbmFnZWQudG9rZW4iOiJ0cnVlIiwia25veC5pZCI6IjY4NGVlY2M4LTNiYmUtNDczZC1hOWZhLTdjNzU3MTZjNDM2OCJ9.mHcADoy9-q136i8Z2oux8clORuP7FZ_3ernCKjAzErXX1xX957B6iMxCTKojLWcT8D-0B9zujYtqpZclwXYB7OSFR031N8VrTDMJwLFLclm8XBMJ4F_4lT7DwWeYMIhH61w58TKdPsBDIRQDFnfP18v6cdSHd3IUno5CRXu2ZlOs6j2yThk7SvJq8NOFbsS5PS3NQHwdzWoeAzneXtzrOEOPw9QdbPnn9RzcKm9IOZFqPOcVxyg3B1jz2zahZThGmBSzjLm0ojLvBFJfloTSoaJgmb1n446enE1JJNTP8BApHNuOGSJNoqbRMwK3_4QdRJ_eT8wWWvHENPfCuGWdAA","token_id":"684eecc8-3bbe-473d-a9fa-7c75716c4368","managed":"true","endpoint_public_ cert":"MIIDZDCCAkygAwIBAgIIAbJD1h0J2cAwDQYJKoZIhvcNAQELBQAwXzESMBAGA1UEAwwJbG9jYWxob3N0MQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQKDAZIYWRvb3AxDTALBgNVBAcMBFRlc3QxDTALBgNVBAgMBFRlc3QxCzAJBgNVBAYTAlVTMB4XDTI2MDQxNjEyNTIyOVoXDTI3MDQxNjEyNTIyOVowXzESMBAGA1UEAwwJbG9jYWxob3N0MQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQKDAZIYWRvb3AxDTALBgNVBAcMBFRlc3QxDTALBgNVBAgMBFRlc3QxCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm5XaN8VbAwwERG5e88U8IlKLpq3PvdT9nxjkp7DiVxkRaJAQkSrDOv9UUK8g9VV/xMD6No1yS6PhoufrxUd2e14L4QmXrwW/Qh2V9W7RQMHgUDmxt+5tr3hTbrM9UjavXkOh2d/XDpt/yb/v6MkxH3KUH/snU9iN6KRjdCrITRQ5JneHXzedwDxpGRIH1m4X90086HLUWfLwaxtmjXchokSeg+6s44XuO3oaeCJMpITJQEOKmsOigWx3RZwTRK3MrK/MOrRuF0lOphXVrMw1vvVmeTJQs1UzFuQOE9w7THo3qak+dANnq+rfIXd7M+Hinw6L9QrfQqSoEtLg4piTZQIDAQABoyQwIjAgBgNVHREEGTAXggpLNjM1MlA2SjQxgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAAwjRAziVRGeB9IfZYRM/1yLy9KGf8nzVhWTRzRa2Y8NLgjLt1yG62iS7t8M1CjLEYAowvR4a+GrRC2TRaOgOAsIYLt5yLbT4YaFxprTBajdz55Xqeiy7AgETfLE8FYfQTVGWp/snehmpMAme0Pc6ys6WCRLvOJWMXZHlR0w1b GUYsUC4QGAcKVS5GP27FXijigxoCVfoBkJ2NvsK62Uc3Pz1hFtzu+pY4+CrdockdHzrDZnJrpmmYH/g+B4hXWjouAkEoYUDtdwyQ1zdrE/BTiD3ozPdcmAnEH7utWChIzPcFr0KlpIYcYiySGBTf4PcJPGd6qeEgXTu3MGVQ6uMO8=","token_type":"Bearer","expires_in":1795772181545,"passcode":"TmpnMFpXVmpZemd0TTJKaVpTMDBOek5rTFdFNVptRXROMk0zTlRjeE5tTTBNelk0OjpOamcxTlRjeFpqVXRaakptTkMwME9HTTRMVGcxWmpRdFpURXlaR1kyTkRreVpXUXk="}$ $ curl -k -X POST \ > "https://localhost:8443/gateway/tokenexchange/knoxtoken/api/v2/token" \ > -H "Content-Type: application/x-www-form-urlencoded" \ > -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \ > -d "subject_token=$SUBJECT_TOKEN" \ > -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \ > -d "actor_token=$ACTOR_TOKEN" <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 400 invalid_request: actor_token_type is required when actor_token is present</title> </head> <body><h2>HTTP ERROR 400 invalid_request: actor_token_type is required when actor_token is present</h2> <table> <tr><th>URI:</th><td>/gateway/tokenexchange/knoxtoken/api/v2/token</td></tr> <tr><th>STATUS:</th><td>400</td></tr> <tr><th>MESSAGE:</th><td>invalid_request: actor_token_type is required when actor_token is present</td></tr> <tr><th>SERVLET:</th><td>tokenexchange-knox-gateway-servlet</td></tr> </table> </body> </html> $ curl -k -X POST \ > "https://localhost:8443/gateway/tokenexchange/knoxtoken/api/v2/token" \ > -H "Content-Type: application/x-www-form-urlencoded" \ > -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \ > -d "subject_token=$SUBJECT_TOKEN" \ > -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \ > -d "actor_token=$ACTOR_TOKEN" \ > -d "actor_token_type=urn:ietf:params:oauth:token-type:dummy" <html> <head> <meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1"/> <title>Error 400 unsupported_token_type: unsupported actor_token_type urn:ietf:params:oauth:token-type:dummy</title> </head> <body><h2>HTTP ERROR 400 unsupported_token_type: unsupported actor_token_type urn:ietf:params:oauth:token-type:dummy</h2> <table> <tr><th>URI:</th><td>/gateway/tokenexchange/knoxtoken/api/v2/token</td></tr> <tr><th>STATUS:</th><td>400</td></tr> <tr><th>MESSAGE:</th><td>unsupported_token_type: unsupported actor_token_type urn:ietf:params:oauth:token-type:dummy</td></tr> <tr><th>SERVLET:</th><td>tokenexchange-knox-gateway-servlet</td></tr> </table> </body> </html> $ curl -k -X POST \ > "https://localhost:8443/gateway/tokenexchange/knoxtoken/api/v2/token" \ > -H "Content-Type: application/x-www-form-urlencoded" \ > -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \ > -d "subject_token=$SUBJECT_TOKEN" \ > -d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \ > -d "actor_token=$ACTOR_TOKEN" \ > -d "actor_token_type=urn:ietf:params:oauth:token-type:jwt" {"access_token":"eyJqa3UiOiJodHRwczovL2xvY2FsaG9zdDo4NDQzL2dhdGV3YXkvdG9rZW5leGNoYW5nZS9rbm94dG9rZW4vYXBpL3YyL2p3a3MuanNvbiIsImtpZCI6IkdwT2w4WGZzTkFPRnpST1ZoYi1ITTd2SjVDUnFSMUdNTTdwWEhkUTI5VlEiLCJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImFjdCI6eyJzdWIiOiJzYW0ifSwiamt1IjoiaHR0cHM6Ly9sb2NhbGhvc3Q6ODQ0My9nYXRld2F5L3Rva2VuZXhjaGFuZ2Uva25veHRva2VuL2FwaS92Mi9qd2tzLmpzb24iLCJraWQiOiJHcE9sOFhmc05BT0Z6Uk9WaGItSE03dko1Q1JxUjFHTU03cFhIZFEyOVZRIiwiaXNzIjoiS05PWFNTTyIsImV4cCI6MTc5NTc3MzA1MywibWFuYWdlZC50b2tlbiI6InRydWUiLCJrbm94LmlkIjoiYmExMmQ2YjktODdhYy00YTBhLWFmYmUtZDQ2MDRkZWZlZmZlIn0.AHQhDm3YEIrQhBSJ4dFpPumjZMfT60kiC3mkPdTziPk1v-r8AsFtPdyFPvctUvaG-jDinJojgGh0fQ-65YiC3j0uZ-6dXi5uD6X9M4pSs8MRuD_vfvKwUdMrynoZJIo35Dd7JO773eNAMTNQcvDtN1mmiZ_fsiuzwkHXyQUMqvXI4M0rtWe-Nmqm-GUMZQumW8tFheg1XN-3P3F7HeLEjmj8mCv-9a9pVccUO_DXe1B3mRWxw2jOs5qUVkLqyFBglAa-RdaattqgullPec6hWQ2dfJsiOvSVu19Na7obuPR8eR5wX5eNor6q4Lb71mo7QDR6lFbjo_Ahe-lTiKoMYQ","token_id":"ba12d6b9-87ac-4a0a-afbe-d4604defeffe","manage d":"true","endpoint_public_cert":"MIIDZDCCAkygAwIBAgIIAbJD1h0J2cAwDQYJKoZIhvcNAQELBQAwXzESMBAGA1UEAwwJbG9jYWxob3N0MQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQKDAZIYWRvb3AxDTALBgNVBAcMBFRlc3QxDTALBgNVBAgMBFRlc3QxCzAJBgNVBAYTAlVTMB4XDTI2MDQxNjEyNTIyOVoXDTI3MDQxNjEyNTIyOVowXzESMBAGA1UEAwwJbG9jYWxob3N0MQ0wCwYDVQQLDARUZXN0MQ8wDQYDVQQKDAZIYWRvb3AxDTALBgNVBAcMBFRlc3QxDTALBgNVBAgMBFRlc3QxCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm5XaN8VbAwwERG5e88U8IlKLpq3PvdT9nxjkp7DiVxkRaJAQkSrDOv9UUK8g9VV/xMD6No1yS6PhoufrxUd2e14L4QmXrwW/Qh2V9W7RQMHgUDmxt+5tr3hTbrM9UjavXkOh2d/XDpt/yb/v6MkxH3KUH/snU9iN6KRjdCrITRQ5JneHXzedwDxpGRIH1m4X90086HLUWfLwaxtmjXchokSeg+6s44XuO3oaeCJMpITJQEOKmsOigWx3RZwTRK3MrK/MOrRuF0lOphXVrMw1vvVmeTJQs1UzFuQOE9w7THo3qak+dANnq+rfIXd7M+Hinw6L9QrfQqSoEtLg4piTZQIDAQABoyQwIjAgBgNVHREEGTAXggpLNjM1MlA2SjQxgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAAwjRAziVRGeB9IfZYRM/1yLy9KGf8nzVhWTRzRa2Y8NLgjLt1yG62iS7t8M1CjLEYAowvR4a+GrRC2TRaOgOAsIYLt5yLbT4YaFxprTBajdz55Xqeiy7AgETfLE8FYfQTVGWp/snehmpMA me0Pc6ys6WCRLvOJWMXZHlR0w1bGUYsUC4QGAcKVS5GP27FXijigxoCVfoBkJ2NvsK62Uc3Pz1hFtzu+pY4+CrdockdHzrDZnJrpmmYH/g+B4hXWjouAkEoYUDtdwyQ1zdrE/BTiD3ozPdcmAnEH7utWChIzPcFr0KlpIYcYiySGBTf4PcJPGd6qeEgXTu3MGVQ6uMO8=","token_type":"Bearer","expires_in":1795773053429,"passcode":"WW1FeE1tUTJZamt0T0RkaFl5MDBZVEJoTFdGbVltVXRaRFEyTURSa1pXWmxabVpsOjpNMlprTmpBd056Y3RZVFprTnkwME9UWXpMV0kxT1RrdE5XWTVPV05oT1Rsa01EazQ="} ``` Decoded access tokens: <img width="1340" height="552" alt="image" src="https://github.com/user-attachments/assets/7e6539ca-e9f7-4035-9843-367780e275a8" /> <img width="1318" height="616" alt="image" src="https://github.com/user-attachments/assets/322de74d-896e-460f-8a08-6cc5f2a2cbd6" /> ## Integration Tests N/A ## UI changes N/A -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
