davsclaus commented on code in PR #29749:
URL: https://github.com/apache/camel/pull/29749#discussion_r4555816947


##########
components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcServiceOptions.java:
##########
@@ -81,26 +87,58 @@ public void configureOptions(CamelContext camelContext) {
      * Read OIDC token from given path. Caches the token for future usage.
      */
     public String retrieveOidcToken() {
-        if (oidcToken == null || !cacheTokens) {
-            try {
-                oidcToken = IOHelper
-                        
.loadText(ResourceHelper.resolveMandatoryResourceAsInputStream(getCamelContext(),
 oidcTokenPath))
-                        .trim();
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
+        String token = oidcToken;
+        if (token == null || !cacheTokens) {
+            token = loadOidcToken();
+            oidcToken = token;
         }
 
-        return oidcToken;
+        return token;
     }
 
     /**
      * Force renew of token by reading again from token path. Token may have 
been renewed already via volume mount
      * update.
      */
     public String renewOidcToken() {
-        oidcToken = null;
-        return retrieveOidcToken();
+        String token = loadOidcToken();
+        oidcToken = token;
+        return token;
+    }
+
+    /**
+     * Whether the given value of the <code>Authorization</code> header is 
<code>Bearer</code> followed by the token at
+     * the token path. The comparison takes the same time wherever the first 
difference is. When the cached token does

Review Comment:
   Thanks for checking this, but on the JDKs Camel supports (21+) 
`MessageDigest.isEqual` does not return early when the lengths differ. Since 
JDK-8136459 it folds the length difference into the result (`result |= lenA - 
lenB`) and always loops over the length of the **first** argument. `matches` 
passes the expected `Bearer <token>` first and the request header second, so 
the time depends only on the length of the mounted token, never on the header 
the client sends, and timing a header of varying length tells nothing.
   
   So I kept the guarantee, but made it explicit in 5c58b4d1caf8: the Javadoc 
now says the time depends only on the length of the token, and a comment on 
`matches` says why the expected value has to be the first argument, so a later 
edit does not swap them.
   
   _Claude Code on behalf of davsclaus_



##########
components/camel-knative/camel-knative-http/src/main/java/org/apache/camel/component/knative/http/KnativeOidcServiceOptions.java:
##########
@@ -81,26 +87,58 @@ public void configureOptions(CamelContext camelContext) {
      * Read OIDC token from given path. Caches the token for future usage.
      */
     public String retrieveOidcToken() {
-        if (oidcToken == null || !cacheTokens) {
-            try {
-                oidcToken = IOHelper
-                        
.loadText(ResourceHelper.resolveMandatoryResourceAsInputStream(getCamelContext(),
 oidcTokenPath))
-                        .trim();
-            } catch (IOException e) {
-                throw new RuntimeException(e);
-            }
+        String token = oidcToken;
+        if (token == null || !cacheTokens) {
+            token = loadOidcToken();
+            oidcToken = token;
         }
 
-        return oidcToken;
+        return token;
     }
 
     /**
      * Force renew of token by reading again from token path. Token may have 
been renewed already via volume mount
      * update.
      */
     public String renewOidcToken() {
-        oidcToken = null;
-        return retrieveOidcToken();
+        String token = loadOidcToken();
+        oidcToken = token;
+        return token;
+    }
+
+    /**
+     * Whether the given value of the <code>Authorization</code> header is 
<code>Bearer</code> followed by the token at
+     * the token path. The comparison takes the same time wherever the first 
difference is. When the cached token does
+     * not match, the token is read again, as Knative may have renewed the 
mounted token since it was read: the renewed
+     * token is then accepted, and the token it replaces is no longer accepted.
+     *
+     * @param  authorization the value of the <code>Authorization</code> 
header of the request, may be null
+     * @return               true when the request carries the token at the 
token path
+     */
+    public boolean isAuthorized(String authorization) {
+        if (authorization == null) {
+            return false;
+        }
+        if (matches(authorization, retrieveOidcToken())) {
+            return true;
+        }
+        // without the cache the token was just read
+        return cacheTokens && matches(authorization, renewOidcToken());
+    }
+
+    private static boolean matches(String authorization, String token) {
+        byte[] expected = ("Bearer " + token).getBytes(StandardCharsets.UTF_8);
+        return MessageDigest.isEqual(expected, 
authorization.getBytes(StandardCharsets.UTF_8));
+    }
+
+    private String loadOidcToken() {
+        try {
+            return IOHelper
+                    
.loadText(ResourceHelper.resolveMandatoryResourceAsInputStream(getCamelContext(),
 oidcTokenPath))
+                    .trim();
+        } catch (IOException e) {
+            throw new RuntimeException(e);

Review Comment:
   Done in 5c58b4d1caf8: the exception now reads `Failed to load OIDC token 
from: <path>`.
   
   _Claude Code on behalf of davsclaus_



##########
components/camel-knative/camel-knative-http/src/main/docs/knative-http-component.adoc:
##########
@@ -245,3 +245,36 @@ camel.knative.client.oidc.token.path=oidc/token
 camel.knative.client.oidc.renew.tokens.on.forbidden=false
 camel.knative.client.oidc.cache.tokens=false
 ----
+
+== Knative Http consumer factory
+
+=== OIDC request verification
+
+With OIDC enabled on the service side, the Knative Http consumer accepts a 
request only when its `Authorization` header is `Bearer` followed by the token 
mounted at the token path. Any other request, also one without the header, gets 
a `401` response and does not reach the route.

Review Comment:
   Good point. Done in 5c58b4d1caf8, worded slightly differently: the header is 
`Bearer <token>` (`Bearer`, a space, and the token), where `<token>` is the 
token mounted at the token path.
   
   _Claude Code on behalf of davsclaus_



-- 
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]

Reply via email to