Skip to content

feat(security): add signed URL generator (closes #161)#195

Merged
antosubash merged 2 commits into
mainfrom
claude/fix-simple-issues-4JhNS
May 13, 2026
Merged

feat(security): add signed URL generator (closes #161)#195
antosubash merged 2 commits into
mainfrom
claude/fix-simple-issues-4JhNS

Conversation

@antosubash
Copy link
Copy Markdown
Owner

Summary

Implements ISignedUrlGenerator for tamper-proof, expiring URLs (#161).

  • New ISignedUrlGenerator + SignedUrlClaims in framework/SimpleModule.Core/Security.
  • Implementation backed by IDataProtectionProvider (handles key rotation automatically).
  • RequireSignedUrl(purpose?) endpoint filter — returns 403 on tamper / expiry / purpose mismatch, stashes SignedUrlClaims in HttpContext.Items.
  • Canonical signing string is the path plus query (sorted by key+value, percent-encoded), with expires and purpose bound into the signature.
  • Signature comparison via CryptographicOperations.FixedTimeEquals.
  • Registers TimeProvider.System and the generator in SimpleModuleHostExtensions.

Acceptance criteria from #161

  • ISignedUrlGenerator in SimpleModule.Core
  • RequireSignedUrl(purpose?) endpoint filter
  • Constant-time signature comparison
  • Expiration enforcement
  • xUnit tests: tampered query, tampered signature, missing signature, expiry, no-expiry, purpose mismatch / match / isolation, query-order independence, reserved-key rejection, cross-key-ring isolation
  • Email module unsubscribe-link integration sample — deferred to a follow-up so this PR stays scoped

Test plan

  • dotnet test tests/SimpleModule.Core.Tests --filter SignedUrlGeneratorTests → 13/13 pass
  • dotnet test tests/SimpleModule.Core.Tests → 210/210 pass
  • dotnet build framework/SimpleModule.Hosting → clean

Generated by Claude Code

claude added 2 commits May 13, 2026 12:24
Adds ISignedUrlGenerator with HMAC-strength authenticated signing via
IDataProtectionProvider, plus a RequireSignedUrl endpoint filter that
returns 403 on tamper/expiry/purpose mismatch. Signatures are bound to
an optional purpose tag to prevent reuse across endpoints. Validation
uses FixedTimeEquals.
- Reject URLs that carry a purpose when the validator wasn't told to expect
  one (and vice-versa). Previously a purpose-bound token would pass an
  endpoint that didn't specify the purpose, defeating the binding.
- Catch InvalidOperationException from IDataProtector.Unprotect alongside
  CryptographicException / FormatException so malformed signatures
  always 403 instead of 500ing.
- Reject paths containing '?' at sign time so the canonical can never
  diverge between Sign and TryValidate.
- Replace the public string Items key with a typed
  HttpContext.GetSignedUrlClaims() accessor.
- Drop the dead PathString.HasValue ternary; routing guarantees a value.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying simplemodule-website with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2e8bae4
Status: ✅  Deploy successful!
Preview URL: https://a4c7836f.simplemodule-website.pages.dev
Branch Preview URL: https://claude-fix-simple-issues-4jh.simplemodule-website.pages.dev

View logs

@antosubash antosubash marked this pull request as ready for review May 13, 2026 16:47
@antosubash antosubash merged commit 2447492 into main May 13, 2026
6 checks passed
@antosubash antosubash deleted the claude/fix-simple-issues-4JhNS branch May 13, 2026 16:47
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