Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2ce0e6d
chore: Update docker-compose and Makefile configurations
damarinov Feb 12, 2026
a28e849
chore: Remove redundant .env file copy from Makefile
damarinov Feb 12, 2026
fd6da5e
feat: Add Docker build configuration for OpenSign client
damarinov Feb 12, 2026
26311dc
feat: Enhance email configuration and handling in OpenSignServer
damarinov Feb 12, 2026
837ab45
bugfix: docx file upload
damarinov Feb 12, 2026
6809780
feat: Implement global email branding functionality (#4)
damarinov Feb 16, 2026
9eeab62
feat: Implement SSO login and configuration support (#5)
damarinov Feb 19, 2026
d3dab83
feat: Add SDK Sign Requests API implementation (#6)
damarinov Feb 20, 2026
a6a34cb
feat: Update Caddyfile and add createBatchDocs function
damarinov Mar 10, 2026
bdc1a78
Feature/move confidential sso (#7)
nstankov-bg Mar 12, 2026
1720575
refactor: Simplify createBatchDocs function and enhance error handlin…
damarinov Mar 26, 2026
677bf16
feat: per-document callback_url for SDK sign requests (#12) (#13)
nstankov-bg Mar 26, 2026
69819de
feat: add metadata field to SDK sign requests with webhook pass-throu…
damarinov Mar 28, 2026
f4b9283
feat: enhance email sender configuration for SDK sign requests (#15)
damarinov May 8, 2026
3b39c49
docs: expand CLAUDE.md with behavioral guidelines for coding practices
damarinov May 27, 2026
db09a8b
chore: merge upstream v2.39.0 into main
damarinov May 27, 2026
63d7ef7
refactor: remove unused variable for uploaded file size in docxtopdf.js
damarinov May 27, 2026
84e94e1
update: README
damarinov May 27, 2026
e542ee0
docs: update role mapping in README
damarinov May 27, 2026
71f8ec2
feat: implement SSO access token handling in AuthCallback and Login c…
damarinov May 27, 2026
8b1ea28
Merge pull request #16 from Cargofort/docs/update-readme
damarinov Jun 2, 2026
a648299
Merge pull request #17 from Cargofort/fix/SSO-access-token-handling-i…
damarinov Jun 2, 2026
12729df
feat: implement org-scoped access control for OrgAdmins (#18)
damarinov Jun 2, 2026
dbc1df5
chore: pin MongoDB image to 7.0.43
damarinov Sep 17, 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
50 changes: 49 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,40 @@ REACT_APP_APPID=opensign
# Google tag manager container id for configuring GTM on all pages of the app
REACT_APP_GTM=GTM-123N7LD

# OAuth2/OIDC SSO - set both to enable "Sign in with SSO" on login
REACT_APP_OAUTH_ISSUER=https://auth.yourdomain.com/application/o
REACT_APP_OAUTH_CLIENT_ID=your-client-id

# Backend SSO — BFF token exchange (Confidential + PKCE)
SSO_API_URL=https://auth.yourdomain.com/application/o
SSO_USERINFO_PATH=/userinfo/
# Must match REACT_APP_OAUTH_CLIENT_ID
SSO_OAUTH_CLIENT_ID=
SSO_OAUTH_CLIENT_SECRET=

# Restrict SSO to internal users only. If set, user must be in one of these groups.
# 1. In oauth2 provider: Customization → Property Mappings → Create → Scope Mapping
# 2. Scope: "profile" (must match the scope we request)
# 3. Expression: return {"groups": [str(g.name) for g in request.user.ak_groups.all()]}
# 4. Assign this mapping to your OAuth2 Provider for OpenSign
SSO_ALLOWED_GROUPS=opensign-internal,employees
# userinfo claim name (default: groups)
SSO_GROUPS_CLAIM=groups
# userinfo claim for first-time SSO role mapping (Editor, OrgAdmin, default User)
SSO_ROLE_CLAIM=opensign_role

# SDK API (service-to-service) auth via oauth2 provider client_credentials
OAUTH_INTROSPECTION_URL=https://auth.yourdomain.com/application/o/introspect/
OAUTH_CLIENT_ID=your-api-client-id
OAUTH_CLIENT_SECRET=your-api-client-secret
# Admin user OpenSign will act as when creating sign requests.
# Must exist in OpenSign and belong to the tenant that will own these documents.
SDK_ACT_AS_ADMIN_EMAIL=admin@example.com
# SDK-created sign request email display name and Reply-To header.
# The actual From address still comes from SMTP_USER_EMAIL or MAILGUN_SENDER.
SDK_SIGN_REQUEST_FROM_NAME="Cargofort Sign"
SDK_SIGN_REQUEST_REPLY_TO=no-reply@example.com

# Backend ExpressJS config ****************************************************************************************************************************************************************************************
# A 12 character long random app identifier. The value of this should be same as REACT_APP_APPID which is a variable used by Frontend React App.
APP_ID=opensign
Expand All @@ -24,6 +58,8 @@ MONGODB_URI=mongodb://username:password@localhost:27017
PARSE_MOUNT=/app
# Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead)
SERVER_URL=https://app.opensignlabs.com/api/app
# Optional: internal Parse base URL for server-to-server calls (defaults to http://localhost:8080/app)
# INTERNAL_SERVER_URL=http://localhost:8080/app

# Storage config ************************************************************************************************************************************************************************************************
# Digital ocean space name or AWS S3 bucket name for uploading documents
Expand All @@ -45,14 +81,26 @@ DO_REGION=us-west
MAILGUN_API_KEY=XXXXX
MAILGUN_DOMAIN=mail.yourdomain.com
MAILGUN_SENDER=postmaster@mail.yourdomain.com

SMTP_ENABLE=
SMTP_HOST=
SMTP_PORT=
# SMTP auth username (for AWS SES this can be IAM SMTP credentials and not an email)
SMTP_USERNAME=
# Verified sender address shown in the From header
SMTP_USER_EMAIL=
SMTP_PASS=
# Optional SMTP envelope MAIL FROM override (defaults to SMTP_USER_EMAIL)
SMTP_MAIL_FROM=

# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
PFX_BASE64=

# Provide Pass pharse of above PFX or p12 document
PASS_PHRASE=
PASS_PHRASE=

# Webhook notifications ****************************************************************************************************************************
# URL to POST webhook events to. Both WEBHOOK_URL and WEBHOOK_SECRET must be set to enable webhooks.
WEBHOOK_URL=
# Shared secret for HMAC-SHA256 payload signing. The receiver verifies the x-webhook-signature header.
WEBHOOK_SECRET=
14 changes: 12 additions & 2 deletions .env.local_dev
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,17 @@ USE_LOCAL=true
MAILGUN_API_KEY=
MAILGUN_DOMAIN=mail.yourdomain.com
MAILGUN_SENDER=postmaster@mail.yourdomain.com

SMTP_ENABLE=true
SMTP_HOST=smtp.yourhost.com
SMTP_PORT=443
SMTP_PORT=587
# SMTP auth username (for AWS SES this can be IAM SMTP credentials and not an email)
SMTP_USERNAME=AKIA...
# Verified sender address shown in the From header
SMTP_USER_EMAIL=mailer@yourdomain.com
SMTP_PASS=password # if your password includes spaces then write password in single quotes ('asdf pasd asdf bgds').
SMTP_PASS=password
# Optional SMTP envelope MAIL FROM override (defaults to SMTP_USER_EMAIL)
SMTP_MAIL_FROM=


# Base64 encoded PFX or p12 document signing certificate file *********************************************************************************************************************
Expand Down Expand Up @@ -110,5 +116,9 @@ CRUxFgQUDYlgGVxSxuOknhQc256x3++7BDwwMTAhMAkGBSsOAwIaBQAEFFjASdYl
# Provide Pass pharse of above PFX or p12 document
PASS_PHRASE=opensign

# BFF token exchange — Confidential + PKCE (must match REACT_APP_OAUTH_CLIENT_ID)
SSO_OAUTH_CLIENT_ID=
SSO_OAUTH_CLIENT_SECRET=

# (DEPRECATED) This should not be changed if provided; it should be 'opensign'.
APP_ID=opensign
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ yarn-debug.log*
yarn-error.log*
apps/OpenSign/public/mfbuild/*
microfrontends/SignDocuments/build/*
apps/OpenSignServer/files/files/*
apps/OpenSignServer/files/files/*


docker-compose.swarm*.yml
.env.empty
.env.swarm.worktrees
157 changes: 157 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# CLAUDE.md

Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.

**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.

## 1. Think Before Coding

**Don't assume. Don't hide confusion. Surface tradeoffs.**

Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.

## 2. Simplicity First

**Minimum code that solves the problem. Nothing speculative.**

- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.

Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.

## 3. Surgical Changes

**Touch only what you must. Clean up only your own mess.**

When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.

When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.

The test: Every changed line should trace directly to the user's request.

## 4. Goal-Driven Execution

**Define success criteria. Loop until verified.**

Transform tasks into verifiable goals:
- "Add validation" → "Write tests for invalid inputs, then make them pass"
- "Fix the bug" → "Write a test that reproduces it, then make it pass"
- "Refactor X" → "Ensure tests pass before and after"

For multi-step tasks, state a brief plan:
```
1. [Step] → verify: [check]
2. [Step] → verify: [check]
3. [Step] → verify: [check]
```

Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.

---

**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.

## Project Overview

OpenSign is an open-source e-signature platform (DocuSign alternative). This is a **Cargofort fork** that adds SSO/OAuth2 support, global email branding, SDK sign requests API, and batch document processing.

## Architecture

**Monorepo with two independent apps** (no monorepo tooling — each app has its own `package.json`):

- **`apps/OpenSign/`** — React 19 frontend (Vite, Redux Toolkit, Tailwind CSS, react-pdf, pdf-lib)
- **`apps/OpenSignServer/`** — Node.js backend (Express 5 + Parse Server 8.x, MongoDB)

**Parse Server** is the core backend framework. All business logic lives in **cloud functions** registered in `apps/OpenSignServer/cloud/main.js` (73+ functions). The database schema uses Parse classes prefixed with `contracts_` (documents, users, signatures, templates) and `partners_` (tenants, global settings).

**Docker Compose** orchestrates: server (port 8080), client (port 3000), MongoDB (port 27018), Caddy reverse proxy (ports 3001/80/443), and mailcatcher (ports 1025/1080). Caddy routes `/api/*` to the server and everything else to the client.

**File storage**: Local filesystem by default (`USE_LOCAL=true`), or S3/DigitalOcean Spaces in production. Configured via `DO_*` env vars.

## Development Commands

### Container-Only Development

Do **not** run project code, package scripts, installs, tests, linters, builds, database tools, or app servers directly on the host machine. All execution must happen inside Docker containers.

- Use host commands only to manage Docker itself, inspect files, or edit files.
- For container execution, use `docker compose exec <service> ...` in already-running containers.
- For one-off runs, use `docker compose run --rm <service> ...` when a container is not running.
- Do not run local `npm`, `node`, `jasmine`, `vite`, `mongodb-runner`, or similar project tooling on the host.

### Docker (full stack)
```bash
make build # Build and start all containers (requires HOST_URL env var)
make up # Start containers without rebuilding
make down # Stop all containers
make ssh # Shell into the server container
```

### Frontend (`apps/OpenSign/`)
```bash
docker compose exec client npm run dev
docker compose exec client npm run build
docker compose exec client npm test
docker compose exec client npm run test:watch
```

### Backend (`apps/OpenSignServer/`)
```bash
docker compose exec server npm start
docker compose exec server npm run watch
docker compose exec server npm test
docker compose exec server npm run coverage
docker compose exec server npm run lint
docker compose exec server npm run lint-fix
```

### Root
```bash
# Pre-commit hook runs prettier on staged .js files via lint-staged
```

## Environment Setup

Copy `.env.local_dev` to `.env` for local development. Key variables:
- `REACT_APP_SERVERURL` — backend API URL (frontend)
- `REACT_APP_APPID` / `APP_ID` — must be `opensign`
- `MONGODB_URI` — MongoDB connection string
- `MASTER_KEY` — Parse Server master key
- `USE_LOCAL=true` — use local file storage instead of S3
- `PFX_BASE64` / `PASS_PHRASE` — PDF signing certificate

Frontend env vars must be prefixed with `REACT_APP_` (Vite config maps them from `process.env` to work with the CRA-era codebase).

## Fork-Specific Features (SSO, Email Branding, SDK API)

**SSO**: `apps/OpenSignServer/auth/authadapter.js` + `cloud/parsefunction/ssoLogin.js`. Configured via `REACT_APP_OAUTH_ISSUER`, `REACT_APP_OAUTH_CLIENT_ID`, `SSO_API_URL`, `SSO_USERINFO_PATH`, `SSO_ALLOWED_GROUPS`, `SSO_GROUPS_CLAIM`, `SSO_ROLE_CLAIM`.

**Global Email Branding**: `cloud/parsefunction/getGlobalEmailBranding.js` and `updateGlobalEmailBranding.js`. Stored in `partners_GlobalSettings` Parse class.

**SDK Sign Requests**: `cloud/parsefunction/sdkSignRequests.js`. OAuth2 client credentials flow for service-to-service document signing.

**Batch Documents**: `cloud/parsefunction/createBatchDocs.js`. Bulk document creation.

**Webhooks**: `utils/webhook.js` dispatches `document.signed` and `document.completed` events to `WEBHOOK_URL` with HMAC-SHA256 signing (`WEBHOOK_SECRET`). Integration points are in `cloud/parsefunction/pdf/PDF.js`. Fire-and-forget, no retries.

## Key Patterns

- Cloud functions are in `apps/OpenSignServer/cloud/parsefunction/` and registered in `cloud/main.js`
- Custom HTTP routes are in `apps/OpenSignServer/cloud/customRoute/`
- Database migrations are in `apps/OpenSignServer/databases/migrations/`
- Frontend pages/routes are defined in `apps/OpenSign/src/App.jsx`
- Frontend env var access uses `process.env.REACT_APP_*` (Vite translates this at build time)
- Node version requirement: 18, 20, or 22
5 changes: 3 additions & 2 deletions Caddyfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{$HOST_URL} {
reverse_proxy client:3000
handle_path /api/* {
reverse_proxy server:8080
rewrite * {uri}
}
handle {
reverse_proxy client:3000
}
}
13 changes: 8 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
build:
@echo "Building with HOST_URL=${HOST_URL}"
cp .env.local_dev .env
cd apps/OpenSign && cp ../../.env.local_dev .env && npm install && npm run build
HOST_URL=${HOST_URL} docker compose up --build --force-recreate
HOST_URL=${HOST_URL} docker compose up -d --build --force-recreate

run:
up:
@echo "Building with HOST_URL=${HOST_URL}"
cp .env.local_dev .env
docker compose up -d

down:
docker compose down

ssh:
docker compose exec server bash
Loading