This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new f5207843dafe camel-netty-http: decode Basic auth credentials as UTF-8 
(#29711)
f5207843dafe is described below

commit f5207843dafeb5858c45830ac1adb55c138b7960
Author: dhairya05 <[email protected]>
AuthorDate: Thu Nov 12 23:39:09 2026 +0530

    camel-netty-http: decode Basic auth credentials as UTF-8 (#29711)
---
 .../http/handlers/HttpServerChannelHandler.java    | 33 +++++-----
 .../HttpServerChannelHandlerBasicAuthTest.java     | 75 ++++++++++++++++++++++
 2 files changed, 93 insertions(+), 15 deletions(-)

diff --git 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java
 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java
index a3a57ffb4db4..7a004ff7f020 100644
--- 
a/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java
+++ 
b/components/camel-netty-http/src/main/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandler.java
@@ -18,8 +18,8 @@ package org.apache.camel.component.netty.http.handlers;
 
 import java.net.URI;
 import java.nio.channels.ClosedChannelException;
-import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.util.Base64;
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Set;
@@ -30,7 +30,6 @@ import javax.security.auth.login.LoginException;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.DecoderResult;
-import io.netty.handler.codec.base64.Base64;
 import io.netty.handler.codec.http.DefaultFullHttpResponse;
 import io.netty.handler.codec.http.DefaultHttpResponse;
 import io.netty.handler.codec.http.FullHttpRequest;
@@ -379,21 +378,25 @@ public class HttpServerChannelHandler extends 
ServerChannelHandler {
             String constraint = StringHelper.before(auth, " ");
             if (constraint != null) {
                 if ("Basic".equalsIgnoreCase(constraint.trim())) {
-                    String decoded = StringHelper.after(auth, " ");
-                    // the decoded part is base64 encoded, so we need to 
decode that
-                    ByteBuf buf = 
NettyConverter.toByteBuffer(decoded.getBytes());
-                    ByteBuf out = Base64.decode(buf);
+                    String encoded = StringHelper.after(auth, " ");
+                    byte[] credentials;
                     try {
-                        String userAndPw = 
out.toString(Charset.defaultCharset());
-                        String username = StringHelper.before(userAndPw, ":");
-                        String password = StringHelper.after(userAndPw, ":");
-                        HttpPrincipal principal = new HttpPrincipal(username, 
password);
-                        LOG.debug("Extracted Basic Auth principal from HTTP 
header: {}", principal);
-                        return principal;
-                    } finally {
-                        buf.release();
-                        out.release();
+                        // the value is base64 encoded, so we need to decode 
that
+                        credentials = 
Base64.getDecoder().decode(encoded.trim());
+                    } catch (IllegalArgumentException e) {
+                        // a client sent something that is not base64 at all: 
no principal, so the request is
+                        // answered with 401 rather than an error
+                        LOG.debug("Cannot decode the Basic Auth header, it is 
not valid base64");
+                        return null;
                     }
+                    // RFC 7617: the credentials are encoded with UTF-8, so 
the platform default charset must not
+                    // decide how a non-ASCII user name or password is read 
back
+                    String userAndPw = new String(credentials, 
StandardCharsets.UTF_8);
+                    String username = StringHelper.before(userAndPw, ":");
+                    String password = StringHelper.after(userAndPw, ":");
+                    HttpPrincipal principal = new HttpPrincipal(username, 
password);
+                    LOG.debug("Extracted Basic Auth principal from HTTP 
header: {}", principal);
+                    return principal;
                 }
             }
         }
diff --git 
a/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandlerBasicAuthTest.java
 
b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandlerBasicAuthTest.java
new file mode 100644
index 000000000000..e64d2184ddb7
--- /dev/null
+++ 
b/components/camel-netty-http/src/test/java/org/apache/camel/component/netty/http/handlers/HttpServerChannelHandlerBasicAuthTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.netty.http.handlers;
+
+import io.netty.handler.codec.http.DefaultHttpRequest;
+import io.netty.handler.codec.http.HttpMethod;
+import io.netty.handler.codec.http.HttpRequest;
+import io.netty.handler.codec.http.HttpVersion;
+import org.apache.camel.component.netty.http.HttpPrincipal;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+/**
+ * RFC 7617 encodes the Basic credentials with UTF-8, so what the server reads 
back must not depend on the JVM's default
+ * charset.
+ */
+class HttpServerChannelHandlerBasicAuthTest {
+
+    @Test
+    void shouldDecodeCredentialsAsUtf8() {
+        // "user:pässword" base64-encoded from its UTF-8 bytes, the way a 
client sends it
+        HttpPrincipal principal = extract("Basic dXNlcjpww6Rzc3dvcmQ=");
+
+        assertNotNull(principal);
+        assertEquals("user", principal.getName());
+        assertEquals("pässword", principal.getPassword());
+    }
+
+    @Test
+    void shouldDecodeAsciiCredentials() {
+        // "scott:tiger"
+        HttpPrincipal principal = extract("Basic c2NvdHQ6dGlnZXI=");
+
+        assertNotNull(principal);
+        assertEquals("scott", principal.getName());
+        assertEquals("tiger", principal.getPassword());
+    }
+
+    @Test
+    void shouldIgnoreAnotherScheme() {
+        assertNull(extract("Bearer c2NvdHQ6dGlnZXI="));
+    }
+
+    @Test
+    void shouldIgnoreAValueThatIsNotBase64() {
+        // a client can send anything: no principal, so the request is 
answered with 401 rather than an error.
+        // Note the JDK decoder does not require the trailing padding, so an 
unpadded value is still decoded
+        assertNull(extract("Basic %%%"));
+        assertNull(extract("Basic not base64!"));
+    }
+
+    private static HttpPrincipal extract(String authorization) {
+        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, 
HttpMethod.GET, "/");
+        request.headers().set("Authorization", authorization);
+        return HttpServerChannelHandler.extractBasicAuthSubject(request);
+    }
+
+}

Reply via email to