Background
There is no first-class way to mint a temporary signed URL for downloads, unsubscribes, password-reset links, file shares, etc. Modules currently roll their own ad-hoc tokens. Laravel exposes URL::temporarySignedRoute('verification.verify', now()->addMinutes(60), [...]) plus middleware that validates the signature.
Motivation
- Safer, cheaper than DB-backed tokens for one-shot links
- Works without a session / login (perfect for mail links)
- Avoids per-module reinventions and the bugs they bring
Design sketch
Add to SimpleModule.Core:
publicinterfaceISignedUrlGenerator{stringSign(stringpath,IDictionary<string,string?>?query=null,DateTimeOffset?expiresAt=null,string?purpose=null);boolTryValidate(HttpRequestrequest,outSignedUrlClaimsclaims);}publicsealedrecordSignedUrlClaims(stringPath,string?Purpose,DateTimeOffset?ExpiresAt);Implementation:
- HMAC-SHA256 over
path?canonicalized-query&expires={unix}&purpose={tag} using IDataProtectionProvider-rooted key (rotates with the data-protection ring) - Signature appended as
?signature=...&expires=...&purpose=... [ValidateSignedUrl] endpoint filter (or extension on RouteHandlerBuilder) verifies and short-circuits 403 on failure- Optional
purpose binds the signature to a specific use case (e.g. purpose=invoice-download) so the same signed URL cannot be replayed against another endpoint
Acceptance criteria
References
Background
There is no first-class way to mint a temporary signed URL for downloads, unsubscribes, password-reset links, file shares, etc. Modules currently roll their own ad-hoc tokens. Laravel exposes
URL::temporarySignedRoute('verification.verify', now()->addMinutes(60), [...])plus middleware that validates the signature.Motivation
Design sketch
Add to
SimpleModule.Core:Implementation:
path?canonicalized-query&expires={unix}&purpose={tag}usingIDataProtectionProvider-rooted key (rotates with the data-protection ring)?signature=...&expires=...&purpose=...[ValidateSignedUrl]endpoint filter (or extension onRouteHandlerBuilder) verifies and short-circuits 403 on failurepurposebinds the signature to a specific use case (e.g.purpose=invoice-download) so the same signed URL cannot be replayed against another endpointAcceptance criteria
ISignedUrlGeneratorinSimpleModule.CoreRequireSignedUrl(purpose?)expiresReferences