Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
d1dbde9
chore(frontend): add eslint v9 config and fix lint issues
AdrianAcala Jan 17, 2026
20ef4ee
feat: implement basic authentication system
AdrianAcala Jan 17, 2026
1a52fe8
feat(auth): enhance authentication system with multi-user support and…
AdrianAcala Jan 18, 2026
15ac634
feat(auth): add password reset functionality and user model update
AdrianAcala Jan 18, 2026
260a898
test: stabilize e2e auth and rate limits
AdrianAcala Jan 19, 2026
5d819b0
Bump diff from 5.2.0 to 5.2.2 in /frontend
dependabot[bot] Jan 20, 2026
7ea1c3e
Bump qs from 6.14.0 to 6.14.1 in /backend
dependabot[bot] Jan 14, 2026
08d1479
Bump react-router and react-router-dom in /frontend
dependabot[bot] Jan 9, 2026
77c2291
Fix: Save complete app state (#40)
RAY-EZ Jan 20, 2026
865285f
fix: sync pasted/uploaded images across collaborating tabs (#36)
AdrianAcala Jan 20, 2026
af07a73
feat(auth): enhance authentication system with login attempt tracking…
AdrianAcala Jan 21, 2026
f7c9a1a
chore(tests): enable server start during end-to-end tests
AdrianAcala Jan 21, 2026
9170930
Bump lodash-es from 4.17.21 to 4.17.23 in /frontend
dependabot[bot] Jan 22, 2026
5d29cd9
Bump lodash from 4.17.21 to 4.17.23 in /frontend
dependabot[bot] Jan 23, 2026
d9013b8
feat(auth): add user authentication database schema
D35P4C1T0 Jan 24, 2026
78ab52b
feat(security): add database schema for security features
D35P4C1T0 Jan 24, 2026
381dd95
feat(config): add feature flags for optional security features
D35P4C1T0 Jan 24, 2026
b175706
feat(auth): add authentication middleware and utilities
D35P4C1T0 Jan 24, 2026
b6e9514
feat(auth): add authentication endpoints (login, register, refresh, me)
D35P4C1T0 Jan 24, 2026
2998fad
feat(security): add audit logging utility
D35P4C1T0 Jan 24, 2026
29af9fa
feat(backend): integrate authentication and user isolation
D35P4C1T0 Jan 24, 2026
f1a1ff3
feat(frontend): add authentication context and API client
D35P4C1T0 Jan 24, 2026
5f47654
feat(frontend): add login and register pages
D35P4C1T0 Jan 24, 2026
b834f77
feat(frontend): add password reset pages
D35P4C1T0 Jan 24, 2026
112d58a
feat(frontend): add profile page for user management
D35P4C1T0 Jan 24, 2026
cbe83ef
feat(frontend): add select all button to Dashboard
D35P4C1T0 Jan 24, 2026
f6e337a
feat(frontend): add auto-hide header to Editor
D35P4C1T0 Jan 24, 2026
9c6b7dd
test: add tests for audit logging utility
D35P4C1T0 Jan 24, 2026
804adb7
docs: add FORK.md with feature summary
D35P4C1T0 Jan 24, 2026
9fe3a21
chore: update tests and configuration for auth integration
D35P4C1T0 Jan 24, 2026
4f53b89
chore: add dependencies for authentication features
D35P4C1T0 Jan 24, 2026
7dfa69d
fix export source and verisoning
ZimengXiong Jan 30, 2026
0d1fe8e
Bump lodash from 4.17.21 to 4.17.23 in /backend
dependabot[bot] Feb 2, 2026
fd3b972
merge: bring main into pre-release
ZimengXiong Feb 6, 2026
700e153
merge: pull PR48 auth and UX into pre-release
ZimengXiong Feb 6, 2026
75a1f11
feat(auth): consolidate multi-user auth and admin controls
ZimengXiong Feb 6, 2026
7a54123
fix(export): include excalidraw source/version metadata
ZimengXiong Feb 6, 2026
d68fe6a
fix(auth): stabilize refresh expiry and frontend URL handling
ZimengXiong Feb 6, 2026
302d9bd
merge(pr): record PR #41 on pre-release
ZimengXiong Feb 6, 2026
da299d0
merge(pr): record PR #44 on pre-release
ZimengXiong Feb 6, 2026
bc13cc3
merge(pr): record PR #46 on pre-release
ZimengXiong Feb 6, 2026
887818c
merge(pr): record PR #47 on pre-release
ZimengXiong Feb 6, 2026
d832e55
merge(pr): record PR #52 on pre-release
ZimengXiong Feb 6, 2026
dd966f6
merge(pr): record PR #51 on pre-release
ZimengXiong Feb 6, 2026
40a645b
chore(deps): apply dependabot updates
ZimengXiong Feb 6, 2026
7977a3e
feat(auth): default to single-user mode with enable toggle
ZimengXiong Feb 6, 2026
b075a0c
fix(dev): avoid auth redirect when backend/schema missing
ZimengXiong Feb 6, 2026
2e370f9
fix(dev): reset legacy dev.db and apply migrations
ZimengXiong Feb 6, 2026
e4941ad
fix(dev): avoid native deps in predev migrate
ZimengXiong Feb 6, 2026
1e61702
Add admin password reset flow
ZimengXiong Feb 6, 2026
0253ebb
admin dashboard
ZimengXiong Feb 6, 2026
5e782e4
fix: scope drawings cache by userId and add Socket.io authentication
Copilot Feb 6, 2026
ef75f9e
test: add user data sandboxing security tests
Copilot Feb 6, 2026
94694de
fix: address code review feedback - add error handling and fix import…
Copilot Feb 6, 2026
01fda32
test(import): add legacy import compatibility coverage
ZimengXiong Feb 6, 2026
f462b2e
minor UI fixes
ZimengXiong Feb 7, 2026
08135ee
fix test failures, new export/backup solutions
ZimengXiong Feb 7, 2026
734f0a2
fix graphQL
ZimengXiong Feb 7, 2026
ea06cd9
fix graphQL
ZimengXiong Feb 7, 2026
7aa33a1
graph QL
ZimengXiong Feb 7, 2026
f214e4f
Ensure non multi-user flow stays
ZimengXiong Feb 7, 2026
bbb23ca
chore: pre-release v0.4.0-dev
ZimengXiong Feb 7, 2026
06f4c0f
remove dev dependencies from development containers
ZimengXiong Feb 7, 2026
26017fa
fix JWT secret
ZimengXiong Feb 7, 2026
812f1cb
chore: pre-release v0.4.1-dev
ZimengXiong Feb 7, 2026
8161a56
chore: pre-release v0.4.1-dev
ZimengXiong Feb 7, 2026
173c050
fix HTTPS reuqirement when frontend URL is nto HTTPS
ZimengXiong Feb 7, 2026
2e74d2a
chore: pre-release v0.4.2-dev
ZimengXiong Feb 7, 2026
154dcbb
update resopnsiveness hamburger
ZimengXiong Feb 7, 2026
a366acf
chore: pre-release v0.4.3-dev
ZimengXiong Feb 7, 2026
f20412c
separate debounced autosave
ZimengXiong Feb 7, 2026
8fcca43
chore: pre-release v0.4.4-dev
ZimengXiong Feb 7, 2026
c40a5f4
fix colliding drawing IDs
ZimengXiong Feb 7, 2026
dd0f381
chore: pre-release v0.4.5-dev
ZimengXiong Feb 7, 2026
de254d4
concurrency
ZimengXiong Feb 7, 2026
02736d6
chore: pre-release v0.4.6-dev
ZimengXiong Feb 7, 2026
2aa749a
prevent preview updates from overwriting drawings
ZimengXiong Feb 7, 2026
35bbbb9
images in preview
ZimengXiong Feb 8, 2026
6bee0e2
refactor index.ts
ZimengXiong Feb 8, 2026
fd013de
add tests on refactor
ZimengXiong Feb 8, 2026
70103e1
sign CSRF with cookie, Login rate-limit key hardened against identifi…
ZimengXiong Feb 8, 2026
1117dc5
resolve e2e
ZimengXiong Feb 8, 2026
bb028ef
fix csrf token hardset, remove cookie from localstorage
ZimengXiong Feb 10, 2026
1c71a08
Plan OIDC integration and audit
ZimengXiong Feb 10, 2026
2cbd11c
fix impersonation issues
ZimengXiong Feb 11, 2026
08d2165
fix(dashboard): normalize route id params for express 5 typings
tototomate123 Feb 12, 2026
da13183
add production stuff
tototomate123 Feb 12, 2026
e05edff
fix socket in editor
tototomate123 Feb 12, 2026
6fe2ab3
fix(deploy): align /api routing, socket path, and proxy-aware auth li…
tototomate123 Feb 12, 2026
6061d4a
fix(auth): align frontend password validation with production policy
tototomate123 Feb 12, 2026
fe58cf7
update to excalidraw 0.18.0
tototomate123 Feb 12, 2026
9e248f9
css fix
tototomate123 Feb 12, 2026
12da89b
Update README.md
Feb 13, 2026
75cbe97
feat(collab): restore cross-account sharing and reliable realtime sync
tototomate123 Feb 13, 2026
fd5470a
fix(editor): flush lifecycle saves and recover from version conflicts
tototomate123 Feb 13, 2026
0ffe410
feat(collab): add server-authoritative sync and preview-only updates
tototomate123 Feb 13, 2026
5d613ea
perf(collab): reduce cursor and preview update churn
tototomate123 Feb 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,9 @@ dist
.env
.DS_Store
*.log
backend
frontend/node_modules
frontend/dist
frontend/coverage
frontend/test-results
frontend/playwright-report
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ jobs:
run: |
# Start backend server in background
cd backend
DATABASE_URL="file:${{ github.workspace }}/backend/prisma/e2e-test.db" FRONTEND_URL="http://localhost:5173" npm run dev &
DATABASE_URL="file:${{ github.workspace }}/backend/prisma/e2e-test.db" FRONTEND_URL="http://localhost:6767" npm run dev &
BACKEND_PID=$!
cd ..

Expand All @@ -132,7 +132,7 @@ jobs:
# Wait for frontend to be ready
echo "Waiting for frontend server..."
for i in {1..30}; do
if curl -s http://localhost:5173 > /dev/null; then
if curl -s http://localhost:6767 > /dev/null; then
echo "Frontend is ready!"
break
fi
Expand Down
1 change: 1 addition & 0 deletions .pnpm-store/v10/projects/3d9dc8786854ec3088fcfd3c145118e4
69 changes: 69 additions & 0 deletions FORK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Fork Summary

This fork adds optional security features and UX improvements with **zero breaking changes** and **minimal migration overhead**. All security features are **disabled by default** via feature flags.

## Security Features Added

1. **Password Reset** - Token-based password reset flow (`/auth/password-reset-request`, `/auth/password-reset-confirm`)
2. **Refresh Token Rotation** - Prevents token reuse by rotating refresh tokens on each use
3. **Audit Logging** - Logs security events (logins, password changes, deletions) for compliance

## UX Improvements Added

1. **Profile Page** - View and edit personal information, change password (`/profile`)
2. **Select All Button** - Quick selection of all drawings in current view
3. **Sort Dropdown** - Improved sort controls with icons and separate direction toggle
4. **Auto-hide Header** - Editor header auto-hides to maximize drawing space (with toggle)

## Backward Compatibility

✅ All security features disabled by default
✅ No breaking changes to existing code
✅ Graceful degradation (missing tables don't cause errors)
✅ Optional database migration

## Enable Security Features

Set in `backend/.env`:
```bash
ENABLE_PASSWORD_RESET=true
ENABLE_REFRESH_TOKEN_ROTATION=true
ENABLE_AUDIT_LOGGING=true
```

Then run migration:
```bash
cd backend && npx prisma migrate deploy
```

## Migration Strategy

**For base project:** Keep features disabled (default) - no migration needed, zero risk.

**For this fork:** Enable features via environment variables when ready.

## Database Changes

Migration adds 3 optional tables (only used when features enabled):
- `PasswordResetToken` - For password reset flow
- `RefreshToken` - For token rotation tracking
- `AuditLog` - For security event logging

## Code Changes

### Backend
- Feature flags in `backend/src/config.ts`
- Conditional logic in auth endpoints
- Graceful error handling for missing tables
- New endpoints: `/auth/profile` (PUT), `/auth/change-password` (POST)
- Audit logging utility (`backend/src/utils/audit.ts`)

### Frontend
- Password reset pages (`/reset-password`, `/reset-password-confirm`)
- Profile page (`/profile`)
- Select All button in Dashboard
- Sort dropdown with icons
- Auto-hide header in Editor with toggle
- Updated API client for token rotation

All changes are backward compatible and optional.
58 changes: 57 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)
[![Docker](https://img.shields.io/badge/docker-ready-blue.svg)](https://hub.docker.com)

_Original repo can be found [here](https://github.com/ZimengXiong/ExcaliDash)_

A self-hosted dashboard and organizer for [Excalidraw](https://github.com/excalidraw/excalidraw) with live collaboration features.

## Screenshots
Expand Down Expand Up @@ -99,6 +101,10 @@ docker compose -f docker-compose.prod.yml up -d
# Access the frontend at localhost:6767
```

For single-container deployments, `JWT_SECRET` can be omitted and will be auto-generated and persisted in the backend volume on first start. For portability and all multi-instance deployments, set a fixed `JWT_SECRET` explicitly.

By default, the provided Compose files set `TRUST_PROXY=false` for safer setup. Only set `TRUST_PROXY` to a positive hop count (for example, `1`) when requests always pass through a trusted reverse proxy that correctly sets forwarded headers.

## Docker Build

[Install Docker](https://docs.docker.com/desktop/)
Expand All @@ -121,6 +127,7 @@ docker compose up -d
When running ExcaliDash behind Traefik, Nginx, or another reverse proxy, configure both containers so that API + WebSocket calls resolve correctly:

- `FRONTEND_URL` (backend) must match the public URL that users hit (e.g. `https://excalidash.example.com`). This controls CORS and Socket.IO origin checks. **Supports multiple comma-separated URLs** for accessing from different addresses.
- `TRUST_PROXY` (backend) should be set to `1` when requests pass through one trusted reverse proxy hop (for example: frontend nginx -> backend) and forwarded headers are sanitized. This ensures rate limiting and logging use the real client IP from trusted proxy headers.
- `BACKEND_URL` (frontend) tells the Nginx container how to reach the backend from inside Docker/Kubernetes. Override it if your reverse proxy exposes the backend under a different hostname.

```yaml
Expand All @@ -129,6 +136,8 @@ backend:
environment:
# Single URL
- FRONTEND_URL=https://excalidash.example.com
# Trust exactly one reverse-proxy hop
- TRUST_PROXY=1
# Or multiple URLs (comma-separated) for local + network access
# - FRONTEND_URL=http://localhost:6767,http://192.168.1.100:6767,http://nas.local:6767
frontend:
Expand All @@ -141,7 +150,7 @@ frontend:

### Multi-Container / Kubernetes Deployments

When running multiple backend replicas (e.g., Kubernetes, Docker Swarm, or load-balanced containers), you **must** set the `CSRF_SECRET` environment variable to the same value across all instances.
When running multiple backend replicas (e.g., Kubernetes, Docker Swarm, or load-balanced containers), you **must** set both `JWT_SECRET` and `CSRF_SECRET` to the same values across all instances.

```bash
# Generate a secure secret
Expand All @@ -152,11 +161,37 @@ openssl rand -base64 32
# docker-compose.yml or k8s deployment
backend:
environment:
- JWT_SECRET=your-generated-jwt-secret-here
- CSRF_SECRET=your-generated-secret-here
```

Without this, each container generates its own ephemeral CSRF secret, causing token validation failures when requests are routed to different replicas. Single-container deployments work without this setting.

### Authentication Modes (Local + OIDC)

ExcaliDash supports three auth modes via backend `AUTH_MODE`:

- `local` (default): native email/password login only.
- `hybrid`: native login + OIDC login.
- `oidc_enforced`: OIDC-only login (native login/register disabled).

For OIDC modes (`hybrid` or `oidc_enforced`), set:

```yaml
backend:
environment:
- AUTH_MODE=oidc_enforced
- OIDC_PROVIDER_NAME=Authentik
- OIDC_ISSUER_URL=https://auth.example.com/application/o/excalidash/
- OIDC_CLIENT_ID=your-client-id
- OIDC_CLIENT_SECRET=your-client-secret
- OIDC_REDIRECT_URI=https://excalidash.example.com/api/auth/oidc/callback
- OIDC_SCOPES=openid profile email
```

In `oidc_enforced` mode, unauthenticated users are automatically redirected to `/api/auth/oidc/start`.
Users are linked by `(issuer, sub)` first, then by verified email, and optionally auto-provisioned.

# Development

## Clone the Repository
Expand Down Expand Up @@ -197,6 +232,27 @@ npx prisma db push
npm run dev
```

### Simulate Auth Onboarding (Development)

To simulate first-run authentication choice flows in local development:

```bash
cd ExcaliDash/backend

# Preview what would change (no data modifications)
npm run dev:simulate-auth-onboarding:dry-run

# Simulate "fresh install" onboarding state
# (wipes drawings/collections/libraries and removes non-bootstrap users)
npm run dev:simulate-auth-onboarding:fresh

# Simulate "migration" onboarding state (ensures legacy data exists)
npm run dev:simulate-auth-onboarding:migration
```

After running a simulation while the backend is already running, wait about 5 seconds
(auth mode cache TTL) or restart the backend before refreshing the UI.

## Project Structure

```
Expand Down
46 changes: 6 additions & 40 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,9 @@
CSRF Protection (8a78b2b)
Multi user setup is opt-in, single user by default

- Implemented comprehensive CSRF (Cross-Site Request Forgery) protection for enhanced security
- Added new backend/src/security.ts module for security utilities
- Frontend API layer now handles CSRF tokens automatically
- Added integration tests for CSRF validation
Multi-user support for excalidash
- Admin dashboard
- Password reset, force user password reset (admin only), account lockout recovery
- Rate limits

Upload Progress Indicator (8f9b9b4)
Deprecates .json and .sqlite database backups in favor of .excalidash archives (user scoped, prevents exporting of senstive information). Legacy import is maintained.

- Added a visual upload progress bar when users upload files
- New UploadContext for managing upload state across components
- New UploadStatus component displaying real-time upload progress
- Save status indicator when navigating back from the editor
- Improved error handling and recovery for failed uploads

Bug Fixes

- Fixed broken e2e tests (cae8f3c)
- Replaced deprecated substr() with substring()
- Fixed stale state issues in error handling
- Fixed missing useEffect dependencies
- Fixed CSS class conflicts in progress bar styling
- Added error recovery for save state in Editor

Infrastructure

- Updated docker-compose configurations with new environment variables
- E2E test suite improvements and reliability fixes
- Added Kubernetes deployment note in README

### Kubernetes

A `CSRF_SECRET` environment variable is now required for CSRF protection. Generate a secure 32+ character random string:

```bash
openssl rand -base64 32

Add it to your deployment:
- Docker Compose: Add CSRF_SECRET=<your-secret> to the backend service environment
- Kubernetes: Add to your ConfigMap/Secret and reference in the backend deployment

If not set, the backend will refuse to start.
```
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.3.2
0.4.6
4 changes: 4 additions & 0 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ dist
*.log
prisma/dev.db
prisma/dev.db-journal
src/generated
coverage
*.test.ts
*.spec.ts
26 changes: 25 additions & 1 deletion backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,28 @@
PORT=8000
NODE_ENV=production
DATABASE_URL=file:/app/prisma/dev.db
FRONTEND_URL=http://localhost:6767
FRONTEND_URL=https://draw.louiscreates.com
API_BASE_PATH=/api
# Keep disabled unless traffic always comes through a trusted reverse proxy.
TRUST_PROXY=false
AUTH_MODE=local
JWT_SECRET=change-this-secret-in-production-min-32-chars

# Optional Feature Flags (all default to false for backward compatibility)
# Set to "true" or "1" to enable:
# ENABLE_PASSWORD_RESET=false
# ENABLE_REFRESH_TOKEN_ROTATION=false
# ENABLE_AUDIT_LOGGING=false

# OIDC Configuration (required when AUTH_MODE=hybrid or AUTH_MODE=oidc_enforced)
# OIDC_PROVIDER_NAME=Authentik
# OIDC_ISSUER_URL=https://auth.example.com/application/o/excalidash/
# OIDC_CLIENT_ID=your-client-id
# OIDC_CLIENT_SECRET=your-client-secret
# OIDC_REDIRECT_URI=https://excalidash.example.com/api/auth/oidc/callback
# OIDC_SCOPES=openid profile email
# OIDC_EMAIL_CLAIM=email
# OIDC_EMAIL_VERIFIED_CLAIM=email_verified
# OIDC_REQUIRE_EMAIL_VERIFIED=true
# OIDC_JIT_PROVISIONING=true
# OIDC_FIRST_USER_ADMIN=true
15 changes: 9 additions & 6 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ FROM node:20-alpine AS builder

WORKDIR /app

# Native build deps for modules that may compile from source (e.g., better-sqlite3 on arm64)
RUN apk add --no-cache python3 make g++

# Copy package files
COPY package*.json ./
COPY tsconfig.json ./

# Install dependencies
RUN npm ci
RUN npm ci && npm cache clean --force

# Copy prisma schema
COPY prisma ./prisma/
Expand All @@ -25,7 +28,7 @@ RUN npx tsc
# Production stage
FROM node:20-alpine

# Install OpenSSL for Prisma and su-exec, create non-root user
# Install runtime packages and create non-root user
RUN apk add --no-cache openssl su-exec && \
addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
Expand All @@ -36,7 +39,10 @@ WORKDIR /app
COPY package*.json ./

# Install production dependencies only
RUN npm ci --only=production
RUN apk add --no-cache --virtual .build-deps python3 make g++ && \
npm ci --omit=dev && \
npm cache clean --force && \
apk del .build-deps

# Copy prisma schema and migrations for runtime and hydration template
COPY prisma ./prisma/
Expand All @@ -48,9 +54,6 @@ COPY --from=builder /app/dist ./dist
# Copy the generated Prisma Client from builder to maintain the same structure
COPY --from=builder /app/src/generated ./dist/generated

# Generate Prisma Client in production (updates node_modules)
RUN npx prisma generate

# Create necessary directories (ownership will be set in entrypoint)
RUN mkdir -p /app/uploads /app/prisma

Expand Down
Loading