Secure document sharing for Box folders via QR codes. Generate QR codes that link to Box folder content, accessible only to users with verified email addresses on approved domains.
- Admin connects to Box via OAuth2 and creates a "share" — specifying a Box folder ID and one or more allowed email domains.
- A QR code is generated containing a cryptographically signed URL pointing to the SecureQR application.
- Users scan the QR code, enter their email, and receive a magic link (passwordless authentication).
- If the user's email domain is authorised, they see the Box folder contents rendered via Box UI Elements — powered by a downscoped token from the admin's Box account.
Users never need a Box account. Access is controlled entirely through email domain verification.
- Docker and Docker Compose
- A Box Developer OAuth2 application (Standard OAuth 2.0)
- An SMTP server for sending magic link emails (or use console output in development)
# Clone and enter the project
cd secureqr
# Create your environment file
cp .env.example .envEdit .env with your credentials:
# Generate with: openssl rand -hex 32
APP_SECRET=<64-char-hex-string>
# Admin password (min 12 characters)
ADMIN_PASSWORD=<strong-password>
# From Box Developer Console
BOX_CLIENT_ID=<your-client-id>
BOX_CLIENT_SECRET=<your-client-secret>
# Your public-facing URL
BASE_URL=http://localhost:3000
# SMTP (optional in development — emails print to console)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=<user>
SMTP_PASS=<pass>
SMTP_FROM=noreply@yourdomain.comStart the application:
docker compose up --buildThen open http://localhost:3000.
- Go to the Box Developer Console and create a new Custom App.
- Select User Authentication (OAuth 2.0) as the authentication method.
- Under Configuration, set the Redirect URI to:
(Replace with your
http://localhost:3000/auth/box/callbackBASE_URLin production.) - Copy the Client ID and Client Secret into your
.envfile. - In the admin dashboard, click Connect to Box to complete the OAuth flow.
- Navigate to
/admin/loginand sign in with yourADMIN_PASSWORD. - Click Connect to Box to authorise the application (one-time setup).
- Click Create New Share:
- Enter the numeric Box Folder ID (visible in the Box web URL).
- Enter one or more allowed email domains (comma-separated, e.g.
acme.com, partner.org). - Optionally add a label for the share.
- Download or display the generated QR code. The share URL is also available for direct distribution.
- Scan the QR code (or open the share URL).
- Enter your email address.
- Check your inbox for the magic link (expires in 15 minutes, single-use).
- Click the link to view the shared Box folder.
| Variable | Required | Default | Description |
|---|---|---|---|
APP_SECRET |
Yes | — | Master secret for key derivation (min 32 chars, use openssl rand -hex 32) |
ADMIN_PASSWORD |
Yes | — | Admin login password (min 12 chars) |
BASE_URL |
No | http://localhost:3000 |
Public-facing URL (must be HTTPS in production) |
PORT |
No | 3000 |
Server listen port |
BOX_CLIENT_ID |
Yes* | — | Box OAuth2 client ID |
BOX_CLIENT_SECRET |
Yes* | — | Box OAuth2 client secret |
SMTP_HOST |
No | — | SMTP server hostname (if unset, emails log to console) |
SMTP_PORT |
No | 587 |
SMTP server port |
SMTP_SECURE |
No | false |
Use TLS for SMTP connection |
SMTP_USER |
No | — | SMTP authentication username |
SMTP_PASS |
No | — | SMTP authentication password |
SMTP_FROM |
No | noreply@secureqr.local |
Sender address for magic link emails |
MAGIC_LINK_TTL_MINUTES |
No | 15 |
Magic link expiry time |
MAGIC_LINK_MAX_PER_EMAIL |
No | 5 |
Max magic links per email per TTL window |
SESSION_MAX_AGE_HOURS |
No | 2 |
User session duration |
NODE_ENV |
No | development |
Set to production for secure cookies and HTTPS enforcement |
*Required to use Box features. The app starts without them but Box connect will fail.
- Key derivation: All keys are derived from a single
APP_SECRETusing HKDF-SHA256 (RFC 5869) with purpose-specific info labels, ensuring domain separation between encryption, signing, session, and CSRF keys. - Token encryption: Box OAuth tokens are encrypted at rest with AES-256-GCM (96-bit random IV, 128-bit auth tag).
- QR URL signing: Share URLs are signed with HMAC-SHA256 using a purpose-derived key. Signatures are verified with constant-time comparison.
- Magic link tokens: 256-bit random tokens. Only SHA-256 hashes are stored in the database. Single-use, time-limited.
- Admin password: Verified with bcrypt (cost factor 12).
- Domain-based authorisation re-validated on every request (not just at login).
- Session fixation protection via
session.regenerate()on all login flows. - OAuth state parameter prevents CSRF on the Box connect flow.
- Anti-enumeration: rejected email domains receive the same response as accepted ones.
- Magic link requests: 5 per IP per 15 minutes.
- Magic link verification: 10 per IP per 15 minutes.
- Admin login: 5 per IP per 15 minutes.
- Helmet.js security headers (HSTS, X-Frame-Options, X-Content-Type-Options, etc.).
- Content Security Policy tuned for Box UI Elements CDN.
- CSRF double-submit cookie on all state-changing endpoints.
httpOnly,sameSite=lax, andsecure(production) session cookies.
All security-relevant actions are logged to the audit_log table:
- Admin login / logout / failed attempts
- Box OAuth connect and token refresh
- Share creation and deactivation
- Magic link creation, verification, and domain rejections
- Viewer folder access
# Install dependencies
npm install
# Start with auto-reload
npm run devWithout SMTP configured, magic link emails are printed to the console.
src/
├── app.js # Express setup, middleware chain
├── server.js # HTTP listener
├── config.js # Environment validation
├── db/
│ ├── connection.js # SQLite (WAL mode, foreign keys)
│ └── migrations.js # Schema: box_tokens, shares, users, magic_links, audit_log
├── middleware/
│ ├── adminAuth.js # Admin session guard
│ ├── viewerAuth.js # Viewer session + domain re-check
│ ├── csrf.js # Double-submit cookie CSRF
│ └── rateLimiter.js # express-rate-limit configs
├── routes/
│ ├── admin.js # Dashboard, share CRUD, QR generation
│ ├── auth.js # Box OAuth callback, magic link flow
│ └── viewer.js # Folder view, downscoped token API
├── services/
│ ├── crypto.js # HKDF, AES-256-GCM, HMAC-SHA256
│ ├── boxService.js # Box SDK, OAuth, token storage, downscoping
│ ├── qrService.js # QR generation, URL signing
│ ├── tokenService.js # Magic link lifecycle
│ ├── emailService.js # Nodemailer wrapper
│ └── audit.js # Audit log writes
└── views/ # EJS templates
Private — all rights reserved.