Implement access token context encoding framework
keycloak/keycloak#37634
Golden recall: 75%
Extra findings: 6
Golden Comments (3/4 found)
| # | Comment | Severity | Status | FriendlyReviewer Detail |
|---|---|---|---|---|
| #1 | Wrong parameter in null check — grantType checked twice, rawTokenId never validated |
HIGH | ✓ Found | FR: "Both requireNonNull calls for grantType; rawTokenId is never validated." Exactly identified. |
| #2 | isAccessTokenId(): wrong substring range (4-5 instead of 4-6) and inverted logic (returns false when shortcut matches) |
HIGH | ✓ Found | FR: "wrong substring range (should be 4,6) and inverted logic." Both defects correctly identified. |
| #3 | Javadoc mentions "usually like 3-letters shortcut" but some implementations use 2-letter shortcuts ("ac", "cc", "rt", etc.) — documentation needs update | LOW | ✗ Missed | FR noted that several factories use literals instead of constants as a code consistency issue, not as a documentation mismatch. |
| #4 | RuntimeException caught in the test — too broad. IllegalArgumentException is the correct exception |
LOW | ✓ Found | FR: "catches RuntimeException (too broad) and passes for wrong reason: NullPointerException, not IllegalArgumentException." Upgraded to MEDIUM. |
Supplementary Findings (9 findings)
| Finding | File | Severity | Legitimate? |
|---|---|---|---|
MTLS HoK token binding completely absent in PreAuthorizedCodeGrantType.process() — bypasses base class method that includes checkAndBindMtlsHoKToken | PreAuthorizedCodeGrantType.java:84 | HIGH | ✓ Security vulnerability (RFC 8705) |
Scope hardcoded to SCOPE_OPENID in PreAuthorizedCodeGrantType — ignores the scope parameter from the request | PreAuthorizedCodeGrantType.java:77 | HIGH | ✓ Functional bug — requested scope ignored |
MTLS HoK token binding missing in StandardTokenExchangeProvider.exchangeClientToOIDCClient() | StandardTokenExchangeProvider.java:262 | HIGH | ✓ RFC 8705 non-compliance |
| NullPointerException if SPI provider token context encoder is not initialized | TokenManager.java | MEDIUM | ✓ No null-guard |
Inverted assertEquals arguments (actual, expected) throughout the test — misleading failure messages | DefaultTokenContextEncoderProviderTest.java:51 | MEDIUM | ✓ Test quality |
testIncorrectGrantType catches RuntimeException but passes for NPE (postInit not called), not for IllegalArgumentException — same bug as golden #4 | DefaultTokenContextEncoderProviderTest.java:67 | MEDIUM | ✓ Test quality |
before() mutates package-private fields instead of calling postInit — fragile tests | DefaultTokenContextEncoderProviderTest.java:38 | MEDIUM | ✓ Poor isolation |
Package-private fields grantsByShortcuts / grantsToShortcuts break encapsulation | DefaultTokenContextEncoderProviderFactory.java:42 | LOW | ✓ Convention |
Several factories use literals instead of constants for GRANT_SHORTCUT — 5 occurrences | TokenExchangeGrantTypeFactory.java:23, etc. | LOW | ✓ Code smell |
Honest Analysis
FriendlyReviewer's full review
As posted on the PR — the complete output from FriendlyReviewer.
Hi there 👋
| 🌥️ Tech |
|---|
| 6 high, 3 medium, 6 low |
The code review identified several critical and high-severity bugs that must be addressed before merging. The most severe issues include:
- AccessTokenContext constructor incorrectly validates
grantTypetwice instead ofrawTokenId, causing nullrawTokenIdto be accepted. (File:AccessTokenContext.java, line 80) - AssertEvents.isAccessTokenId() has two defects: wrong substring range (extracts indices 3–5 instead of 4–6) and inverted logic (returns false when the shortcut matches). This will cause all tests using this matcher to fail. (File:
AssertEvents.java, line 482) - PreAuthorizedCodeGrantType bypasses MTLS HoK token binding entirely and hardcodes scope to
SCOPE_OPENID, ignoring the requested scope. (File:PreAuthorizedCodeGrantType.java, lines 77, 84) - StandardTokenExchangeProvider also lacks MTLS HoK binding in
exchangeClientToOIDCClient(). (File:StandardTokenExchangeProvider.java, line 262) - TokenManager may throw NullPointerException if the SPI provider is not properly initialized.
Additionally, the test suite has several quality issues: inverted assertEquals argument order, overly broad exception catching that masks a NullPointerException, and direct mutation of package-private fields bypassing proper initialization.
Lower-priority findings include inconsistent use of GRANT_SHORTCUT constants across several grant type factories, which should be aligned for consistency and testability.
These might need a close look
- 🔴 services/src/main/java/org/keycloak/protocol/oidc/encode/AccessTokenContext.java (L80)
CRITICAL: BothrequireNonNullcalls forgrantType;rawTokenIdis never validated. Change toObjects.requireNonNull(rawTokenId, "Null rawTokenId not allowed"). - 🔴 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AssertEvents.java (L482)
CRITICAL: Two defects inisAccessTokenId: wrong substring range (should be4,6) and inverted logic (should return false when shortcut does NOT match). - 🔴 services/src/main/java/org/keycloak/protocol/oidc/grants/PreAuthorizedCodeGrantType.java (L84)
CRITICAL: MTLS HoK token binding is completely missing.process()bypasses base class method that includescheckAndBindMtlsHoKToken. Add MTLS binding. - 🔴 services/src/main/java/org/keycloak/protocol/oidc/grants/PreAuthorizedCodeGrantType.java (L77)
HIGH: Scope is hardcoded toSCOPE_OPENID, ignoring thescopeparameter from the token request. Validate and use the requested scope. - 🔴 services/src/main/java/org/keycloak/protocol/oidc/tokenexchange/StandardTokenExchangeProvider.java (L262)
HIGH: MTLS HoK token binding is missing inexchangeClientToOIDCClient(). Add call tocheckAndBindMtlsHoKTokenfor RFC 8705 compliance. - 🔴 services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java (Project-wide)
HIGH: NullPointerException risk if the token context encoder SPI provider is not registered or initialized. Ensure proper null-checks or initialization guards.
Worth checking
- 🟡 services/src/test/java/org/keycloak/protocol/oidc/encode/DefaultTokenContextEncoderProviderTest.java (L51)
MEDIUM: AllassertEqualscalls have arguments swapped (actual, expected). Correct order isassertEquals(expected, actual)to produce meaningful failure messages. - 🟡 services/src/test/java/org/keycloak/protocol/oidc/encode/DefaultTokenContextEncoderProviderTest.java (L67)
MEDIUM:testIncorrectGrantTypecatchesRuntimeException(too broad) and passes for wrong reason:postInitnot called leads to NullPointerException, not IllegalArgumentException. Catch specific exception or callpostInit. - 🟡 services/src/test/java/org/keycloak/protocol/oidc/encode/DefaultTokenContextEncoderProviderTest.java (L38)
MEDIUM:before()bypassespostInitby directly mutating package-private fields. This creates fragile tests; should callpostInitwith mocked session factory or provide a test-specific initializer.
Small things (take or leave)
- 🔵 services/src/main/java/org/keycloak/protocol/oidc/encode/DefaultTokenContextEncoderProviderFactory.java (L42)
LOW: Package-private fieldsgrantsByShortcutsandgrantsToShortcutsbreak encapsulation for testing. Consider a test-only constructor or factory method. - 🔵 services/src/main/java/org/keycloak/protocol/oidc/grants/TokenExchangeGrantTypeFactory.java (L23)
LOW: Inconsistent shortcut pattern — returns literal"te"instead of using a static constant. - 🔵 services/src/main/java/org/keycloak/protocol/oidc/grants/ClientCredentialsGrantTypeFactory.java (L37)
LOW: Inconsistent shortcut pattern — returns literal"cc"instead of using a static constant. - 🔵 services/src/main/java/org/keycloak/protocol/oidc/grants/PermissionGrantTypeFactory.java (L37)
LOW: Inconsistent shortcut pattern — returns literal"pg"instead of using a static constant. - 🔵 services/src/main/java/org/keycloak/protocol/oidc/grants/ResourceOwnerPasswordCredentialsGrantTypeFactory.java (L37)
LOW: Inconsistent shortcut pattern — returns literal"ro"instead of using a static constant. - 🔵 services/src/main/java/org/keycloak/protocol/oidc/grants/PreAuthorizedCodeGrantTypeFactory.java (L57)
LOW: Inconsistent shortcut pattern — returns literal"pc"instead of using a static constant.
FriendlyReviewer detected 3 of 4 golden bugs. The null check duplication (#1) and the inverted substring logic (#2) were both correctly identified with HIGH severity. The broad
RuntimeExceptioncatch (#4) was found and upgraded to MEDIUM. The Javadoc shortcut inconsistency (#3) was missed — FriendlyReviewer treated it as a code consistency issue rather than a documentation problem.The 9 supplementary findings include 3 HIGH-severity security issues that are arguably more important than any golden comment: MTLS HoK token binding is completely absent from
PreAuthorizedCodeGrantTypeandStandardTokenExchangeProvider, violating RFC 8705. The hardcodedSCOPE_OPENIDinPreAuthorizedCodeGrantTypemeans the requested scope is silently ignored — a real functional bug.The inverted
assertEqualsarguments across the entire test file and the fragilebefore()method (mutating package-private fields instead of callingpostInit) show test quality issues that FriendlyReviewer systematically tracks.