Skip to content

feat: configurable idempotency key validation (max length + pattern) - Spring boot starter - #6

Open
ashudhanda wants to merge 1 commit into
ravocode:mainfrom
ashudhanda:feat/key-validation
Open

feat: configurable idempotency key validation (max length + pattern) - Spring boot starter#6
ashudhanda wants to merge 1 commit into
ravocode:mainfrom
ashudhanda:feat/key-validation

Conversation

@ashudhanda

Copy link
Copy Markdown

Implements the key-validation half of #2 in the Spring Boot starter.

What changed

Two new configuration properties under avoonce.idempotency.* (IdempotencyProperties):

avoonce:
  idempotency:
    key-pattern: "^[a-fA-F0-9\\-]{36}$"  # optional; disabled by default
    max-key-length: 128                    # default 255
  • key-pattern — optional regex the key must fully match (e.g. UUID format, as suggested in the issue). null/blank (the default) disables it, so existing setups are unaffected. The pattern is compiled once in the IdempotencyFilter constructor, not per request.
  • max-key-length — keys longer than this are rejected with HTTP 400 before touching the store, preventing abuse via overly long keys. Default 255.
  • Blank/whitespace-only keys are now also rejected with 400 — previously an all-spaces header would flow into the state machine as a "real" key.

Validation runs in IdempotencyFilter after the existing null/enforce check and before the state machine is engaged, so an invalid key never acquires a lock or writes a record. The 400 body names the configured header and the specific violation.

Tests

Added to IdempotencyFilterTest:

  • blank key → 400, controller never executes
  • key over max-key-length → 400; key within the limit → 201 (normal flow)
  • key failing key-pattern → 400; UUID-shaped key → 201 (normal flow)

Not in this PR

The second part of the issue — sourcing the key from a query param or JSON-body field instead of a header — is a larger, separable change (it touches CachedBodyHttpServletRequest buffering order and the JAX-RS adapter for parity). Happy to follow up with it in a separate PR if the header-first shape here looks right to you; the same validation would then apply to whichever source is configured.

Refs #2

@raghavgopal

Copy link
Copy Markdown
Member

Implements the key-validation half of #2 in the Spring Boot starter.

What changed

Two new configuration properties under avoonce.idempotency.* (IdempotencyProperties):

avoonce:
  idempotency:
    key-pattern: "^[a-fA-F0-9\\-]{36}$"  # optional; disabled by default
    max-key-length: 128                    # default 255
  • key-pattern — optional regex the key must fully match (e.g. UUID format, as suggested in the issue). null/blank (the default) disables it, so existing setups are unaffected. The pattern is compiled once in the IdempotencyFilter constructor, not per request.
  • max-key-length — keys longer than this are rejected with HTTP 400 before touching the store, preventing abuse via overly long keys. Default 255.
  • Blank/whitespace-only keys are now also rejected with 400 — previously an all-spaces header would flow into the state machine as a "real" key.

Validation runs in IdempotencyFilter after the existing null/enforce check and before the state machine is engaged, so an invalid key never acquires a lock or writes a record. The 400 body names the configured header and the specific violation.

Tests

Added to IdempotencyFilterTest:

  • blank key → 400, controller never executes
  • key over max-key-length → 400; key within the limit → 201 (normal flow)
  • key failing key-pattern → 400; UUID-shaped key → 201 (normal flow)

Not in this PR

The second part of the issue — sourcing the key from a query param or JSON-body field instead of a header — is a larger, separable change (it touches CachedBodyHttpServletRequest buffering order and the JAX-RS adapter for parity). Happy to follow up with it in a separate PR if the header-first shape here looks right to you; the same validation would then apply to whichever source is configured.

Refs #2

Thanks for the PR, I will get back after reviewing.

@raghavgopal raghavgopal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just a minor improvement on validation.

*
* @param maxKeyLength the maximum key length to use
*/
public void setMaxKeyLength(int maxKeyLength) { this.maxKeyLength = maxKeyLength; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Setter for maxKeyLength seems to accepting any number. It needs to have defensive validation against negative or zero value.

@raghavgopal raghavgopal changed the title feat: configurable idempotency key validation (max length + pattern) feat: configurable idempotency key validation (max length + pattern) - Spring boot starter Aug 17, 2026
@raghavgopal

raghavgopal commented Aug 17, 2026

Copy link
Copy Markdown
Member

@ashudhanda Sorry for the delay reviewing your changes. Thank you for contributing, please feel free to implement the key (from the header) validation for JAX-RS in a separate PR.

Also its worth raising another PR to handle the customization of the key placement, perhaps I can carve out a separate issue altogether to keep things clean and trackable.

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.

2 participants