From 8852ec148314a51f558b97c8fa64aba98e168de9 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Tue, 7 Apr 2026 09:16:42 +0200 Subject: [PATCH] feat: add diagnostic logging for OIDC scope and role extraction Logs received scopes, rolesClaim path, extracted roles, and all claim keys at each stage of the OIDC auth flow to help debug Logto integration. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../server/app/security/JwtAuthenticationFilter.java | 4 ++++ .../com/cameleer3/server/app/security/OidcAuthController.java | 2 ++ .../com/cameleer3/server/app/security/OidcTokenExchanger.java | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtAuthenticationFilter.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtAuthenticationFilter.java index a97f760a..66d4e231 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtAuthenticationFilter.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/JwtAuthenticationFilter.java @@ -111,7 +111,10 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { */ private List extractRolesFromScopes(Jwt jwt) { String scopeStr = jwt.getClaimAsString("scope"); + log.info("OIDC access token scopes: '{}', subject={}, all claims={}", + scopeStr, jwt.getSubject(), jwt.getClaims().keySet()); if (scopeStr == null || scopeStr.isBlank()) { + log.warn("OIDC token has no 'scope' claim — defaulting to VIEWER"); return List.of("VIEWER"); } for (String scope : scopeStr.split(" ")) { @@ -120,6 +123,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { if ("OPERATOR".equals(normalized)) return List.of("OPERATOR"); if ("VIEWER".equals(normalized)) return List.of("VIEWER"); } + log.warn("OIDC scopes '{}' contain no recognized role — defaulting to VIEWER", scopeStr); return List.of("VIEWER"); } diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/OidcAuthController.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/OidcAuthController.java index bb826a11..e1b88c25 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/OidcAuthController.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/OidcAuthController.java @@ -173,6 +173,8 @@ public class OidcAuthController { private void syncOidcRoles(String userId, List oidcRoles, OidcConfig config) { List roleNames = !oidcRoles.isEmpty() ? oidcRoles : config.defaultRoles(); + log.info("syncOidcRoles: userId={}, oidcRoles={}, defaultRoles={}, using={}", + userId, oidcRoles, config.defaultRoles(), roleNames); // Resolve desired role IDs from OIDC scopes Set desired = new HashSet<>(); diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/OidcTokenExchanger.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/OidcTokenExchanger.java index f811cfa8..44e9ed2d 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/OidcTokenExchanger.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/security/OidcTokenExchanger.java @@ -111,7 +111,8 @@ public class OidcTokenExchanger { List roles = extractRoles(claims, config.rolesClaim()); - log.info("OIDC user authenticated: id={}, email={}", subject, email); + log.info("OIDC user authenticated: id={}, email={}, rolesClaim='{}', extractedRoles={}, allClaims={}", + subject, email, config.rolesClaim(), roles, claims.getClaims().keySet()); return new OidcUserInfo(subject, email != null ? email : "", name != null ? name : "", roles, idTokenStr); }