[ https://issues.apache.org/jira/browse/KAFKA-14496?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Kirk True updated KAFKA-14496: ------------------------------ Priority: Blocker (was: Major) > Wrong Base64 encoder used by OIDC OAuthBearerLoginCallbackHandler > ----------------------------------------------------------------- > > Key: KAFKA-14496 > URL: https://issues.apache.org/jira/browse/KAFKA-14496 > Project: Kafka > Issue Type: Bug > Components: clients > Affects Versions: 3.3.1 > Reporter: Endre Vig > Assignee: Kirk True > Priority: Blocker > Fix For: 3.4.0, 3.3.2 > > Attachments: base64test.zip > > > Currently our team is setting up a blueprint for our Kafka > consumers/producers to provide guidelines on how to connect to our broker > using the OIDC security mechanism. The blueprint is written in Java using the > latest 3.3.1 Kafka library dependencies managed by Spring Boot 3.0.0. > While trying to use the new built-in > {{org.apache.kafka.common.security.oauthbearer.secured.OAuthBearerLoginCallbackHandler}} > introduced by [KIP-768: Extend SASL/OAUTHBEARER with Support for > OIDC|https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=186877575], > we've noticed that some calls to retrieve the token work out well, while > some of them (seemingly randomly) are failing with 401 Unauthorized. > After some debugging we've got to the conclusion that the faulty behavior is > caused by > {{org.apache.kafka.common.security.oauthbearer.secured.HttpAccessTokenRetriever#formatAuthorizationHeader:}} > {code:java} > static String formatAuthorizationHeader(String clientId, String clientSecret) > { > clientId = sanitizeString("the token endpoint request client ID > parameter", clientId); > clientSecret = sanitizeString("the token endpoint request client secret > parameter", clientSecret); > > String s = String.format("%s:%s", clientId, clientSecret); > String encoded = Base64.getUrlEncoder().encodeToString(Utils.utf8(s)); > return String.format("Basic %s", encoded); > } {code} > The above code is using {{java.util.Base64#getUrlEncoder}} on line 311 to > encode the authorization header value, which is using the alphabet described > in [section 5 of the RFC|https://www.rfc-editor.org/rfc/rfc4648#section-5] > during the encoding algorithm. As stated by the Basic Authentication Scheme > [definition|https://www.rfc-editor.org/rfc/rfc7617#section-2] however, > [section 4 of the RFC|https://www.rfc-editor.org/rfc/rfc4648#section-4] > should be used: > ??4. and obtains the basic-credentials by encoding this octet sequence using > Base64 ([RFC4648], Section 4) into a sequence of US-ASCII characters > ([RFC0020]).?? > The difference between the 2 alphabets are only on two characters (62: '+' > vs. '-' and 63: '/' vs. '_'), that's why the 401 Unauthorized response arises > only for certain credential values. > Here's a concrete example use case: > > {code:java} > String s = String.format("%s:%s", "SOME_RANDOM_LONG_USER_01234", > "9Q|0`8i~ute-n9ksjLWb\\50\"AX@UUED5E"); > System.out.println(Base64.getUrlEncoder().encodeToString(Utils.utf8(s))); > {code} > would print out: > {code:java} > U09NRV9SQU5ET01fTE9OR19VU0VSXzAxMjM0OjlRfDBgOGl-dXRlLW45a3NqTFdiXDUwIkFYQFVVRUQ1RQ== > {code} > while > {code:java} > String s = String.format("%s:%s", "SOME_RANDOM_LONG_USER_01234", > "9Q|0`8i~ute-n9ksjLWb\\50\"AX@UUED5E"); > System.out.println(Base64.getEncoder().encodeToString(Utils.utf8(s))); {code} > would give: > {code:java} > U09NRV9SQU5ET01fTE9OR19VU0VSXzAxMjM0OjlRfDBgOGl+dXRlLW45a3NqTFdiXDUwIkFYQFVVRUQ1RQ== > {code} > Please notice the '-' vs. '+' characters. > > The 2 code snippets above would not behave differently for other credentials, > where the encoded result doesn't use the 62nd character of the alphabet: > {code:java} > String s = String.format("%s:%s", "SHORT_USER_01234", > "9Q|0`8i~ute-n9ksjLWb\\50\"AX@UUED5E"); > System.out.println(Base64.getEncoder().encodeToString(Utils.utf8(s))); {code} > {code:java} > U0hPUlRfVVNFUl8wMTIzNDo5UXwwYDhpfnV0ZS1uOWtzakxXYlw1MCJBWEBVVUVENUU= > {code} > > As a *conclusion* I would suggest that line 311 of > {{HttpAccessTokenRetriever}} should be modified to use > {{Base64.getEncoder().encodeToString(...)}} instead of > {{Base64.getUrlEncoder().encodeToString(...).}} > > I'm attaching a short sample application with tests proving that the above > encoding method is rejected by the standard Spring Security HTTP basic > authentication as well. -- This message was sent by Atlassian Jira (v8.20.10#820010)