Repository: OpenLiberty/open-liberty (2025-08-07T13:42:33Z)

Issue:
openidConnectClient cannot handle low case "bearer" as token_type
**Describe the bug**  
Open Liberty can handle "Bearer" but not "bearer" as token_type.
The RFC 6749 defines token type’s value is case insensitive.
https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.2
"Value is case insensitive."

**Expected behavior**  
Open Liberty can handle "Bearer" and "bearer" as token_type.

**Diagnostic information:**  
We found this issue at the following versions.
OpenLiberty Version: 21.0.0.9, 25.0.0.6



Discussion:

Patch:
diff --git a/dev/com.ibm.ws.security.common/src/com/ibm/ws/security/common/http/AuthUtils.java b/dev/com.ibm.ws.security.common/src/com/ibm/ws/security/common/http/AuthUtils.java
index 8c030966b4c5..7ce234bd2d78 100644
--- a/dev/com.ibm.ws.security.common/src/com/ibm/ws/security/common/http/AuthUtils.java
+++ b/dev/com.ibm.ws.security.common/src/com/ibm/ws/security/common/http/AuthUtils.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2019, 2024 IBM Corporation and others.
+ * Copyright (c) 2019, 2025 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -51,7 +51,16 @@ public String getBearerTokenFromHeader(@Sensitive String rawHeaderValue, String
         }
         if (scheme != null) {
             scheme = scheme.trim();
-            if (rawHeaderValue.startsWith(scheme)) {
+            
+            // Case-insensitive check for "Bearer" token
+            if ("Bearer ".equalsIgnoreCase(scheme.trim()) || "Bearer".equalsIgnoreCase(scheme.trim())) {
+                // Check if rawHeaderValue starts with any case variation of "Bearer "
+                if (rawHeaderValue.toLowerCase().startsWith("bearer ")) {
+                    return rawHeaderValue.substring("bearer ".length()).trim();
+                }
+            }
+            // Original case-sensitive check for other schemes
+            else if (rawHeaderValue.startsWith(scheme)) {
                 return rawHeaderValue.substring(scheme.length()).trim();
             }
         }
diff --git a/dev/com.ibm.ws.security.openidconnect.client/src/com/ibm/ws/security/openidconnect/client/internal/AccessTokenAuthenticator.java b/dev/com.ibm.ws.security.openidconnect.client/src/com/ibm/ws/security/openidconnect/client/internal/AccessTokenAuthenticator.java
index 927796d6d4d7..a18a84f915a7 100644
--- a/dev/com.ibm.ws.security.openidconnect.client/src/com/ibm/ws/security/openidconnect/client/internal/AccessTokenAuthenticator.java
+++ b/dev/com.ibm.ws.security.openidconnect.client/src/com/ibm/ws/security/openidconnect/client/internal/AccessTokenAuthenticator.java
@@ -69,7 +69,7 @@ public class AccessTokenAuthenticator {
     private static final String INVALID_TOKEN = "invalid_token";
     private static final String JWT_SEGMENTS = "-segments";
     private static final String JWT_SEGMENT_INDEX = "-";
-    private static final String BEARER_SCHEME = "Bearer ";
+    private static final String BEARER_SCHEME = "bearer ";
 
     OidcClientUtil oidcClientUtil = new OidcClientUtil();
     SSLSupport sslSupport = null;
@@ -1010,7 +1010,7 @@ private static String getAccessTokenFromAuthorizationHeaderOrPostParameter(HttpS
 
     @Trivial
     private static boolean isBearerToken(String hdrValue) {
-        return hdrValue != null && hdrValue.startsWith(BEARER_SCHEME);
+        return hdrValue != null && hdrValue.toLowerCase().startsWith(BEARER_SCHEME);
     }
 
     public boolean canUseIssuerAsSelectorForInboundPropagation(HttpServletRequest req, OidcClientConfig clientConfig) {


Test patch:
diff --git a/dev/com.ibm.ws.security.common/test/com/ibm/ws/security/common/http/AuthUtilsTest.java b/dev/com.ibm.ws.security.common/test/com/ibm/ws/security/common/http/AuthUtilsTest.java
index aef7c5f5a1e6..89afeb32bebc 100644
--- a/dev/com.ibm.ws.security.common/test/com/ibm/ws/security/common/http/AuthUtilsTest.java
+++ b/dev/com.ibm.ws.security.common/test/com/ibm/ws/security/common/http/AuthUtilsTest.java
@@ -100,4 +100,30 @@ public void test_getBearerTokenFromHeader_goldenPath() {
         assertEquals("Returned value did not match the expected value", expectedValue, result);
     }
 
+    @Test
+    public void test_getBearerTokenFromHeader_caseInsensitive_lowercase() {
+        String expectedValue = "myToken";
+        String scheme = "Bearer ";
+        String rawHeaderValue = "bearer " + expectedValue;
+        String result = utils.getBearerTokenFromHeader(rawHeaderValue, scheme);
+        assertEquals("Returned value did not match the expected value with lowercase bearer", expectedValue, result);
+    }
+
+    @Test
+    public void test_getBearerTokenFromHeader_caseInsensitive_mixedCase() {
+        String expectedValue = "myToken";
+        String scheme = "Bearer ";
+        String rawHeaderValue = "BeArEr " + expectedValue;
+        String result = utils.getBearerTokenFromHeader(rawHeaderValue, scheme);
+        assertEquals("Returned value did not match the expected value with mixed case bearer", expectedValue, result);
+    }
+
+    @Test
+    public void test_getBearerTokenFromHeader_caseInsensitive_uppercase() {
+        String expectedValue = "myToken";
+        String scheme = "Bearer ";
+        String rawHeaderValue = "BEARER " + expectedValue;
+        String result = utils.getBearerTokenFromHeader(rawHeaderValue, scheme);
+        assertEquals("Returned value did not match the expected value with uppercase bearer", expectedValue, result);
+    }
 }
diff --git a/dev/com.ibm.ws.security.openidconnect.client/test/com/ibm/ws/security/openidconnect/client/internal/AccessTokenAuthenticatorTest.java b/dev/com.ibm.ws.security.openidconnect.client/test/com/ibm/ws/security/openidconnect/client/internal/AccessTokenAuthenticatorTest.java
index e8d5d841e4a7..3e1d519cfc0e 100644
--- a/dev/com.ibm.ws.security.openidconnect.client/test/com/ibm/ws/security/openidconnect/client/internal/AccessTokenAuthenticatorTest.java
+++ b/dev/com.ibm.ws.security.openidconnect.client/test/com/ibm/ws/security/openidconnect/client/internal/AccessTokenAuthenticatorTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016, 2022 IBM Corporation and others.
+ * Copyright (c) 2016, 2025 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
  * which accompanies this distribution, and is available at
@@ -193,6 +193,32 @@ public void tearDown() {
         outputMgr.resetStreams();
     }
 
+    @Test
+    public void testIsBearerToken_CaseInsensitive() {
+
+        // Test different case variations of "Bearer" scheme by using getBearerAccessTokenToken
+        // which internally uses the private isBearerToken method
+        
+        // Setup for lowercase "bearer" token
+        withHeaderName(null);
+        withAuthorizationHeader("bearer token123");
+        String result2 = AccessTokenAuthenticator.getBearerAccessTokenToken(req, clientConfig);
+        assertEquals("Lowercase bearer token should be recognized", "token123", result2);
+        
+        // Setup for mixed case "BeArEr" token
+        withHeaderName(null);
+        withAuthorizationHeader("BeArEr token123");
+        String result3 = AccessTokenAuthenticator.getBearerAccessTokenToken(req, clientConfig);
+        assertEquals("Mixed case bearer token should be recognized", "token123", result3);
+        
+        // Setup for uppercase "BEARER" token
+        withHeaderName(null);
+        withAuthorizationHeader("BEARER token123");
+        String result4 = AccessTokenAuthenticator.getBearerAccessTokenToken(req, clientConfig);
+        assertEquals("All caps BEARER token should be recognized", "token123", result4);
+        
+    }
+
     @Test
     public void testConstructor() {
         try {
@@ -1344,4 +1370,4 @@ public Map<String, Object> checkToken(String tokenInfor, String clientId, @Sensi
             }
         }
     }
-}
\ No newline at end of file
+}