Skip to content

Add better support for JWT bearer token authentication in openidconnectbearer mode to enable external API calls#9174

Open
tylerjmchugh wants to merge 5 commits intogeonetwork:mainfrom
tylerjmchugh:fix-bearer-auth
Open

Add better support for JWT bearer token authentication in openidconnectbearer mode to enable external API calls#9174
tylerjmchugh wants to merge 5 commits intogeonetwork:mainfrom
tylerjmchugh:fix-bearer-auth

Conversation

@tylerjmchugh
Copy link
Contributor

@tylerjmchugh tylerjmchugh commented Feb 17, 2026

Currently, when users authenticate via JWT bearer tokens with the openidconnectbearer auth type, the application cannot access the original token to pass to external APIs. The previous approach relied on OAuth2AuthenticationToken with OAuth2AuthorizedClientManager, which requires an authorized client in the application. This fails for bearer token users since they are external (never logged into the application directly) and have no authorized client.

Additionally, bearer token authentication is stateless with no server session. The JWT token is lost after parsing into an OAuth2User principal, making it unavailable for subsequent external API calls.

This PR aims to fix this by:

  • Refactoring token parsing to return Spring's native Jwt object instead of Map, preserving the original token throughout the request lifecycle
  • Replacing OAuth2AuthenticationToken with JwtAuthenticationToken, which stores the decoded Jwt containing the original token accessible via getToken().getTokenValue()
  • Updating security utilities to detect bearer token authentication and return the token directly without requiring an authorized client
  • Consolidating authentication type handling (Keycloak, OIDC, OAuth2, JWT) across listeners and criteria with unified username extraction
  • Adding required Spring Security OAuth2 dependencies

This enables the openidconnectbearer auth type to make authenticated external API calls with the user's token.

Checklist

  • I have read the contribution guidelines
  • Pull request provided for main branch, backports managed with label
  • Good housekeeping of code, cleaning up comments, tests, and documentation
  • Clean commit history broken into understandable chucks, avoiding big commits with hundreds of files, cautious of reformatting and whitespace changes
  • Clean commit messages, longer verbose messages are encouraged
  • API Changes are identified in commit messages
  • Testing provided for features or enhancements using automatic tests
  • User documentation provided for new features or enhancements in manual
  • Build documentation provided for development instructions in README.md files
  • Library management using pom.xml dependency management. Update build documentation with intended library use and library tutorials or documentation

username = ((OAuth2User)principal).getAttribute(StandardClaimNames.PREFERRED_USERNAME);
}
else if (authentication instanceof JwtAuthenticationToken) {
username = authentication.getName();
Copy link
Contributor

@xiechangning20 xiechangning20 Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you're trying to get the username but this will actually return the subject of the principal, based on Spring java docs In our case, it will be the uuid from Keycloak
Sth like following might work but I have not tested it
authentication.getToken().getClaim(StandardClaimNames.PREFERRED_USERNAME)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should not be the subject as I set the name myself when I create the authentication in GeonetworkJwtAuthenticationProvider using the 3 arg constructor. The value comes from the extractUsername method which extracts the username from a list of options starting with the value configured in geonetwork.

@ianwallen ianwallen added this to the 4.4.10 milestone Feb 18, 2026
@tylerjmchugh tylerjmchugh marked this pull request as ready for review February 18, 2026 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments