<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Support\Facades\URL;
class DownloadableFile extends Model
{
protected $fillable = [
'product_id', 'disk', 'path', 'original_name', 'size_bytes', 'mime_type',
];
protected $hidden = ['disk', 'path'];
public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
}
public function temporaryUrl(int $minutes = 10): string
{
return URL::temporarySignedRoute(
'downloads.fetch',
now()->addMinutes($minutes),
['file' => $this->getKey()]
);
}
public function humanSize(): string
{
$mb = $this->size_bytes / 1048576;
return number_format($mb, 2) . ' MB';
}
}
<?php
namespace App\Policies;
use App\Models\DownloadableFile;
use App\Models\User;
class DownloadPolicy
{
public function download(User $user, DownloadableFile $file): bool
{
return $user->orders()
->where('status', 'completed')
->whereHas('items', function ($query) use ($file) {
$query->where('product_id', $file->product_id);
})
->exists();
}
}
<?php
namespace App\Http\Controllers;
use App\Models\DownloadableFile;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
class DownloadController extends Controller
{
public function __construct()
{
$this->middleware(['auth', 'signed']);
}
public function fetch(DownloadableFile $file): StreamedResponse
{
$this->authorize('download', $file);
$disk = Storage::disk($file->disk);
abort_unless($disk->exists($file->path), 404, 'File is no longer available.');
return $disk->download(
$file->path,
$file->original_name,
['Content-Type' => $file->mime_type]
);
}
}
<?php
use App\Http\Controllers\DownloadController;
use Illuminate\Support\Facades\Route;
Route::middleware(['auth', 'signed'])
->get('/downloads/{file}', [DownloadController::class, 'fetch'])
->name('downloads.fetch');
This snippet shows how a Laravel application ties downloadable files to a completed purchase and then serves them without ever exposing the underlying storage path. The core idea is that a raw storage location (an S3 key or a local disk path) should never appear in a link the browser can bookmark or share. Instead the app hands out a short-lived, cryptographically signed URL that proves both who may download and for how long.
In DownloadableFile model the pivot-like relationship is expressed: each row belongs to a product and carries the private disk and path plus display metadata. The accessor temporaryUrl() is the heart of the design — it calls URL::temporarySignedRoute, baking the file id and an expiry timestamp into a signature. Because the route is signed, tampering with the id or expiry invalidates the URL, so no server-side token table is required for expiry.
DownloadPolicy enforces ownership independently of the signature. A signed URL only guarantees the link is intact and unexpired; it does not prove the current user paid. The download method checks that the authenticated user has a completed order line containing the file's product, which closes the hole where a valid signed link could be forwarded to a non-buyer.
DownloadController wires it together. The signed middleware rejects malformed or expired URLs before any code runs, then authorize('download', $file) applies the policy. Only after both gates pass does it stream the bytes with Storage::disk(...)->download(...), which sets Content-Disposition and streams rather than loading the whole file into memory — important for large binaries. Using download on a private disk means the file itself is never publicly readable.
routes/web.php registers the named route downloads.fetch that temporaryUrl() references, guarded by auth and signed. The trade-off of signed routes over stored tokens is statelessness: nothing to clean up, but the expiry window is fixed at generation time and cannot be revoked early without rotating APP_KEY or adding a revocation check. For most purchase-download flows the combination of a signed URL plus a policy is the right balance of security and simplicity.
Related snips
payload = {
sub: user.id,
iss: 'https://auth.example.com',
aud: 'codesnips-api',
exp: 15.minutes.from_now.to_i,
iat: Time.now.to_i,
JWT issuance and verification without common footguns
require "csv"
class PeopleCsvStream
include Enumerable
HEADERS = %w[id full_name email signed_up_at plan].freeze
Resilient CSV Export as a Streamed Response
#!/usr/bin/env bash
set -euo pipefail
export VAULT_ADDR="https://vault.internal:8200"
export VAULT_TOKEN="${VAULT_TOKEN:?missing VAULT_TOKEN}"
Secrets management with environment isolation and Vault
<?php
namespace App\Providers;
use App\Contracts\PaymentGateway;
use App\Services\StripePaymentGateway;
Laravel service container and dependency injection
import { randomBytes, createHash } from "crypto";
import jwt from "jsonwebtoken";
import { RefreshTokenStore } from "./store";
const ACCESS_SECRET = process.env.ACCESS_SECRET!;
const ACCESS_TTL = "15m";
JWT access + refresh token rotation (conceptual)
package files
import (
"context"
"time"
Presigned S3 upload URLs (AWS SDK v2)
Share this code
Here's the card — post it anywhere.