Skip to content

CAMEL-24281: camel-platform-http-main - fail closed when JWT authentication has no issuer or audience - #25187

Merged
oscerd merged 3 commits into
apache:mainfrom
oscerd:fix/CAMEL-24281
Jul 28, 2026
Merged

CAMEL-24281: camel-platform-http-main - fail closed when JWT authentication has no issuer or audience#25187
oscerd merged 3 commits into
apache:mainfrom
oscerd:fix/CAMEL-24281

Conversation

@oscerd

@oscerd oscerd commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

CAMEL-24281

When JWT authentication is enabled on the camel-main embedded HTTP server, JWTAuthenticationConfigurer.buildJwtOptions(...) returns null if neither jwtIssuer nor jwtAudience is configured, and the caller then skips JWTAuthOptions.setJWTOptions(...). The resulting Vert.x JWTAuth is built from the keystore alone, so tokens are only checked for signature and expiry and the iss and aud claims are not validated. Nothing signals this: the server starts normally, so a deployment configured the documented way can be enforcing less than intended.

Change

  • assertIssuerOrAudienceConfigured(...) is called at the top of both configureAuthentication overloads, so the application server and the management server are both covered. It throws IllegalArgumentException when a JWT keystore is configured but neither value is set, naming the properties involved.
  • New jwtAllowMissingIssuerAndAudience option (default false, marked security = "insecure:dev") on HttpServerConfigurationProperties and HttpManagementServerConfigurationProperties, for deployments that genuinely want signature and expiry validation only.

This mirrors the posture camel-oauth already uses, where DefaultOAuthTokenValidationFactory.validateResolvedConfiguration refuses to operate unless an expected audience and issuer are configured (allow-missing-audience and allow-missing-issuer both default to false).

Compatibility

This changes startup behaviour for an existing configuration, so it is documented in the 4.22 upgrade guide together with the opt-out.

jwtIssuer and jwtAudience were only added in 4.21.0 by CAMEL-23525, so the fail-closed behaviour applies to main only. It cannot be backported as-is, because 4.18.x and 4.14.x have no such options to satisfy the requirement. Those branches instead receive the options themselves, so operators there can enforce iss/aud by configuration:

Both are additive and change no existing behaviour.

Tests

JWTIssuerAudienceRequiredMainHttpServerTest (4): startup fails without an issuer or audience and the message names all three properties; the opt-out starts and still serves; a token minted for a different issuer and audience is rejected once both are configured; the opt-out defaults to false on both properties classes.

One existing test config, jwt-auth.properties, is keystore-only and therefore no longer starts. It now sets jwtAllowMissingIssuerAndAudience=true, which keeps JWTAuthenticationMainHttpServerTest testing what it was written to test and exercises the opt-out.

Module suite green (23), camel-main green (231), full reactor build green with generated files committed. Re-verified after merging main, which brought Vert.x 4.5.31.

The component documentation previously described signature and expiry checking as the default with issuer and audience as an optional extra. It now states that without them any unexpired token signed by a trusted key is accepted regardless of issuer or audience.

Review round

  • security = "insecure:dev" added to the opt-out on both properties classes (@gnodet), verified through to the generated metadata.
  • Test now shares one Vertx instance and closes it in @AfterAll instead of creating one per test method (@gnodet).
  • Merged main and resolved the camel-4x-upgrade-guide-4_22.adoc conflict with the CAMEL-24279 entry; both notes kept.

Claude Code on behalf of oscerd

…cation has no issuer or audience

The JWT authenticator was built from the configured keystore alone when neither
jwtIssuer nor jwtAudience was set, because buildJwtOptions returned null and the
caller then skipped setJWTOptions. Tokens were therefore only checked for
signature and expiry, and the iss and aud claims were not validated, with nothing
signalling that to the user.

The embedded server and the management server now fail to start when a JWT
keystore is configured but neither jwtIssuer nor jwtAudience is set. Deployments
that want signature and expiry validation only can set
jwtAllowMissingIssuerAndAudience to true, mirroring the allow-missing-audience and
allow-missing-issuer options that camel-oauth already exposes.

The check is applied to both configureAuthentication overloads, since the
application server and the management server share this configurer.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Andrea Cosentino <ancosen@gmail.com>

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Well-designed security hardening — fail closed when JWT authentication has no issuer or audience configured.

What I checked:

  • assertIssuerOrAudienceConfigured() is called at the top of both configureAuthentication overloads (application server and management server), before the JWTAuth is constructed — fails fast at startup
  • jwtAllowMissingIssuerAndAudience defaults to false on both properties classes, following the project's "denied unless opted in" security posture
  • The error message names all three properties (jwtIssuer, jwtAudience, jwtAllowMissingIssuerAndAudience) so the operator knows exactly what to set
  • Test coverage: startup failure without issuer/audience, opt-out works, foreign-token rejection with issuer+audience configured, default value verified
  • Existing jwt-auth.properties adapted with the opt-out to keep JWTAuthenticationMainHttpServerTest testing what it was designed to test
  • Upgrade guide entry present with the opt-out, component docs updated, generated files regenerated
  • Conventions followed: AssertJ, package-private test class, @Metadata(label = "security")

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Well-crafted security-hardening PR that correctly implements fail-closed JWT authentication. The fail-closed logic in assertIssuerOrAudienceConfigured is correct: it runs eagerly at configuration time, the IllegalArgumentException propagates cleanly to main.start(), and the error message correctly names the relevant properties without leaking sensitive values.

Two observations about the @Metadata annotation on the new opt-out flag, and two minor test observations below.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of Guillaume Nodet

@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • catalog/camel-catalog
  • components/camel-platform-http-main
  • core/camel-main
  • docs

ℹ️ Dependent modules were not tested because the total number of affected modules exceeded the threshold (50). Use the test-dependents label to force testing all dependents.


🔬 Scalpel shadow comparison — Scalpel: 63 tested, 27 compile-only — current: 61 all tested

Maveniverse Scalpel detected 90 affected modules (current approach: 61).

⚠️ Modules only in Scalpel (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

Skip-tests mode would test 63 modules (4 direct + 59 downstream), skip tests for 27 (generated code, meta-modules)

Modules Scalpel would test (63)
  • camel-a2a
  • camel-as2
  • camel-catalog
  • camel-clickup
  • camel-consul
  • camel-core-all
  • camel-csv
  • camel-fhir
  • camel-groovy
  • camel-hazelcast
  • camel-hl7
  • camel-ibm-watsonx-ai
  • camel-iso8583
  • camel-java-joor-dsl
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-jetty
  • camel-jsonpath
  • camel-kamelet
  • camel-kamelet-main-support
  • camel-knative-http
  • camel-launcher-container
  • camel-mail
  • camel-mail-microsoft-oauth
  • camel-main
  • camel-maven-plugin
  • camel-micrometer-prometheus
  • camel-microprofile-health
  • camel-netty
  • camel-netty-http
  • camel-oauth
  • camel-observability-services
  • camel-once
  • camel-openapi-validator
  • camel-platform-http-main
  • camel-platform-http-vertx
  • camel-plc4x
  • camel-pubnub
  • camel-quickfix
  • camel-rest-openapi
  • camel-restdsl-openapi-plugin
  • camel-salesforce
  • camel-salesforce-codegen
  • camel-salesforce-maven-plugin
  • camel-spring-main
  • camel-syslog
  • camel-telegram
  • camel-test-main-junit5
  • camel-test-main-junit6
  • camel-vertx-websocket
  • camel-webhook
  • camel-whatsapp
  • camel-xml-io-dsl
  • camel-xml-jaxb-dsl
  • camel-xml-jaxb-dsl-test-definition
  • camel-xml-jaxb-dsl-test-spring
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
  • camel-zookeeper
  • docs
Modules with tests skipped (27)
  • apache-camel
  • camel-allcomponents
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

Build reactor — dependencies compiled but only changed modules were tested (90 modules)
  • Camel :: AI :: A2A
  • Camel :: AS2 :: Component
  • Camel :: All Components Sync point
  • Camel :: All Core Sync point
  • Camel :: Assembly
  • Camel :: CSV
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Lucene (deprecated)
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: ClickUp
  • Camel :: Component DSL
  • Camel :: Consul
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: FHIR
  • Camel :: Groovy
  • Camel :: HL7
  • Camel :: HazelCast
  • Camel :: IBM :: watsonx.ai
  • Camel :: ISO-8583
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: JSon Path
  • Camel :: Java DSL with jOOR
  • Camel :: Jetty
  • Camel :: Kamelet
  • Camel :: Kamelet Main
  • Camel :: Kamelet Main :: Support
  • Camel :: Knative HTTP
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: Mail
  • Camel :: Mail :: Microsoft OAuth
  • Camel :: Main
  • Camel :: Maven Plugins :: Camel Maven Plugin
  • Camel :: Maven Plugins :: OpenApi REST DSL Generator
  • Camel :: MicroProfile :: Health
  • Camel :: Micrometer :: Prometheus
  • Camel :: Netty
  • Camel :: Netty HTTP
  • Camel :: OAuth
  • Camel :: Observability Services
  • Camel :: Once
  • Camel :: OpenAPI :: Validator
  • Camel :: PLC4X
  • Camel :: Platform HTTP :: Main
  • Camel :: Platform HTTP :: Vert.x
  • Camel :: PubNub
  • Camel :: QuickFIX/J
  • Camel :: REST OpenApi
  • Camel :: Salesforce
  • Camel :: Salesforce :: CodeGen
  • Camel :: Salesforce :: Maven Plugin
  • Camel :: Spring Main
  • Camel :: Syslog
  • Camel :: Telegram
  • Camel :: Test :: Main :: JUnit5
  • Camel :: Test :: Main :: JUnit6
  • Camel :: Vert.x :: WebSocket
  • Camel :: Webhook
  • Camel :: Whatsapp
  • Camel :: XML DSL Jaxb :: Test :: Definition
  • Camel :: XML DSL Jaxb :: Test :: Spring
  • Camel :: XML DSL with camel-xml-io
  • Camel :: XML DSL with camel-xml-jaxb
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin
  • Camel :: Zookeeper

⚙️ View full build and test results

oscerd and others added 2 commits July 28, 2026 14:19
# Conflicts:
#	docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
Mark jwtAllowMissingIssuerAndAudience with security = "insecure:dev" on both
HttpServerConfigurationProperties and HttpManagementServerConfigurationProperties,
so the security policy framework can report on it when the flag is enabled. The
management properties class already uses that marker for devConsoleEnabled,
uploadEnabled, downloadEnabled and sendEnabled.

Share a single Vertx instance across the tests and close it afterwards, rather
than creating one per test method and leaving it open.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Andrea Cosentino <ancosen@gmail.com>
@oscerd oscerd added the enhancement New feature or request label Jul 28, 2026
@oscerd oscerd self-assigned this Jul 28, 2026
@oscerd oscerd added this to the 4.22.0 milestone Jul 28, 2026
@oscerd
oscerd merged commit 4dbb938 into apache:main Jul 28, 2026
6 checks passed
@oscerd

oscerd commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Two follow-up notes on the review round, beyond the three inline replies.

On insecure:dev: applied to both properties classes as suggested, and it propagates correctly into the generated metadata for camel.server.jwtAllowMissingIssuerAndAudience and camel.management.jwtAllowMissingIssuerAndAudience. Worth flagging though that design/security.adoc defines insecure:dev as "development/debug features that should not be enabled in production" (devConsoleEnabled, debugBreakpoints), whereas this flag weakens authentication rather than enabling a dev feature. None of the three valid values (insecure:ssl, insecure:serialization, insecure:dev) really covers auth-weakening options. insecure:dev is the closest fit and matches what this file already does, so it is what I used, but a dedicated insecure:auth category may be the better long-term answer. That would be a separate change across the annotation, the docs and SecurityUtils generation, so I have not attempted it here.

Re-verified against the Vert.x upgrade: main moved from Vert.x 4.5.28 to 4.5.31 while this PR was open. Since the change depends on how Vert.x applies JWTOptions claim checks, I merged main and re-ran rather than assuming: module suite is green (23 tests) on 4.5.31, behaviour unchanged. The merge also resolved a conflict in camel-4x-upgrade-guide-4_22.adoc against the CAMEL-24279 entry that landed in the meantime; both notes are kept.

Full reactor build mvn clean install -DskipTests green, no regenerated-file drift.

Claude Code on behalf of oscerd

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants