feat: secure proof-of-payment download routes with shareId validation
Changes: - Update download links in UI to use shareId instead of locationID - Add shareId validation to per-bill proof download route - Add shareId validation to combined proof download route - Validate TTL before allowing downloads - Extract locationId from shareId using extractShareId helper Security: - Download routes now validate checksum and TTL - Prevents unauthorized access to proof-of-payment files - Returns 404 for invalid/expired share links 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import Link from "next/link";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import { toast } from "react-toastify";
|
||||
import { get } from "http";
|
||||
import { generateShareLink } from "../lib/actions/locationActions";
|
||||
|
||||
export interface LocationCardProps {
|
||||
location: BillingLocation;
|
||||
@@ -33,13 +34,17 @@ export const LocationCard: FC<LocationCardProps> = ({ location, currency }) => {
|
||||
// sum all the paid bill amounts (regardless of who pays)
|
||||
const monthlyExpense = bills.reduce((acc, bill) => bill.paid ? acc + (bill.payedAmount ?? 0) : acc, 0);
|
||||
|
||||
const handleCopyLinkClick = () => {
|
||||
const handleCopyLinkClick = async () => {
|
||||
// copy URL to clipboard
|
||||
const url = `${window.location.origin}/${currentLocale}/share/location/${_id}`;
|
||||
navigator.clipboard.writeText(url);
|
||||
const shareLink = await generateShareLink(_id);
|
||||
|
||||
if(shareLink.error) {
|
||||
toast.error(shareLink.error, { theme: "dark" });
|
||||
} else {
|
||||
navigator.clipboard.writeText(shareLink.shareUrl as string);
|
||||
toast.success(t("link-copy-message"), { theme: "dark" });
|
||||
}
|
||||
|
||||
// use NextJS toast to notiy user that the link was copied
|
||||
toast.success(t("link-copy-message"), { theme: "dark" });
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -130,7 +130,7 @@ export const ViewBillCard: FC<ViewBillCardProps> = ({ location, bill, shareId })
|
||||
proofOfPaymentFilename ? (
|
||||
<div className="mt-3 ml-[-.5rem]">
|
||||
<Link
|
||||
href={`/share/proof-of-payment/per-bill/${locationID}-${billID}/`}
|
||||
href={`/share/proof-of-payment/per-bill/${shareId || locationID}-${billID}/`}
|
||||
target="_blank"
|
||||
className='text-center w-full max-w-[20rem] text-nowrap truncate inline-block'
|
||||
>
|
||||
|
||||
@@ -195,7 +195,7 @@ export const ViewLocationCard: FC<ViewLocationCardProps> = ({ location, userSett
|
||||
attachmentFilename ? (
|
||||
<div className="mt-3 ml-[-.5rem]">
|
||||
<Link
|
||||
href={`/share/proof-of-payment/combined/${_id}/`}
|
||||
href={`/share/proof-of-payment/combined/${shareId || _id}/`}
|
||||
target="_blank"
|
||||
className='text-center w-full max-w-[20rem] text-nowrap truncate inline-block'
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user