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]