application security

Secure video-sharing platform

A full-stack web app for athletes and coaches to share practice and competition video. Built so the browser moves media directly to and from a private S3 bucket through short-lived server-signed pre-signed URLs, credentials never reach the client. Role-based access control, JWT auth in HTTP-only cookies, and bcrypt-hashed passwords handle the rest.

Status complete
Started January 2026
Source GitHub ↗

Problem

There was no good way to share practice and competition video with athletes. Texting compresses the footage and kills quality, email caps attachment sizes, and apps like Snapchat combine the worst of both since the video is lossy and disappears. I wanted one place to upload and download full-quality video with role-based access so each athlete sees only their own footage while coaches can see everyone. It needed to be easy to use and coaches and athlete should not need to worry about the security of their data.

Approach

Every request is authenticated and authorized along the way. Uploads and downloads are handled by the browser using short-lived pre-signed URLs instead of going through the backend.

Control plane — authorize & sign Data plane — media transfer Browser React SPA Express API auth · RBAC · signs URLs IAM key, server-side only SQLite users · roles Amazon S3 private bucket · all public access blocked encrypted at rest (AES-256) Request + JWT cookie Pre-signed URL Verify role + ownership signs URL, 15-min TTL Upload / download — direct to S3, bypassing the backend

Request and authorization flow. The backend authenticates every request and signs a short-lived S3 URL; the video itself moves directly between the browser and the private bucket over HTTPS/TLS, so AWS credentials never reach the client.

Access control

Role-Based Access Control governs three roles. Athletes can view and download only their own footage, but can upload footage for other athletes, coaches can browse, upload, and download footage for every athlete, and admins can do everything a coach can as well as manage users and delete files. Before signing a download or streaming URL, the server checks that the requested object belongs to the user making the request so that no one can get ahold of someone else’s footage by making a guess.

Data protection in transit and at rest

All traffic for the application is served over HTTPS/TLS to protect it while in transit. The S3 bucket blocks public access and objects are encrypted at rest using AES-256 encryption. The pre-signed URLs used expire after 15 minutes, so that intercepted links cannot be saved for later use.

Credential isolation and app hardening

The AWS keys belong to a single IAM user with permissions to just the bucket use for the project and live only in server-side environment variables. Sessions are a JWT stored in an HTTP-only cookie to prevent token theft through XSS. User passwords are bcrypt-hashed, and all database access uses parameterized queries to prevent SQL injection.

Tools

React + Vite

Frontend

Node.js + Express

Backend / API

SQLite

User & role data

AWS S3 (pre-signed URLs)

Private video storage

JWT + bcrypt

Auth & password hashing

AWS IAM

Scoped storage credentials

What I learned

Using RBAC from the start instead of adding it afterwards saved a lot of time and prevented me from needing to redesign and rework the project after getting it working. By making the application secure from the start, it limited the number of things I needed to worry about.

The biggest lesson was that issuing a pre-signed URL is an authorization decision. Scoping every URL to the requesting user's folder prefix and validating key ownership server-side before signing prevents someone with the URL but not the actual user account from accessing footage.

What I would change